gdharness 0.13.2 → 0.13.3

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/build/index.js CHANGED
@@ -10142,19 +10142,18 @@ import { execFile as execFile4, spawn } from "node:child_process";
10142
10142
  import { randomUUID as randomUUID2 } from "node:crypto";
10143
10143
  import {
10144
10144
  closeSync,
10145
- existsSync as existsSync8,
10145
+ existsSync as existsSync9,
10146
10146
  fstatSync,
10147
10147
  mkdirSync as mkdirSync5,
10148
10148
  mkdtempSync as mkdtempSync2,
10149
10149
  openSync as openSync2,
10150
10150
  readdirSync as readdirSync6,
10151
- readFileSync as readFileSync10,
10151
+ readFileSync as readFileSync11,
10152
10152
  readSync,
10153
- realpathSync as realpathSync2,
10154
10153
  rmSync as rmSync5
10155
10154
  } from "node:fs";
10156
10155
  import { tmpdir as tmpdir4 } from "node:os";
10157
- import { basename as basename2, dirname as dirname4, join as join10, normalize as normalize2 } from "node:path";
10156
+ import { basename as basename2, dirname as dirname4, join as join11, normalize as normalize2 } from "node:path";
10158
10157
  import { setTimeout as delay3 } from "node:timers/promises";
10159
10158
  import { fileURLToPath as fileURLToPath2 } from "node:url";
10160
10159
  import { promisify as promisify4 } from "node:util";
@@ -24779,7 +24778,7 @@ function withdrawBridge(path) {
24779
24778
  }
24780
24779
 
24781
24780
  // src/class-cache.ts
24782
- import { existsSync as existsSync2, readdirSync, readFileSync as readFileSync4 } from "node:fs";
24781
+ import { existsSync as existsSync2, readdirSync, readFileSync as readFileSync4, statSync } from "node:fs";
24783
24782
  import { join as join3 } from "node:path";
24784
24783
  function declaredClasses(projectPath) {
24785
24784
  const declared = new Map;
@@ -24817,6 +24816,14 @@ function cachedClasses(projectPath) {
24817
24816
  }
24818
24817
  return listed;
24819
24818
  }
24819
+ function cacheWrittenAt(projectPath) {
24820
+ const cache = join3(projectPath, ".godot", "global_script_class_cache.cfg");
24821
+ try {
24822
+ return statSync(cache).mtimeMs;
24823
+ } catch {
24824
+ return null;
24825
+ }
24826
+ }
24820
24827
  function staleAgainst(cached, projectPath) {
24821
24828
  return [...declaredClasses(projectPath)].filter(([name, path]) => cached.get(name) !== path).map(([name]) => name);
24822
24829
  }
@@ -24829,6 +24836,33 @@ function unseenByEditor(projectPath, editorHolds) {
24829
24836
  return [...declaredClasses(projectPath)].filter(([name]) => !held.has(name)).map(([className, path]) => ({ className, path }));
24830
24837
  }
24831
24838
 
24839
+ // src/config-pin.ts
24840
+ import { existsSync as existsSync3, readFileSync as readFileSync5 } from "node:fs";
24841
+ import { join as join4 } from "node:path";
24842
+ var PINNED = /\bgdharness@(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)\b/g;
24843
+ function versionsNamedInConfig(projectPath) {
24844
+ const config = join4(projectPath, ".mcp.json");
24845
+ if (!existsSync3(config)) {
24846
+ return [];
24847
+ }
24848
+ let text;
24849
+ try {
24850
+ text = readFileSync5(config, "utf8");
24851
+ JSON.parse(text);
24852
+ } catch {
24853
+ return [];
24854
+ }
24855
+ return [...new Set([...text.matchAll(PINNED)].map((found) => found[1] ?? ""))].filter((version) => version !== "");
24856
+ }
24857
+ function configDisagrees(projectPath, serverVersion) {
24858
+ const named = versionsNamedInConfig(projectPath);
24859
+ const others = named.filter((version) => version !== serverVersion);
24860
+ if (others.length === 0) {
24861
+ return null;
24862
+ }
24863
+ return `.mcp.json in this project names gdharness@${others.join(" and gdharness@")}, and this server ` + `is ${serverVersion}. Whichever is meant to be right, nothing will change version until the ` + "MCP server is reconnected, and a pin kept anywhere else does not reach this file.";
24864
+ }
24865
+
24832
24866
  // src/dap_client.ts
24833
24867
  import { createConnection } from "node:net";
24834
24868
  import { setTimeout as delay } from "node:timers/promises";
@@ -25554,11 +25588,19 @@ var import_websocket = __toESM(require_websocket(), 1);
25554
25588
  var import_websocket_server = __toESM(require_websocket_server(), 1);
25555
25589
 
25556
25590
  // src/paths.ts
25591
+ import { realpathSync } from "node:fs";
25557
25592
  import { isAbsolute, relative, resolve, sep } from "node:path";
25558
25593
  var RESOURCE_SCHEME = "res://";
25559
25594
  var OTHER_SCHEME = /^[a-zA-Z][a-zA-Z\d+.-]+:\/\//;
25595
+ function realPathOr(path) {
25596
+ try {
25597
+ return realpathSync.native(path);
25598
+ } catch {
25599
+ return path;
25600
+ }
25601
+ }
25560
25602
  function isSameDirectory(onePath, otherPath) {
25561
- return relative(resolve(onePath), resolve(otherPath)) === "";
25603
+ return relative(realPathOr(resolve(onePath)), realPathOr(resolve(otherPath))) === "";
25562
25604
  }
