@edgemodders/edgedeck-mcp 0.28.0 → 0.29.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/bin/edgedeck.mjs +1 -1
- package/dist/core.js +295 -120
- package/dist/mcp.js +38 -20
- package/package.json +1 -1
package/bin/edgedeck.mjs
CHANGED
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
// edgedeck mcp serve the same verbs as MCP tools over stdio
|
|
24
24
|
//
|
|
25
25
|
// Options: --library <folder> (default %USERPROFILE%\EDGEdeck), --game <folder> (default: the
|
|
26
|
-
// library's remembered game, else the Steam
|
|
26
|
+
// library's remembered game, else the game found through Steam, GOG or Epic), --json.
|
|
27
27
|
|
|
28
28
|
import { createRequire } from "node:module";
|
|
29
29
|
import * as fs from "node:fs";
|
package/dist/core.js
CHANGED
|
@@ -1250,8 +1250,8 @@ var require_semaphore = __commonJS({
|
|
|
1250
1250
|
this._waiting = [];
|
|
1251
1251
|
}
|
|
1252
1252
|
lock(thunk) {
|
|
1253
|
-
return new Promise((
|
|
1254
|
-
this._waiting.push({ thunk, resolve:
|
|
1253
|
+
return new Promise((resolve5, reject) => {
|
|
1254
|
+
this._waiting.push({ thunk, resolve: resolve5, reject });
|
|
1255
1255
|
this.runNext();
|
|
1256
1256
|
});
|
|
1257
1257
|
}
|
|
@@ -2877,9 +2877,9 @@ ${JSON.stringify(message, null, 4)}`);
|
|
|
2877
2877
|
if (typeof cancellationStrategy.sender.enableCancellation === "function") {
|
|
2878
2878
|
cancellationStrategy.sender.enableCancellation(requestMessage);
|
|
2879
2879
|
}
|
|
2880
|
-
return new Promise(async (
|
|
2880
|
+
return new Promise(async (resolve5, reject) => {
|
|
2881
2881
|
const resolveWithCleanup = (r) => {
|
|
2882
|
-
|
|
2882
|
+
resolve5(r);
|
|
2883
2883
|
cancellationStrategy.sender.cleanup(id);
|
|
2884
2884
|
disposable?.dispose();
|
|
2885
2885
|
};
|
|
@@ -3308,10 +3308,10 @@ var require_ril = __commonJS({
|
|
|
3308
3308
|
return api_1.Disposable.create(() => this.stream.off("end", listener));
|
|
3309
3309
|
}
|
|
3310
3310
|
write(data, encoding) {
|
|
3311
|
-
return new Promise((
|
|
3311
|
+
return new Promise((resolve5, reject) => {
|
|
3312
3312
|
const callback = (error) => {
|
|
3313
3313
|
if (error === void 0 || error === null) {
|
|
3314
|
-
|
|
3314
|
+
resolve5();
|
|
3315
3315
|
} else {
|
|
3316
3316
|
reject(error);
|
|
3317
3317
|
}
|
|
@@ -3447,9 +3447,9 @@ var require_main = __commonJS({
|
|
|
3447
3447
|
exports2.createMessageConnection = createMessageConnection2;
|
|
3448
3448
|
var ril_1 = __importDefault(require_ril());
|
|
3449
3449
|
ril_1.default.install();
|
|
3450
|
-
var
|
|
3450
|
+
var path15 = __importStar(require("path"));
|
|
3451
3451
|
var os4 = __importStar(require("os"));
|
|
3452
|
-
var
|
|
3452
|
+
var fs16 = __importStar(require("fs"));
|
|
3453
3453
|
var crypto_1 = require("crypto");
|
|
3454
3454
|
var net_1 = require("net");
|
|
3455
3455
|
var api_1 = require_api();
|
|
@@ -3590,7 +3590,7 @@ var require_main = __commonJS({
|
|
|
3590
3590
|
}
|
|
3591
3591
|
let randomLength = 32;
|
|
3592
3592
|
const fixedLength = "/lsp-.sock".length;
|
|
3593
|
-
const tmpDir =
|
|
3593
|
+
const tmpDir = fs16.realpathSync(XDG_RUNTIME_DIR ?? os4.tmpdir());
|
|
3594
3594
|
const limit = safeIpcPathLengths.get(process.platform);
|
|
3595
3595
|
if (limit !== void 0) {
|
|
3596
3596
|
randomLength = Math.min(limit - tmpDir.length - fixedLength, randomLength);
|
|
@@ -3599,14 +3599,14 @@ var require_main = __commonJS({
|
|
|
3599
3599
|
throw new Error(`Unable to generate a random pipe name with ${randomLength} characters.`);
|
|
3600
3600
|
}
|
|
3601
3601
|
const randomSuffix = (0, crypto_1.randomBytes)(Math.floor(randomLength / 2)).toString("hex");
|
|
3602
|
-
return
|
|
3602
|
+
return path15.join(tmpDir, `lsp-${randomSuffix}.sock`);
|
|
3603
3603
|
}
|
|
3604
3604
|
function createClientPipeTransport(pipeName, encoding = "utf-8") {
|
|
3605
3605
|
let connectResolve;
|
|
3606
|
-
const connected = new Promise((
|
|
3607
|
-
connectResolve =
|
|
3606
|
+
const connected = new Promise((resolve5, _reject) => {
|
|
3607
|
+
connectResolve = resolve5;
|
|
3608
3608
|
});
|
|
3609
|
-
return new Promise((
|
|
3609
|
+
return new Promise((resolve5, reject) => {
|
|
3610
3610
|
const server = (0, net_1.createServer)((socket) => {
|
|
3611
3611
|
server.close();
|
|
3612
3612
|
connectResolve([
|
|
@@ -3617,7 +3617,7 @@ var require_main = __commonJS({
|
|
|
3617
3617
|
server.on("error", reject);
|
|
3618
3618
|
server.listen(pipeName, () => {
|
|
3619
3619
|
server.removeListener("error", reject);
|
|
3620
|
-
|
|
3620
|
+
resolve5({
|
|
3621
3621
|
onConnected: () => {
|
|
3622
3622
|
return connected;
|
|
3623
3623
|
}
|
|
@@ -3634,10 +3634,10 @@ var require_main = __commonJS({
|
|
|
3634
3634
|
}
|
|
3635
3635
|
function createClientSocketTransport(port, encoding = "utf-8") {
|
|
3636
3636
|
let connectResolve;
|
|
3637
|
-
const connected = new Promise((
|
|
3638
|
-
connectResolve =
|
|
3637
|
+
const connected = new Promise((resolve5, _reject) => {
|
|
3638
|
+
connectResolve = resolve5;
|
|
3639
3639
|
});
|
|
3640
|
-
return new Promise((
|
|
3640
|
+
return new Promise((resolve5, reject) => {
|
|
3641
3641
|
const server = (0, net_1.createServer)((socket) => {
|
|
3642
3642
|
server.close();
|
|
3643
3643
|
connectResolve([
|
|
@@ -3654,7 +3654,7 @@ var require_main = __commonJS({
|
|
|
3654
3654
|
return;
|
|
3655
3655
|
}
|
|
3656
3656
|
const boundPort = address.port;
|
|
3657
|
-
|
|
3657
|
+
resolve5({
|
|
3658
3658
|
port: () => boundPort,
|
|
3659
3659
|
onConnected: () => {
|
|
3660
3660
|
return connected;
|
|
@@ -3700,6 +3700,7 @@ __export(core_exports, {
|
|
|
3700
3700
|
BridgeClient: () => BridgeClient,
|
|
3701
3701
|
BridgeRepo: () => BridgeRepo,
|
|
3702
3702
|
DefaultLoadout: () => DefaultLoadout,
|
|
3703
|
+
FrameworkSources: () => FrameworkSources,
|
|
3703
3704
|
FrameworksSection: () => FrameworksSection,
|
|
3704
3705
|
KnownModNames: () => KnownModNames,
|
|
3705
3706
|
ManifestName: () => ManifestName,
|
|
@@ -3737,6 +3738,7 @@ __export(core_exports, {
|
|
|
3737
3738
|
deleteLoadout: () => deleteLoadout,
|
|
3738
3739
|
deleteSection: () => deleteSection,
|
|
3739
3740
|
deploy: () => deploy,
|
|
3741
|
+
deployMethod: () => deployMethod,
|
|
3740
3742
|
deployRedmod: () => deployRedmod,
|
|
3741
3743
|
deployReportsFolder: () => deployReportsFolder,
|
|
3742
3744
|
deployedRedmodPackages: () => deployedRedmodPackages,
|
|
@@ -3768,12 +3770,15 @@ __export(core_exports, {
|
|
|
3768
3770
|
formatBytes: () => formatBytes,
|
|
3769
3771
|
forwarderCommand: () => forwarderCommand,
|
|
3770
3772
|
frameworkDetail: () => frameworkDetail,
|
|
3773
|
+
frameworkFamily: () => frameworkFamily,
|
|
3774
|
+
frameworkStates: () => frameworkStates,
|
|
3771
3775
|
frameworkSummary: () => frameworkSummary,
|
|
3772
3776
|
gameExe: () => gameExe,
|
|
3773
3777
|
gameRunning: () => gameRunning,
|
|
3774
3778
|
gameVersion: () => gameVersion,
|
|
3775
3779
|
guessName: () => guessName,
|
|
3776
3780
|
hasWebSocket: () => hasWebSocket,
|
|
3781
|
+
holdsLibrary: () => holdsLibrary,
|
|
3777
3782
|
idFor: () => idFor,
|
|
3778
3783
|
importFromVortex: () => importFromVortex,
|
|
3779
3784
|
importLoadout: () => importLoadout,
|
|
@@ -3785,6 +3790,7 @@ __export(core_exports, {
|
|
|
3785
3790
|
isGameFolder: () => isGameFolder,
|
|
3786
3791
|
keepDownload: () => keepDownload,
|
|
3787
3792
|
latestBridgeRelease: () => latestBridgeRelease,
|
|
3793
|
+
latestFrameworkRelease: () => latestFrameworkRelease,
|
|
3788
3794
|
launchTarget: () => launchTarget,
|
|
3789
3795
|
libraryFolder: () => libraryFolder,
|
|
3790
3796
|
librarySize: () => librarySize,
|
|
@@ -3797,6 +3803,7 @@ __export(core_exports, {
|
|
|
3797
3803
|
lostFiles: () => lostFiles,
|
|
3798
3804
|
makeActive: () => makeActive,
|
|
3799
3805
|
md5Of: () => md5Of,
|
|
3806
|
+
missingFrameworks: () => missingFrameworks,
|
|
3800
3807
|
modDeployPaths: () => modDeployPaths,
|
|
3801
3808
|
modFolder: () => modFolder,
|
|
3802
3809
|
modPageUrl: () => modPageUrl,
|
|
@@ -3805,8 +3812,10 @@ __export(core_exports, {
|
|
|
3805
3812
|
modsRemoved: () => modsRemoved,
|
|
3806
3813
|
modsTouched: () => modsTouched,
|
|
3807
3814
|
moveInSection: () => moveInSection,
|
|
3815
|
+
moveLibrary: () => moveLibrary,
|
|
3808
3816
|
moveMod: () => moveMod,
|
|
3809
3817
|
moveSection: () => moveSection,
|
|
3818
|
+
moveTarget: () => moveTarget,
|
|
3810
3819
|
needsRefresh: () => needsRefresh,
|
|
3811
3820
|
newLoadout: () => newLoadout,
|
|
3812
3821
|
newestFile: () => newestFile,
|
|
@@ -3836,6 +3845,7 @@ __export(core_exports, {
|
|
|
3836
3845
|
renameFamily: () => renameFamily,
|
|
3837
3846
|
renameLoadout: () => renameLoadout,
|
|
3838
3847
|
renameSection: () => renameSection,
|
|
3848
|
+
sameDrive: () => sameDrive,
|
|
3839
3849
|
sameNameMod: () => sameNameMod,
|
|
3840
3850
|
saveLibrary: () => saveLibrary,
|
|
3841
3851
|
saveLoadout: () => saveLoadout,
|
|
@@ -4039,8 +4049,8 @@ var NexusClient = class {
|
|
|
4039
4049
|
get usesToken() {
|
|
4040
4050
|
return "bearer" in this.auth;
|
|
4041
4051
|
}
|
|
4042
|
-
async get(
|
|
4043
|
-
const res = await fetch(`${this.options.api ?? NexusApi}${
|
|
4052
|
+
async get(path15, retried = false) {
|
|
4053
|
+
const res = await fetch(`${this.options.api ?? NexusApi}${path15}`, {
|
|
4044
4054
|
headers: {
|
|
4045
4055
|
..."bearer" in this.auth ? { authorization: `Bearer ${this.auth.bearer}` } : { apikey: this.auth.apiKey },
|
|
4046
4056
|
accept: "application/json",
|
|
@@ -4057,7 +4067,7 @@ var NexusClient = class {
|
|
|
4057
4067
|
const bearer = await this.options.refresh();
|
|
4058
4068
|
if (bearer) {
|
|
4059
4069
|
this.auth = { bearer };
|
|
4060
|
-
return this.get(
|
|
4070
|
+
return this.get(path15, true);
|
|
4061
4071
|
}
|
|
4062
4072
|
}
|
|
4063
4073
|
if (!res.ok) {
|
|
@@ -4163,11 +4173,11 @@ async function installDotnetRuntime(log) {
|
|
|
4163
4173
|
try {
|
|
4164
4174
|
await download(DotnetInstallScript, script);
|
|
4165
4175
|
log(`Installing the .NET 10 runtime into ${root}`);
|
|
4166
|
-
await new Promise((
|
|
4176
|
+
await new Promise((resolve5, reject) => {
|
|
4167
4177
|
(0, import_node_child_process3.execFile)("powershell.exe", ["-NoProfile", "-ExecutionPolicy", "Bypass", "-File", script, "-Channel", "10.0", "-Runtime", "dotnet", "-InstallDir", root, "-NoPath"], { windowsHide: true, maxBuffer: 1 << 24 }, (err, stdout, stderr) => {
|
|
4168
4178
|
for (const line of `${stdout}
|
|
4169
4179
|
${stderr}`.split(/\r?\n/)) if (line.trim()) log(` ${line.trim()}`);
|
|
4170
|
-
err ? reject(new Error(`dotnet-install failed: ${err.message}`)) :
|
|
4180
|
+
err ? reject(new Error(`dotnet-install failed: ${err.message}`)) : resolve5();
|
|
4171
4181
|
});
|
|
4172
4182
|
});
|
|
4173
4183
|
} finally {
|
|
@@ -4699,6 +4709,21 @@ function canHardlink(libraryRoot, gamePath) {
|
|
|
4699
4709
|
return false;
|
|
4700
4710
|
}
|
|
4701
4711
|
}
|
|
4712
|
+
var hardlinkable = /* @__PURE__ */ new Map();
|
|
4713
|
+
function deployMethod(libraryRoot, gamePath, current) {
|
|
4714
|
+
try {
|
|
4715
|
+
if (fs8.statSync(libraryRoot).dev !== fs8.statSync(gamePath).dev) return "copy";
|
|
4716
|
+
} catch {
|
|
4717
|
+
return current ?? "copy";
|
|
4718
|
+
}
|
|
4719
|
+
if (current === "link") return "link";
|
|
4720
|
+
const k = `${path7.resolve(libraryRoot).toLowerCase()}|${path7.resolve(gamePath).toLowerCase()}`;
|
|
4721
|
+
if (!hardlinkable.has(k)) hardlinkable.set(k, canHardlink(libraryRoot, gamePath));
|
|
4722
|
+
return hardlinkable.get(k) ? "link" : "copy";
|
|
4723
|
+
}
|
|
4724
|
+
function otherMethod(entry, method) {
|
|
4725
|
+
return method === "link" !== !!entry.ino;
|
|
4726
|
+
}
|
|
4702
4727
|
function sourceReplaced(entry, method, d) {
|
|
4703
4728
|
if (d.content !== void 0) return false;
|
|
4704
4729
|
const src = identity(d.src);
|
|
@@ -4706,10 +4731,10 @@ function sourceReplaced(entry, method, d) {
|
|
|
4706
4731
|
if (method === "link" && entry.ino) return src.ino !== entry.ino;
|
|
4707
4732
|
return src.size !== entry.size || Math.abs(src.mtimeMs - entry.mtimeMs) >= 2e3;
|
|
4708
4733
|
}
|
|
4709
|
-
function stillOurs(entry,
|
|
4734
|
+
function stillOurs(entry, _method, gamePath) {
|
|
4710
4735
|
const id = identity(gameFile(gamePath, entry.path));
|
|
4711
4736
|
if (!id) return "missing";
|
|
4712
|
-
if (
|
|
4737
|
+
if (entry.ino) {
|
|
4713
4738
|
if (id.ino !== entry.ino) return "changed";
|
|
4714
4739
|
if (id.nlink < 2 && entry.mod !== Generated) return "sourceMissing";
|
|
4715
4740
|
return "ok";
|
|
@@ -4725,7 +4750,7 @@ function generatedDiffers(file, content) {
|
|
|
4725
4750
|
}
|
|
4726
4751
|
function plan(lib, gamePath) {
|
|
4727
4752
|
const manifest = readManifest(gamePath);
|
|
4728
|
-
const method =
|
|
4753
|
+
const method = deployMethod(lib.root, gamePath, manifest?.method);
|
|
4729
4754
|
const desired = /* @__PURE__ */ new Map();
|
|
4730
4755
|
const conflicts = /* @__PURE__ */ new Map();
|
|
4731
4756
|
for (const mod of lib.mods) {
|
|
@@ -4756,6 +4781,7 @@ function plan(lib, gamePath) {
|
|
|
4756
4781
|
if (!e || (d.mod?.id ?? Generated) !== e.mod) return true;
|
|
4757
4782
|
if (d.content !== void 0 && generatedDiffers(gameFile(gamePath, d.rel), d.content)) return true;
|
|
4758
4783
|
if (stillOurs(e, method, gamePath) !== "ok") return true;
|
|
4784
|
+
if (d.content === void 0 && otherMethod(e, method)) return true;
|
|
4759
4785
|
return sourceReplaced(e, method, d);
|
|
4760
4786
|
});
|
|
4761
4787
|
return { method, desired: [...desired.values()], conflicts: [...conflicts.values()], stale, add, vortex: vortexManages(gamePath) };
|
|
@@ -4890,6 +4916,7 @@ function deploy(lib, gamePath, decisions = /* @__PURE__ */ new Map(), progress,
|
|
|
4890
4916
|
const p = plan(lib, gamePath);
|
|
4891
4917
|
const manifest = readManifest(gamePath) ?? { version: 1, library: lib.root, gamePath, method: p.method, time: "", files: [], dirs: [] };
|
|
4892
4918
|
manifest.library = lib.root;
|
|
4919
|
+
manifest.method = p.method;
|
|
4893
4920
|
const result = { linked: 0, removed: 0, kept: 0, backedUp: 0, method: manifest.method, skipped: [], changes: [], modsInGame: 0, lost: lostFiles(p) };
|
|
4894
4921
|
const entries = new Map(manifest.files.map((e) => [key(e.path), e]));
|
|
4895
4922
|
const scope = only ? modDeployPaths(gamePath, p, only) : void 0;
|
|
@@ -5016,6 +5043,7 @@ function pendingChanges(lib, gamePath, p) {
|
|
|
5016
5043
|
const st = stillOurs(e, p.method, gamePath);
|
|
5017
5044
|
if (st === "missing") out.push({ path: d.rel, mod: id, kind: "restore", detail: "missing from the game folder" });
|
|
5018
5045
|
else if (st === "changed") out.push({ path: d.rel, mod: id, kind: "restore", detail: "changed in the game folder; deploy asks whether to keep it" });
|
|
5046
|
+
else if (d.content === void 0 && otherMethod(e, p.method)) out.push({ path: d.rel, mod: id, kind: "relink", detail: p.method === "link" ? "the library is on the game's drive now: a hardlink instead of a copy" : "the library is on another drive now: a copy instead of a hardlink" });
|
|
5019
5047
|
else out.push({ path: d.rel, mod: id, kind: "relink", detail: d.content !== void 0 ? "the generated content changed (load order)" : "the library file was replaced (new version or rebuild)" });
|
|
5020
5048
|
}
|
|
5021
5049
|
}
|
|
@@ -5192,28 +5220,165 @@ function frameworkDetail(c) {
|
|
|
5192
5220
|
return lines;
|
|
5193
5221
|
}
|
|
5194
5222
|
|
|
5195
|
-
// src/
|
|
5223
|
+
// src/frameworkGet.ts
|
|
5224
|
+
var FrameworkSources = [
|
|
5225
|
+
{ name: "RED4ext", nexusId: 2380, repo: "WopsS/RED4ext", asset: /^red4ext-[\d.]+\.zip$/i, signature: "red4ext/RED4ext.dll" },
|
|
5226
|
+
{ name: "Cyber Engine Tweaks", nexusId: 107, repo: "maximegmd/CyberEngineTweaks", asset: /^cet_[\d_]+\.zip$/i, signature: "bin/x64/plugins/cyber_engine_tweaks.asi" },
|
|
5227
|
+
{ name: "redscript", nexusId: 1511, repo: "jac3km4/redscript", asset: /^redscript-v?[\d.]+-windows\.zip$/i, signature: "engine/tools/scc.exe" },
|
|
5228
|
+
{ name: "ArchiveXL", nexusId: 4198, repo: "psiberx/cp2077-archive-xl", asset: /^ArchiveXL-[\d.]+\.zip$/i, signature: "red4ext/plugins/ArchiveXL/ArchiveXL.dll" },
|
|
5229
|
+
{ name: "TweakXL", nexusId: 4197, repo: "psiberx/cp2077-tweak-xl", asset: /^TweakXL-[\d.]+\.zip$/i, signature: "red4ext/plugins/TweakXL/TweakXL.dll" },
|
|
5230
|
+
{ name: "Codeware", nexusId: 7780, repo: "psiberx/cp2077-codeware", asset: /^Codeware-[\d.]+\.zip$/i, signature: "red4ext/plugins/Codeware/Codeware.dll" }
|
|
5231
|
+
];
|
|
5232
|
+
function frameworkFamily(lib, f) {
|
|
5233
|
+
const sig = f.signature.toLowerCase();
|
|
5234
|
+
return families(lib).find((fam) => fam.nexusId === f.nexusId || fam.mods.some((m) => m.files.some((x) => x.toLowerCase() === sig)));
|
|
5235
|
+
}
|
|
5236
|
+
function missingFrameworks(lib) {
|
|
5237
|
+
return FrameworkSources.filter((f) => !frameworkFamily(lib, f));
|
|
5238
|
+
}
|
|
5239
|
+
async function latestFrameworkRelease(f, fetchImpl = fetch, api = "https://api.github.com") {
|
|
5240
|
+
const headers = { Accept: "application/vnd.github+json", "User-Agent": "EDGEdeck" };
|
|
5241
|
+
if (process.env.GITHUB_TOKEN) headers.Authorization = `Bearer ${process.env.GITHUB_TOKEN}`;
|
|
5242
|
+
const res = await fetchImpl(`${api}/repos/${f.repo}/releases/latest`, { headers });
|
|
5243
|
+
if (!res.ok) throw new Error(res.status === 403 ? "GitHub's limit for calls without an account was reached; try again within the hour." : `GitHub answered ${res.status}.`);
|
|
5244
|
+
const rel = await res.json();
|
|
5245
|
+
const asset = rel.assets.find((a) => f.asset.test(a.name));
|
|
5246
|
+
if (!asset) throw new Error(`release ${rel.tag_name} has no file named like ${f.asset.source}.`);
|
|
5247
|
+
return { version: rel.tag_name.replace(/^v/i, ""), tag: rel.tag_name, assetName: asset.name, url: asset.browser_download_url, size: asset.size, page: rel.html_url };
|
|
5248
|
+
}
|
|
5249
|
+
async function frameworkStates(lib, fetchImpl, api) {
|
|
5250
|
+
return Promise.all(
|
|
5251
|
+
FrameworkSources.map(async (source) => {
|
|
5252
|
+
const family = frameworkFamily(lib, source);
|
|
5253
|
+
try {
|
|
5254
|
+
const release = await latestFrameworkRelease(source, fetchImpl, api);
|
|
5255
|
+
const newest2 = family?.mods[0]?.version ?? "";
|
|
5256
|
+
return { source, family, release, wanted: !family || !!newest2 && versionNewer(release.version, newest2) };
|
|
5257
|
+
} catch (err) {
|
|
5258
|
+
return { source, family, error: err instanceof Error ? err.message : String(err), wanted: !family };
|
|
5259
|
+
}
|
|
5260
|
+
})
|
|
5261
|
+
);
|
|
5262
|
+
}
|
|
5263
|
+
|
|
5264
|
+
// src/libraryMove.ts
|
|
5196
5265
|
var fs9 = __toESM(require("node:fs"));
|
|
5197
5266
|
var path8 = __toESM(require("node:path"));
|
|
5267
|
+
function holdsLibrary(dir) {
|
|
5268
|
+
return fs9.existsSync(path8.join(dir, "library.json")) || fs9.existsSync(path8.join(dir, "library"));
|
|
5269
|
+
}
|
|
5270
|
+
function moveTarget(picked) {
|
|
5271
|
+
if (!fs9.existsSync(picked)) return picked;
|
|
5272
|
+
return fs9.readdirSync(picked).length ? path8.join(picked, "EDGEdeck") : picked;
|
|
5273
|
+
}
|
|
5274
|
+
function volume(p) {
|
|
5275
|
+
let dir = path8.resolve(p);
|
|
5276
|
+
for (; ; ) {
|
|
5277
|
+
try {
|
|
5278
|
+
return fs9.statSync(dir).dev;
|
|
5279
|
+
} catch {
|
|
5280
|
+
const up = path8.dirname(dir);
|
|
5281
|
+
if (up === dir) return void 0;
|
|
5282
|
+
dir = up;
|
|
5283
|
+
}
|
|
5284
|
+
}
|
|
5285
|
+
}
|
|
5286
|
+
function sameDrive(a, b) {
|
|
5287
|
+
const va = volume(a);
|
|
5288
|
+
const vb = volume(b);
|
|
5289
|
+
return va !== void 0 && va === vb;
|
|
5290
|
+
}
|
|
5291
|
+
function inside(child, parent) {
|
|
5292
|
+
const rel = path8.relative(parent, child);
|
|
5293
|
+
return !!rel && !rel.startsWith("..") && !path8.isAbsolute(rel);
|
|
5294
|
+
}
|
|
5295
|
+
function listFiles(root) {
|
|
5296
|
+
const out = [];
|
|
5297
|
+
const walk2 = (dir) => {
|
|
5298
|
+
for (const e of fs9.readdirSync(dir, { withFileTypes: true })) {
|
|
5299
|
+
const abs = path8.join(dir, e.name);
|
|
5300
|
+
if (e.isDirectory()) walk2(abs);
|
|
5301
|
+
else if (e.isFile()) out.push({ rel: path8.relative(root, abs), size: fs9.statSync(abs).size });
|
|
5302
|
+
}
|
|
5303
|
+
};
|
|
5304
|
+
walk2(root);
|
|
5305
|
+
return out;
|
|
5306
|
+
}
|
|
5307
|
+
async function moveLibrary(from, to, progress, cancelled) {
|
|
5308
|
+
const src = path8.resolve(from);
|
|
5309
|
+
const dest = path8.resolve(to);
|
|
5310
|
+
if (src.toLowerCase() === dest.toLowerCase()) throw new Error(`The library is already in ${dest}.`);
|
|
5311
|
+
if (inside(dest, src)) throw new Error(`${dest} is inside the library itself; pick a folder outside it.`);
|
|
5312
|
+
if (!holdsLibrary(src)) throw new Error(`${src} does not hold a library.`);
|
|
5313
|
+
const existed = fs9.existsSync(dest);
|
|
5314
|
+
if (existed && fs9.readdirSync(dest).length) throw new Error(`${dest} is not empty.`);
|
|
5315
|
+
if (sameDrive(src, dest)) {
|
|
5316
|
+
const files2 = listFiles(src);
|
|
5317
|
+
fs9.mkdirSync(path8.dirname(dest), { recursive: true });
|
|
5318
|
+
if (existed) fs9.rmdirSync(dest);
|
|
5319
|
+
fs9.renameSync(src, dest);
|
|
5320
|
+
return { how: "rename", files: files2.length, bytes: files2.reduce((n, f) => n + f.size, 0), leftBehind: [] };
|
|
5321
|
+
}
|
|
5322
|
+
const files = listFiles(src);
|
|
5323
|
+
const totalBytes = files.reduce((n, f) => n + f.size, 0);
|
|
5324
|
+
const done = { files: 0, totalFiles: files.length, bytes: 0, totalBytes };
|
|
5325
|
+
try {
|
|
5326
|
+
fs9.mkdirSync(dest, { recursive: true });
|
|
5327
|
+
for (const f of files) {
|
|
5328
|
+
if (cancelled?.()) throw new Error("Cancelled: the library stays where it was.");
|
|
5329
|
+
const a = path8.join(src, f.rel);
|
|
5330
|
+
const b = path8.join(dest, f.rel);
|
|
5331
|
+
fs9.mkdirSync(path8.dirname(b), { recursive: true });
|
|
5332
|
+
await fs9.promises.copyFile(a, b);
|
|
5333
|
+
const st = fs9.statSync(a);
|
|
5334
|
+
fs9.utimesSync(b, st.atime, st.mtime);
|
|
5335
|
+
if (fs9.statSync(b).size !== f.size) throw new Error(`${f.rel} did not copy whole.`);
|
|
5336
|
+
done.files++;
|
|
5337
|
+
done.bytes += f.size;
|
|
5338
|
+
progress?.({ ...done });
|
|
5339
|
+
}
|
|
5340
|
+
} catch (err) {
|
|
5341
|
+
fs9.rmSync(dest, { recursive: true, force: true });
|
|
5342
|
+
if (existed) fs9.mkdirSync(dest, { recursive: true });
|
|
5343
|
+
throw err;
|
|
5344
|
+
}
|
|
5345
|
+
const leftBehind = [];
|
|
5346
|
+
for (const f of files) {
|
|
5347
|
+
try {
|
|
5348
|
+
fs9.rmSync(path8.join(src, f.rel), { force: true });
|
|
5349
|
+
} catch {
|
|
5350
|
+
leftBehind.push(path8.join(src, f.rel));
|
|
5351
|
+
}
|
|
5352
|
+
}
|
|
5353
|
+
try {
|
|
5354
|
+
fs9.rmSync(src, { recursive: true, force: true });
|
|
5355
|
+
} catch {
|
|
5356
|
+
}
|
|
5357
|
+
return { how: "copy", files: files.length, bytes: totalBytes, leftBehind };
|
|
5358
|
+
}
|
|
5359
|
+
|
|
5360
|
+
// src/loadouts.ts
|
|
5361
|
+
var fs10 = __toESM(require("node:fs"));
|
|
5362
|
+
var path9 = __toESM(require("node:path"));
|
|
5198
5363
|
var DefaultLoadout = "Default";
|
|
5199
5364
|
function loadoutsFolder(root) {
|
|
5200
|
-
return
|
|
5365
|
+
return path9.join(root, "loadouts");
|
|
5201
5366
|
}
|
|
5202
5367
|
function slug(name) {
|
|
5203
5368
|
return name.replace(/[<>:"/\\|?*]+/g, "-").trim() || "loadout";
|
|
5204
5369
|
}
|
|
5205
5370
|
function readLoadout(file) {
|
|
5206
|
-
return JSON.parse(
|
|
5371
|
+
return JSON.parse(fs10.readFileSync(file, "utf8"));
|
|
5207
5372
|
}
|
|
5208
5373
|
function listLoadouts(root) {
|
|
5209
5374
|
const dir = loadoutsFolder(root);
|
|
5210
|
-
if (!
|
|
5375
|
+
if (!fs10.existsSync(dir)) return [];
|
|
5211
5376
|
const out = [];
|
|
5212
|
-
for (const f of
|
|
5377
|
+
for (const f of fs10.readdirSync(dir)) {
|
|
5213
5378
|
if (!f.toLowerCase().endsWith(".json")) continue;
|
|
5214
5379
|
try {
|
|
5215
|
-
const l = readLoadout(
|
|
5216
|
-
out.push({ name: l.name, file:
|
|
5380
|
+
const l = readLoadout(path9.join(dir, f));
|
|
5381
|
+
out.push({ name: l.name, file: path9.join(dir, f), saved: l.saved, mods: l.mods.length, on: l.mods.filter((m) => m.enabled).length });
|
|
5217
5382
|
} catch {
|
|
5218
5383
|
}
|
|
5219
5384
|
}
|
|
@@ -5242,13 +5407,13 @@ function entryFor(f, mod, enabled) {
|
|
|
5242
5407
|
return { family: f.key, id: mod.id, name: f.name, version: mod.version, enabled, nexusModId: f.nexusId };
|
|
5243
5408
|
}
|
|
5244
5409
|
function writeLoadout(root, loadout, file) {
|
|
5245
|
-
|
|
5410
|
+
fs10.mkdirSync(loadoutsFolder(root), { recursive: true });
|
|
5246
5411
|
let target = file;
|
|
5247
5412
|
if (!target) {
|
|
5248
|
-
target =
|
|
5249
|
-
for (let n = 2;
|
|
5413
|
+
target = path9.join(loadoutsFolder(root), `${slug(loadout.name)}.json`);
|
|
5414
|
+
for (let n = 2; fs10.existsSync(target); n++) target = path9.join(loadoutsFolder(root), `${slug(loadout.name)}-${n}.json`);
|
|
5250
5415
|
}
|
|
5251
|
-
|
|
5416
|
+
fs10.writeFileSync(target, JSON.stringify(loadout, null, 2) + "\n");
|
|
5252
5417
|
return target;
|
|
5253
5418
|
}
|
|
5254
5419
|
function saveLoadout(lib, name, gameVersion2) {
|
|
@@ -5352,7 +5517,7 @@ function renameLoadout(lib, from, to) {
|
|
|
5352
5517
|
if (!info) throw new Error(`There is no loadout called ${from}.`);
|
|
5353
5518
|
const loadout = readLoadout(info.file);
|
|
5354
5519
|
loadout.name = to.trim();
|
|
5355
|
-
|
|
5520
|
+
fs10.rmSync(info.file);
|
|
5356
5521
|
writeLoadout(lib.root, loadout);
|
|
5357
5522
|
if (lib.info.activeLoadout === from) {
|
|
5358
5523
|
lib.info.activeLoadout = to.trim();
|
|
@@ -5362,18 +5527,18 @@ function renameLoadout(lib, from, to) {
|
|
|
5362
5527
|
function deleteLoadout(lib, name) {
|
|
5363
5528
|
if (lib.info.activeLoadout === name) throw new Error(`${name} is the active loadout; make another one active first.`);
|
|
5364
5529
|
const info = findLoadout(lib.root, name);
|
|
5365
|
-
if (info)
|
|
5530
|
+
if (info) fs10.rmSync(info.file);
|
|
5366
5531
|
}
|
|
5367
5532
|
function exportLoadout(lib, name, dest, gameVersion2) {
|
|
5368
5533
|
if (lib.info.activeLoadout === name) syncActiveLoadout(lib, gameVersion2);
|
|
5369
5534
|
const info = findLoadout(lib.root, name);
|
|
5370
5535
|
if (!info) throw new Error(`There is no loadout called ${name}.`);
|
|
5371
|
-
|
|
5536
|
+
fs10.copyFileSync(info.file, dest);
|
|
5372
5537
|
}
|
|
5373
5538
|
function importLoadout(lib, file) {
|
|
5374
5539
|
const loadout = readLoadout(file);
|
|
5375
|
-
if (!Array.isArray(loadout.mods)) throw new Error(`${
|
|
5376
|
-
let name = (loadout.name ||
|
|
5540
|
+
if (!Array.isArray(loadout.mods)) throw new Error(`${path9.basename(file)} is not a EDGEdeck loadout.`);
|
|
5541
|
+
let name = (loadout.name || path9.basename(file, ".json")).trim();
|
|
5377
5542
|
for (let n = 2; findLoadout(lib.root, name); n++) name = `${(loadout.name || "Loadout").trim()} (${n})`;
|
|
5378
5543
|
writeLoadout(lib.root, { ...loadout, name, saved: (/* @__PURE__ */ new Date()).toISOString() });
|
|
5379
5544
|
return name;
|
|
@@ -5398,7 +5563,7 @@ function signIn(options) {
|
|
|
5398
5563
|
let done = false;
|
|
5399
5564
|
let attempts = 0;
|
|
5400
5565
|
let socket;
|
|
5401
|
-
return new Promise((
|
|
5566
|
+
return new Promise((resolve5, reject) => {
|
|
5402
5567
|
const finish = (err, key2) => {
|
|
5403
5568
|
if (done) return;
|
|
5404
5569
|
done = true;
|
|
@@ -5408,7 +5573,7 @@ function signIn(options) {
|
|
|
5408
5573
|
socket?.close();
|
|
5409
5574
|
} catch {
|
|
5410
5575
|
}
|
|
5411
|
-
if (key2)
|
|
5576
|
+
if (key2) resolve5(key2);
|
|
5412
5577
|
else reject(err ?? new Error("Nexus sign-in failed"));
|
|
5413
5578
|
};
|
|
5414
5579
|
const onAbort = () => finish(new Error("Sign-in cancelled"));
|
|
@@ -5539,7 +5704,7 @@ function signInWithOAuth(options) {
|
|
|
5539
5704
|
const { verifier, challenge } = pkce();
|
|
5540
5705
|
const state = crypto3.randomUUID();
|
|
5541
5706
|
let done = false;
|
|
5542
|
-
return new Promise((
|
|
5707
|
+
return new Promise((resolve5, reject) => {
|
|
5543
5708
|
const server = http.createServer();
|
|
5544
5709
|
const finish = (err, tokens) => {
|
|
5545
5710
|
if (done) return;
|
|
@@ -5547,7 +5712,7 @@ function signInWithOAuth(options) {
|
|
|
5547
5712
|
clearTimeout(timer);
|
|
5548
5713
|
options.signal?.removeEventListener("abort", onAbort);
|
|
5549
5714
|
server.close();
|
|
5550
|
-
if (tokens)
|
|
5715
|
+
if (tokens) resolve5(tokens);
|
|
5551
5716
|
else reject(err ?? new Error("Nexus sign-in failed"));
|
|
5552
5717
|
};
|
|
5553
5718
|
const onAbort = () => finish(new Error("Sign-in cancelled"));
|
|
@@ -5668,10 +5833,10 @@ function describeHandler(command) {
|
|
|
5668
5833
|
}
|
|
5669
5834
|
|
|
5670
5835
|
// src/project.ts
|
|
5671
|
-
var
|
|
5672
|
-
var
|
|
5836
|
+
var fs11 = __toESM(require("node:fs"));
|
|
5837
|
+
var path10 = __toESM(require("node:path"));
|
|
5673
5838
|
function addToProject(lib, mod, files, projectFile, replace = /* @__PURE__ */ new Set()) {
|
|
5674
|
-
const resources =
|
|
5839
|
+
const resources = path10.join(path10.dirname(projectFile), "source", "resources");
|
|
5675
5840
|
const out = { copied: [], same: [], kept: [], archives: [] };
|
|
5676
5841
|
for (const f of files) {
|
|
5677
5842
|
if (f.toLowerCase().endsWith(".archive")) {
|
|
@@ -5679,10 +5844,10 @@ function addToProject(lib, mod, files, projectFile, replace = /* @__PURE__ */ ne
|
|
|
5679
5844
|
continue;
|
|
5680
5845
|
}
|
|
5681
5846
|
const src = sourcePath(lib, mod, f);
|
|
5682
|
-
const rel =
|
|
5683
|
-
const dest =
|
|
5684
|
-
if (
|
|
5685
|
-
if (
|
|
5847
|
+
const rel = path10.join("source", "resources", ...f.split("/"));
|
|
5848
|
+
const dest = path10.join(resources, ...f.split("/"));
|
|
5849
|
+
if (fs11.existsSync(dest)) {
|
|
5850
|
+
if (fs11.readFileSync(dest).equals(fs11.readFileSync(src))) {
|
|
5686
5851
|
out.same.push(rel);
|
|
5687
5852
|
continue;
|
|
5688
5853
|
}
|
|
@@ -5691,39 +5856,39 @@ function addToProject(lib, mod, files, projectFile, replace = /* @__PURE__ */ ne
|
|
|
5691
5856
|
continue;
|
|
5692
5857
|
}
|
|
5693
5858
|
}
|
|
5694
|
-
|
|
5695
|
-
|
|
5859
|
+
fs11.mkdirSync(path10.dirname(dest), { recursive: true });
|
|
5860
|
+
fs11.copyFileSync(src, dest);
|
|
5696
5861
|
out.copied.push(rel);
|
|
5697
5862
|
}
|
|
5698
5863
|
return out;
|
|
5699
5864
|
}
|
|
5700
5865
|
function findProjectFile(target) {
|
|
5701
|
-
if (
|
|
5702
|
-
const files =
|
|
5703
|
-
return files.length ?
|
|
5866
|
+
if (fs11.existsSync(target) && fs11.statSync(target).isFile()) return target.toLowerCase().endsWith(".cpmodproj") ? target : void 0;
|
|
5867
|
+
const files = fs11.existsSync(target) ? fs11.readdirSync(target).filter((f) => f.toLowerCase().endsWith(".cpmodproj")) : [];
|
|
5868
|
+
return files.length ? path10.join(target, files[0]) : void 0;
|
|
5704
5869
|
}
|
|
5705
5870
|
function projectName(projectFile) {
|
|
5706
5871
|
try {
|
|
5707
|
-
const xml =
|
|
5872
|
+
const xml = fs11.readFileSync(projectFile, "utf8");
|
|
5708
5873
|
const m = /<ModName>\s*([^<]+?)\s*<\//.exec(xml) ?? /<Name>\s*([^<]+?)\s*<\//.exec(xml);
|
|
5709
5874
|
if (m) return m[1];
|
|
5710
5875
|
} catch {
|
|
5711
5876
|
}
|
|
5712
|
-
return
|
|
5877
|
+
return path10.basename(projectFile, path10.extname(projectFile));
|
|
5713
5878
|
}
|
|
5714
5879
|
function packedFolder(projectFile) {
|
|
5715
|
-
return
|
|
5880
|
+
return path10.join(path10.dirname(projectFile), "packed");
|
|
5716
5881
|
}
|
|
5717
5882
|
function classifyProject(projectFile) {
|
|
5718
5883
|
const packed = packedFolder(projectFile);
|
|
5719
|
-
if (!
|
|
5884
|
+
if (!fs11.existsSync(packed)) return { placements: [], kinds: [], unplaced: [], ignored: [] };
|
|
5720
5885
|
return classify(packed, projectName(projectFile));
|
|
5721
5886
|
}
|
|
5722
5887
|
function newest(dir) {
|
|
5723
5888
|
let t = 0;
|
|
5724
|
-
for (const e of
|
|
5725
|
-
const p =
|
|
5726
|
-
t = Math.max(t, e.isDirectory() ? newest(p) :
|
|
5889
|
+
for (const e of fs11.readdirSync(dir, { withFileTypes: true })) {
|
|
5890
|
+
const p = path10.join(dir, e.name);
|
|
5891
|
+
t = Math.max(t, e.isDirectory() ? newest(p) : fs11.statSync(p).mtimeMs);
|
|
5727
5892
|
}
|
|
5728
5893
|
return t;
|
|
5729
5894
|
}
|
|
@@ -5732,10 +5897,10 @@ function syncProjects(lib) {
|
|
|
5732
5897
|
for (const mod of lib.mods) {
|
|
5733
5898
|
if (mod.source.type !== "project" || !mod.source.path) continue;
|
|
5734
5899
|
const packed = packedFolder(mod.source.path);
|
|
5735
|
-
if (!
|
|
5900
|
+
if (!fs11.existsSync(packed)) continue;
|
|
5736
5901
|
const libNewest = mod.files.reduce((t, f) => {
|
|
5737
5902
|
try {
|
|
5738
|
-
return Math.max(t,
|
|
5903
|
+
return Math.max(t, fs11.statSync(sourcePath(lib, mod, f)).mtimeMs);
|
|
5739
5904
|
} catch {
|
|
5740
5905
|
return t;
|
|
5741
5906
|
}
|
|
@@ -5746,11 +5911,11 @@ function syncProjects(lib) {
|
|
|
5746
5911
|
const keep = /* @__PURE__ */ new Set();
|
|
5747
5912
|
for (const p of c.placements) {
|
|
5748
5913
|
const dest = sourcePath(lib, mod, p.to);
|
|
5749
|
-
|
|
5750
|
-
|
|
5914
|
+
fs11.mkdirSync(path10.dirname(dest), { recursive: true });
|
|
5915
|
+
fs11.copyFileSync(p.from, dest);
|
|
5751
5916
|
keep.add(p.to);
|
|
5752
5917
|
}
|
|
5753
|
-
for (const f of mod.files) if (!keep.has(f))
|
|
5918
|
+
for (const f of mod.files) if (!keep.has(f)) fs11.rmSync(sourcePath(lib, mod, f), { force: true });
|
|
5754
5919
|
mod.files = [...keep].sort();
|
|
5755
5920
|
mod.kinds = c.kinds;
|
|
5756
5921
|
saveMod(lib, mod);
|
|
@@ -5806,11 +5971,11 @@ function installProject(lib, projectFile, beside) {
|
|
|
5806
5971
|
const keep = /* @__PURE__ */ new Set();
|
|
5807
5972
|
for (const p of c.placements) {
|
|
5808
5973
|
const dest = sourcePath(lib, existing, p.to);
|
|
5809
|
-
|
|
5810
|
-
|
|
5974
|
+
fs11.mkdirSync(path10.dirname(dest), { recursive: true });
|
|
5975
|
+
fs11.copyFileSync(p.from, dest);
|
|
5811
5976
|
keep.add(p.to);
|
|
5812
5977
|
}
|
|
5813
|
-
for (const f of existing.files) if (!keep.has(f))
|
|
5978
|
+
for (const f of existing.files) if (!keep.has(f)) fs11.rmSync(sourcePath(lib, existing, f), { force: true });
|
|
5814
5979
|
existing.files = [...keep].sort();
|
|
5815
5980
|
existing.kinds = c.kinds;
|
|
5816
5981
|
activateVersion(lib, existing);
|
|
@@ -5821,12 +5986,12 @@ function installProject(lib, projectFile, beside) {
|
|
|
5821
5986
|
|
|
5822
5987
|
// src/redmod.ts
|
|
5823
5988
|
var import_node_child_process6 = require("node:child_process");
|
|
5824
|
-
var
|
|
5825
|
-
var
|
|
5989
|
+
var fs12 = __toESM(require("node:fs"));
|
|
5990
|
+
var path11 = __toESM(require("node:path"));
|
|
5826
5991
|
var import_node_util4 = require("node:util");
|
|
5827
5992
|
var run4 = (0, import_node_util4.promisify)(import_node_child_process6.execFile);
|
|
5828
5993
|
function redModExe(gamePath) {
|
|
5829
|
-
return
|
|
5994
|
+
return path11.join(gamePath, "tools", "redmod", "bin", "redMod.exe");
|
|
5830
5995
|
}
|
|
5831
5996
|
function enabledRedmodPackages(lib) {
|
|
5832
5997
|
const names = [];
|
|
@@ -5853,13 +6018,13 @@ function deployedRedmodPackages(lib, gamePath) {
|
|
|
5853
6018
|
async function deployRedmod(lib, gamePath, log, packages = enabledRedmodPackages(lib)) {
|
|
5854
6019
|
const exe = redModExe(gamePath);
|
|
5855
6020
|
if (!packages.length) return { ran: false, packages, output: "no REDmod packages enabled" };
|
|
5856
|
-
if (!
|
|
5857
|
-
const listFile =
|
|
5858
|
-
|
|
5859
|
-
const args = ["deploy", `-root=${gamePath}`, `-modlist=${listFile}`, `-rttiSchemaPath=${
|
|
6021
|
+
if (!fs12.existsSync(exe)) return { ran: false, packages, output: `redMod.exe not found at ${exe}; the REDmod tools are a free DLC on the game's store page` };
|
|
6022
|
+
const listFile = path11.join(lib.root, "redmod-modlist.txt");
|
|
6023
|
+
fs12.writeFileSync(listFile, packages.join("\r\n") + "\r\n");
|
|
6024
|
+
const args = ["deploy", `-root=${gamePath}`, `-modlist=${listFile}`, `-rttiSchemaPath=${path11.join(gamePath, "tools", "redmod", "metadata.json")}`];
|
|
5860
6025
|
log?.(`redMod.exe ${args.join(" ")}`);
|
|
5861
6026
|
try {
|
|
5862
|
-
const { stdout, stderr } = await run4(exe, args, { cwd:
|
|
6027
|
+
const { stdout, stderr } = await run4(exe, args, { cwd: path11.dirname(exe), windowsHide: true, maxBuffer: 1 << 26 });
|
|
5863
6028
|
const output = (stdout + stderr).trim();
|
|
5864
6029
|
for (const line of output.split(/\r?\n/)) if (line.trim()) log?.(` ${line}`);
|
|
5865
6030
|
return { ran: true, packages, output };
|
|
@@ -5977,8 +6142,8 @@ function setSectionOn(lib, name, on) {
|
|
|
5977
6142
|
}
|
|
5978
6143
|
|
|
5979
6144
|
// src/sizes.ts
|
|
5980
|
-
var
|
|
5981
|
-
var
|
|
6145
|
+
var fs13 = __toESM(require("node:fs"));
|
|
6146
|
+
var path12 = __toESM(require("node:path"));
|
|
5982
6147
|
var cache = /* @__PURE__ */ new Map();
|
|
5983
6148
|
function modSize(lib, mod) {
|
|
5984
6149
|
const stamp = `${mod.files.length}:${mod.added}:${lib.root}`;
|
|
@@ -5991,7 +6156,7 @@ function modSize(lib, mod) {
|
|
|
5991
6156
|
for (const f of mod.files) {
|
|
5992
6157
|
let n = 0;
|
|
5993
6158
|
try {
|
|
5994
|
-
const st =
|
|
6159
|
+
const st = fs13.statSync(path12.join(folder, ...f.split("/")));
|
|
5995
6160
|
n = st.size;
|
|
5996
6161
|
mtimes.set(f, st.mtimeMs);
|
|
5997
6162
|
} catch {
|
|
@@ -6010,10 +6175,10 @@ function forgetSizes(modId) {
|
|
|
6010
6175
|
function folderSize(dir) {
|
|
6011
6176
|
let total = 0;
|
|
6012
6177
|
try {
|
|
6013
|
-
for (const e of
|
|
6014
|
-
const p =
|
|
6178
|
+
for (const e of fs13.readdirSync(dir, { withFileTypes: true })) {
|
|
6179
|
+
const p = path12.join(dir, e.name);
|
|
6015
6180
|
if (e.isDirectory()) total += folderSize(p);
|
|
6016
|
-
else if (e.isFile()) total +=
|
|
6181
|
+
else if (e.isFile()) total += fs13.statSync(p).size;
|
|
6017
6182
|
}
|
|
6018
6183
|
} catch {
|
|
6019
6184
|
}
|
|
@@ -6034,20 +6199,20 @@ function formatBytes(n) {
|
|
|
6034
6199
|
}
|
|
6035
6200
|
|
|
6036
6201
|
// src/verbs.ts
|
|
6037
|
-
var
|
|
6202
|
+
var fs15 = __toESM(require("node:fs"));
|
|
6038
6203
|
var os3 = __toESM(require("node:os"));
|
|
6039
|
-
var
|
|
6204
|
+
var path14 = __toESM(require("node:path"));
|
|
6040
6205
|
|
|
6041
6206
|
// src/vortex.ts
|
|
6042
|
-
var
|
|
6043
|
-
var
|
|
6207
|
+
var fs14 = __toESM(require("node:fs"));
|
|
6208
|
+
var path13 = __toESM(require("node:path"));
|
|
6044
6209
|
function vortexStagingPath() {
|
|
6045
|
-
return
|
|
6210
|
+
return path13.join(process.env.APPDATA ?? path13.join(process.env.USERPROFILE ?? "", "AppData", "Roaming"), "Vortex", "cyberpunk2077", "mods");
|
|
6046
6211
|
}
|
|
6047
6212
|
function manifestStaging(gamePath) {
|
|
6048
6213
|
if (!gamePath) return void 0;
|
|
6049
6214
|
try {
|
|
6050
|
-
const m = JSON.parse(
|
|
6215
|
+
const m = JSON.parse(fs14.readFileSync(path13.join(gamePath, "vortex.deployment.json"), "utf8"));
|
|
6051
6216
|
return m.stagingPath || void 0;
|
|
6052
6217
|
} catch {
|
|
6053
6218
|
return void 0;
|
|
@@ -6055,18 +6220,18 @@ function manifestStaging(gamePath) {
|
|
|
6055
6220
|
}
|
|
6056
6221
|
function driveRoots(gamePath) {
|
|
6057
6222
|
if (process.platform !== "win32") return [];
|
|
6058
|
-
const first = gamePath ?
|
|
6223
|
+
const first = gamePath ? path13.parse(path13.resolve(gamePath)).root.toUpperCase() : "";
|
|
6059
6224
|
const roots = first ? [first] : [];
|
|
6060
6225
|
for (let c = 67; c <= 90; c++) {
|
|
6061
6226
|
const r = `${String.fromCharCode(c)}:\\`;
|
|
6062
|
-
if (r !== first &&
|
|
6227
|
+
if (r !== first && fs14.existsSync(r)) roots.push(r);
|
|
6063
6228
|
}
|
|
6064
6229
|
return roots;
|
|
6065
6230
|
}
|
|
6066
6231
|
function vortexStagingCandidates(gamePath, setting) {
|
|
6067
6232
|
const list = [setting, manifestStaging(gamePath), vortexStagingPath()];
|
|
6068
6233
|
for (const root of driveRoots(gamePath)) {
|
|
6069
|
-
list.push(
|
|
6234
|
+
list.push(path13.join(root, "Vortex Mods", "cyberpunk2077"), path13.join(root, "VortexMods", "cyberpunk2077"), path13.join(root, "Vortex", "cyberpunk2077", "mods"), path13.join(root, "Vortex", "mods", "cyberpunk2077"));
|
|
6070
6235
|
}
|
|
6071
6236
|
const seen = /* @__PURE__ */ new Set();
|
|
6072
6237
|
return list.filter((p) => !!p && !seen.has(p.toLowerCase()) && !!seen.add(p.toLowerCase()));
|
|
@@ -6074,7 +6239,7 @@ function vortexStagingCandidates(gamePath, setting) {
|
|
|
6074
6239
|
function findVortexStaging(gamePath, setting) {
|
|
6075
6240
|
for (const dir of vortexStagingCandidates(gamePath, setting)) {
|
|
6076
6241
|
try {
|
|
6077
|
-
if (
|
|
6242
|
+
if (fs14.readdirSync(dir, { withFileTypes: true }).some((e) => e.isDirectory() && !e.name.startsWith("__"))) return dir;
|
|
6078
6243
|
} catch {
|
|
6079
6244
|
}
|
|
6080
6245
|
}
|
|
@@ -6082,24 +6247,24 @@ function findVortexStaging(gamePath, setting) {
|
|
|
6082
6247
|
}
|
|
6083
6248
|
function countFiles(dir) {
|
|
6084
6249
|
let n = 0;
|
|
6085
|
-
for (const e of
|
|
6086
|
-
if (e.isDirectory()) n += countFiles(
|
|
6250
|
+
for (const e of fs14.readdirSync(dir, { withFileTypes: true })) {
|
|
6251
|
+
if (e.isDirectory()) n += countFiles(path13.join(dir, e.name));
|
|
6087
6252
|
else n++;
|
|
6088
6253
|
}
|
|
6089
6254
|
return n;
|
|
6090
6255
|
}
|
|
6091
6256
|
function guessName(folder) {
|
|
6092
6257
|
const sub = (...parts) => {
|
|
6093
|
-
const d =
|
|
6094
|
-
if (!
|
|
6095
|
-
const names =
|
|
6258
|
+
const d = path13.join(folder, ...parts);
|
|
6259
|
+
if (!fs14.existsSync(d)) return void 0;
|
|
6260
|
+
const names = fs14.readdirSync(d, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
6096
6261
|
return names.length === 1 ? names[0] : void 0;
|
|
6097
6262
|
};
|
|
6098
6263
|
const named = sub("r6", "scripts") ?? sub("bin", "x64", "plugins", "cyber_engine_tweaks", "mods") ?? sub("red4ext", "plugins") ?? sub("r6", "tweaks") ?? sub("mods");
|
|
6099
6264
|
if (named) return named;
|
|
6100
|
-
const archives =
|
|
6101
|
-
if (
|
|
6102
|
-
const a =
|
|
6265
|
+
const archives = path13.join(folder, "archive", "pc", "mod");
|
|
6266
|
+
if (fs14.existsSync(archives)) {
|
|
6267
|
+
const a = fs14.readdirSync(archives).find((f) => f.toLowerCase().endsWith(".archive"));
|
|
6103
6268
|
if (a) return a.replace(/\.archive$/i, "").replace(/^[#!_0-9]+/, "") || a;
|
|
6104
6269
|
}
|
|
6105
6270
|
return void 0;
|
|
@@ -6109,21 +6274,21 @@ function listVortexMods(gamePath, staging) {
|
|
|
6109
6274
|
if (!dir) return [];
|
|
6110
6275
|
staging = dir;
|
|
6111
6276
|
const deployed = /* @__PURE__ */ new Set();
|
|
6112
|
-
const manifest =
|
|
6113
|
-
if (
|
|
6277
|
+
const manifest = path13.join(gamePath, "vortex.deployment.json");
|
|
6278
|
+
if (fs14.existsSync(manifest)) {
|
|
6114
6279
|
try {
|
|
6115
|
-
const m = JSON.parse(
|
|
6280
|
+
const m = JSON.parse(fs14.readFileSync(manifest, "utf8"));
|
|
6116
6281
|
for (const f of m.files ?? []) deployed.add(f.source);
|
|
6117
6282
|
} catch {
|
|
6118
6283
|
}
|
|
6119
6284
|
}
|
|
6120
6285
|
const mods = [];
|
|
6121
|
-
for (const entry of
|
|
6286
|
+
for (const entry of fs14.readdirSync(staging, { withFileTypes: true })) {
|
|
6122
6287
|
if (!entry.isDirectory() || entry.name.startsWith("__")) continue;
|
|
6123
|
-
const folder =
|
|
6288
|
+
const folder = path13.join(staging, entry.name);
|
|
6124
6289
|
const files = countFiles(folder);
|
|
6125
6290
|
if (files === 0) continue;
|
|
6126
|
-
if (
|
|
6291
|
+
if (fs14.existsSync(path13.join(folder, "collection.json")) && files === 1) continue;
|
|
6127
6292
|
const m = /^(.*?)-(\d+)-([\d-]+)-(\d{10})$/.exec(entry.name);
|
|
6128
6293
|
const guid = /^[0-9a-f]{2}_[0-9a-f]{2}_[0-9a-f]{2}_[0-9a-f-]{36}$/i.test(entry.name);
|
|
6129
6294
|
mods.push({
|
|
@@ -6211,7 +6376,7 @@ var verbs = {
|
|
|
6211
6376
|
/** Every mod with its status and size, the library totals and the frameworks line. */
|
|
6212
6377
|
async list(s, largestFirst = false) {
|
|
6213
6378
|
const { lib, game } = s;
|
|
6214
|
-
const p =
|
|
6379
|
+
const p = fs15.existsSync(game) && isGameFolder(game) ? plan(lib, game) : void 0;
|
|
6215
6380
|
const manifest = p ? readManifest(game) : void 0;
|
|
6216
6381
|
const changes = p ? detectChanges(game) : [];
|
|
6217
6382
|
const rows = lib.mods.map((m) => ({ id: m.id, name: m.name, version: m.version, enabled: m.enabled, kinds: m.kinds, files: m.files.length, bytes: modSize(lib, m).total, status: p ? modStatus(lib, game, m, p, manifest, changes) : "" }));
|
|
@@ -6253,14 +6418,14 @@ ${mod.files.join("\n")}`;
|
|
|
6253
6418
|
},
|
|
6254
6419
|
/** Adds a zip, 7z, rar, a single mod file or a folder. An archive with options needs the option's label. */
|
|
6255
6420
|
async add(s, source, name, option) {
|
|
6256
|
-
if (!
|
|
6257
|
-
const isFile =
|
|
6258
|
-
const modName = name ??
|
|
6421
|
+
if (!fs15.existsSync(source)) throw new Error(`${source} does not exist`);
|
|
6422
|
+
const isFile = fs15.statSync(source).isFile();
|
|
6423
|
+
const modName = name ?? path14.basename(source, isFile ? path14.extname(source) : "");
|
|
6259
6424
|
let folder = source;
|
|
6260
6425
|
let temp;
|
|
6261
6426
|
try {
|
|
6262
6427
|
if (isFile) {
|
|
6263
|
-
temp =
|
|
6428
|
+
temp = fs15.mkdtempSync(path14.join(os3.tmpdir(), "edgedeck-verb-"));
|
|
6264
6429
|
await unpackOrCopy(source, temp, s.sevenZa);
|
|
6265
6430
|
folder = temp;
|
|
6266
6431
|
}
|
|
@@ -6277,7 +6442,7 @@ ${mod.files.join("\n")}`;
|
|
|
6277
6442
|
const note = settled.replaced ? `; a new version of ${settled.replaced.name} ${settled.replaced.version}, which is off now` : settled.older ? `; older than ${settled.older.version}, so it is off` : "";
|
|
6278
6443
|
return { data: { added: { id: mod.id, name: mod.name, version: mod.version, kinds: mod.kinds, files: mod.files.length, enabled: mod.enabled }, replaced: settled.replaced?.id, unplaced: c.unplaced }, text: `added ${mod.id} (${mod.files.length} files, ${mod.kinds.join(", ")})${note}; ${mod.enabled ? "it is on, " : ""}deploy to put it in the game${c.unplaced.length ? `; not placed: ${c.unplaced.join(", ")}` : ""}` };
|
|
6279
6444
|
} finally {
|
|
6280
|
-
if (temp)
|
|
6445
|
+
if (temp) fs15.rmSync(temp, { recursive: true, force: true });
|
|
6281
6446
|
}
|
|
6282
6447
|
},
|
|
6283
6448
|
/** Adds a REDcode or WolvenKit project's packed output, or refreshes the mod that follows it, and turns it on. */
|
|
@@ -6287,9 +6452,9 @@ ${mod.files.join("\n")}`;
|
|
|
6287
6452
|
* place). Without it, the project is a mod of its own and the text says what it sits beside.
|
|
6288
6453
|
*/
|
|
6289
6454
|
installProject(s, projectFile, sameName) {
|
|
6290
|
-
if (!
|
|
6455
|
+
if (!fs15.existsSync(projectFile)) throw new Error(`${projectFile} does not exist`);
|
|
6291
6456
|
const other = sameNameMod(s.lib, projectFile);
|
|
6292
|
-
const before = other && `${other.id}${other.source.type === "project" && other.source.path ? ` (${
|
|
6457
|
+
const before = other && `${other.id}${other.source.type === "project" && other.source.path ? ` (${path14.dirname(other.source.path)})` : ""}`;
|
|
6293
6458
|
const { mod, added, beside } = installProject(s.lib, projectFile, other && sameName ? { mod: other, how: sameName } : void 0);
|
|
6294
6459
|
const note = beside?.how === "follow" ? `; ${before} follows this project now` : beside ? `; another version of ${before}, on in its place` : other ? `; note: the library also has ${before} under the same name, a separate mod; the one higher in the list wins the files they share (call again with sameName follow or version to settle it)` : "";
|
|
6295
6460
|
return {
|
|
@@ -6373,7 +6538,7 @@ ${mod.files.join("\n")}`;
|
|
|
6373
6538
|
}
|
|
6374
6539
|
if (action === "load") {
|
|
6375
6540
|
if (!name) throw new Error("loadout load needs a name or file");
|
|
6376
|
-
const target =
|
|
6541
|
+
const target = fs15.existsSync(name) && name.toLowerCase().endsWith(".json") ? importLoadout(s.lib, name) : name;
|
|
6377
6542
|
const r = makeActive(s.lib, target);
|
|
6378
6543
|
return { data: { ...r, active: target }, text: `loadout ${target} active: ${r.on} on, ${r.off} off${r.missing.length ? `; not in the library: ${r.missing.join(", ")}` : ""}; deploy to apply` };
|
|
6379
6544
|
}
|
|
@@ -6397,6 +6562,7 @@ ${mod.files.join("\n")}`;
|
|
|
6397
6562
|
BridgeClient,
|
|
6398
6563
|
BridgeRepo,
|
|
6399
6564
|
DefaultLoadout,
|
|
6565
|
+
FrameworkSources,
|
|
6400
6566
|
FrameworksSection,
|
|
6401
6567
|
KnownModNames,
|
|
6402
6568
|
ManifestName,
|
|
@@ -6434,6 +6600,7 @@ ${mod.files.join("\n")}`;
|
|
|
6434
6600
|
deleteLoadout,
|
|
6435
6601
|
deleteSection,
|
|
6436
6602
|
deploy,
|
|
6603
|
+
deployMethod,
|
|
6437
6604
|
deployRedmod,
|
|
6438
6605
|
deployReportsFolder,
|
|
6439
6606
|
deployedRedmodPackages,
|
|
@@ -6465,12 +6632,15 @@ ${mod.files.join("\n")}`;
|
|
|
6465
6632
|
formatBytes,
|
|
6466
6633
|
forwarderCommand,
|
|
6467
6634
|
frameworkDetail,
|
|
6635
|
+
frameworkFamily,
|
|
6636
|
+
frameworkStates,
|
|
6468
6637
|
frameworkSummary,
|
|
6469
6638
|
gameExe,
|
|
6470
6639
|
gameRunning,
|
|
6471
6640
|
gameVersion,
|
|
6472
6641
|
guessName,
|
|
6473
6642
|
hasWebSocket,
|
|
6643
|
+
holdsLibrary,
|
|
6474
6644
|
idFor,
|
|
6475
6645
|
importFromVortex,
|
|
6476
6646
|
importLoadout,
|
|
@@ -6482,6 +6652,7 @@ ${mod.files.join("\n")}`;
|
|
|
6482
6652
|
isGameFolder,
|
|
6483
6653
|
keepDownload,
|
|
6484
6654
|
latestBridgeRelease,
|
|
6655
|
+
latestFrameworkRelease,
|
|
6485
6656
|
launchTarget,
|
|
6486
6657
|
libraryFolder,
|
|
6487
6658
|
librarySize,
|
|
@@ -6494,6 +6665,7 @@ ${mod.files.join("\n")}`;
|
|
|
6494
6665
|
lostFiles,
|
|
6495
6666
|
makeActive,
|
|
6496
6667
|
md5Of,
|
|
6668
|
+
missingFrameworks,
|
|
6497
6669
|
modDeployPaths,
|
|
6498
6670
|
modFolder,
|
|
6499
6671
|
modPageUrl,
|
|
@@ -6502,8 +6674,10 @@ ${mod.files.join("\n")}`;
|
|
|
6502
6674
|
modsRemoved,
|
|
6503
6675
|
modsTouched,
|
|
6504
6676
|
moveInSection,
|
|
6677
|
+
moveLibrary,
|
|
6505
6678
|
moveMod,
|
|
6506
6679
|
moveSection,
|
|
6680
|
+
moveTarget,
|
|
6507
6681
|
needsRefresh,
|
|
6508
6682
|
newLoadout,
|
|
6509
6683
|
newestFile,
|
|
@@ -6533,6 +6707,7 @@ ${mod.files.join("\n")}`;
|
|
|
6533
6707
|
renameFamily,
|
|
6534
6708
|
renameLoadout,
|
|
6535
6709
|
renameSection,
|
|
6710
|
+
sameDrive,
|
|
6536
6711
|
sameNameMod,
|
|
6537
6712
|
saveLibrary,
|
|
6538
6713
|
saveLoadout,
|
package/dist/mcp.js
CHANGED
|
@@ -2984,7 +2984,7 @@ var require_compile = __commonJS({
|
|
|
2984
2984
|
const schOrFunc = root.refs[ref];
|
|
2985
2985
|
if (schOrFunc)
|
|
2986
2986
|
return schOrFunc;
|
|
2987
|
-
let _sch =
|
|
2987
|
+
let _sch = resolve5.call(this, root, ref);
|
|
2988
2988
|
if (_sch === void 0) {
|
|
2989
2989
|
const schema = (_a3 = root.localRefs) === null || _a3 === void 0 ? void 0 : _a3[ref];
|
|
2990
2990
|
const { schemaId } = this.opts;
|
|
@@ -3011,7 +3011,7 @@ var require_compile = __commonJS({
|
|
|
3011
3011
|
function sameSchemaEnv(s1, s2) {
|
|
3012
3012
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
3013
3013
|
}
|
|
3014
|
-
function
|
|
3014
|
+
function resolve5(root, ref) {
|
|
3015
3015
|
let sch;
|
|
3016
3016
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
3017
3017
|
ref = sch;
|
|
@@ -3841,7 +3841,7 @@ var require_fast_uri = __commonJS({
|
|
|
3841
3841
|
}
|
|
3842
3842
|
return uri;
|
|
3843
3843
|
}
|
|
3844
|
-
function
|
|
3844
|
+
function resolve5(baseURI, relativeURI, options2) {
|
|
3845
3845
|
const schemelessOptions = options2 ? Object.assign({ scheme: "null" }, options2) : { scheme: "null" };
|
|
3846
3846
|
const {
|
|
3847
3847
|
parsed: baseParsed,
|
|
@@ -4210,7 +4210,7 @@ var require_fast_uri = __commonJS({
|
|
|
4210
4210
|
var fastUri = {
|
|
4211
4211
|
SCHEMES,
|
|
4212
4212
|
normalize,
|
|
4213
|
-
resolve:
|
|
4213
|
+
resolve: resolve5,
|
|
4214
4214
|
resolveComponent,
|
|
4215
4215
|
equal,
|
|
4216
4216
|
serialize,
|
|
@@ -15901,7 +15901,7 @@ var recursive = /* @__PURE__ */ new WeakMap();
|
|
|
15901
15901
|
var NONE = 0;
|
|
15902
15902
|
var ASSUMED = 1;
|
|
15903
15903
|
var PROVEN = 2;
|
|
15904
|
-
function isRecursive(inst, stack,
|
|
15904
|
+
function isRecursive(inst, stack, resolve5) {
|
|
15905
15905
|
const cached2 = recursive.get(inst);
|
|
15906
15906
|
if (cached2 !== void 0)
|
|
15907
15907
|
return cached2 ? PROVEN : NONE;
|
|
@@ -15911,7 +15911,7 @@ function isRecursive(inst, stack, resolve4) {
|
|
|
15911
15911
|
let result = NONE;
|
|
15912
15912
|
const check2 = (child) => {
|
|
15913
15913
|
if (result !== PROVEN && child?._zod) {
|
|
15914
|
-
const answer = isRecursive(child, stack,
|
|
15914
|
+
const answer = isRecursive(child, stack, resolve5);
|
|
15915
15915
|
if (answer > result)
|
|
15916
15916
|
result = answer;
|
|
15917
15917
|
}
|
|
@@ -15922,7 +15922,7 @@ function isRecursive(inst, stack, resolve4) {
|
|
|
15922
15922
|
const desc = Object.getOwnPropertyDescriptor(sh, key2);
|
|
15923
15923
|
if (spread && !desc.enumerable)
|
|
15924
15924
|
continue;
|
|
15925
|
-
const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack,
|
|
15925
|
+
const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve5) : NONE;
|
|
15926
15926
|
if (child > answer)
|
|
15927
15927
|
answer = child;
|
|
15928
15928
|
}
|
|
@@ -15986,7 +15986,7 @@ function isRecursive(inst, stack, resolve4) {
|
|
|
15986
15986
|
break;
|
|
15987
15987
|
// `$ZodLazy` caches its inner on the def, so a resolved edge is followed exactly
|
|
15988
15988
|
case "lazy": {
|
|
15989
|
-
const inner = def._cachedInner ?? (
|
|
15989
|
+
const inner = def._cachedInner ?? (resolve5 ? inst._zod.innerType : void 0);
|
|
15990
15990
|
merge2(inner ? isRecursive(inner, stack, false) : ASSUMED);
|
|
15991
15991
|
break;
|
|
15992
15992
|
}
|
|
@@ -34424,7 +34424,7 @@ var Protocol = class {
|
|
|
34424
34424
|
return;
|
|
34425
34425
|
}
|
|
34426
34426
|
const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
|
|
34427
|
-
await new Promise((
|
|
34427
|
+
await new Promise((resolve5) => setTimeout(resolve5, pollInterval));
|
|
34428
34428
|
options2?.signal?.throwIfAborted();
|
|
34429
34429
|
}
|
|
34430
34430
|
} catch (error62) {
|
|
@@ -34441,7 +34441,7 @@ var Protocol = class {
|
|
|
34441
34441
|
*/
|
|
34442
34442
|
request(request, resultSchema, options2) {
|
|
34443
34443
|
const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options2 ?? {};
|
|
34444
|
-
return new Promise((
|
|
34444
|
+
return new Promise((resolve5, reject) => {
|
|
34445
34445
|
const earlyReject = (error62) => {
|
|
34446
34446
|
reject(error62);
|
|
34447
34447
|
};
|
|
@@ -34519,7 +34519,7 @@ var Protocol = class {
|
|
|
34519
34519
|
if (!parseResult.success) {
|
|
34520
34520
|
reject(parseResult.error);
|
|
34521
34521
|
} else {
|
|
34522
|
-
|
|
34522
|
+
resolve5(parseResult.data);
|
|
34523
34523
|
}
|
|
34524
34524
|
} catch (error62) {
|
|
34525
34525
|
reject(error62);
|
|
@@ -34781,12 +34781,12 @@ var Protocol = class {
|
|
|
34781
34781
|
}
|
|
34782
34782
|
} catch {
|
|
34783
34783
|
}
|
|
34784
|
-
return new Promise((
|
|
34784
|
+
return new Promise((resolve5, reject) => {
|
|
34785
34785
|
if (signal.aborted) {
|
|
34786
34786
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
34787
34787
|
return;
|
|
34788
34788
|
}
|
|
34789
|
-
const timeoutId = setTimeout(
|
|
34789
|
+
const timeoutId = setTimeout(resolve5, interval);
|
|
34790
34790
|
signal.addEventListener("abort", () => {
|
|
34791
34791
|
clearTimeout(timeoutId);
|
|
34792
34792
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
@@ -35917,7 +35917,7 @@ var McpServer = class {
|
|
|
35917
35917
|
let task = createTaskResult.task;
|
|
35918
35918
|
const pollInterval = task.pollInterval ?? 5e3;
|
|
35919
35919
|
while (task.status !== "completed" && task.status !== "failed" && task.status !== "cancelled") {
|
|
35920
|
-
await new Promise((
|
|
35920
|
+
await new Promise((resolve5) => setTimeout(resolve5, pollInterval));
|
|
35921
35921
|
const updatedTask = await extra.taskStore.getTask(taskId);
|
|
35922
35922
|
if (!updatedTask) {
|
|
35923
35923
|
throw new McpError(ErrorCode.InternalError, `Task ${taskId} not found during polling`);
|
|
@@ -36581,12 +36581,12 @@ var StdioServerTransport = class {
|
|
|
36581
36581
|
this.onclose?.();
|
|
36582
36582
|
}
|
|
36583
36583
|
send(message) {
|
|
36584
|
-
return new Promise((
|
|
36584
|
+
return new Promise((resolve5) => {
|
|
36585
36585
|
const json2 = serializeMessage(message);
|
|
36586
36586
|
if (this._stdout.write(json2)) {
|
|
36587
|
-
|
|
36587
|
+
resolve5();
|
|
36588
36588
|
} else {
|
|
36589
|
-
this._stdout.once("drain",
|
|
36589
|
+
this._stdout.once("drain", resolve5);
|
|
36590
36590
|
}
|
|
36591
36591
|
});
|
|
36592
36592
|
}
|
|
@@ -37148,6 +37148,21 @@ function canHardlink(libraryRoot, gamePath) {
|
|
|
37148
37148
|
return false;
|
|
37149
37149
|
}
|
|
37150
37150
|
}
|
|
37151
|
+
var hardlinkable = /* @__PURE__ */ new Map();
|
|
37152
|
+
function deployMethod(libraryRoot, gamePath, current) {
|
|
37153
|
+
try {
|
|
37154
|
+
if (fs4.statSync(libraryRoot).dev !== fs4.statSync(gamePath).dev) return "copy";
|
|
37155
|
+
} catch {
|
|
37156
|
+
return current ?? "copy";
|
|
37157
|
+
}
|
|
37158
|
+
if (current === "link") return "link";
|
|
37159
|
+
const k = `${path4.resolve(libraryRoot).toLowerCase()}|${path4.resolve(gamePath).toLowerCase()}`;
|
|
37160
|
+
if (!hardlinkable.has(k)) hardlinkable.set(k, canHardlink(libraryRoot, gamePath));
|
|
37161
|
+
return hardlinkable.get(k) ? "link" : "copy";
|
|
37162
|
+
}
|
|
37163
|
+
function otherMethod(entry, method) {
|
|
37164
|
+
return method === "link" !== !!entry.ino;
|
|
37165
|
+
}
|
|
37151
37166
|
function sourceReplaced(entry, method, d) {
|
|
37152
37167
|
if (d.content !== void 0) return false;
|
|
37153
37168
|
const src = identity(d.src);
|
|
@@ -37155,10 +37170,10 @@ function sourceReplaced(entry, method, d) {
|
|
|
37155
37170
|
if (method === "link" && entry.ino) return src.ino !== entry.ino;
|
|
37156
37171
|
return src.size !== entry.size || Math.abs(src.mtimeMs - entry.mtimeMs) >= 2e3;
|
|
37157
37172
|
}
|
|
37158
|
-
function stillOurs(entry,
|
|
37173
|
+
function stillOurs(entry, _method, gamePath) {
|
|
37159
37174
|
const id = identity(gameFile(gamePath, entry.path));
|
|
37160
37175
|
if (!id) return "missing";
|
|
37161
|
-
if (
|
|
37176
|
+
if (entry.ino) {
|
|
37162
37177
|
if (id.ino !== entry.ino) return "changed";
|
|
37163
37178
|
if (id.nlink < 2 && entry.mod !== Generated) return "sourceMissing";
|
|
37164
37179
|
return "ok";
|
|
@@ -37174,7 +37189,7 @@ function generatedDiffers(file2, content) {
|
|
|
37174
37189
|
}
|
|
37175
37190
|
function plan(lib, gamePath) {
|
|
37176
37191
|
const manifest = readManifest(gamePath);
|
|
37177
|
-
const method =
|
|
37192
|
+
const method = deployMethod(lib.root, gamePath, manifest?.method);
|
|
37178
37193
|
const desired = /* @__PURE__ */ new Map();
|
|
37179
37194
|
const conflicts = /* @__PURE__ */ new Map();
|
|
37180
37195
|
for (const mod of lib.mods) {
|
|
@@ -37205,6 +37220,7 @@ function plan(lib, gamePath) {
|
|
|
37205
37220
|
if (!e || (d.mod?.id ?? Generated) !== e.mod) return true;
|
|
37206
37221
|
if (d.content !== void 0 && generatedDiffers(gameFile(gamePath, d.rel), d.content)) return true;
|
|
37207
37222
|
if (stillOurs(e, method, gamePath) !== "ok") return true;
|
|
37223
|
+
if (d.content === void 0 && otherMethod(e, method)) return true;
|
|
37208
37224
|
return sourceReplaced(e, method, d);
|
|
37209
37225
|
});
|
|
37210
37226
|
return { method, desired: [...desired.values()], conflicts: [...conflicts.values()], stale, add, vortex: vortexManages(gamePath) };
|
|
@@ -37336,6 +37352,7 @@ function deploy(lib, gamePath, decisions = /* @__PURE__ */ new Map(), progress,
|
|
|
37336
37352
|
const p = plan(lib, gamePath);
|
|
37337
37353
|
const manifest = readManifest(gamePath) ?? { version: 1, library: lib.root, gamePath, method: p.method, time: "", files: [], dirs: [] };
|
|
37338
37354
|
manifest.library = lib.root;
|
|
37355
|
+
manifest.method = p.method;
|
|
37339
37356
|
const result = { linked: 0, removed: 0, kept: 0, backedUp: 0, method: manifest.method, skipped: [], changes: [], modsInGame: 0, lost: lostFiles(p) };
|
|
37340
37357
|
const entries = new Map(manifest.files.map((e) => [key(e.path), e]));
|
|
37341
37358
|
const scope = only ? modDeployPaths(gamePath, p, only) : void 0;
|
|
@@ -37462,6 +37479,7 @@ function pendingChanges(lib, gamePath, p) {
|
|
|
37462
37479
|
const st = stillOurs(e, p.method, gamePath);
|
|
37463
37480
|
if (st === "missing") out.push({ path: d.rel, mod: id, kind: "restore", detail: "missing from the game folder" });
|
|
37464
37481
|
else if (st === "changed") out.push({ path: d.rel, mod: id, kind: "restore", detail: "changed in the game folder; deploy asks whether to keep it" });
|
|
37482
|
+
else if (d.content === void 0 && otherMethod(e, p.method)) out.push({ path: d.rel, mod: id, kind: "relink", detail: p.method === "link" ? "the library is on the game's drive now: a hardlink instead of a copy" : "the library is on another drive now: a copy instead of a hardlink" });
|
|
37465
37483
|
else out.push({ path: d.rel, mod: id, kind: "relink", detail: d.content !== void 0 ? "the generated content changed (load order)" : "the library file was replaced (new version or rebuild)" });
|
|
37466
37484
|
}
|
|
37467
37485
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@edgemodders/edgedeck-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.29.0",
|
|
4
4
|
"description": "EDGEdeck's MCP server and command line: manage Cyberpunk 2077 mods (add, turn on or off, order, deploy, undo, conflicts, loadouts) from an AI assistant or a script.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Edgemodders",
|