@edgemodders/edgedeck-mcp 0.29.0 → 0.29.2

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.
Files changed (3) hide show
  1. package/dist/core.js +57 -21
  2. package/dist/mcp.js +38 -7
  3. package/package.json +1 -1
package/dist/core.js CHANGED
@@ -3842,6 +3842,7 @@ __export(core_exports, {
3842
3842
  registerNxmHandler: () => registerNxmHandler,
3843
3843
  removeMod: () => removeMod,
3844
3844
  removeOlderBridges: () => removeOlderBridges,
3845
+ removeTemp: () => removeTemp,
3845
3846
  renameFamily: () => renameFamily,
3846
3847
  renameLoadout: () => renameLoadout,
3847
3848
  renameSection: () => renameSection,
@@ -3858,6 +3859,7 @@ __export(core_exports, {
3858
3859
  settleNewVersion: () => settleNewVersion,
3859
3860
  signIn: () => signIn,
3860
3861
  signInWithOAuth: () => signInWithOAuth,
3862
+ singleRednodeApp: () => singleRednodeApp,
3861
3863
  snapshot: () => snapshot,
3862
3864
  sourcePath: () => sourcePath,
3863
3865
  ssoPageUrl: () => ssoPageUrl,
@@ -4003,6 +4005,14 @@ function findRarTool() {
4003
4005
  if (unrar) return { exe: unrar, args: (file, dest) => ["x", "-y", "-o+", file, dest + path2.sep] };
4004
4006
  return void 0;
4005
4007
  }
4008
+ function removeTemp(dir) {
4009
+ try {
4010
+ fs2.rmSync(dir, { recursive: true, force: true, maxRetries: 6, retryDelay: 250 });
4011
+ return void 0;
4012
+ } catch (err) {
4013
+ return `could not remove the temporary folder ${dir} (${err instanceof Error ? err.message : String(err)}); Windows clears its temp folder in time`;
4014
+ }
4015
+ }
4006
4016
  async function unpackOrCopy(file, dest, sevenZa) {
4007
4017
  if (isArchiveFile(file)) return extractArchive(file, dest, sevenZa);
4008
4018
  fs2.mkdirSync(dest, { recursive: true });
@@ -4181,7 +4191,7 @@ ${stderr}`.split(/\r?\n/)) if (line.trim()) log(` ${line.trim()}`);
4181
4191
  });
4182
4192
  });
4183
4193
  } finally {
4184
- fs4.rmSync(temp, { recursive: true, force: true });
4194
+ removeTemp(temp);
4185
4195
  }
4186
4196
  if (!hasNet10(root)) throw new Error("The .NET 10 runtime did not install. See the log.");
4187
4197
  return root;
@@ -4231,7 +4241,7 @@ async function installBridge(release, storageDir, sevenZa, progress, log) {
4231
4241
  fs4.mkdirSync(target, { recursive: true });
4232
4242
  await extractArchive(zip, target, sevenZa);
4233
4243
  } finally {
4234
- fs4.rmSync(temp, { recursive: true, force: true });
4244
+ removeTemp(temp);
4235
4245
  }
4236
4246
  if (!fs4.existsSync(exe)) throw new Error("The zip did not contain REDbridge.exe.");
4237
4247
  log(`REDbridge ${release.version} installed at ${exe}`);
@@ -4306,7 +4316,8 @@ function folderName(name) {
4306
4316
  return name.replace(/[<>:"/\\|?*]+/g, "").trim() || "Mod";
4307
4317
  }
4308
4318
  function rednodeManifest(placements) {
4309
- const p = placements.find((x) => /^red4ext\/plugins\/REDnode\/apps\/[^/]+\/rednode-app\.json$/i.test(x.to));
4319
+ const to = singleRednodeApp(placements.map((x) => x.to));
4320
+ const p = to === void 0 ? void 0 : placements.find((x) => x.to === to);
4310
4321
  if (!p) return void 0;
4311
4322
  try {
4312
4323
  const m = JSON.parse(fs5.readFileSync(p.from, "utf8"));
@@ -4316,6 +4327,12 @@ function rednodeManifest(placements) {
4316
4327
  return void 0;
4317
4328
  }
4318
4329
  }
4330
+ function singleRednodeApp(paths) {
4331
+ const manifests = paths.filter((f) => /^red4ext\/plugins\/REDnode\/apps\/[^/]+\/rednode-app\.json$/i.test(f));
4332
+ if (manifests.length !== 1) return void 0;
4333
+ const folder = manifests[0].slice(0, -"rednode-app.json".length).toLowerCase();
4334
+ return paths.every((f) => f.toLowerCase().startsWith(folder)) ? manifests[0] : void 0;
4335
+ }
4319
4336
  function rednodeId(manifest) {
4320
4337
  try {
4321
4338
  const m = JSON.parse(fs5.readFileSync(manifest, "utf8"));
@@ -5073,11 +5090,8 @@ function baseName(name) {
5073
5090
  return name.toLowerCase().replace(/[_\-]+/g, " ").replace(/\bv?\d+(?:\.\d+)+[a-z0-9]*\b/g, " ").replace(/[^a-z0-9]+/g, " ").trim();
5074
5091
  }
5075
5092
  function rednodePackageId(mod) {
5076
- for (const f of mod.files) {
5077
- const m = /^red4ext\/plugins\/REDnode\/apps\/([^/]+)\/rednode-app\.json$/i.exec(f);
5078
- if (m) return m[1].toLowerCase();
5079
- }
5080
- return void 0;
5093
+ const manifest = singleRednodeApp(mod.files);
5094
+ return manifest ? /^red4ext\/plugins\/REDnode\/apps\/([^/]+)\//i.exec(manifest)[1].toLowerCase() : void 0;
5081
5095
  }
5082
5096
  function familyKey(mod) {
5083
5097
  if (mod.family) return mod.family;
@@ -5098,11 +5112,11 @@ function families(lib) {
5098
5112
  for (const [key2, mods] of byKey) {
5099
5113
  const sorted = [...mods].sort(newestFirst);
5100
5114
  const active = sorted.find((m) => m.enabled) ?? sorted[0];
5101
- const newest2 = sorted[0];
5115
+ const newest = sorted[0];
5102
5116
  let update;
5103
5117
  for (const m of mods) {
5104
5118
  const latest = m.nexus?.latestVersion;
5105
- if (latest && versionNewer(latest, newest2.version) && (!update || versionNewer(latest, update.version))) update = { version: latest, fileId: m.nexus?.latestFileId };
5119
+ if (latest && versionNewer(latest, newest.version) && (!update || versionNewer(latest, update.version))) update = { version: latest, fileId: m.nexus?.latestFileId };
5106
5120
  }
5107
5121
  out.push({ key: key2, name: active.name, nexusId: active.source.modId ?? mods.find((m) => m.source.modId)?.source.modId, mods: sorted, active, on: mods.some((m) => m.enabled), update });
5108
5122
  }
@@ -5252,8 +5266,8 @@ async function frameworkStates(lib, fetchImpl, api) {
5252
5266
  const family = frameworkFamily(lib, source);
5253
5267
  try {
5254
5268
  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) };
5269
+ const newest = family?.mods[0]?.version ?? "";
5270
+ return { source, family, release, wanted: !family || !!newest && versionNewer(release.version, newest) };
5257
5271
  } catch (err) {
5258
5272
  return { source, family, error: err instanceof Error ? err.message : String(err), wanted: !family };
5259
5273
  }
@@ -5833,6 +5847,7 @@ function describeHandler(command) {
5833
5847
  }
5834
5848
 
5835
5849
  // src/project.ts
5850
+ var import_node_crypto = require("node:crypto");
5836
5851
  var fs11 = __toESM(require("node:fs"));
5837
5852
  var path10 = __toESM(require("node:path"));
5838
5853
  function addToProject(lib, mod, files, projectFile, replace = /* @__PURE__ */ new Set()) {
@@ -5884,13 +5899,22 @@ function classifyProject(projectFile) {
5884
5899
  if (!fs11.existsSync(packed)) return { placements: [], kinds: [], unplaced: [], ignored: [] };
5885
5900
  return classify(packed, projectName(projectFile));
5886
5901
  }
5887
- function newest(dir) {
5888
- let t = 0;
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);
5892
- }
5893
- return t;
5902
+ function packedState(dir) {
5903
+ let newest = 0;
5904
+ const files = [];
5905
+ const walk2 = (d, rel) => {
5906
+ for (const e of fs11.readdirSync(d, { withFileTypes: true })) {
5907
+ const p = path10.join(d, e.name);
5908
+ const r = rel ? `${rel}/${e.name}` : e.name;
5909
+ if (e.isDirectory()) walk2(p, r);
5910
+ else {
5911
+ newest = Math.max(newest, fs11.statSync(p).mtimeMs);
5912
+ files.push(r.toLowerCase());
5913
+ }
5914
+ }
5915
+ };
5916
+ walk2(dir, "");
5917
+ return { newest, list: (0, import_node_crypto.createHash)("sha1").update(files.sort().join("\n")).digest("hex").slice(0, 16) };
5894
5918
  }
5895
5919
  function syncProjects(lib) {
5896
5920
  const refreshed = [];
@@ -5905,7 +5929,15 @@ function syncProjects(lib) {
5905
5929
  return t;
5906
5930
  }
5907
5931
  }, 0);
5908
- if (newest(packed) <= libNewest + 1e3) continue;
5932
+ const state = packedState(packed);
5933
+ const listChanged = mod.packedList !== void 0 && mod.packedList !== state.list;
5934
+ if (state.newest <= libNewest + 1e3 && !listChanged) {
5935
+ if (mod.packedList === void 0) {
5936
+ mod.packedList = state.list;
5937
+ saveMod(lib, mod);
5938
+ }
5939
+ continue;
5940
+ }
5909
5941
  const c = classifyProject(mod.source.path);
5910
5942
  if (!c.placements.length) continue;
5911
5943
  const keep = /* @__PURE__ */ new Set();
@@ -5918,6 +5950,7 @@ function syncProjects(lib) {
5918
5950
  for (const f of mod.files) if (!keep.has(f)) fs11.rmSync(sourcePath(lib, mod, f), { force: true });
5919
5951
  mod.files = [...keep].sort();
5920
5952
  mod.kinds = c.kinds;
5953
+ mod.packedList = state.list;
5921
5954
  saveMod(lib, mod);
5922
5955
  refreshed.push(mod);
5923
5956
  }
@@ -5978,6 +6011,7 @@ function installProject(lib, projectFile, beside) {
5978
6011
  for (const f of existing.files) if (!keep.has(f)) fs11.rmSync(sourcePath(lib, existing, f), { force: true });
5979
6012
  existing.files = [...keep].sort();
5980
6013
  existing.kinds = c.kinds;
6014
+ existing.packedList = packedState(packedFolder(projectFile)).list;
5981
6015
  activateVersion(lib, existing);
5982
6016
  existing.enabled = true;
5983
6017
  saveMod(lib, existing);
@@ -6442,7 +6476,7 @@ ${mod.files.join("\n")}`;
6442
6476
  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` : "";
6443
6477
  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(", ")}` : ""}` };
6444
6478
  } finally {
6445
- if (temp) fs15.rmSync(temp, { recursive: true, force: true });
6479
+ if (temp) removeTemp(temp);
6446
6480
  }
6447
6481
  },
6448
6482
  /** Adds a REDcode or WolvenKit project's packed output, or refreshes the mod that follows it, and turns it on. */
@@ -6704,6 +6738,7 @@ ${mod.files.join("\n")}`;
6704
6738
  registerNxmHandler,
6705
6739
  removeMod,
6706
6740
  removeOlderBridges,
6741
+ removeTemp,
6707
6742
  renameFamily,
6708
6743
  renameLoadout,
6709
6744
  renameSection,
@@ -6720,6 +6755,7 @@ ${mod.files.join("\n")}`;
6720
6755
  settleNewVersion,
6721
6756
  signIn,
6722
6757
  signInWithOAuth,
6758
+ singleRednodeApp,
6723
6759
  snapshot,
6724
6760
  sourcePath,
6725
6761
  ssoPageUrl,
package/dist/mcp.js CHANGED
@@ -36665,7 +36665,8 @@ function folderName(name) {
36665
36665
  return name.replace(/[<>:"/\\|?*]+/g, "").trim() || "Mod";
36666
36666
  }
36667
36667
  function rednodeManifest(placements) {
36668
- const p = placements.find((x) => /^red4ext\/plugins\/REDnode\/apps\/[^/]+\/rednode-app\.json$/i.test(x.to));
36668
+ const to = singleRednodeApp(placements.map((x) => x.to));
36669
+ const p = to === void 0 ? void 0 : placements.find((x) => x.to === to);
36669
36670
  if (!p) return void 0;
36670
36671
  try {
36671
36672
  const m = JSON.parse(fs.readFileSync(p.from, "utf8"));
@@ -36675,6 +36676,12 @@ function rednodeManifest(placements) {
36675
36676
  return void 0;
36676
36677
  }
36677
36678
  }
36679
+ function singleRednodeApp(paths) {
36680
+ const manifests = paths.filter((f) => /^red4ext\/plugins\/REDnode\/apps\/[^/]+\/rednode-app\.json$/i.test(f));
36681
+ if (manifests.length !== 1) return void 0;
36682
+ const folder = manifests[0].slice(0, -"rednode-app.json".length).toLowerCase();
36683
+ return paths.every((f) => f.toLowerCase().startsWith(folder)) ? manifests[0] : void 0;
36684
+ }
36678
36685
  function rednodeId(manifest) {
36679
36686
  try {
36680
36687
  const m = JSON.parse(fs.readFileSync(manifest, "utf8"));
@@ -36925,11 +36932,8 @@ function baseName(name) {
36925
36932
  return name.toLowerCase().replace(/[_\-]+/g, " ").replace(/\bv?\d+(?:\.\d+)+[a-z0-9]*\b/g, " ").replace(/[^a-z0-9]+/g, " ").trim();
36926
36933
  }
36927
36934
  function rednodePackageId(mod) {
36928
- for (const f of mod.files) {
36929
- const m = /^red4ext\/plugins\/REDnode\/apps\/([^/]+)\/rednode-app\.json$/i.exec(f);
36930
- if (m) return m[1].toLowerCase();
36931
- }
36932
- return void 0;
36935
+ const manifest = singleRednodeApp(mod.files);
36936
+ return manifest ? /^red4ext\/plugins\/REDnode\/apps\/([^/]+)\//i.exec(manifest)[1].toLowerCase() : void 0;
36933
36937
  }
36934
36938
  function familyKey(mod) {
36935
36939
  if (mod.family) return mod.family;
@@ -37530,6 +37534,14 @@ function findRarTool() {
37530
37534
  if (unrar) return { exe: unrar, args: (file2, dest) => ["x", "-y", "-o+", file2, dest + path5.sep] };
37531
37535
  return void 0;
37532
37536
  }
37537
+ function removeTemp(dir) {
37538
+ try {
37539
+ fs5.rmSync(dir, { recursive: true, force: true, maxRetries: 6, retryDelay: 250 });
37540
+ return void 0;
37541
+ } catch (err) {
37542
+ return `could not remove the temporary folder ${dir} (${err instanceof Error ? err.message : String(err)}); Windows clears its temp folder in time`;
37543
+ }
37544
+ }
37533
37545
  async function unpackOrCopy(file2, dest, sevenZa) {
37534
37546
  if (isArchiveFile(file2)) return extractArchive(file2, dest, sevenZa);
37535
37547
  fs5.mkdirSync(dest, { recursive: true });
@@ -37707,6 +37719,7 @@ function importLoadout(lib, file2) {
37707
37719
  }
37708
37720
 
37709
37721
  // src/project.ts
37722
+ var import_node_crypto = require("node:crypto");
37710
37723
  var fs7 = __toESM(require("node:fs"));
37711
37724
  var path7 = __toESM(require("node:path"));
37712
37725
  function projectName(projectFile) {
@@ -37726,6 +37739,23 @@ function classifyProject(projectFile) {
37726
37739
  if (!fs7.existsSync(packed)) return { placements: [], kinds: [], unplaced: [], ignored: [] };
37727
37740
  return classify(packed, projectName(projectFile));
37728
37741
  }
37742
+ function packedState(dir) {
37743
+ let newest = 0;
37744
+ const files = [];
37745
+ const walk2 = (d, rel) => {
37746
+ for (const e of fs7.readdirSync(d, { withFileTypes: true })) {
37747
+ const p = path7.join(d, e.name);
37748
+ const r = rel ? `${rel}/${e.name}` : e.name;
37749
+ if (e.isDirectory()) walk2(p, r);
37750
+ else {
37751
+ newest = Math.max(newest, fs7.statSync(p).mtimeMs);
37752
+ files.push(r.toLowerCase());
37753
+ }
37754
+ }
37755
+ };
37756
+ walk2(dir, "");
37757
+ return { newest, list: (0, import_node_crypto.createHash)("sha1").update(files.sort().join("\n")).digest("hex").slice(0, 16) };
37758
+ }
37729
37759
  function projectMod(lib, projectFile) {
37730
37760
  return lib.mods.find((m) => m.source.type === "project" && m.source.path?.toLowerCase() === projectFile.toLowerCase());
37731
37761
  }
@@ -37781,6 +37811,7 @@ function installProject(lib, projectFile, beside) {
37781
37811
  for (const f of existing.files) if (!keep.has(f)) fs7.rmSync(sourcePath(lib, existing, f), { force: true });
37782
37812
  existing.files = [...keep].sort();
37783
37813
  existing.kinds = c.kinds;
37814
+ existing.packedList = packedState(packedFolder(projectFile)).list;
37784
37815
  activateVersion(lib, existing);
37785
37816
  existing.enabled = true;
37786
37817
  saveMod(lib, existing);
@@ -38130,7 +38161,7 @@ ${mod.files.join("\n")}`;
38130
38161
  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` : "";
38131
38162
  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(", ")}` : ""}` };
38132
38163
  } finally {
38133
- if (temp) fs11.rmSync(temp, { recursive: true, force: true });
38164
+ if (temp) removeTemp(temp);
38134
38165
  }
38135
38166
  },
38136
38167
  /** Adds a REDcode or WolvenKit project's packed output, or refreshes the mod that follows it, and turns it on. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@edgemodders/edgedeck-mcp",
3
- "version": "0.29.0",
3
+ "version": "0.29.2",
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",