25563
25605
  function isWithinRoot(rootPath, candidatePath) {
25564
25606
  const step = relative(resolve(rootPath), resolve(candidatePath));
@@ -26082,14 +26124,14 @@ function getDefaultBridge(ownProject = null) {
26082
26124
 
26083
26125
  // src/godot-path.ts
26084
26126
  import { execFile } from "node:child_process";
26085
- import { existsSync as existsSync4 } from "node:fs";
26127
+ import { existsSync as existsSync5 } from "node:fs";
26086
26128
  import { normalize } from "node:path";
26087
26129
  import { promisify } from "node:util";
26088
26130
 
26089
26131
  // src/detection.ts
26090
- import { existsSync as existsSync3, readdirSync as readdirSync2, statSync } from "node:fs";
26132
+ import { existsSync as existsSync4, readdirSync as readdirSync2, statSync as statSync2 } from "node:fs";
26091
26133
  import { homedir as homedir2 } from "node:os";
26092
- import { join as join4 } from "node:path";
26134
+ import { join as join5 } from "node:path";
26093
26135
  function resolveHomeDirectory() {
26094
26136
  try {
26095
26137
  return homedir2();
@@ -26098,7 +26140,7 @@ function resolveHomeDirectory() {
26098
26140
  }
26099
26141
  }
26100
26142
  function scanDirectoryForGodotBinaries(directory, platform) {
26101
- if (!directory || !existsSync3(directory)) {
26143
+ if (!directory || !existsSync4(directory)) {
26102
26144
  return [];
26103
26145
  }
26104
26146
  let entries;
@@ -26113,16 +26155,16 @@ function scanDirectoryForGodotBinaries(directory, platform) {
26113
26155
  if (!pattern.test(name)) {
26114
26156
  continue;
26115
26157
  }
26116
- const fullPath = join4(directory, name);
26158
+ const fullPath = join5(directory, name);
26117
26159
  try {
26118
- const stat = statSync(fullPath);
26160
+ const stat = statSync2(fullPath);
26119
26161
  if (stat.isFile()) {
26120
26162
  matches.push({ name, mtime: stat.mtimeMs });
26121
26163
  }
26122
26164
  } catch {}
26123
26165
  }
26124
26166
  matches.sort((a, b) => b.mtime - a.mtime);
26125
- return matches.map((m) => join4(directory, m.name));
26167
+ return matches.map((m) => join5(directory, m.name));
26126
26168
  }
26127
26169
  function conventionalPaths(platform, home) {
26128
26170
  const paths = ["godot"];
@@ -26254,7 +26296,7 @@ class GodotLocator {
26254
26296
  return known;
26255
26297
  }
26256
26298
  let ok = false;
26257
- if (path === "godot" || existsSync4(path)) {
26299
+ if (path === "godot" || existsSync5(path)) {
26258
26300
  try {
26259
26301
  await run(path, ["--version"]);
26260
26302
  ok = true;
@@ -26271,7 +26313,7 @@ class GodotLocator {
26271
26313
  import { execFile as execFile2 } from "node:child_process";
26272
26314
  import { mkdtempSync, rmSync as rmSync2, writeFileSync as writeFileSync3 } from "node:fs";
26273
26315
  import { tmpdir as tmpdir2 } from "node:os";
26274
- import { join as join5 } from "node:path";
26316
+ import { join as join6 } from "node:path";
26275
26317
  import { promisify as promisify2 } from "node:util";
26276
26318
  var run2 = promisify2(execFile2);
26277
26319
  function snakeCased(params) {
@@ -26312,13 +26354,13 @@ function reason(stdout, stderr) {
26312
26354
  return stdout.trim().split(/\r?\n/).at(-1) ?? "no output at all";
26313
26355
  }
26314
26356
  async function runOperation(engine, operation, params, projectPath) {
26315
- const paramsDir = mkdtempSync(join5(tmpdir2(), "gdharness-params-"));
26316
- const paramsFile = join5(paramsDir, `${operation}.json`);
26357
+ const paramsDir = mkdtempSync(join6(tmpdir2(), "gdharness-params-"));
26358
+ const paramsFile = join6(paramsDir, `${operation}.json`);
26317
26359
  writeFileSync3(paramsFile, JSON.stringify(snakeCased(params)), "utf8");
26318
26360
  const args = [
26319
26361
  "--headless",
26320
26362
  "--log-file",
26321
- join5(paramsDir, "engine.log"),
26363
+ join6(paramsDir, "engine.log"),
26322
26364
  "--path",
26323
26365
  projectPath,
26324
26366
  "--script",
@@ -26392,6 +26434,9 @@ var HEADLESS_OPERATIONS = dictionary({
26392
26434
  inheritance: "inspect_inheritance"
26393
26435
  })
26394
26436
  });
26437
+ var EDITOR_READS = dictionary({
26438
+ project_settings: dictionary({ get: "get_project_setting" })
26439
+ });
26395
26440
 
26396
26441
  // src/issues.ts
26397
26442
  import { createHash as createHash2 } from "node:crypto";
@@ -26718,7 +26763,7 @@ function orphansPrinted(printed) {
26718
26763
  }
26719
26764
 
26720
26765
  // src/lsp_client.ts
26721
- import { realpathSync } from "node:fs";
26766
+ import { realpathSync as realpathSync2 } from "node:fs";
26722
26767
  import { readFile, realpath } from "node:fs/promises";
26723
26768
  import { createConnection as createConnection2 } from "node:net";
26724
26769
  import { dirname as dirname2, resolve as resolve2 } from "node:path";
@@ -26736,7 +26781,7 @@ function diagnosticsKey(uri) {
26736
26781
  return decoded;
26737
26782
  }
26738
26783
  try {
26739
- return realpathSync.native(fileURLToPath(decoded)).toLowerCase();
26784
+ return realpathSync2.native(fileURLToPath(decoded)).toLowerCase();
26740
26785
  } catch {
26741
26786
  return decoded.toLowerCase();
26742
26787
  }
@@ -27287,14 +27332,14 @@ function secondsFromClock(printed) {
27287
27332
  }
27288
27333
 
27289
27334
  // src/project-scan.ts
27290
- import { existsSync as existsSync5, readdirSync as readdirSync3, readFileSync as readFileSync5 } from "node:fs";
27291
- import { join as join6 } from "node:path";
27335
+ import { existsSync as existsSync6, readdirSync as readdirSync3, readFileSync as readFileSync6 } from "node:fs";
27336
+ import { join as join7 } from "node:path";
27292
27337
  var SKIPPED = new Set(["node_modules"]);
27293
27338
  function skipped(name) {
27294
27339
  return name.startsWith(".") || SKIPPED.has(name);
27295
27340
  }
27296
27341
  function steppedOver(directory) {
27297
- return existsSync5(join6(directory, ".gdignore"));
27342
+ return existsSync6(join7(directory, ".gdignore"));
27298
27343
  }
27299
27344
  var ASSET_EXTENSIONS = new Set(["png", "jpg", "jpeg", "webp", "svg", "ttf", "otf", "wav", "mp3", "ogg"]);
27300
27345
  function projectStructure(projectPath) {
@@ -27308,7 +27353,7 @@ function projectStructure(projectPath) {
27308
27353
  continue;
27309
27354
  }
27310
27355
  if (entry.isDirectory()) {
27311
- visit(join6(directory, entry.name));
27356
+ visit(join7(directory, entry.name));
27312
27357
  } else if (entry.isFile()) {
27313
27358
  const extension = entry.name.split(".").pop()?.toLowerCase() ?? "";
27314
27359
  if (extension === "tscn") {
@@ -27353,7 +27398,7 @@ function searchProject(projectPath, options) {
27353
27398
  if (skipped(entry.name)) {
27354
27399
  continue;
27355
27400
  }
27356
- const entryPath = join6(directory, entry.name);
27401
+ const entryPath = join7(directory, entry.name);
27357
27402
  if (entry.isDirectory()) {
27358
27403
  visit(entryPath);
27359
27404
  continue;
@@ -27364,7 +27409,7 @@ function searchProject(projectPath, options) {
27364
27409
  }
27365
27410
  result.summary.files_searched += 1;
27366
27411
  const matches = [];
27367
- for (const [index, line] of readFileSync5(entryPath, "utf8").split(`
27412
+ for (const [index, line] of readFileSync6(entryPath, "utf8").split(`
27368
27413
  `).entries()) {
27369
27414
  if (full()) {
27370
27415
  break;
@@ -27387,7 +27432,7 @@ function searchProject(projectPath, options) {
27387
27432
  }
27388
27433
 
27389
27434
  // src/resources.ts
27390
- import { readFileSync as readFileSync6 } from "node:fs";
27435
+ import { readFileSync as readFileSync7 } from "node:fs";
27391
27436
  import { extname, resolve as resolve3 } from "node:path";
27392
27437
  var STATIC_RESOURCES = [
27393
27438
  {
@@ -27558,7 +27603,7 @@ function readResourceText(uri, getProjectPath) {
27558
27603
  const parsedUri = parseGodotUri(uri);
27559
27604
  if (parsedUri.kind === "project-info") {
27560
27605
  const projectFilePath = resolveProjectFile(projectPath, "project.godot");
27561
- const rawProject = readFileSync6(projectFilePath, "utf-8");
27606
+ const rawProject = readFileSync7(projectFilePath, "utf-8");
27562
27607
  const parsedProject = parseProjectGodot(rawProject);
27563
27608
  return {
27564
27609
  mimeType: "application/json",
@@ -27567,7 +27612,7 @@ function readResourceText(uri, getProjectPath) {
27567
27612
  }
27568
27613
  const filePath = resolveProjectFile(projectPath, parsedUri.resourcePath);
27569
27614
  ensureAllowedExtension(parsedUri.kind, filePath);
27570
- const text = readFileSync6(filePath, "utf-8");
27615
+ const text = readFileSync7(filePath, "utf-8");
27571
27616
  return {
27572
27617
  mimeType: parsedUri.kind === "script" ? "text/x-gdscript" : "text/plain",
27573
27618
  text
@@ -27605,14 +27650,14 @@ function setupResourceHandlers(mcp, getProjectPath) {
27605
27650
 
27606
27651
  // src/run-record.ts
27607
27652
  import { execFileSync } from "node:child_process";
27608
- import { mkdirSync as mkdirSync3, openSync, readdirSync as readdirSync5, readFileSync as readFileSync8, rmSync as rmSync3, statSync as statSync2, writeFileSync as writeFileSync4 } from "node:fs";
27609
- import { basename, join as join8 } from "node:path";
27653
+ import { mkdirSync as mkdirSync3, openSync, readdirSync as readdirSync5, readFileSync as readFileSync9, rmSync as rmSync3, statSync as statSync3, writeFileSync as writeFileSync4 } from "node:fs";
27654
+ import { basename, join as join9 } from "node:path";
27610
27655
 
27611
27656
  // src/runtime-client.ts
27612
- import { existsSync as existsSync6, readdirSync as readdirSync4, readFileSync as readFileSync7, unlinkSync } from "node:fs";
27657
+ import { existsSync as existsSync7, readdirSync as readdirSync4, readFileSync as readFileSync8, unlinkSync } from "node:fs";
27613
27658
  import { createConnection as createConnection3 } from "node:net";
27614
27659
  import { tmpdir as tmpdir3 } from "node:os";
27615
- import { join as join7, resolve as resolve4 } from "node:path";
27660
+ import { join as join8, resolve as resolve4 } from "node:path";
27616
27661
  import { setTimeout as delay2 } from "node:timers/promises";
27617
27662
 
27618
27663
  // src/tool-args.ts
@@ -27680,13 +27725,13 @@ function runtimeDirectory(variables = process.env) {
27680
27725
  return explicit;
27681
27726
  }
27682
27727
  const perUser = envValue("XDG_RUNTIME_DIR", variables);
27683
- return join7(perUser ?? tmpdir3(), "gdharness");
27728
+ return join8(perUser ?? tmpdir3(), "gdharness");
27684
27729
  }
27685
27730
  function runtimeDirectories(variables = process.env) {
27686
27731
  const candidates = [runtimeDirectory(variables)];
27687
27732
  const fallbacks = process.platform === "win32" ? [envValue("SystemRoot", variables) ?? envValue("windir", variables) ?? "C:\\Windows"] : ["/tmp", "/var/tmp"];
27688
27733
  for (const base of fallbacks) {
27689
- candidates.push(join7(base, process.platform === "win32" ? "Temp" : "", "gdharness"));
27734
+ candidates.push(join8(base, process.platform === "win32" ? "Temp" : "", "gdharness"));
27690
27735
  }
27691
27736
  return [...new Set(candidates.map((path) => resolve4(path)))];
27692
27737
  }
@@ -27701,7 +27746,7 @@ function processAlive(pid) {
27701
27746
  function parseAnnouncement(file, pid) {
27702
27747
  let fields;
27703
27748
  try {
27704
- fields = asParams(JSON.parse(readFileSync7(file, "utf8")));
27749
+ fields = asParams(JSON.parse(readFileSync8(file, "utf8")));
27705
27750
  } catch {
27706
27751
  return { kind: "rubbish" };
27707
27752
  }
@@ -27740,7 +27785,7 @@ function discoverRuntimes(directories = runtimeDirectories()) {
27740
27785
  return runtimesAnnounced(directories).running;
27741
27786
  }
27742
27787
  function announcedIn(directory) {
27743
- if (!existsSync6(directory)) {
27788
+ if (!existsSync7(directory)) {
27744
27789
  return [];
27745
27790
  }
27746
27791
  const found = [];
@@ -27749,7 +27794,7 @@ function announcedIn(directory) {
27749
27794
  if (!match) {
27750
27795
  continue;
27751
27796
  }
27752
- const file = join7(directory, entry);
27797
+ const file = join8(directory, entry);
27753
27798
  const pid = Number.parseInt(match[1] ?? "", 10);
27754
27799
  const announced = processAlive(pid) ? parseAnnouncement(file, pid) : { kind: "rubbish" };
27755
27800
  if (announced.kind === "rubbish") {
@@ -27910,17 +27955,17 @@ function runtimeRequest(endpoint, command, params, timeoutMs) {
27910
27955
 
27911
27956
  // src/run-record.ts
27912
27957
  function runsDirectory() {
27913
- return join8(runtimeDirectory(), "runs");
27958
+ return join9(runtimeDirectory(), "runs");
27914
27959
  }
27915
27960
  function recordPath() {
27916
- return join8(runsDirectory(), "run.json");
27961
+ return join9(runsDirectory(), "run.json");
27917
27962
  }
27918
27963
  function recordPaths() {
27919
- return runtimeDirectories().map((directory) => join8(directory, "runs", "run.json"));
27964
+ return runtimeDirectories().map((directory) => join9(directory, "runs", "run.json"));
27920
27965
  }
27921
27966
  function openTranscript(startedAt) {
27922
27967
  mkdirSync3(runsDirectory(), { recursive: true });
27923
- const path = join8(runsDirectory(), `run-${startedAt}.log`);
27968
+ const path = join9(runsDirectory(), `run-${startedAt}.log`);
27924
27969
  return { path, fd: openSync(path, "a") };
27925
27970
  }
27926
27971
  function writeRunRecord(record) {
@@ -27949,7 +27994,7 @@ function readRunRecord() {
27949
27994
  function recordAt(path) {
27950
27995
  let parsed;
27951
27996
  try {
27952
- parsed = JSON.parse(readFileSync8(path, "utf8"));
27997
+ parsed = JSON.parse(readFileSync9(path, "utf8"));
27953
27998
  } catch {
27954
27999
  return null;
27955
28000
  }
@@ -27985,7 +28030,7 @@ function runningAs(pid) {
27985
28030
  }
27986
28031
  try {
27987
28032
  if (process.platform === "linux") {
27988
- const raw = readFileSync8(`/proc/${pid}/cmdline`, "utf8").replaceAll("\x00", " ").trim();
28033
+ const raw = readFileSync9(`/proc/${pid}/cmdline`, "utf8").replaceAll("\x00", " ").trim();
27989
28034
  return raw === "" ? null : { kind: "commandLine", text: raw };
27990
28035
  }
27991
28036
  const args = execFileSync("ps", ["-p", String(pid), "-o", "args="], {
@@ -28060,7 +28105,7 @@ function clearRunRecord(ours = () => true) {
28060
28105
  }
28061
28106
  function ageMs(path, now) {
28062
28107
  try {
28063
- return now - statSync2(path).mtimeMs;
28108
+ return now - statSync3(path).mtimeMs;
28064
28109
  } catch {
28065
28110
  return 0;
28066
28111
  }
@@ -28078,7 +28123,7 @@ function sweepTranscripts(keepMs = 24 * 60 * 60 * 1000, now = Date.now()) {
28078
28123
  if (!entry.startsWith("run-") || !entry.endsWith(".log")) {
28079
28124
  continue;
28080
28125
  }
28081
- const path = join8(directory, entry);
28126
+ const path = join9(directory, entry);
28082
28127
  if (path === keep || ageMs(path, now) < keepMs) {
28083
28128
  continue;
28084
28129
  }
@@ -28089,8 +28134,8 @@ function sweepTranscripts(keepMs = 24 * 60 * 60 * 1000, now = Date.now()) {
28089
28134
  }
28090
28135
 
28091
28136
  // src/setup.ts
28092
- import { cpSync, existsSync as existsSync7, mkdirSync as mkdirSync4, readFileSync as readFileSync9, rmSync as rmSync4, writeFileSync as writeFileSync5 } from "node:fs";
28093
- import { dirname as dirname3, join as join9 } from "node:path";
28137
+ import { cpSync, existsSync as existsSync8, mkdirSync as mkdirSync4, readFileSync as readFileSync10, rmSync as rmSync4, writeFileSync as writeFileSync5 } from "node:fs";
28138
+ import { dirname as dirname3, join as join10 } from "node:path";
28094
28139
  var ADDONS = ["gdharness_editor", "gdharness_runtime", "auto_reload"];
28095
28140
  var RUNTIME_AUTOLOAD = {
28096
28141
  name: "GdharnessRuntime",
@@ -28098,15 +28143,23 @@ var RUNTIME_AUTOLOAD = {
28098
28143
  };
28099
28144
  var VERSION_MARKER = ".gdharness-version";
28100
28145
  function installedAddonVersion(projectPath) {
28101
- const marker = join9(projectPath, "addons", ADDONS[0], VERSION_MARKER);
28102
- if (!existsSync7(marker)) {
28146
+ const marker = join10(projectPath, "addons", ADDONS[0], VERSION_MARKER);
28147
+ if (!existsSync8(marker)) {
28103
28148
  return null;
28104
28149
  }
28105
- const said = readFileSync9(marker, "utf8").trim();
28150
+ const said = readFileSync10(marker, "utf8").trim();
28106
28151
  return said === "" ? null : said;
28107
28152
  }
28108
28153
 
28109
28154
  // src/tool-definitions.ts
28155
+ var PROJECT_INFO_SECTIONS = [
28156
+ "autoloads",
28157
+ "plugins",
28158
+ "export_presets",
28159
+ "audio_buses",
28160
+ "health",
28161
+ "validation"
28162
+ ];
28110
28163
  function opTakes(spec, op, name) {
28111
28164
  const ops = spec.parameters[name]?.ops;
28112
28165
  return ops === undefined || ops.includes(op);
@@ -28140,7 +28193,7 @@ var NODE_PATH = {
28140
28193
  };
28141
28194
  var PROPERTIES = {
28142
28195
  type: "object",
28143
- description: 'Properties to set, keyed by Godot property name. Vectors, colours and the like may be written as {"x": 1, "y": 2} or tagged {"_type": "Vector2", "x": 1, "y": 2}.',
28196
+ description: 'Properties to set, keyed by Godot property name. Vectors, colours and the like may be written as {"x": 1, "y": 2} or tagged {"_type": "Vector2", "x": 1, "y": 2}, which is the form a read answers with, so a value read off one node can be written straight to another.',
28144
28197
  additionalProperties: true
28145
28198
  };
28146
28199
  var XY = {
@@ -28263,10 +28316,7 @@ var TOOL_SPECS = [
28263
28316
  projectPath: PROJECT_PATH,
28264
28317
  include: {
28265
28318
  type: "array",
28266
- items: {
28267
- type: "string",
28268
- enum: ["autoloads", "plugins", "export_presets", "audio_buses", "health", "validation"]
28269
- },
28319
+ items: { type: "string", enum: PROJECT_INFO_SECTIONS },
28270
28320
  description: "Extra sections: registered autoloads, addons and whether each is enabled, export presets, the audio bus layout, a health report, or export validation."
28271
28321
  },
28272
28322
  preset: { type: "string", description: "For validation: the export preset to validate against." },
@@ -28284,6 +28334,12 @@ var TOOL_SPECS = [
28284
28334
  parameters: {
28285
28335
  projectPath: PROJECT_PATH,
28286
28336
  setting: { type: "string", description: 'Setting path, such as "display/window/size/viewport_width".' },
28337
+ from: {
28338
+ type: "string",
28339
+ enum: ["disk", "editor"],
28340
+ ops: ["get"],
28341
+ description: "get: where to read. Default disk, which starts a short-lived engine and reads project.godot, needs no editor and is what an automatic check can reproduce. editor asks the open editor instead, which costs no engine and answers what the editor holds, including changes nobody has saved. Asking for editor with none connected is refused rather than answered from disk, so an answer never means the other one quietly."
28342
+ },
28287
28343
  prefix: {
28288
28344
  type: "string",
28289
28345
  ops: ["get"],
@@ -29285,8 +29341,34 @@ function wrongTypes(spec, args) {
29285
29341
  }
29286
29342
  return complaints;
29287
29343
  }
29344
+ function unlistedValues(schema, value, path) {
29345
+ if (typeof schema !== "object" || schema === null) {
29346
+ return [];
29347
+ }
29348
+ const fields = schema;
29349
+ const listed = fields["enum"];
29350
+ if (Array.isArray(listed) && typeof value === "string") {
29351
+ return listed.includes(value) ? [] : [`${path} as one of ${listed.join(", ")}, not ${JSON.stringify(value)}`];
29352
+ }
29353
+ if (Array.isArray(value)) {
29354
+ return value.flatMap((element, index) => unlistedValues(fields["items"], element, `${path}[${index}]`));
29355
+ }
29356
+ const properties = fields["properties"];
29357
+ if (isPlainObject3(properties) && isPlainObject3(value)) {
29358
+ return Object.entries(value).flatMap(([field, held]) => Object.hasOwn(properties, field) ? unlistedValues(properties[field], held, `${path}.${field}`) : []);
29359
+ }
29360
+ return [];
29361
+ }
29362
+ function isPlainObject3(value) {
29363
+ return typeof value === "object" && value !== null && !Array.isArray(value);
29364
+ }
29365
+ function outsideTheirLists(spec, args) {
29366
+ return Object.entries(args).flatMap(([name, value]) => unlistedValues(spec.parameters[name], value, name));
29367
+ }
29288
29368
  var BRIDGE_RETRY_MS = 2000;
29289
29369
  var SUCCESSOR_CHECK_MS = 1e4;
29370
+ var CACHE_WRITE_MS = 2000;
29371
+ var SCAN_START_MS = 2000;
29290
29372
  var WAIT_FOR_RUN_MS = 600000;
29291
29373
  var RUN_POLL_MS = 250;
29292
29374
  var PATH_SOLUTIONS = [
@@ -29320,7 +29402,7 @@ var DEBUG_STATE_CALLS = dictionary({
29320
29402
  output: "dap_get_output",
29321
29403
  variables: "dap_get_variables"
29322
29404
  });
29323
- var PROJECT_INFO_SECTIONS = dictionary({
29405
+ var PROJECT_INFO_SECTIONS2 = dictionary({
29324
29406
  autoloads: { operation: "list_autoloads", params: () => ({}) },
29325
29407
  plugins: { operation: "list_plugins", params: () => ({}) },
29326
29408
  export_presets: { operation: "list_export_presets", params: () => ({}) },
@@ -29331,17 +29413,6 @@ var PROJECT_INFO_SECTIONS = dictionary({
29331
29413
  params: (args, detailed) => ({ preset: readString(args, "preset") ?? "", includeSuggestions: detailed })
29332
29414
  }
29333
29415
  });
29334
- function realPathOr(path) {
29335
- try {
29336
- return realpathSync2.native(path);
29337
- } catch {
29338
- return path;
29339
- }
29340
- }
29341
- function samePath(one, other) {
29342
- const settled = (path) => realPathOr(path).replaceAll("\\", "/").replace(/\/+$/, "").toLowerCase();
29343
- return settled(one) === settled(other);
29344
- }
29345
29416
  var RUNNER_DIRECTORY = "addons/gdUnit4/";
29346
29417
  function aboveTheRunner(entry) {
29347
29418
  const runner = entry.detail.findIndex((line) => line.includes(RUNNER_DIRECTORY));
@@ -29356,7 +29427,7 @@ function aboveTheRunner(entry) {
29356
29427
  }
29357
29428
  function elsewhereIn(projectPath, asked) {
29358
29429
  const wanted = asked.replace(/^res:\/\//, "").replace(/\/+$/, "");
29359
- if (wanted === "" || existsSync8(join10(projectPath, wanted))) {
29430
+ if (wanted === "" || existsSync9(join11(projectPath, wanted))) {
29360
29431
  return "";
29361
29432
  }
29362
29433
  const root = (wanted.split("/")[0] ?? "").toLowerCase();
@@ -29370,11 +29441,11 @@ function elsewhereIn(projectPath, asked) {
29370
29441
  return near.length === 0 ? "" : ` The project does have ${there}.`;
29371
29442
  }
29372
29443
  function hasMainScene(projectFile) {
29373
- const scene = parseProjectGodot(readFileSync10(projectFile, "utf8"))["application"]?.["run/main_scene"];
29444
+ const scene = parseProjectGodot(readFileSync11(projectFile, "utf8"))["application"]?.["run/main_scene"];
29374
29445
  return typeof scene === "string" && scene !== "";
29375
29446
  }
29376
29447
  function editorPlaysHeadless(projectFile) {
29377
- const runArgs = parseProjectGodot(readFileSync10(projectFile, "utf8"))["editor"]?.["run/main_run_args"];
29448
+ const runArgs = parseProjectGodot(readFileSync11(projectFile, "utf8"))["editor"]?.["run/main_run_args"];
29378
29449
  return typeof runArgs === "string" && /(?:^|\s)--headless(?:\s|$)/.test(runArgs);
29379
29450
  }
29380
29451
  function camelCased(params) {
@@ -29390,7 +29461,7 @@ function camelCased(params) {
29390
29461
  class GodotServer {
29391
29462
  mcp;
29392
29463
  locator = new GodotLocator;
29393
- operationsScript = join10(__dirname2, "godot", "operations", "godot_operations.gd");
29464
+ operationsScript = join11(__dirname2, "godot", "operations", "godot_operations.gd");
29394
29465
  godotBridge;
29395
29466
  tools = buildToolDefinitions();
29396
29467
  activeProcess = null;
@@ -29664,6 +29735,18 @@ class GodotServer {
29664
29735
  }
29665
29736
  });
29666
29737
  }
29738
+ const disagreement = this.configDisagreement();
29739
+ if (disagreement !== null) {
29740
+ this.noticedUpdate = true;
29741
+ this.callsSinceNotice = 0;
29742
+ return this.saying(answer, {
29743
+ config_names_another_version: {
29744
+ server_is: SERVER_VERSION,
29745
+ said: disagreement,
29746
+ what_to_do: "Tell the user which version their config names and which one is answering, and that " + "the two will not meet until .mcp.json is right and the MCP server is reconnected. Do " + "not edit their config without asking: which of the two they meant is theirs to say."
29747
+ }
29748
+ });
29749
+ }
29667
29750
  const notice = this.updates.notice();
29668
29751
  if (notice === null) {
29669
29752
  return answer;
@@ -29690,6 +29773,9 @@ class GodotServer {
29690
29773
  const installed = installedAddonVersion(this.ownProject);
29691
29774
  return installed === null || installed === SERVER_VERSION ? null : installed;
29692
29775
  }
29776
+ configDisagreement() {
29777
+ return this.ownProject === null ? null : configDisagrees(this.ownProject, SERVER_VERSION);
29778
+ }
29693
29779
  withFeedbackNotice(answer) {
29694
29780
  this.callsSinceFeedback += 1;
29695
29781
  if (this.callsSinceFeedback < FEEDBACK_NOTICE_EVERY) {
@@ -29715,7 +29801,7 @@ class GodotServer {
29715
29801
  response: this.createErrorResponse(`${where} does not take ${unknown.join(", ")}. ${takes} This is gdharness ${SERVER_VERSION}, which is what decides whether an argument exists.`)
29716
29802
  };
29717
29803
  }
29718
- const wrong = wrongTypes(spec, args);
29804
+ const wrong = [...wrongTypes(spec, args), ...outsideTheirLists(spec, args)];
29719
29805
  if (wrong.length > 0) {
29720
29806
  return { ok: false, response: this.createErrorResponse(`${spec.name} takes ${wrong.join("; ")}.`) };
29721
29807
  }
@@ -29767,7 +29853,25 @@ class GodotServer {
29767
29853
  async dispatch(tool, op, args) {
29768
29854
  const headless = HEADLESS_OPERATIONS[tool]?.[op];
29769
29855
  if (headless !== undefined) {
29770
- const answered = await this.headless(headless, args);
29856
+ const { from: asked, ...answerable } = args;
29857
+ if (asked === "editor") {
29858
+ const editorSide = EDITOR_READS[tool]?.[op];
29859
+ if (editorSide === undefined) {
29860
+ return this.createErrorResponse(`${tool} ${op} cannot be read from the editor, only from disk.`, [
29861
+ 'Leave from unset, or pass from: "disk"'
29862
+ ]);
29863
+ }
29864
+ if (!this.godotBridge.isConnected()) {
29865
+ return this.createErrorResponse(`${tool} ${op} was asked of the editor, and no editor has reached this server.`, [
29866
+ "editor_status says whether the bridge is up and whether an editor may yet connect",
29867
+ 'Open the project with editor_launch, or pass from: "disk" to read the file instead'
29868
+ ]);
29869
+ }
29870
+ const { op: _dropped, ...asks } = answerable;
29871
+ const contained = this.containProjectFiles(asks);
29872
+ return contained.ok ? await this.handleViaBridge(editorSide, contained.value) : contained.response;
29873
+ }
29874
+ const answered = await this.headless(headless, answerable);
29771
29875
  return headless === "refresh_class_cache" && this.godotBridge.isConnected() ? await this.alsoSayWhatTheEditorCannotSee(answered, args) : answered;
29772
29876
  }
29773
29877
  const { op: _op, ...arguments_ } = args;
@@ -29982,8 +30086,8 @@ class GodotServer {
29982
30086
  if (path === undefined) {
29983
30087
  return { ok: false, response: this.createErrorResponse("projectPath is required.") };
29984
30088
  }
29985
- const file = join10(path, "project.godot");
29986
- if (!existsSync8(file)) {
30089
+ const file = join11(path, "project.godot");
30090
+ if (!existsSync9(file)) {
29987
30091
  return {
29988
30092
  ok: false,
29989
30093
  response: this.createErrorResponse(`Not a Godot project: ${path}`, [
@@ -30140,9 +30244,9 @@ class GodotServer {
30140
30244
  }
30141
30245
  async handleProjectInfo(args) {
30142
30246
  const include = readStringArray(args, "include") ?? [];
30143
- const unknown = include.filter((name) => PROJECT_INFO_SECTIONS[name] === undefined);
30247
+ const unknown = include.filter((name) => PROJECT_INFO_SECTIONS2[name] === undefined);
30144
30248
  if (unknown.length > 0) {
30145
- return this.createErrorResponse(`project_info cannot include ${unknown.join(", ")}. Sections: ${Object.keys(PROJECT_INFO_SECTIONS).join(", ")}.`);
30249
+ return this.createErrorResponse(`project_info cannot include ${unknown.join(", ")}. Sections: ${Object.keys(PROJECT_INFO_SECTIONS2).join(", ")}.`);
30146
30250
  }
30147
30251
  const project = this.project(args);
30148
30252
  if (!project.ok) {
@@ -30152,7 +30256,7 @@ class GodotServer {
30152
30256
  if (!engine.ok) {
30153
30257
  return engine.response;
30154
30258
  }
30155
- const application = parseProjectGodot(readFileSync10(project.value.file, "utf8"))["application"] ?? {};
30259
+ const application = parseProjectGodot(readFileSync11(project.value.file, "utf8"))["application"] ?? {};
30156
30260
  const name = application["config/name"];
30157
30261
  const mainScene = application["run/main_scene"];
30158
30262
  const info = {
@@ -30164,7 +30268,7 @@ class GodotServer {
30164
30268
  };
30165
30269
  const detailed = args["detail"] === "full";
30166
30270
  for (const section of include) {
30167
- const spec = PROJECT_INFO_SECTIONS[section];
30271
+ const spec = PROJECT_INFO_SECTIONS2[section];
30168
30272
  if (spec === undefined) {
30169
30273
  continue;
30170
30274
  }
@@ -30220,11 +30324,11 @@ class GodotServer {
30220
30324
  return this.createErrorResponse(`Cannot create ${dirname4(output.relativePath)} to export into: ${errorMessage(error)}`);
30221
30325
  }
30222
30326
  const debug = readBoolean(args, "debug") ?? false;
30223
- const exportLogs = mkdtempSync2(join10(tmpdir4(), "gdharness-export-"));
30327
+ const exportLogs = mkdtempSync2(join11(tmpdir4(), "gdharness-export-"));
30224
30328
  const exportArgs = [
30225
30329
  "--headless",
30226
30330
  "--log-file",
30227
- join10(exportLogs, "engine.log"),
30331
+ join11(exportLogs, "engine.log"),
30228
30332
  "--path",
30229
30333
  project.value.path,
30230
30334
  debug ? "--export-debug" : "--export-release",
@@ -30252,7 +30356,7 @@ class GodotServer {
30252
30356
  log.finish();
30253
30357
  const problems = log.select({ severity: "warning", sinceLastCall: false, limit: 200 });
30254
30358
  const verdict = {
30255
- exported: exitCode === 0 && log.count("error") === 0 && existsSync8(output.absolutePath),
30359
+ exported: exitCode === 0 && log.count("error") === 0 && existsSync9(output.absolutePath),
30256
30360
  preset,
30257
30361
  outputPath: output.relativePath,
30258
30362
  debug,
@@ -30282,7 +30386,7 @@ class GodotServer {
30282
30386
  return contained.response;
30283
30387
  }
30284
30388
  const runner = "addons/gdUnit4/bin/GdUnitCmdTool.gd";
30285
- if (!existsSync8(join10(project.value.path, runner))) {
30389
+ if (!existsSync9(join11(project.value.path, runner))) {
30286
30390
  return this.createErrorResponse(`gdUnit4 is not installed in this project: no ${runner}.`, [
30287
30391
  "Install gdUnit4 under addons/gdUnit4, from https://github.com/godot-gdunit-labs/gdUnit4"
30288
30392
  ]);
@@ -30317,7 +30421,7 @@ class GodotServer {
30317
30421
  ];
30318
30422
  const timeoutMs = readPositiveNumber(args, "timeoutMs") ?? 600000;
30319
30423
  this.logDebug(`Running tests: ${engine.value} ${cmdArgs.join(" ")}`);
30320
- const userData = mkdtempSync2(join10(tmpdir4(), "gdharness-tests-"));
30424
+ const userData = mkdtempSync2(join11(tmpdir4(), "gdharness-tests-"));
30321
30425
  const run = this.spawnGame(engine.value, cmdArgs, userDataIn(userData));
30322
30426
  const hung = await new Promise((resolve) => {
30323
30427
  const timer = setTimeout(() => {
@@ -30333,14 +30437,14 @@ class GodotServer {
30333
30437
  resolve(false);
30334
30438
  });
30335
30439
  });
30336
- const reportsDir = join10(project.value.path, ".godot", ...ours.split("/"));
30440
+ const reportsDir = join11(project.value.path, ".godot", ...ours.split("/"));
30337
30441
  let report = null;
30338
30442
  let reportProblem = null;
30339
30443
  try {
30340
- const written = existsSync8(reportsDir) ? readdirSync6(reportsDir).filter((name) => name.startsWith("report_")).sort((a, b) => Number(a.slice("report_".length)) - Number(b.slice("report_".length))) : [];
30444
+ const written = existsSync9(reportsDir) ? readdirSync6(reportsDir).filter((name) => name.startsWith("report_")).sort((a, b) => Number(a.slice("report_".length)) - Number(b.slice("report_".length))) : [];
30341
30445
  const newest = written.at(-1);
30342
30446
  if (newest !== undefined) {
30343
- report = parseJUnit(readFileSync10(join10(reportsDir, newest, "results.xml"), "utf8"));
30447
+ report = parseJUnit(readFileSync11(join11(reportsDir, newest, "results.xml"), "utf8"));
30344
30448
  }
30345
30449
  } catch (error) {
30346
30450
  reportProblem = errorMessage(error);
@@ -30503,6 +30607,7 @@ class GodotServer {
30503
30607
  retryingBridge: this.bridgeRetry === null ? undefined : true,
30504
30608
  announcedAt: this.announcedAt ?? undefined,
30505
30609
  projectIs: this.projectHasMovedOn() ?? undefined,
30610
+ configIs: this.configDisagreement() ?? undefined,
30506
30611
  note: isPortConflict ? "Bridge port is already in use. Another gdharness instance owns the editor bridge, so this server cannot reach the editor. Usually the server this one replaced, still on its way out." : undefined,
30507
30612
  suggestion: isPortConflict ? `This server is asking for the port again every ${BRIDGE_RETRY_MS / 1000}s and takes it the moment the other one lets go, so editor tools come back on their own. Ask again, or end the older gdharness process to have it now.` : undefined
30508
30613
  };
@@ -30758,7 +30863,7 @@ class GodotServer {
30758
30863
  return runIsUp(run, editorSays);
30759
30864
  }
30760
30865
  async runtimeUp(projectPath, before, budgetMs) {
30761
- if (!existsSync8(join10(projectPath, RUNTIME_AUTOLOAD.path))) {
30866
+ if (!existsSync9(join11(projectPath, RUNTIME_AUTOLOAD.path))) {
30762
30867
  return runtimeVerdict(null, { addon: false, budgetMs, heldAt: null, running: false });
30763
30868
  }
30764
30869
  const endpoint = await announcedSince(projectPath, before, {
@@ -31013,7 +31118,7 @@ class GodotServer {
31013
31118
  if (mine === null) {
31014
31119
  return false;
31015
31120
  }
31016
- return project !== "" && samePath(mine, project);
31121
+ return project !== "" && isSameDirectory(mine, project);
31017
31122
  }
31018
31123
  currentRun() {
31019
31124
  return this.activeProcess ?? this.adoptRecordedRun();
@@ -31144,6 +31249,11 @@ class GodotServer {
31144
31249
  async handleRescanFilesystem(args) {
31145
31250
  const timeoutMs = readPositiveNumber(args, "timeoutMs") ?? 30000;
31146
31251
  const started = Date.now();
31252
+ const projectPath = typeof args["projectPath"] === "string" ? args["projectPath"] : "";
31253
+ const before = projectPath === "" ? null : cachedClasses(projectPath);
31254
+ const writtenBefore = projectPath === "" ? null : cacheWrittenAt(projectPath);
31255
+ const blind = projectPath === "" ? { unseen: [] } : await this.classesTheEditorCannotSee(args);
31256
+ const atRisk = before === null ? [] : blind.unseen.filter((one) => before.has(one.className));
31147
31257
  const first = await this.handleViaBridge("rescan_filesystem", args);
31148
31258
  if (first.isError) {
31149
31259
  return first;
@@ -31152,17 +31262,27 @@ class GodotServer {
31152
31262
  while (busy && Date.now() - started < timeoutMs) {
31153
31263
  await new Promise((settle) => setTimeout(settle, 100));
31154
31264
  const status = asParams(await this.godotBridge.invokeTool("rescan_filesystem", { ...args, statusOnly: true }));
31155
- busy = Boolean(status["scanning"]) || Boolean(status["importing"]);
31265
+ const pending = Boolean(status["pending"]) && Date.now() - started < SCAN_START_MS;
31266
+ busy = Boolean(status["scanning"]) || Boolean(status["importing"]) || pending;
31267
+ }
31268
+ if (!busy && atRisk.length > 0) {
31269
+ const until = Date.now() + CACHE_WRITE_MS;
31270
+ while (Date.now() < until && cacheWrittenAt(projectPath) === writtenBefore) {
31271
+ await new Promise((settle) => setTimeout(settle, 100));
31272
+ }
31156
31273
  }
31274
+ const after = projectPath === "" || busy ? null : cachedClasses(projectPath);
31275
+ const lost = before === null || after === null ? [] : [...before.keys()].filter((name) => !after.has(name));
31157
31276
  const checked = busy ? { unseen: [] } : await this.classesTheEditorCannotSee(args);
31158
31277
  const unseen = checked.unseen;
31159
31278
  return this.jsonTextResponse({
31160
- ok: !busy && unseen.length === 0 && checked.unchecked === undefined,
31279
+ ok: !busy && unseen.length === 0 && lost.length === 0 && checked.unchecked === undefined,
31161
31280
  stillWorking: busy,
31162
31281
  waitedMs: Date.now() - started,
31163
31282
  unseenByEditor: unseen.length > 0 ? unseen : undefined,
31283
+ cacheLost: lost.length > 0 ? lost : undefined,
31164
31284
  classesUnchecked: checked.unchecked,
31165
- note: busy ? "The editor was still scanning or importing when the wait ran out, so new files may not be visible yet." : unseen.length > 0 ? "The scan finished and these classes are still not in the list the editor resolves against, so every use of them reads as an unknown identifier. Its walk skips a file another engine has already imported. Change the declaring script and rescan, or restart the editor with editor_launch restart." : undefined
31285
+ note: busy ? "The editor was still scanning or importing when the wait ran out, so new files may not be visible yet." : lost.length > 0 ? "The scan wrote the class cache from the list this editor is holding, and that list is shorter than the file was: these classes were in it before the scan and are not now. Every engine that reads the cache next, including a test run, will report them as unknown identifiers in files nobody touched. Run project_import refresh_classes to put them back, and do not rescan again until the editor has been restarted with editor_launch restart, because it will write the same short list over it." : unseen.length > 0 ? "The scan finished and these classes are still not in the list the editor resolves against, so every use of them reads as an unknown identifier. Its walk skips a file another engine has already imported. Change the declaring script and rescan, or restart the editor with editor_launch restart." : undefined
31166
31286
  });
31167
31287
  }
31168
31288
  async alsoSayWhatTheEditorCannotSee(answered, args) {
@@ -31187,7 +31307,7 @@ class GodotServer {
31187
31307
  return { unseen: [], unchecked: "no projectPath, so there was nothing to read the cache from" };
31188
31308
  }
31189
31309
  const open = this.godotBridge.getStatus().projectPath;
31190
- if (open === undefined || !samePath(open, projectPath)) {
31310
+ if (open === undefined || !isSameDirectory(open, projectPath)) {
31191
31311
  return {
31192
31312
  unseen: [],
31193
31313
  unchecked: `the connected editor is open on ${open ?? "a project it did not name"}, not this one`
@@ -31215,6 +31335,14 @@ class GodotServer {
31215
31335
  "editor_status says whether the bridge is up and what it is listening on"
31216
31336
  ]);
31217
31337
  }
31338
+ const open = this.godotBridge.getStatus().projectPath;
31339
+ const meant = readNonEmptyString(args, "projectPath");
31340
+ if (open !== undefined && open !== "" && meant !== undefined && !isSameDirectory(open, meant)) {
31341
+ return this.createErrorResponse(`This call names ${meant}, and the editor on this bridge has ${open} open.`, [
31342
+ "editor_status names the project this server's editor is showing",
31343
+ "One server serves one editor: start a second server for the other project"
31344
+ ]);
31345
+ }
31218
31346
  try {
31219
31347
  return this.jsonTextResponse(markIfStale(await this.godotBridge.invokeTool(toolName, args), this.godotBridge.getStatus().addonVersion, SERVER_VERSION));
31220
31348
  } catch (error) {
@@ -31233,8 +31361,8 @@ class GodotServer {
31233
31361
  return this.createErrorResponse(choice.problem);
31234
31362
  }
31235
31363
  const expectsScreenshot = command === "capture_screenshot" || command === "capture_viewport";
31236
- const screenshotDir = expectsScreenshot ? mkdtempSync2(join10(tmpdir4(), "gdharness-runtime-screenshot-")) : null;
31237
- const screenshotPath = screenshotDir ? join10(screenshotDir, "capture.png") : null;
31364
+ const screenshotDir = expectsScreenshot ? mkdtempSync2(join11(tmpdir4(), "gdharness-runtime-screenshot-")) : null;
31365
+ const screenshotPath = screenshotDir ? join11(screenshotDir, "capture.png") : null;
31238
31366
  try {
31239
31367
  const reply = await runtimeRequest(choice.endpoint, command, screenshotPath ? { ...params, output_path: screenshotPath } : params, timeoutMs);
31240
31368
  if (!reply.ok) {
@@ -31255,7 +31383,7 @@ class GodotServer {
31255
31383
  return {
31256
31384
  content: [
31257
31385
  { type: "text", text: `Screenshot captured: ${dimensions}` },
31258
- { type: "image", data: readFileSync10(screenshotPath).toString("base64"), mimeType: "image/png" }
31386
+ { type: "image", data: readFileSync11(screenshotPath).toString("base64"), mimeType: "image/png" }
31259
31387
  ]
31260
31388
  };
31261
31389
  } finally {