gdharness 1.2.27 → 1.2.28

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/cli.js CHANGED
@@ -8198,24 +8198,106 @@ var init_engine_run = __esm(() => {
8198
8198
  init_game_log();
8199
8199
  });
8200
8200
 
8201
+ // src/library-copies.ts
8202
+ import { existsSync as existsSync5, readdirSync as readdirSync3, readFileSync as readFileSync3, renameSync, unlinkSync } from "node:fs";
8203
+ import { basename as basename3, dirname as dirname4, isAbsolute as isAbsolute2, join as join5 } from "node:path";
8204
+ function freeLibraryCopies(projectPath, platform = process.platform) {
8205
+ const setAside = [];
8206
+ const swept = [];
8207
+ if (platform !== "win32") {
8208
+ return { setAside, swept };
8209
+ }
8210
+ for (const library of windowsLibraries(projectPath)) {
8211
+ const folder = dirname4(library);
8212
+ const copy = `~${basename3(library)}`;
8213
+ if (existsSync5(join5(folder, copy))) {
8214
+ const aside = join5(folder, `${copy}.aside-${process.pid}-${Date.now()}`);
8215
+ try {
8216
+ renameSync(join5(folder, copy), aside);
8217
+ setAside.push(aside);
8218
+ } catch {}
8219
+ }
8220
+ let names;
8221
+ try {
8222
+ names = readdirSync3(folder);
8223
+ } catch {
8224
+ continue;
8225
+ }
8226
+ for (const name of names) {
8227
+ if (name.length > copy.length && name.toLowerCase().startsWith(copy.toLowerCase())) {
8228
+ try {
8229
+ unlinkSync(join5(folder, name));
8230
+ swept.push(join5(folder, name));
8231
+ } catch {}
8232
+ }
8233
+ }
8234
+ }
8235
+ return { setAside, swept };
8236
+ }
8237
+ function windowsLibraries(projectPath) {
8238
+ const listed = readOrNull(join5(projectPath, ".godot", "extension_list.cfg"));
8239
+ if (listed === null) {
8240
+ return [];
8241
+ }
8242
+ const libraries = new Set;
8243
+ for (const line of listed.split(/\r?\n/)) {
8244
+ const manifest = resolveResource(projectPath, projectPath, line.trim());
8245
+ const text = manifest === null ? null : readOrNull(manifest);
8246
+ if (manifest === null || text === null) {
8247
+ continue;
8248
+ }
8249
+ let inLibraries = false;
8250
+ for (const row of text.split(/\r?\n/)) {
8251
+ const section = /^\s*\[([^\]]+)\]/.exec(row)?.[1];
8252
+ if (section !== undefined) {
8253
+ inLibraries = section.trim() === "libraries";
8254
+ continue;
8255
+ }
8256
+ const named = inLibraries ? /^\s*windows[\w.]*\s*=\s*"([^"]+)"/.exec(row)?.[1] : undefined;
8257
+ const library = named === undefined ? null : resolveResource(projectPath, dirname4(manifest), named);
8258
+ if (library !== null) {
8259
+ libraries.add(library);
8260
+ }
8261
+ }
8262
+ }
8263
+ return [...libraries];
8264
+ }
8265
+ function resolveResource(projectPath, relativeTo, path) {
8266
+ if (path === "") {
8267
+ return null;
8268
+ }
8269
+ if (path.startsWith("res://")) {
8270
+ return join5(projectPath, path.slice("res://".length));
8271
+ }
8272
+ return isAbsolute2(path) ? path : join5(relativeTo, path);
8273
+ }
8274
+ function readOrNull(path) {
8275
+ try {
8276
+ return readFileSync3(path, "utf8");
8277
+ } catch {
8278
+ return null;
8279
+ }
8280
+ }
8281
+ var init_library_copies = () => {};
8282
+
8201
8283
  // src/reimport.ts
8202
8284
  import { createHash } from "node:crypto";
8203
- import { existsSync as existsSync5, readFileSync as readFileSync3, rmSync as rmSync3, statSync as statSync3, utimesSync, writeFileSync as writeFileSync3 } from "node:fs";
8204
- import { basename as basename3, join as join5 } from "node:path";
8285
+ import { existsSync as existsSync6, readFileSync as readFileSync4, rmSync as rmSync3, statSync as statSync3, utimesSync, writeFileSync as writeFileSync3 } from "node:fs";
8286
+ import { basename as basename4, join as join6 } from "node:path";
8205
8287
  function forceNextImport(projectPath, resourcePath) {
8206
8288
  const relative = resourcePath.replace(/^res:\/\//, "");
8207
- const sidecar = join5(projectPath, `${relative}.import`);
8208
- if (!existsSync5(sidecar)) {
8289
+ const sidecar = join6(projectPath, `${relative}.import`);
8290
+ if (!existsSync6(sidecar)) {
8209
8291
  return;
8210
8292
  }
8211
8293
  const { atime, mtimeMs } = statSync3(sidecar);
8212
- writeFileSync3(sidecar, readFileSync3(sidecar, "utf8").replace(/^valid=false\r?\n/m, ""));
8294
+ writeFileSync3(sidecar, readFileSync4(sidecar, "utf8").replace(/^valid=false\r?\n/m, ""));
8213
8295
  utimesSync(sidecar, atime, new Date(mtimeMs + 2000));
8214
8296
  rmSync3(importRecord(projectPath, resourcePath), { force: true });
8215
8297
  }
8216
8298
  function importRecord(projectPath, resourcePath) {
8217
8299
  const digest = createHash("md5").update(resourcePath).digest("hex");
8218
- return join5(projectPath, ".godot", "imported", `${basename3(resourcePath)}-${digest}.md5`);
8300
+ return join6(projectPath, ".godot", "imported", `${basename4(resourcePath)}-${digest}.md5`);
8219
8301
  }
8220
8302
  var init_reimport = () => {};
8221
8303
 
@@ -8235,17 +8317,17 @@ function alive(pid) {
8235
8317
  var init_alive = () => {};
8236
8318
 
8237
8319
  // src/scratch.ts
8238
- import { existsSync as existsSync6, mkdtempSync, readdirSync as readdirSync3, rmSync as rmSync4 } from "node:fs";
8320
+ import { existsSync as existsSync7, mkdtempSync, readdirSync as readdirSync4, rmSync as rmSync4 } from "node:fs";
8239
8321
  import { tmpdir as tmpdir2 } from "node:os";
8240
- import { join as join6 } from "node:path";
8322
+ import { join as join7 } from "node:path";
8241
8323
  import process6 from "node:process";
8242
8324
  function scratchDirectory(kind) {
8243
- return mkdtempSync(join6(tmpdir2(), `gdharness-${kind}-${process6.pid}-`));
8325
+ return mkdtempSync(join7(tmpdir2(), `gdharness-${kind}-${process6.pid}-`));
8244
8326
  }
8245
8327
  function sweepAbandonedScratch(directory = tmpdir2()) {
8246
8328
  let entries;
8247
8329
  try {
8248
- entries = readdirSync3(directory);
8330
+ entries = readdirSync4(directory);
8249
8331
  } catch {
8250
8332
  return;
8251
8333
  }
@@ -8256,7 +8338,7 @@ function sweepAbandonedScratch(directory = tmpdir2()) {
8256
8338
  continue;
8257
8339
  }
8258
8340
  try {
8259
- remove(join6(directory, entry));
8341
+ remove(join7(directory, entry));
8260
8342
  } catch {}
8261
8343
  }
8262
8344
  }
@@ -8294,7 +8376,7 @@ function tryAgain(path, attempt) {
8294
8376
  lastTryOnExit();
8295
8377
  }
8296
8378
  const after = AGAIN_AFTER_MS[attempt];
8297
- if (after === undefined || !existsSync6(path)) {
8379
+ if (after === undefined || !existsSync7(path)) {
8298
8380
  waiting.delete(path);
8299
8381
  if (after === undefined) {
8300
8382
  atExit.add(path);
@@ -8341,8 +8423,8 @@ var init_scratch = __esm(() => {
8341
8423
  });
8342
8424
 
8343
8425
  // src/headless.ts
8344
- import { existsSync as existsSync7, readFileSync as readFileSync4, realpathSync as realpathSync2, writeFileSync as writeFileSync4 } from "node:fs";
8345
- import { dirname as dirname4, join as join7 } from "node:path";
8426
+ import { existsSync as existsSync8, readFileSync as readFileSync5, realpathSync as realpathSync2, writeFileSync as writeFileSync4 } from "node:fs";
8427
+ import { dirname as dirname5, join as join8 } from "node:path";
8346
8428
  function snakeCased(params) {
8347
8429
  const result = emptyRecord();
8348
8430
  for (const [key, value] of Object.entries(params)) {
@@ -8397,7 +8479,7 @@ function refusalsIn(log) {
8397
8479
  }
8398
8480
  function scriptFault(problems, script) {
8399
8481
  const spelled = (path) => `(${path.replaceAll("\\", "/").toLowerCase()}/`;
8400
- const directories = [...new Set([spelled(dirname4(script)), spelled(resolvedOr(dirname4(script)))])];
8482
+ const directories = [...new Set([spelled(dirname5(script)), spelled(resolvedOr(dirname5(script)))])];
8401
8483
  return problems.find((entry) => {
8402
8484
  const at = entry.detail.find((line) => line.startsWith("at: "))?.replaceAll("\\", "/").toLowerCase();
8403
8485
  return entry.severity === "error" && at !== undefined && directories.some((directory) => at.includes(directory));
@@ -8422,7 +8504,7 @@ function librariesNotCopied(messages) {
8422
8504
  }
8423
8505
  function extensionRetryNote(libraries, stillNotLoaded = []) {
8424
8506
  const second = stillNotLoaded.length === 0 ? "so the import was run again and loaded the extension, and the messages are from that second run." : `so the import was run again, and it still could not load ${stillNotLoaded.join(" and ")}: this answer is from an import without ${stillNotLoaded.length === 1 ? "it" : "them"}, and its messages say what the engine hit.`;
8425
- return `The first import could not load ${libraries.join(" and ")}: on Windows an engine running as the editor ` + "loads a GDExtension from a ~ copy beside the library, and the editor open on this project holds its own " + "copy there, so the first engine started beside it cannot make one. That failed attempt moves the " + `editor's copy aside, ${second} The editor's copy is left beside the library as a ~RF….TMP file, which ` + "can be deleted once the editor has exited.";
8507
+ return `The first import could not load ${libraries.join(" and ")}: on Windows an engine running as the editor ` + "loads a GDExtension from a ~ copy beside the library, and the editor open on this project holds its own " + "copy there, so the first engine started beside it cannot make one. That failed attempt moves the " + `editor's copy aside, ${second} The editor's copy is left beside the library as a ~RF….TMP file, which ` + "the next import, export or editor this server starts on the project deletes once the editor has exited.";
8426
8508
  }
8427
8509
  async function runImport(godotPath, projectPath, options = {}) {
8428
8510
  const once = options.once ?? importOnce;
@@ -8430,6 +8512,7 @@ async function runImport(godotPath, projectPath, options = {}) {
8430
8512
  for (const target of options.forcing ?? []) {
8431
8513
  forceNextImport(projectPath, target);
8432
8514
  }
8515
+ freeLibraryCopies(projectPath);
8433
8516
  return once(godotPath, projectPath);
8434
8517
  };
8435
8518
  const first = await pass();
@@ -8453,7 +8536,7 @@ async function importOnce(godotPath, projectPath) {
8453
8536
  ran = await runEngine(godotPath, [
8454
8537
  "--headless",
8455
8538
  "--log-file",
8456
- join7(logDir, "engine.log"),
8539
+ join8(logDir, "engine.log"),
8457
8540
  "--path",
8458
8541
  projectPath,
8459
8542
  "--import"
@@ -8473,13 +8556,13 @@ async function importOnce(godotPath, projectPath) {
8473
8556
  }
8474
8557
  async function runOperation(engine, operation, params, projectPath) {
8475
8558
  const paramsDir = scratchDirectory("params");
8476
- const paramsFile = join7(paramsDir, `${operation}.json`);
8477
- const answerFile = join7(paramsDir, "answer.json");
8559
+ const paramsFile = join8(paramsDir, `${operation}.json`);
8560
+ const answerFile = join8(paramsDir, "answer.json");
8478
8561
  writeFileSync4(paramsFile, JSON.stringify(snakeCased(params)), "utf8");
8479
8562
  const args = [
8480
8563
  "--headless",
8481
8564
  "--log-file",
8482
- join7(paramsDir, "engine.log"),
8565
+ join8(paramsDir, "engine.log"),
8483
8566
  "--path",
8484
8567
  projectPath,
8485
8568
  "--script",
@@ -8549,10 +8632,10 @@ async function runOperation(engine, operation, params, projectPath) {
8549
8632
  };
8550
8633
  }
8551
8634
  function answerIn(file) {
8552
- if (!existsSync7(file)) {
8635
+ if (!existsSync8(file)) {
8553
8636
  return { kind: "none" };
8554
8637
  }
8555
- const text = readFileSync4(file, "utf8");
8638
+ const text = readFileSync5(file, "utf8");
8556
8639
  try {
8557
8640
  const parsed = JSON.parse(text);
8558
8641
  return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? { kind: "answer", payload: parsed } : { kind: "unreadable", why: `it is JSON but not an object: ${text.slice(0, 200)}` };
@@ -8566,14 +8649,15 @@ function answerIn(file) {
8566
8649
  var AFTER_ANSWER_NOTE = "Printed after this operation had written its answer, while the engine ran the project's autoloads and shut down: Godot starts a project's autoloads in every script run and cannot be told not to. An error raised in a project script here is that autoload's own, and none of these changes the answer.", OWN_ERROR = "[ERROR] ", ANSWERED = "[gdharness] answered", MESSAGES_KEPT = 200, ERRORS_NAMED = 3;
8567
8650
  var init_headless = __esm(() => {
8568
8651
  init_engine_run();
8652
+ init_library_copies();
8569
8653
  init_reimport();
8570
8654
  init_scratch();
8571
8655
  });
8572
8656
 
8573
8657
  // src/update-check.ts
8574
- import { existsSync as existsSync8, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "node:fs";
8658
+ import { existsSync as existsSync9, mkdirSync as mkdirSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "node:fs";
8575
8659
  import { homedir as homedir3, tmpdir as tmpdir3 } from "node:os";
8576
- import { join as join8 } from "node:path";
8660
+ import { join as join9 } from "node:path";
8577
8661
  function packageUrl(root) {
8578
8662
  return `${root.replace(/\/+$/, "")}/gdharness/latest`;
8579
8663
  }
@@ -8595,28 +8679,28 @@ function cacheDirectory(environment) {
8595
8679
  };
8596
8680
  const home = set("HOME") ?? homedir3();
8597
8681
  if (process.platform === "win32") {
8598
- return join8(set("LOCALAPPDATA") ?? home, "gdharness");
8682
+ return join9(set("LOCALAPPDATA") ?? home, "gdharness");
8599
8683
  }
8600
8684
  if (process.platform === "darwin") {
8601
- return join8(home, "Library", "Caches", "gdharness");
8685
+ return join9(home, "Library", "Caches", "gdharness");
8602
8686
  }
8603
- return join8(set("XDG_CACHE_HOME") ?? join8(home, ".cache"), "gdharness");
8687
+ return join9(set("XDG_CACHE_HOME") ?? join9(home, ".cache"), "gdharness");
8604
8688
  }
8605
8689
  function cacheFile(environment = process.env) {
8606
8690
  try {
8607
8691
  const directory = cacheDirectory(environment);
8608
8692
  mkdirSync3(directory, { recursive: true, mode: 448 });
8609
- return join8(directory, "update-check.json");
8693
+ return join9(directory, "update-check.json");
8610
8694
  } catch {
8611
- return join8(tmpdir3(), "gdharness-update-check.json");
8695
+ return join9(tmpdir3(), "gdharness-update-check.json");
8612
8696
  }
8613
8697
  }
8614
8698
  function readCache(path) {
8615
8699
  try {
8616
- if (!existsSync8(path)) {
8700
+ if (!existsSync9(path)) {
8617
8701
  return null;
8618
8702
  }
8619
- const parsed = JSON.parse(readFileSync5(path, "utf8"));
8703
+ const parsed = JSON.parse(readFileSync6(path, "utf8"));
8620
8704
  if (typeof parsed !== "object" || parsed === null) {
8621
8705
  return null;
8622
8706
  }
@@ -8764,7 +8848,7 @@ var init_update_check = __esm(() => {
8764
8848
  });
8765
8849
 
8766
8850
  // src/server-version.ts
8767
- import { readFileSync as readFileSync6 } from "node:fs";
8851
+ import { readFileSync as readFileSync7 } from "node:fs";
8768
8852
  function editorIsStale(addonVersion, serverVersion, addonDigest, shippedDigest) {
8769
8853
  if (addonDigest !== undefined && addonDigest !== "" && shippedDigest !== undefined) {
8770
8854
  return addonDigest !== shippedDigest;
@@ -8835,7 +8919,7 @@ var init_server_version = __esm(() => {
8835
8919
  GODOT_DEBUG_MODE_DEFAULT = process.env["GODOT_DEBUG"] === "true" || DEBUG_MODE;
8836
8920
  SERVER_VERSION = (() => {
8837
8921
  try {
8838
- const pkg = JSON.parse(readFileSync6(new URL("../package.json", import.meta.url), "utf8"));
8922
+ const pkg = JSON.parse(readFileSync7(new URL("../package.json", import.meta.url), "utf8"));
8839
8923
  return typeof pkg.version === "string" ? pkg.version : "0.0.0";
8840
8924
  } catch {
8841
8925
  return "0.0.0";
@@ -9620,8 +9704,8 @@ var init_gdscript_source = __esm(() => {
9620
9704
  });
9621
9705
 
9622
9706
  // src/class-cache.ts
9623
- import { existsSync as existsSync9, readdirSync as readdirSync4, readFileSync as readFileSync7, statSync as statSync4 } from "node:fs";
9624
- import { join as join9, posix } from "node:path";
9707
+ import { existsSync as existsSync10, readdirSync as readdirSync5, readFileSync as readFileSync8, statSync as statSync4 } from "node:fs";
9708
+ import { join as join10, posix } from "node:path";
9625
9709
  function isToolScript(source) {
9626
9710
  return TOOL.test(codeOf(source));
9627
9711
  }
@@ -9637,18 +9721,18 @@ function declaredClasses(projectPath) {
9637
9721
  }
9638
9722
  function eachScript(projectPath, each) {
9639
9723
  const visit = (directory, prefix) => {
9640
- if (existsSync9(join9(directory, ".gdignore"))) {
9724
+ if (existsSync10(join10(directory, ".gdignore"))) {
9641
9725
  return;
9642
9726
  }
9643
- for (const entry of readdirSync4(directory, { withFileTypes: true })) {
9727
+ for (const entry of readdirSync5(directory, { withFileTypes: true })) {
9644
9728
  if (entry.name.startsWith(".")) {
9645
9729
  continue;
9646
9730
  }
9647
- const path = join9(directory, entry.name);
9731
+ const path = join10(directory, entry.name);
9648
9732
  if (entry.isDirectory()) {
9649
9733
  visit(path, `${prefix}${entry.name}/`);
9650
9734
  } else if (entry.isFile() && entry.name.endsWith(".gd")) {
9651
- each(`res://${prefix}${entry.name}`, readFileSync7(path, "utf8"));
9735
+ each(`res://${prefix}${entry.name}`, readFileSync8(path, "utf8"));
9652
9736
  }
9653
9737
  }
9654
9738
  };
@@ -9670,12 +9754,12 @@ function scriptsNaming(projectPath, classes) {
9670
9754
  return naming.sort((a, b) => a.script.localeCompare(b.script));
9671
9755
  }
9672
9756
  function cachedClasses(projectPath) {
9673
- const cache = join9(projectPath, ".godot", "global_script_class_cache.cfg");
9674
- if (!existsSync9(cache)) {
9757
+ const cache = join10(projectPath, ".godot", "global_script_class_cache.cfg");
9758
+ if (!existsSync10(cache)) {
9675
9759
  return null;
9676
9760
  }
9677
9761
  const listed = new Map;
9678
- const text = readFileSync7(cache, "utf8");
9762
+ const text = readFileSync8(cache, "utf8");
9679
9763
  for (const entry of text.matchAll(/"class":\s*&"([^"]+)"[\s\S]*?"path":\s*"([^"]+)"/g)) {
9680
9764
  listed.set(entry[1] ?? "", entry[2] ?? "");
9681
9765
  }
@@ -9941,7 +10025,7 @@ function uncachedClassNote(types) {
9941
10025
  return `${types.join(", ")} ${one ? "is declared" : "are declared"} in this project and missing from ` + `.godot/global_script_class_cache.cfg, so a game launched now would not resolve ${one ? "it" : "them"} ` + "either. Try editor_rescan first: it asks the editor already running and starts nothing, and it " + "cleared this in 314ms in a second project with a 31-worker fan-out still importing. It is not " + "always enough, because a file another engine has already imported reads as settled and the " + "walk does not look inside it. project_import refresh_classes rewrites the cache from the " + "declarations on disk and does reach that case, at the cost of a short headless engine, which " + "is worth knowing when something else is already running against this project.";
9942
10026
  }
9943
10027
  function cacheWrittenAt(projectPath) {
9944
- const cache = join9(projectPath, ".godot", "global_script_class_cache.cfg");
10028
+ const cache = join10(projectPath, ".godot", "global_script_class_cache.cfg");
9945
10029
  try {
9946
10030
  return statSync4(cache).mtimeMs;
9947
10031
  } catch {
@@ -9955,14 +10039,14 @@ function declaredSince(projectPath, paths, names, since) {
9955
10039
  return false;
9956
10040
  }
9957
10041
  try {
9958
- return statSync4(join9(projectPath, path.slice("res://".length))).mtimeMs > since;
10042
+ return statSync4(join10(projectPath, path.slice("res://".length))).mtimeMs > since;
9959
10043
  } catch {
9960
10044
  return false;
9961
10045
  }
9962
10046
  });
9963
10047
  }
9964
10048
  function cachedAtMissingPaths(cached, projectPath) {
9965
- return [...cached].filter(([, path]) => path.startsWith("res://") && !existsSync9(join9(projectPath, path.slice("res://".length)))).map(([name]) => name).sort();
10049
+ return [...cached].filter(([, path]) => path.startsWith("res://") && !existsSync10(join10(projectPath, path.slice("res://".length)))).map(([name]) => name).sort();
9966
10050
  }
9967
10051
  function staleAgainst(cached, projectPath) {
9968
10052
  return [...declaredClasses(projectPath)].filter(([name, path]) => cached.get(name) !== path).map(([name]) => name);
@@ -9987,8 +10071,8 @@ var init_class_cache = __esm(() => {
9987
10071
  });
9988
10072
 
9989
10073
  // src/project-scan.ts
9990
- import { existsSync as existsSync10, readdirSync as readdirSync5, readFileSync as readFileSync8 } from "node:fs";
9991
- import { extname, join as join10 } from "node:path";
10074
+ import { existsSync as existsSync11, readdirSync as readdirSync6, readFileSync as readFileSync9 } from "node:fs";
10075
+ import { extname, join as join11 } from "node:path";
9992
10076
  function skipped(name) {
9993
10077
  return name.startsWith(".") || SKIPPED.has(name);
9994
10078
  }
@@ -9996,10 +10080,10 @@ function steppedOver(directory) {
9996
10080
  return gdignored(directory) || installedByUs(directory);
9997
10081
  }
9998
10082
  function gdignored(directory) {
9999
- return existsSync10(join10(directory, ".gdignore"));
10083
+ return existsSync11(join11(directory, ".gdignore"));
10000
10084
  }
10001
10085
  function installedByUs(directory) {
10002
- return existsSync10(join10(directory, VERSION_MARKER));
10086
+ return existsSync11(join11(directory, VERSION_MARKER));
10003
10087
  }
10004
10088
  function projectStructure(projectPath) {
10005
10089
  const structure = { scenes: 0, scripts: 0, assets: 0, other: 0 };
@@ -10007,12 +10091,12 @@ function projectStructure(projectPath) {
10007
10091
  if (steppedOver(directory)) {
10008
10092
  return;
10009
10093
  }
10010
- for (const entry of readdirSync5(directory, { withFileTypes: true })) {
10094
+ for (const entry of readdirSync6(directory, { withFileTypes: true })) {
10011
10095
  if (skipped(entry.name)) {
10012
10096
  continue;
10013
10097
  }
10014
10098
  if (entry.isDirectory()) {
10015
- visit(join10(directory, entry.name));
10099
+ visit(join11(directory, entry.name));
10016
10100
  } else if (entry.isFile()) {
10017
10101
  const extension = entry.name.split(".").pop()?.toLowerCase() ?? "";
10018
10102
  if (SCENE_EXTENSIONS.has(extension)) {
@@ -10036,13 +10120,13 @@ function projectFiles(projectPath, extensions) {
10036
10120
  if (steppedOver(directory)) {
10037
10121
  return;
10038
10122
  }
10039
- for (const entry of readdirSync5(directory, { withFileTypes: true })) {
10123
+ for (const entry of readdirSync6(directory, { withFileTypes: true })) {
10040
10124
  if (skipped(entry.name)) {
10041
10125
  continue;
10042
10126
  }
10043
10127
  const spelled = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
10044
10128
  if (entry.isDirectory()) {
10045
- visit(join10(directory, entry.name), spelled);
10129
+ visit(join11(directory, entry.name), spelled);
10046
10130
  } else if (entry.isFile() && extensions.includes(extname(entry.name).toLowerCase())) {
10047
10131
  found.push(spelled);
10048
10132
  }
@@ -10057,20 +10141,20 @@ function scriptsWithoutUid(projectPath) {
10057
10141
  if (steppedOver(directory)) {
10058
10142
  return;
10059
10143
  }
10060
- for (const entry of readdirSync5(directory, { withFileTypes: true })) {
10144
+ for (const entry of readdirSync6(directory, { withFileTypes: true })) {
10061
10145
  if (skipped(entry.name)) {
10062
10146
  continue;
10063
10147
  }
10064
10148
  const spelled = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
10065
10149
  if (entry.isDirectory()) {
10066
- visit(join10(directory, entry.name), spelled);
10150
+ visit(join11(directory, entry.name), spelled);
10067
10151
  continue;
10068
10152
  }
10069
10153
  if (!entry.isFile()) {
10070
10154
  continue;
10071
10155
  }
10072
10156
  const extension = entry.name.split(".").pop()?.toLowerCase() ?? "";
10073
- if (CARRIES_A_UID.has(extension) && !existsSync10(join10(directory, `${entry.name}.uid`))) {
10157
+ if (CARRIES_A_UID.has(extension) && !existsSync11(join11(directory, `${entry.name}.uid`))) {
10074
10158
  found.push(spelled);
10075
10159
  }
10076
10160
  }
@@ -10087,11 +10171,11 @@ function projectTexts(projectPath) {
10087
10171
  if (steppedOver(directory)) {
10088
10172
  return;
10089
10173
  }
10090
- for (const entry of readdirSync5(directory, { withFileTypes: true })) {
10174
+ for (const entry of readdirSync6(directory, { withFileTypes: true })) {
10091
10175
  if (skipped(entry.name)) {
10092
10176
  continue;
10093
10177
  }
10094
- const absolute = join10(directory, entry.name);
10178
+ const absolute = join11(directory, entry.name);
10095
10179
  const spelled = `${prefix}${entry.name}`;
10096
10180
  if (entry.isDirectory()) {
10097
10181
  visit(absolute, `${spelled}/`);
@@ -10101,7 +10185,7 @@ function projectTexts(projectPath) {
10101
10185
  if (!entry.isFile() || BINARY_EXTENSIONS.has(extension)) {
10102
10186
  continue;
10103
10187
  }
10104
- const bytes = readFileSync8(absolute);
10188
+ const bytes = readFileSync9(absolute);
10105
10189
  if (!isBinary(bytes)) {
10106
10190
  found.push({ path: `res://${spelled}`, absolute, text: bytes.toString("utf8") });
10107
10191
  }
@@ -10117,11 +10201,11 @@ function ignoredTexts(projectPath) {
10117
10201
  return;
10118
10202
  }
10119
10203
  const under = ignored || gdignored(directory);
10120
- for (const entry of readdirSync5(directory, { withFileTypes: true })) {
10204
+ for (const entry of readdirSync6(directory, { withFileTypes: true })) {
10121
10205
  if (skipped(entry.name)) {
10122
10206
  continue;
10123
10207
  }
10124
- const absolute = join10(directory, entry.name);
10208
+ const absolute = join11(directory, entry.name);
10125
10209
  const spelled = `${prefix}${entry.name}`;
10126
10210
  if (entry.isDirectory()) {
10127
10211
  visit(absolute, `${spelled}/`, under);
@@ -10131,7 +10215,7 @@ function ignoredTexts(projectPath) {
10131
10215
  if (!under || !entry.isFile() || BINARY_EXTENSIONS.has(extension) || entry.name === ".gdignore") {
10132
10216
  continue;
10133
10217
  }
10134
- const bytes = readFileSync8(absolute);
10218
+ const bytes = readFileSync9(absolute);
10135
10219
  if (!isBinary(bytes)) {
10136
10220
  found.push({ path: `res://${spelled}`, absolute, text: bytes.toString("utf8") });
10137
10221
  }
@@ -10154,14 +10238,14 @@ function searchProject(projectPath, options) {
10154
10238
  if (steppedOver(directory)) {
10155
10239
  return;
10156
10240
  }
10157
- for (const entry of readdirSync5(directory, { withFileTypes: true })) {
10241
+ for (const entry of readdirSync6(directory, { withFileTypes: true })) {
10158
10242
  if (full()) {
10159
10243
  return;
10160
10244
  }
10161
10245
  if (skipped(entry.name)) {
10162
10246
  continue;
10163
10247
  }
10164
- const entryPath = join10(directory, entry.name);
10248
+ const entryPath = join11(directory, entry.name);
10165
10249
  if (entry.isDirectory()) {
10166
10250
  visit(entryPath);
10167
10251
  continue;
@@ -10170,7 +10254,7 @@ function searchProject(projectPath, options) {
10170
10254
  if (!entry.isFile() || extensions !== null && !extensions.has(extension)) {
10171
10255
  continue;
10172
10256
  }
10173
- const bytes = readFileSync8(entryPath);
10257
+ const bytes = readFileSync9(entryPath);
10174
10258
  if (extensions === null && isBinary(bytes)) {
10175
10259
  continue;
10176
10260
  }
@@ -10257,7 +10341,7 @@ var init_answer_json = __esm(() => {
10257
10341
  });
10258
10342
 
10259
10343
  // src/resources.ts
10260
- import { readFileSync as readFileSync9, realpathSync as realpathSync3 } from "node:fs";
10344
+ import { readFileSync as readFileSync10, realpathSync as realpathSync3 } from "node:fs";
10261
10345
  import { extname as extname2, resolve as resolve3 } from "node:path";
10262
10346
  function ensureProjectPath(getProjectPath) {
10263
10347
  const projectPath = getProjectPath();
@@ -10453,7 +10537,7 @@ function readResourceText(uri, getProjectPath) {
10453
10537
  const parsedUri = parseGodotUri(uri);
10454
10538
  if (parsedUri.kind === "project-info") {
10455
10539
  const projectFilePath = resolveProjectFile(projectPath, "project.godot");
10456
- const rawProject = readFileSync9(projectFilePath, "utf-8");
10540
+ const rawProject = readFileSync10(projectFilePath, "utf-8");
10457
10541
  const parsedProject = parseProjectGodot(rawProject);
10458
10542
  return {
10459
10543
  mimeType: "application/json",
@@ -10462,7 +10546,7 @@ function readResourceText(uri, getProjectPath) {
10462
10546
  }
10463
10547
  const filePath = resolveProjectFile(projectPath, parsedUri.resourcePath);
10464
10548
  ensureAllowedExtension(parsedUri.kind, filePath);
10465
- const text = readFileSync9(filePath, "utf-8");
10549
+ const text = readFileSync10(filePath, "utf-8");
10466
10550
  return {
10467
10551
  mimeType: parsedUri.kind === "script" ? "text/x-gdscript" : "text/plain",
10468
10552
  text
@@ -10607,16 +10691,16 @@ import { spawnSync } from "node:child_process";
10607
10691
  import { createHash as createHash3 } from "node:crypto";
10608
10692
  import {
10609
10693
  cpSync,
10610
- existsSync as existsSync11,
10694
+ existsSync as existsSync12,
10611
10695
  mkdirSync as mkdirSync4,
10612
- readdirSync as readdirSync6,
10613
- readFileSync as readFileSync10,
10696
+ readdirSync as readdirSync7,
10697
+ readFileSync as readFileSync11,
10614
10698
  rmdirSync,
10615
10699
  rmSync as rmSync5,
10616
10700
  statSync as statSync5,
10617
10701
  writeFileSync as writeFileSync6
10618
10702
  } from "node:fs";
10619
- import { dirname as dirname5, join as join11 } from "node:path";
10703
+ import { dirname as dirname6, join as join12 } from "node:path";
10620
10704
  import { fileURLToPath } from "node:url";
10621
10705
  function editorAddonDigest(from = shippedAddonsDirectory()) {
10622
10706
  return addonsDigest(EDITOR_PLUGINS, from);
@@ -10627,25 +10711,25 @@ function runtimeAddonDigest(from = shippedAddonsDirectory()) {
10627
10711
  function addonsDigest(names, from) {
10628
10712
  const hash = createHash3("sha256");
10629
10713
  const walk = (directory, relative) => {
10630
- for (const name of readdirSync6(directory).sort()) {
10714
+ for (const name of readdirSync7(directory).sort()) {
10631
10715
  if (name === VERSION_MARKER || name === DIGEST_MARKER) {
10632
10716
  continue;
10633
10717
  }
10634
- const path = join11(directory, name);
10718
+ const path = join12(directory, name);
10635
10719
  const inside = `${relative}/${name}`;
10636
10720
  if (statSync5(path).isDirectory()) {
10637
10721
  walk(path, inside);
10638
10722
  } else {
10639
10723
  hash.update(`${inside}
10640
10724
  `);
10641
- hash.update(readFileSync10(path));
10725
+ hash.update(readFileSync11(path));
10642
10726
  hash.update(`
10643
10727
  `);
10644
10728
  }
10645
10729
  }
10646
10730
  };
10647
10731
  for (const name of names) {
10648
- walk(join11(from, name), name);
10732
+ walk(join12(from, name), name);
10649
10733
  }
10650
10734
  return hash.digest("hex");
10651
10735
  }
@@ -10676,60 +10760,60 @@ function installedRuntimeDigest(projectPath) {
10676
10760
  return digestMarker(projectPath, RUNTIME_ADDON);
10677
10761
  }
10678
10762
  function digestMarker(projectPath, addon) {
10679
- const marker = join11(projectPath, "addons", addon, DIGEST_MARKER);
10680
- if (!existsSync11(marker)) {
10763
+ const marker = join12(projectPath, "addons", addon, DIGEST_MARKER);
10764
+ if (!existsSync12(marker)) {
10681
10765
  return null;
10682
10766
  }
10683
- const said = readFileSync10(marker, "utf8").trim();
10767
+ const said = readFileSync11(marker, "utf8").trim();
10684
10768
  return said === "" ? null : said;
10685
10769
  }
10686
10770
  function shippedAddonsDirectory() {
10687
- return join11(dirname5(fileURLToPath(import.meta.url)), "godot", "addons");
10771
+ return join12(dirname6(fileURLToPath(import.meta.url)), "godot", "addons");
10688
10772
  }
10689
10773
  function shippedOperationsScript() {
10690
- return join11(dirname5(fileURLToPath(import.meta.url)), "godot", "operations", "godot_operations.gd");
10774
+ return join12(dirname6(fileURLToPath(import.meta.url)), "godot", "operations", "godot_operations.gd");
10691
10775
  }
10692
10776
  function installAddons(projectPath, from = shippedAddonsDirectory()) {
10693
10777
  const installed = [];
10694
10778
  for (const name of ADDONS) {
10695
- const source = join11(from, name);
10696
- if (!existsSync11(join11(source, name === RUNTIME_ADDON ? "runtime_autoload.gd" : "plugin.cfg"))) {
10779
+ const source = join12(from, name);
10780
+ if (!existsSync12(join12(source, name === RUNTIME_ADDON ? "runtime_autoload.gd" : "plugin.cfg"))) {
10697
10781
  throw new Error(`The package holds no ${name} addon at ${source}.`);
10698
10782
  }
10699
- const target = join11(projectPath, "addons", name);
10700
- const replaced = existsSync11(target);
10783
+ const target = join12(projectPath, "addons", name);
10784
+ const replaced = existsSync12(target);
10701
10785
  rmSync5(target, { recursive: true, force: true });
10702
- mkdirSync4(dirname5(target), { recursive: true });
10786
+ mkdirSync4(dirname6(target), { recursive: true });
10703
10787
  cpSync(source, target, { recursive: true });
10704
- writeFileSync6(join11(target, VERSION_MARKER), `${SERVER_VERSION}
10788
+ writeFileSync6(join12(target, VERSION_MARKER), `${SERVER_VERSION}
10705
10789
  `);
10706
10790
  installed.push({ name, path: target, replaced });
10707
10791
  }
10708
- writeFileSync6(join11(projectPath, "addons", ADDONS[0], DIGEST_MARKER), `${editorAddonDigest(from)}
10792
+ writeFileSync6(join12(projectPath, "addons", ADDONS[0], DIGEST_MARKER), `${editorAddonDigest(from)}
10709
10793
  `);
10710
- writeFileSync6(join11(projectPath, "addons", RUNTIME_ADDON, DIGEST_MARKER), `${runtimeAddonDigest(from)}
10794
+ writeFileSync6(join12(projectPath, "addons", RUNTIME_ADDON, DIGEST_MARKER), `${runtimeAddonDigest(from)}
10711
10795
  `);
10712
10796
  return installed;
10713
10797
  }
10714
10798
  function installedAddonVersion(projectPath) {
10715
- const marker = join11(projectPath, "addons", ADDONS[0], VERSION_MARKER);
10716
- if (!existsSync11(marker)) {
10799
+ const marker = join12(projectPath, "addons", ADDONS[0], VERSION_MARKER);
10800
+ if (!existsSync12(marker)) {
10717
10801
  return null;
10718
10802
  }
10719
- const said = readFileSync10(marker, "utf8").trim();
10803
+ const said = readFileSync11(marker, "utf8").trim();
10720
10804
  return said === "" ? null : said;
10721
10805
  }
10722
10806
  function removeAddons(projectPath) {
10723
10807
  const removed = [];
10724
10808
  for (const name of ADDONS) {
10725
- const target = join11(projectPath, "addons", name);
10726
- if (existsSync11(target)) {
10809
+ const target = join12(projectPath, "addons", name);
10810
+ if (existsSync12(target)) {
10727
10811
  rmSync5(target, { recursive: true, force: true });
10728
10812
  removed.push(target);
10729
10813
  }
10730
10814
  }
10731
- const addons = join11(projectPath, "addons");
10732
- if (removed.length > 0 && existsSync11(addons) && readdirSync6(addons).length === 0) {
10815
+ const addons = join12(projectPath, "addons");
10816
+ if (removed.length > 0 && existsSync12(addons) && readdirSync7(addons).length === 0) {
10733
10817
  rmdirSync(addons);
10734
10818
  removed.push(addons);
10735
10819
  }
@@ -10763,7 +10847,7 @@ function loaderAmong(projectPath, named) {
10763
10847
  }
10764
10848
  let source = null;
10765
10849
  try {
10766
- source = readFileSync10(join11(projectPath, path), "utf8");
10850
+ source = readFileSync11(join12(projectPath, path), "utf8");
10767
10851
  } catch {}
10768
10852
  if (source?.includes(RUNTIME_AUTOLOAD.path)) {
10769
10853
  return { name: entry, path, exists: true };
@@ -10805,10 +10889,10 @@ function enabledPlugins(settings) {
10805
10889
  function inspectProject(projectPath) {
10806
10890
  const problems = [];
10807
10891
  const addons = ADDONS.map((name) => {
10808
- const directory = join11(projectPath, "addons", name);
10809
- const marker = join11(directory, VERSION_MARKER);
10810
- const installed = existsSync11(directory);
10811
- const version = existsSync11(marker) ? readFileSync10(marker, "utf8").trim() : null;
10892
+ const directory = join12(projectPath, "addons", name);
10893
+ const marker = join12(directory, VERSION_MARKER);
10894
+ const installed = existsSync12(directory);
10895
+ const version = existsSync12(marker) ? readFileSync11(marker, "utf8").trim() : null;
10812
10896
  const current = version === SERVER_VERSION;
10813
10897
  if (!installed) {
10814
10898
  problems.push(`addons/${name} is not installed; run gdharness setup`);
@@ -10817,7 +10901,7 @@ function inspectProject(projectPath) {
10817
10901
  }
10818
10902
  return { name, installed, version, current };
10819
10903
  });
10820
- const settings = parseProjectGodot(readFileSync10(join11(projectPath, "project.godot"), "utf8"));
10904
+ const settings = parseProjectGodot(readFileSync11(join12(projectPath, "project.godot"), "utf8"));
10821
10905
  const pluginsEnabled = enabledPlugins(settings);
10822
10906
  for (const name of EDITOR_PLUGINS) {
10823
10907
  if (!pluginsEnabled.includes(name)) {
@@ -10833,7 +10917,7 @@ function inspectProject(projectPath) {
10833
10917
  const tracked = trackedByGit(projectPath, [...named.values()]);
10834
10918
  const addonMissing = (path) => addons.some((addon) => !addon.installed && path.startsWith(`addons/${addon.name}/`));
10835
10919
  for (const [name, path] of named) {
10836
- if (!existsSync11(join11(projectPath, path))) {
10920
+ if (!existsSync12(join12(projectPath, path))) {
10837
10921
  if (!addonMissing(path)) {
10838
10922
  problems.push(`the ${name} autoload names res://${path}, which is not in this project, so it boots with a missing script; restore that file, or point the autoload at one that is here`);
10839
10923
  }
@@ -12241,12 +12325,12 @@ var init_tool_definitions = __esm(() => {
12241
12325
  },
12242
12326
  metrics: {
12243
12327
  effect: "read",
12244
- summary: "frame time, memory, draw calls and the rest. frame_time is the mean wall time of a frame over the last frames_timed frames (up to 120, timed by the game itself, physics and the wait for the display included), frame_time_max the slowest of them, both in seconds and null before a frame has passed; vsync says whether a frame waits for the display, and max_fps is the game's frame-rate cap, 0 for none. fps, process_time_max and physics_time are the engine's monitors, which it sets once a second: the frames counted in the last second, and the slowest process and physics step in it, so they trail a change by up to a second and the last two are a worst case rather than the frame. The memory, object and render counts are read as asked. memory_static is what the engine allocated on the CPU side, in bytes; render_video_mem_used, render_texture_mem_used and render_buffer_mem_used are what the renderer has on the GPU, in bytes, which memory_static does not count",
12328
+ summary: "frame time, memory, draw calls and the rest. frame_time is the mean wall time of a frame over the last frames_timed frames (up to 120, timed by the game itself, physics and the wait for the display included), frame_time_max the slowest of them, both in seconds and null before a frame has passed; vsync says whether a frame waits for the display, and max_fps is the game's frame-rate cap, 0 for none. physics_steps_per_second is the physics steps taken a second over those frames, fewer than physics_ticks_per_second when a frame needs more than max_physics_steps_per_frame or the editor's Game view slows it. fps, process_time_max and physics_time are the engine's monitors, which it sets once a second: the frames counted in the last second, and the slowest process and physics step in it, so they trail a change by up to a second and the last two are a worst case rather than the frame. The memory, object and render counts are read as asked. memory_static is what the engine allocated on the CPU side, in bytes; render_video_mem_used, render_texture_mem_used and render_buffer_mem_used are what the renderer has on the GPU, in bytes, which memory_static does not count",
12245
12329
  requires: []
12246
12330
  },
12247
12331
  accessibility: {
12248
12332
  effect: "read",
12249
- summary: `what the game tells a screen reader, read through UI Automation as a Windows screen reader reads it, under windows: one tree per window, each element with its role as UI Automation names it (text for static text, button, check_box, group for a control holding others), its name, its value where it has one, its bounds in the window's pixels, the actions a screen reader is offered (click, focus, toggle, set_value, select, expand_collapse, scroll_into_view), and focused on the one holding the focus. The name is what is read aloud, so an empty one is read as nothing; static text takes it from DisplayServer.accessibility_update_set_value, not from a name set on it, and the answer says under _note how many text elements on screen have none. With no control focused, the engine reports the first node under the root as focused, which in a run with the runtime is GdharnessRuntime. Needs a windowed run on Windows started with engineArgs ["--accessibility", "always"] or with the project's accessibility/general/accessibility_support at Always Active, since the engine builds the tree only when asked; reading it takes a second or two, starting PowerShell inside the game so it reaches the game's own desktop`,
12333
+ summary: "what the game tells a screen reader, read through UI Automation as a Windows screen reader reads it, under windows: one tree per window, each element with its role (text for static text, button, check_box, group for a control holding others), name, value, description, placeholder, bounds in the window's pixels, the actions a reader is offered (click, focus, toggle, set_value, select, expand_collapse, scroll_into_view), and focused on the one with the focus. node is the path of the node that made the element; an element a node made as a part of itself has part_of instead, and part: announcement marks the text a window speaks announcements through. An empty name is read as nothing: static text takes it from accessibility_update_set_value, and _note counts the text elements on screen with none. tooltip is the node's tooltip_text, which a Windows reader is not told. With no control focused, the engine reports the first node under the root, GdharnessRuntime, as focused. Needs a windowed run on Windows started with engineArgs [\"--accessibility\", \"always\"] or accessibility_support at Always Active, since the engine builds the tree only when asked; reading takes a second or two",
12250
12334
  requires: []
12251
12335
  }
12252
12336
  },
@@ -34422,10 +34506,10 @@ var init_stdio = __esm(() => {
34422
34506
  });
34423
34507
 
34424
34508
  // src/runtime-client.ts
34425
- import { existsSync as existsSync13, readdirSync as readdirSync8, readFileSync as readFileSync12, statSync as statSync6, unlinkSync } from "node:fs";
34509
+ import { existsSync as existsSync14, readdirSync as readdirSync9, readFileSync as readFileSync13, statSync as statSync6, unlinkSync as unlinkSync2 } from "node:fs";
34426
34510
  import { createConnection } from "node:net";
34427
34511
  import { tmpdir as tmpdir4 } from "node:os";
34428
- import { dirname as dirname8, join as join14, resolve as resolve4 } from "node:path";
34512
+ import { dirname as dirname9, join as join15, resolve as resolve4 } from "node:path";
34429
34513
  import { setTimeout as delay } from "node:timers/promises";
34430
34514
  function runtimeDirectory(variables = process.env) {
34431
34515
  const explicit = envValue("GDHARNESS_RUNTIME_DIR", variables);
@@ -34433,20 +34517,20 @@ function runtimeDirectory(variables = process.env) {
34433
34517
  return explicit;
34434
34518
  }
34435
34519
  const perUser = envValue("XDG_RUNTIME_DIR", variables);
34436
- return join14(perUser ?? tmpdir4(), "gdharness");
34520
+ return join15(perUser ?? tmpdir4(), "gdharness");
34437
34521
  }
34438
34522
  function runtimeDirectories(variables = process.env) {
34439
34523
  const candidates = [runtimeDirectory(variables)];
34440
34524
  const fallbacks = process.platform === "win32" ? [envValue("SystemRoot", variables) ?? envValue("windir", variables) ?? "C:\\Windows"] : ["/tmp", "/var/tmp"];
34441
34525
  for (const base of fallbacks) {
34442
- candidates.push(join14(base, process.platform === "win32" ? "Temp" : "", "gdharness"));
34526
+ candidates.push(join15(base, process.platform === "win32" ? "Temp" : "", "gdharness"));
34443
34527
  }
34444
34528
  return [...new Set(candidates.map((path) => resolve4(path)))];
34445
34529
  }
34446
34530
  function errorReportOf(pid, directories = runtimeDirectories()) {
34447
34531
  for (const directory of directories) {
34448
- const path = join14(directory, `runtime-${pid}.log`);
34449
- if (existsSync13(path)) {
34532
+ const path = join15(directory, `runtime-${pid}.log`);
34533
+ if (existsSync14(path)) {
34450
34534
  return path;
34451
34535
  }
34452
34536
  }
@@ -34477,7 +34561,7 @@ function reportAlreadyInTranscript(transcript, report) {
34477
34561
  function parseAnnouncement(file, pid) {
34478
34562
  let fields;
34479
34563
  try {
34480
- fields = asParams(JSON.parse(readFileSync12(file, "utf8")));
34564
+ fields = asParams(JSON.parse(readFileSync13(file, "utf8")));
34481
34565
  } catch {
34482
34566
  return { kind: "rubbish" };
34483
34567
  }
@@ -34543,7 +34627,7 @@ function discoverRuntimes(directories = runtimeDirectories()) {
34543
34627
  function wentAway(file, pid) {
34544
34628
  let project = "";
34545
34629
  try {
34546
- const fields = asParams(JSON.parse(readFileSync12(file, "utf8")));
34630
+ const fields = asParams(JSON.parse(readFileSync13(file, "utf8")));
34547
34631
  project = readString(readParams(fields, "project") ?? {}, "path") ?? "";
34548
34632
  } catch {
34549
34633
  return;
@@ -34586,23 +34670,23 @@ function strangersAmong(candidates, now, startTimes = startTimesOf) {
34586
34670
  }).map((one) => one.file));
34587
34671
  }
34588
34672
  function announcedIn(directory) {
34589
- if (!existsSync13(directory)) {
34673
+ if (!existsSync14(directory)) {
34590
34674
  return [];
34591
34675
  }
34592
34676
  const found = [];
34593
34677
  const candidates = [];
34594
34678
  const entries = [];
34595
- for (const entry of readdirSync8(directory)) {
34679
+ for (const entry of readdirSync9(directory)) {
34596
34680
  const report = ERROR_REPORT_PATTERN.exec(entry);
34597
34681
  if (report) {
34598
- sweepErrorReport(join14(directory, entry), Number.parseInt(report[1] ?? "", 10));
34682
+ sweepErrorReport(join15(directory, entry), Number.parseInt(report[1] ?? "", 10));
34599
34683
  continue;
34600
34684
  }
34601
34685
  const match = ANNOUNCEMENT_PATTERN.exec(entry);
34602
34686
  if (!match) {
34603
34687
  continue;
34604
34688
  }
34605
- const file = join14(directory, entry);
34689
+ const file = join15(directory, entry);
34606
34690
  const pid = Number.parseInt(match[1] ?? "", 10);
34607
34691
  const running = alive(pid);
34608
34692
  entries.push({ file, pid, running });
@@ -34622,7 +34706,7 @@ function announcedIn(directory) {
34622
34706
  }
34623
34707
  wentAway(file, pid);
34624
34708
  try {
34625
- unlinkSync(file);
34709
+ unlinkSync2(file);
34626
34710
  } catch {}
34627
34711
  verdicts.delete(file);
34628
34712
  continue;
@@ -34630,16 +34714,16 @@ function announcedIn(directory) {
34630
34714
  found.push(announced);
34631
34715
  }
34632
34716
  const held = new Set(entries.map((one) => one.file));
34633
- const here = join14(directory, ".");
34717
+ const here = join15(directory, ".");
34634
34718
  for (const file of verdicts.keys()) {
34635
- if (dirname8(file) === here && !held.has(file)) {
34719
+ if (dirname9(file) === here && !held.has(file)) {
34636
34720
  verdicts.delete(file);
34637
34721
  }
34638
34722
  }
34639
34723
  return found;
34640
34724
  }
34641
34725
  async function announcementEnded(pid, directories = runtimeDirectories()) {
34642
- const files = directories.map((directory) => join14(directory, `runtime-${pid}.json`)).filter((file) => existsSync13(file));
34726
+ const files = directories.map((directory) => join15(directory, `runtime-${pid}.json`)).filter((file) => existsSync14(file));
34643
34727
  const first = files[0];
34644
34728
  if (first === undefined) {
34645
34729
  return;
@@ -34655,7 +34739,7 @@ async function announcementEnded(pid, directories = runtimeDirectories()) {
34655
34739
  wentAway(first, pid);
34656
34740
  for (const file of files) {
34657
34741
  try {
34658
- unlinkSync(file);
34742
+ unlinkSync2(file);
34659
34743
  } catch {}
34660
34744
  verdicts.delete(file);
34661
34745
  }
@@ -34666,7 +34750,7 @@ function sweepErrorReport(path, pid, now = Date.now()) {
34666
34750
  }
34667
34751
  try {
34668
34752
  if (now - statSync6(path).mtimeMs >= ERROR_REPORT_KEEP_MS) {
34669
- unlinkSync(path);
34753
+ unlinkSync2(path);
34670
34754
  }
34671
34755
  } catch {}
34672
34756
  }
@@ -34940,14 +35024,14 @@ var init_runtime_client = __esm(() => {
34940
35024
  });
34941
35025
 
34942
35026
  // src/boot-note.ts
34943
- import { mkdirSync as mkdirSync6, readFileSync as readFileSync13, writeFileSync as writeFileSync8 } from "node:fs";
34944
- import { dirname as dirname9, join as join15 } from "node:path";
35027
+ import { mkdirSync as mkdirSync6, readFileSync as readFileSync14, writeFileSync as writeFileSync8 } from "node:fs";
35028
+ import { dirname as dirname10, join as join16 } from "node:path";
34945
35029
  function bootNotePath(projectPath) {
34946
- return join15(projectPath, ".godot", "gdharness-boot.json");
35030
+ return join16(projectPath, ".godot", "gdharness-boot.json");
34947
35031
  }
34948
35032
  function readScenes(projectPath) {
34949
35033
  try {
34950
- return asParams(asParams(JSON.parse(readFileSync13(bootNotePath(projectPath), "utf8")))["scenes"]);
35034
+ return asParams(asParams(JSON.parse(readFileSync14(bootNotePath(projectPath), "utf8")))["scenes"]);
34951
35035
  } catch {
34952
35036
  return {};
34953
35037
  }
@@ -34963,7 +35047,7 @@ function readBootNote(projectPath, scene) {
34963
35047
  function writeBootNote(projectPath, scene, announcedAfterMs) {
34964
35048
  try {
34965
35049
  const path = bootNotePath(projectPath);
34966
- mkdirSync6(dirname9(path), { recursive: true });
35050
+ mkdirSync6(dirname10(path), { recursive: true });
34967
35051
  const scenes = {
34968
35052
  ...readScenes(projectPath),
34969
35053
  [scene]: {
@@ -34989,10 +35073,10 @@ var init_boot_note = __esm(() => {
34989
35073
  });
34990
35074
 
34991
35075
  // src/breakpoint-note.ts
34992
- import { mkdirSync as mkdirSync7, readFileSync as readFileSync14, rmSync as rmSync7, writeFileSync as writeFileSync9 } from "node:fs";
34993
- import { dirname as dirname10, isAbsolute as isAbsolute2, join as join16, relative as relative3 } from "node:path";
35076
+ import { mkdirSync as mkdirSync7, readFileSync as readFileSync15, rmSync as rmSync7, writeFileSync as writeFileSync9 } from "node:fs";
35077
+ import { dirname as dirname11, isAbsolute as isAbsolute3, join as join17, relative as relative3 } from "node:path";
34994
35078
  function breakpointNotePath(projectPath) {
34995
- return join16(projectPath, ".godot", "gdharness-breakpoints.json");
35079
+ return join17(projectPath, ".godot", "gdharness-breakpoints.json");
34996
35080
  }
34997
35081
  function writeBreakpointNote(projectPath, held) {
34998
35082
  const path = breakpointNotePath(projectPath);
@@ -35009,7 +35093,7 @@ function writeBreakpointNote(projectPath, held) {
35009
35093
  lines: [...one.lines]
35010
35094
  }))
35011
35095
  };
35012
- mkdirSync7(dirname10(path), { recursive: true });
35096
+ mkdirSync7(dirname11(path), { recursive: true });
35013
35097
  writeFileSync9(path, `${JSON.stringify(note, null, 2)}
35014
35098
  `, "utf8");
35015
35099
  } catch (error) {
@@ -35018,7 +35102,7 @@ function writeBreakpointNote(projectPath, held) {
35018
35102
  }
35019
35103
  function readBreakpointNote(projectPath) {
35020
35104
  try {
35021
- const parsed = JSON.parse(readFileSync14(breakpointNotePath(projectPath), "utf8"));
35105
+ const parsed = JSON.parse(readFileSync15(breakpointNotePath(projectPath), "utf8"));
35022
35106
  if (typeof parsed !== "object" || parsed === null || !Array.isArray(parsed.breakpoints)) {
35023
35107
  return [];
35024
35108
  }
@@ -35028,12 +35112,12 @@ function readBreakpointNote(projectPath) {
35028
35112
  continue;
35029
35113
  }
35030
35114
  const { script, lines: listed } = entry;
35031
- if (typeof script !== "string" || isAbsolute2(script) || script.split("/").includes("..")) {
35115
+ if (typeof script !== "string" || isAbsolute3(script) || script.split("/").includes("..")) {
35032
35116
  continue;
35033
35117
  }
35034
35118
  const lines = Array.isArray(listed) ? listed.filter((line) => Number.isInteger(line) && line > 0) : [];
35035
35119
  if (lines.length > 0) {
35036
- held.push({ scriptPath: realPathOr(join16(projectPath, script)), lines });
35120
+ held.push({ scriptPath: realPathOr(join17(projectPath, script)), lines });
35037
35121
  }
35038
35122
  }
35039
35123
  return held;
@@ -35043,7 +35127,7 @@ function readBreakpointNote(projectPath) {
35043
35127
  }
35044
35128
  function withinProject(projectPath, scriptPath) {
35045
35129
  const rel = relative3(projectPath, scriptPath);
35046
- return rel !== "" && !rel.startsWith("..") && !isAbsolute2(rel);
35130
+ return rel !== "" && !rel.startsWith("..") && !isAbsolute3(rel);
35047
35131
  }
35048
35132
  var init_breakpoint_note = __esm(() => {
35049
35133
  init_errors();
@@ -35051,14 +35135,14 @@ var init_breakpoint_note = __esm(() => {
35051
35135
  });
35052
35136
 
35053
35137
  // src/class-note.ts
35054
- import { mkdirSync as mkdirSync8, readFileSync as readFileSync15, writeFileSync as writeFileSync10 } from "node:fs";
35055
- import { dirname as dirname11, join as join17 } from "node:path";
35138
+ import { mkdirSync as mkdirSync8, readFileSync as readFileSync16, writeFileSync as writeFileSync10 } from "node:fs";
35139
+ import { dirname as dirname12, join as join18 } from "node:path";
35056
35140
  function classNotePath(projectPath) {
35057
- return join17(projectPath, ".godot", "gdharness-classes.json");
35141
+ return join18(projectPath, ".godot", "gdharness-classes.json");
35058
35142
  }
35059
35143
  function readClassNote(projectPath) {
35060
35144
  try {
35061
- const fields = asParams(JSON.parse(readFileSync15(classNotePath(projectPath), "utf8")));
35145
+ const fields = asParams(JSON.parse(readFileSync16(classNotePath(projectPath), "utf8")));
35062
35146
  const rebuiltWith = readArray(fields, "rebuiltWith");
35063
35147
  if (rebuiltWith === undefined || !rebuiltWith.every((name) => typeof name === "string")) {
35064
35148
  return null;
@@ -35075,7 +35159,7 @@ function readClassNote(projectPath) {
35075
35159
  function writeClassNote(projectPath, note) {
35076
35160
  try {
35077
35161
  const path = classNotePath(projectPath);
35078
- mkdirSync8(dirname11(path), { recursive: true });
35162
+ mkdirSync8(dirname12(path), { recursive: true });
35079
35163
  writeFileSync10(path, `${JSON.stringify(note, null, 2)}
35080
35164
  `, "utf8");
35081
35165
  } catch {}
@@ -35083,16 +35167,16 @@ function writeClassNote(projectPath, note) {
35083
35167
  var init_class_note = () => {};
35084
35168
 
35085
35169
  // src/config-pin.ts
35086
- import { existsSync as existsSync14, readFileSync as readFileSync16 } from "node:fs";
35087
- import { join as join18 } from "node:path";
35170
+ import { existsSync as existsSync15, readFileSync as readFileSync17 } from "node:fs";
35171
+ import { join as join19 } from "node:path";
35088
35172
  function versionsNamedInConfig(projectPath) {
35089
- const config = join18(projectPath, ".mcp.json");
35090
- if (!existsSync14(config)) {
35173
+ const config = join19(projectPath, ".mcp.json");
35174
+ if (!existsSync15(config)) {
35091
35175
  return [];
35092
35176
  }
35093
35177
  let text;
35094
35178
  try {
35095
- text = readFileSync16(config, "utf8");
35179
+ text = readFileSync17(config, "utf8");
35096
35180
  JSON.parse(text);
35097
35181
  } catch {
35098
35182
  return [];
@@ -35113,26 +35197,26 @@ var init_config_pin = __esm(() => {
35113
35197
  });
35114
35198
 
35115
35199
  // src/cut-test-run.ts
35116
- import { existsSync as existsSync15, mkdirSync as mkdirSync9, readFileSync as readFileSync17, rmSync as rmSync8, writeFileSync as writeFileSync11 } from "node:fs";
35117
- import { dirname as dirname12, join as join19 } from "node:path";
35200
+ import { existsSync as existsSync16, mkdirSync as mkdirSync9, readFileSync as readFileSync18, rmSync as rmSync8, writeFileSync as writeFileSync11 } from "node:fs";
35201
+ import { dirname as dirname13, join as join20 } from "node:path";
35118
35202
  function keptAt(projectPath) {
35119
- return join19(projectPath, ".godot", "gdharness-reports", "cut-run.json");
35203
+ return join20(projectPath, ".godot", "gdharness-reports", "cut-run.json");
35120
35204
  }
35121
35205
  function keepCutTestRun(projectPath, run) {
35122
35206
  const path = keptAt(projectPath);
35123
35207
  try {
35124
- mkdirSync9(dirname12(path), { recursive: true });
35208
+ mkdirSync9(dirname13(path), { recursive: true });
35125
35209
  writeFileSync11(path, `${JSON.stringify(run, null, 2)}
35126
35210
  `, "utf8");
35127
35211
  } catch {}
35128
35212
  }
35129
35213
  function takeCutTestRun(projectPath) {
35130
35214
  const path = keptAt(projectPath);
35131
- if (!existsSync15(path)) {
35215
+ if (!existsSync16(path)) {
35132
35216
  return null;
35133
35217
  }
35134
35218
  try {
35135
- const run = JSON.parse(readFileSync17(path, "utf8"));
35219
+ const run = JSON.parse(readFileSync18(path, "utf8"));
35136
35220
  rmSync8(path, { force: true });
35137
35221
  return typeof run.cutAt === "string" && Array.isArray(run.paths) && run.paths.every((path) => typeof path === "string") ? run : null;
35138
35222
  } catch {
@@ -35948,25 +36032,25 @@ var init_desktop = () => {};
35948
36032
  // src/editor-log.ts
35949
36033
  import {
35950
36034
  closeSync,
35951
- existsSync as existsSync16,
36035
+ existsSync as existsSync17,
35952
36036
  mkdirSync as mkdirSync10,
35953
36037
  openSync,
35954
- readFileSync as readFileSync18,
36038
+ readFileSync as readFileSync19,
35955
36039
  readSync,
35956
36040
  rmSync as rmSync9,
35957
36041
  statSync as statSync7,
35958
36042
  writeFileSync as writeFileSync12
35959
36043
  } from "node:fs";
35960
- import { dirname as dirname13, join as join20 } from "node:path";
36044
+ import { dirname as dirname14, join as join21 } from "node:path";
35961
36045
  function editorLogPath(projectPath) {
35962
- return join20(projectPath, ".godot", "gdharness-editor.log");
36046
+ return join21(projectPath, ".godot", "gdharness-editor.log");
35963
36047
  }
35964
36048
  function editorLogNotePath(projectPath) {
35965
- return join20(projectPath, ".godot", "gdharness-editor.json");
36049
+ return join21(projectPath, ".godot", "gdharness-editor.json");
35966
36050
  }
35967
36051
  function readEditorLogNote(projectPath) {
35968
36052
  try {
35969
- const fields = asParams(JSON.parse(readFileSync18(editorLogNotePath(projectPath), "utf8")));
36053
+ const fields = asParams(JSON.parse(readFileSync19(editorLogNotePath(projectPath), "utf8")));
35970
36054
  const pid = readNumber(fields, "pid");
35971
36055
  const startedAt = readString(fields, "startedAt");
35972
36056
  return pid !== undefined && Number.isInteger(pid) && pid > 0 && startedAt !== undefined ? { pid, startedAt } : null;
@@ -35977,7 +36061,7 @@ function readEditorLogNote(projectPath) {
35977
36061
  function clearEditorLog(projectPath) {
35978
36062
  try {
35979
36063
  const path = editorLogPath(projectPath);
35980
- mkdirSync10(dirname13(path), { recursive: true });
36064
+ mkdirSync10(dirname14(path), { recursive: true });
35981
36065
  rmSync9(path, { force: true });
35982
36066
  rmSync9(editorLogNotePath(projectPath), { force: true });
35983
36067
  } catch {}
@@ -35985,7 +36069,7 @@ function clearEditorLog(projectPath) {
35985
36069
  function writeEditorLogNote(projectPath, pid) {
35986
36070
  try {
35987
36071
  const path = editorLogNotePath(projectPath);
35988
- mkdirSync10(dirname13(path), { recursive: true });
36072
+ mkdirSync10(dirname14(path), { recursive: true });
35989
36073
  writeFileSync12(path, JSON.stringify({ pid, startedAt: new Date().toISOString() }, null, 2), "utf8");
35990
36074
  } catch {}
35991
36075
  }
@@ -36007,7 +36091,7 @@ function editorConsole(projectPath, editorPid, openedByAServer) {
36007
36091
  return { kind: "older server", notedPid: noted?.pid ?? null };
36008
36092
  }
36009
36093
  const path = editorLogPath(projectPath);
36010
- if (!existsSync16(path)) {
36094
+ if (!existsSync17(path)) {
36011
36095
  return { kind: "not written" };
36012
36096
  }
36013
36097
  const log = new GameLog;
@@ -36065,7 +36149,7 @@ function foldProgress(text) {
36065
36149
  function theFirstOf(path) {
36066
36150
  const whole = statSync7(path).size;
36067
36151
  if (whole <= MOST_CONSOLE_BYTES) {
36068
- return { text: readFileSync18(path, "utf8") };
36152
+ return { text: readFileSync19(path, "utf8") };
36069
36153
  }
36070
36154
  const handle = openSync(path, "r");
36071
36155
  try {
@@ -40273,19 +40357,19 @@ var DEBUG_PREFIX = "debug ";
40273
40357
  var init_keeper_channel = () => {};
40274
40358
 
40275
40359
  // src/lifecycle-log.ts
40276
- import { appendFileSync, mkdirSync as mkdirSync11, renameSync, statSync as statSync8 } from "node:fs";
40277
- import { dirname as dirname14, join as join21 } from "node:path";
40360
+ import { appendFileSync, mkdirSync as mkdirSync11, renameSync as renameSync2, statSync as statSync8 } from "node:fs";
40361
+ import { dirname as dirname15, join as join22 } from "node:path";
40278
40362
  import process10 from "node:process";
40279
40363
  function lifecycleLogPath() {
40280
- return join21(runtimeDirectory(), "servers.log");
40364
+ return join22(runtimeDirectory(), "servers.log");
40281
40365
  }
40282
40366
  function writeLifecycle(event, fields = {}) {
40283
40367
  const path = lifecycleLogPath();
40284
40368
  try {
40285
- mkdirSync11(dirname14(path), { recursive: true });
40369
+ mkdirSync11(dirname15(path), { recursive: true });
40286
40370
  if ((statSync8(path, { throwIfNoEntry: false })?.size ?? 0) > LIFECYCLE_LOG_LIMIT) {
40287
40371
  try {
40288
- renameSync(path, `${path}.1`);
40372
+ renameSync2(path, `${path}.1`);
40289
40373
  } catch {}
40290
40374
  }
40291
40375
  const line = {
@@ -40320,10 +40404,10 @@ var init_lifecycle_log = __esm(() => {
40320
40404
  });
40321
40405
 
40322
40406
  // src/lsp_client.ts
40323
- import { existsSync as existsSync17, realpathSync as realpathSync4 } from "node:fs";
40407
+ import { existsSync as existsSync18, realpathSync as realpathSync4 } from "node:fs";
40324
40408
  import { readFile, realpath } from "node:fs/promises";
40325
40409
  import { createConnection as createConnection3 } from "node:net";
40326
- import { dirname as dirname15, join as join22, resolve as resolve5 } from "node:path";
40410
+ import { dirname as dirname16, join as join23, resolve as resolve5 } from "node:path";
40327
40411
  import { setTimeout as delay3 } from "node:timers/promises";
40328
40412
  import { fileURLToPath as fileURLToPath3, pathToFileURL } from "node:url";
40329
40413
  function initializeTimeoutMs() {
@@ -40485,7 +40569,7 @@ class GodotLSPClient {
40485
40569
  if (this.initialized) {
40486
40570
  return;
40487
40571
  }
40488
- const rootPath = this.rootPath ?? dirname15(resolve5(filePath));
40572
+ const rootPath = this.rootPath ?? dirname16(resolve5(filePath));
40489
40573
  await this.initialize(rootPath);
40490
40574
  }
40491
40575
  async sendRequest(method, params, timeoutMs = this.timeouts.requestMs) {
@@ -40986,7 +41070,7 @@ async function resolveLSPPaths(projectPathValue, scriptPathValue) {
40986
41070
  } catch {
40987
41071
  throw new ArgumentRefusal(`Project path does not exist: ${requestedProjectPath}`);
40988
41072
  }
40989
- if (!existsSync17(join22(projectPath, "project.godot"))) {
41073
+ if (!existsSync18(join23(projectPath, "project.godot"))) {
40990
41074
  throw new ArgumentRefusal(`Not a Godot project: ${projectPath}. Point projectPath at the directory holding project.godot.`);
40991
41075
  }
40992
41076
  const contained = resolveWithinProject(projectPath, scriptPathValue);
@@ -41426,8 +41510,8 @@ var init_prompts = __esm(() => {
41426
41510
  });
41427
41511
 
41428
41512
  // src/rename.ts
41429
- import { existsSync as existsSync18, mkdirSync as mkdirSync12, readFileSync as readFileSync19, renameSync as renameSync2, unlinkSync as unlinkSync2, writeFileSync as writeFileSync13 } from "node:fs";
41430
- import { dirname as dirname16, posix as posix2 } from "node:path";
41513
+ import { existsSync as existsSync19, mkdirSync as mkdirSync12, readFileSync as readFileSync20, renameSync as renameSync3, unlinkSync as unlinkSync3, writeFileSync as writeFileSync13 } from "node:fs";
41514
+ import { dirname as dirname17, posix as posix2 } from "node:path";
41431
41515
 
41432
41516
  class Script {
41433
41517
  path;
@@ -42128,26 +42212,26 @@ function rewritesOf(plan, texts, move) {
42128
42212
  }
42129
42213
  function applyRewrites(rewrites) {
42130
42214
  for (const rewrite of rewrites) {
42131
- const now = readFileSync19(rewrite.absolute, "utf8");
42215
+ const now = readFileSync20(rewrite.absolute, "utf8");
42132
42216
  if (now !== rewrite.before) {
42133
42217
  throw new Error(`${rewrite.path} changed on disk while the rename was being worked out; nothing was written`);
42134
42218
  }
42135
- if (rewrite.writeTo !== rewrite.absolute && existsSync18(rewrite.writeTo)) {
42219
+ if (rewrite.writeTo !== rewrite.absolute && existsSync19(rewrite.writeTo)) {
42136
42220
  throw new Error(`${rewrite.writeTo} already exists; nothing was written`);
42137
42221
  }
42138
42222
  }
42139
42223
  const done = [];
42140
42224
  try {
42141
42225
  for (const rewrite of rewrites) {
42142
- mkdirSync12(dirname16(rewrite.writeTo), { recursive: true });
42226
+ mkdirSync12(dirname17(rewrite.writeTo), { recursive: true });
42143
42227
  const staging = `${rewrite.writeTo}.gdharness-rename`;
42144
42228
  writeFileSync13(staging, rewrite.after, "utf8");
42145
- renameSync2(staging, rewrite.writeTo);
42229
+ renameSync3(staging, rewrite.writeTo);
42146
42230
  let movedUid = false;
42147
42231
  if (rewrite.writeTo !== rewrite.absolute) {
42148
- unlinkSync2(rewrite.absolute);
42149
- if (existsSync18(`${rewrite.absolute}.uid`)) {
42150
- renameSync2(`${rewrite.absolute}.uid`, `${rewrite.writeTo}.uid`);
42232
+ unlinkSync3(rewrite.absolute);
42233
+ if (existsSync19(`${rewrite.absolute}.uid`)) {
42234
+ renameSync3(`${rewrite.absolute}.uid`, `${rewrite.writeTo}.uid`);
42151
42235
  movedUid = true;
42152
42236
  }
42153
42237
  }
@@ -42158,9 +42242,9 @@ function applyRewrites(rewrites) {
42158
42242
  try {
42159
42243
  writeFileSync13(rewrite.absolute, rewrite.before, "utf8");
42160
42244
  if (rewrite.writeTo !== rewrite.absolute) {
42161
- unlinkSync2(rewrite.writeTo);
42245
+ unlinkSync3(rewrite.writeTo);
42162
42246
  if (movedUid) {
42163
- renameSync2(`${rewrite.writeTo}.uid`, `${rewrite.absolute}.uid`);
42247
+ renameSync3(`${rewrite.writeTo}.uid`, `${rewrite.absolute}.uid`);
42164
42248
  }
42165
42249
  }
42166
42250
  } catch {}
@@ -42192,8 +42276,8 @@ var init_rename = __esm(() => {
42192
42276
  });
42193
42277
 
42194
42278
  // src/rename-symbol.ts
42195
- import { existsSync as existsSync19, mkdirSync as mkdirSync13, readdirSync as readdirSync9, realpathSync as realpathSync5, rmSync as rmSync10, writeFileSync as writeFileSync14 } from "node:fs";
42196
- import { basename as basename5, dirname as dirname17, join as join23, relative as relative4, resolve as resolve6 } from "node:path";
42279
+ import { existsSync as existsSync20, mkdirSync as mkdirSync13, readdirSync as readdirSync10, realpathSync as realpathSync5, rmSync as rmSync10, writeFileSync as writeFileSync14 } from "node:fs";
42280
+ import { basename as basename6, dirname as dirname18, join as join24, relative as relative4, resolve as resolve6 } from "node:path";
42197
42281
  function resourcePathOf(projectPath, absolute) {
42198
42282
  const inside = relative4(onDiskSpelling(projectPath), onDiskSpelling(absolute));
42199
42283
  if (inside === "" || inside.startsWith("..") || /^[A-Za-z]:/.test(inside)) {
@@ -42206,13 +42290,13 @@ function onDiskSpelling(path) {
42206
42290
  let at = resolve6(path);
42207
42291
  for (;; ) {
42208
42292
  try {
42209
- return join23(realpathSync5.native(at), ...missing.reverse());
42293
+ return join24(realpathSync5.native(at), ...missing.reverse());
42210
42294
  } catch {
42211
- const parent = dirname17(at);
42295
+ const parent = dirname18(at);
42212
42296
  if (parent === at) {
42213
42297
  return resolve6(path);
42214
42298
  }
42215
- missing.push(basename5(at));
42299
+ missing.push(basename6(at));
42216
42300
  at = parent;
42217
42301
  }
42218
42302
  }
@@ -42259,7 +42343,7 @@ async function renameSymbol(request, services) {
42259
42343
  reason: `newScriptPath must be a .gd path inside the project, and ${request.newScriptPath} is not.`
42260
42344
  };
42261
42345
  }
42262
- if (existsSync19(request.newScriptPath)) {
42346
+ if (existsSync20(request.newScriptPath)) {
42263
42347
  return { ok: false, reason: `${moveTo} already exists, so the script cannot be moved there.` };
42264
42348
  }
42265
42349
  }
@@ -42435,7 +42519,7 @@ async function renameSymbol(request, services) {
42435
42519
  scripts,
42436
42520
  from: declaringPath,
42437
42521
  to: moveTo,
42438
- exists: (path) => existsSync19(`${projectPath}/${path.slice("res://".length)}`)
42522
+ exists: (path) => existsSync20(`${projectPath}/${path.slice("res://".length)}`)
42439
42523
  }, plan);
42440
42524
  }
42441
42525
  plan = mentionIgnored(plan, ignoredTexts(projectPath), symbol, moveTo === null ? null : declaringPath);
@@ -42634,15 +42718,15 @@ ${mention.kind}`;
42634
42718
  };
42635
42719
  }
42636
42720
  function writeLeftAloneDetail(projectPath, symbol, detail) {
42637
- const directory = join23(projectPath, ".godot", "gdharness-renames");
42721
+ const directory = join24(projectPath, ".godot", "gdharness-renames");
42638
42722
  mkdirSync13(directory, { recursive: true });
42639
42723
  detailWrites += 1;
42640
- const path = join23(directory, `${String(Date.now())}-${String(detailWrites).padStart(6, "0")}-${String(process.pid)}-${symbol}.json`);
42724
+ const path = join24(directory, `${String(Date.now())}-${String(detailWrites).padStart(6, "0")}-${String(process.pid)}-${symbol}.json`);
42641
42725
  writeFileSync14(path, `${JSON.stringify(detail, null, 2)}
42642
42726
  `, "utf8");
42643
- const kept = readdirSync9(directory).filter((name) => name.endsWith(".json")).sort().reverse();
42727
+ const kept = readdirSync10(directory).filter((name) => name.endsWith(".json")).sort().reverse();
42644
42728
  for (const old of kept.slice(10)) {
42645
- rmSync10(join23(directory, old), { force: true });
42729
+ rmSync10(join24(directory, old), { force: true });
42646
42730
  }
42647
42731
  return path;
42648
42732
  }
@@ -42682,15 +42766,15 @@ var init_rename_symbol = __esm(() => {
42682
42766
  });
42683
42767
 
42684
42768
  // src/restart-note.ts
42685
- import { mkdirSync as mkdirSync14, readFileSync as readFileSync20, rmSync as rmSync11, writeFileSync as writeFileSync15 } from "node:fs";
42686
- import { dirname as dirname18, join as join24 } from "node:path";
42769
+ import { mkdirSync as mkdirSync14, readFileSync as readFileSync21, rmSync as rmSync11, writeFileSync as writeFileSync15 } from "node:fs";
42770
+ import { dirname as dirname19, join as join25 } from "node:path";
42687
42771
  function restartNotePath(projectPath) {
42688
- return join24(projectPath, ".godot", "gdharness-restart.json");
42772
+ return join25(projectPath, ".godot", "gdharness-restart.json");
42689
42773
  }
42690
42774
  function noteRestartBegun(note) {
42691
42775
  const path = restartNotePath(note.projectPath);
42692
42776
  try {
42693
- mkdirSync14(dirname18(path), { recursive: true });
42777
+ mkdirSync14(dirname19(path), { recursive: true });
42694
42778
  writeFileSync15(path, `${JSON.stringify(note, null, 2)}
42695
42779
  `, "utf8");
42696
42780
  } catch (error) {
@@ -42699,7 +42783,7 @@ function noteRestartBegun(note) {
42699
42783
  }
42700
42784
  function restartOwed(projectPath) {
42701
42785
  try {
42702
- const parsed = JSON.parse(readFileSync20(restartNotePath(projectPath), "utf8"));
42786
+ const parsed = JSON.parse(readFileSync21(restartNotePath(projectPath), "utf8"));
42703
42787
  if (typeof parsed !== "object" || parsed === null) {
42704
42788
  return null;
42705
42789
  }
@@ -42724,7 +42808,7 @@ var init_restart_note = __esm(() => {
42724
42808
  });
42725
42809
 
42726
42810
  // src/whole-file.ts
42727
- import { renameSync as renameSync3, rmSync as rmSync12, writeFileSync as writeFileSync16 } from "node:fs";
42811
+ import { renameSync as renameSync4, rmSync as rmSync12, writeFileSync as writeFileSync16 } from "node:fs";
42728
42812
  function writeWhole(path, text) {
42729
42813
  const partial = `${path}.${process.pid}.partial`;
42730
42814
  try {
@@ -42735,7 +42819,7 @@ function writeWhole(path, text) {
42735
42819
  const until = Date.now() + HELD_FILE_MS;
42736
42820
  for (;; ) {
42737
42821
  try {
42738
- renameSync3(partial, path);
42822
+ renameSync4(partial, path);
42739
42823
  return true;
42740
42824
  } catch (error) {
42741
42825
  const code = error.code ?? "";
@@ -42757,24 +42841,24 @@ var init_whole_file = __esm(() => {
42757
42841
  // src/run-record.ts
42758
42842
  import { execFile as execFile4 } from "node:child_process";
42759
42843
  import { createHash as createHash4 } from "node:crypto";
42760
- import { mkdirSync as mkdirSync15, openSync as openSync2, readdirSync as readdirSync10, readFileSync as readFileSync21, realpathSync as realpathSync6, rmSync as rmSync13, statSync as statSync9 } from "node:fs";
42761
- import { basename as basename6, dirname as dirname19, join as join25, resolve as resolve7 } from "node:path";
42844
+ import { mkdirSync as mkdirSync15, openSync as openSync2, readdirSync as readdirSync11, readFileSync as readFileSync22, realpathSync as realpathSync6, rmSync as rmSync13, statSync as statSync9 } from "node:fs";
42845
+ import { basename as basename7, dirname as dirname20, join as join26, resolve as resolve7 } from "node:path";
42762
42846
  import { promisify as promisify4 } from "node:util";
42763
42847
  function runsDirectory() {
42764
- return join25(runtimeDirectory(), "runs");
42848
+ return join26(runtimeDirectory(), "runs");
42765
42849
  }
42766
42850
  function realAsFarAsItExists(path) {
42767
42851
  const missing = [];
42768
42852
  let at = path;
42769
42853
  for (;; ) {
42770
42854
  try {
42771
- return join25(realpathSync6.native(at), ...missing);
42855
+ return join26(realpathSync6.native(at), ...missing);
42772
42856
  } catch {
42773
- const parent = dirname19(at);
42857
+ const parent = dirname20(at);
42774
42858
  if (parent === at) {
42775
42859
  return path;
42776
42860
  }
42777
- missing.unshift(basename6(at));
42861
+ missing.unshift(basename7(at));
42778
42862
  at = parent;
42779
42863
  }
42780
42864
  }
@@ -42785,10 +42869,10 @@ function projectKey(projectPath) {
42785
42869
  return createHash4("sha256").update(folded.replaceAll("\\", "/")).digest("hex").slice(0, 16);
42786
42870
  }
42787
42871
  function runRecordPath(projectPath, directory = runtimeDirectory()) {
42788
- return join25(directory, "runs", `note-${projectKey(projectPath)}.json`);
42872
+ return join26(directory, "runs", `note-${projectKey(projectPath)}.json`);
42789
42873
  }
42790
42874
  function latestRunPath(servedBy, directory = runtimeDirectory()) {
42791
- return join25(directory, "runs", `latest-${projectKey(servedBy)}.json`);
42875
+ return join26(directory, "runs", `latest-${projectKey(servedBy)}.json`);
42792
42876
  }
42793
42877
  function writeLatestRun(servedBy, latest) {
42794
42878
  try {
@@ -42798,7 +42882,7 @@ function writeLatestRun(servedBy, latest) {
42798
42882
  }
42799
42883
  function readLatestRun(servedBy, directory = runtimeDirectory()) {
42800
42884
  try {
42801
- const fields = JSON.parse(readFileSync21(latestRunPath(servedBy, directory), "utf8"));
42885
+ const fields = JSON.parse(readFileSync22(latestRunPath(servedBy, directory), "utf8"));
42802
42886
  if (typeof fields !== "object" || fields === null) {
42803
42887
  return null;
42804
42888
  }
@@ -42813,17 +42897,17 @@ function readLatestRun(servedBy, directory = runtimeDirectory()) {
42813
42897
  }
42814
42898
  function openTranscript(startedAt) {
42815
42899
  mkdirSync15(runsDirectory(), { recursive: true });
42816
- const path = join25(runsDirectory(), `run-${startedAt}.log`);
42900
+ const path = join26(runsDirectory(), `run-${startedAt}.log`);
42817
42901
  return { path, fd: openSync2(path, "a") };
42818
42902
  }
42819
42903
  function profilePath(startedAt) {
42820
- return join25(runsDirectory(), `run-${startedAt}.profile.json`);
42904
+ return join26(runsDirectory(), `run-${startedAt}.profile.json`);
42821
42905
  }
42822
42906
  function noteIsAbout(note, playing) {
42823
42907
  return note.play !== undefined && playing !== null && note.play.play === playing.play && note.play.editorPid === playing.editorPid;
42824
42908
  }
42825
42909
  function editorNotePath(projectPath, directory = runtimeDirectory()) {
42826
- return join25(directory, "runs", `editor-${projectKey(projectPath)}.json`);
42910
+ return join26(directory, "runs", `editor-${projectKey(projectPath)}.json`);
42827
42911
  }
42828
42912
  function writeEditorRunNote(note) {
42829
42913
  mkdirSync15(runsDirectory(), { recursive: true });
@@ -42834,7 +42918,7 @@ function writeEditorRunNote(note) {
42834
42918
  }
42835
42919
  function readEditorRunNote(projectPath) {
42836
42920
  for (const directory of runtimeDirectories()) {
42837
- const note = editorNoteAt(editorNotePath(projectPath, directory)) ?? editorNoteAt(join25(directory, "runs", "editor-run.json"));
42921
+ const note = editorNoteAt(editorNotePath(projectPath, directory)) ?? editorNoteAt(join26(directory, "runs", "editor-run.json"));
42838
42922
  if (note !== null && isSameDirectory(note.projectPath, projectPath)) {
42839
42923
  return note;
42840
42924
  }
@@ -42844,7 +42928,7 @@ function readEditorRunNote(projectPath) {
42844
42928
  function editorNoteAt(path) {
42845
42929
  let parsed;
42846
42930
  try {
42847
- parsed = JSON.parse(readFileSync21(path, "utf8"));
42931
+ parsed = JSON.parse(readFileSync22(path, "utf8"));
42848
42932
  } catch {
42849
42933
  return null;
42850
42934
  }
@@ -42868,7 +42952,7 @@ function readRunRecord(projectPath) {
42868
42952
  if (own !== null) {
42869
42953
  return own;
42870
42954
  }
42871
- const shared = recordAt(join25(directory, "runs", SHARED_NOTE));
42955
+ const shared = recordAt(join26(directory, "runs", SHARED_NOTE));
42872
42956
  if (shared !== null && shared.projectPath !== "" && isSameDirectory(shared.projectPath, projectPath)) {
42873
42957
  return shared;
42874
42958
  }
@@ -42880,13 +42964,13 @@ function everyRunRecord() {
42880
42964
  for (const directory of runtimeDirectories()) {
42881
42965
  let entries;
42882
42966
  try {
42883
- entries = readdirSync10(join25(directory, "runs"));
42967
+ entries = readdirSync11(join26(directory, "runs"));
42884
42968
  } catch {
42885
42969
  continue;
42886
42970
  }
42887
42971
  for (const entry of entries) {
42888
42972
  if (entry === SHARED_NOTE || entry.startsWith("note-") && entry.endsWith(".json")) {
42889
- const record = recordAt(join25(directory, "runs", entry));
42973
+ const record = recordAt(join26(directory, "runs", entry));
42890
42974
  if (record !== null) {
42891
42975
  found.push(record);
42892
42976
  }
@@ -42898,7 +42982,7 @@ function everyRunRecord() {
42898
42982
  function recordAt(path) {
42899
42983
  let parsed;
42900
42984
  try {
42901
- parsed = JSON.parse(readFileSync21(path, "utf8"));
42985
+ parsed = JSON.parse(readFileSync22(path, "utf8"));
42902
42986
  } catch {
42903
42987
  return null;
42904
42988
  }
@@ -42951,7 +43035,7 @@ async function runningAs(pid) {
42951
43035
  const began = await startedAt(pid, null);
42952
43036
  const when = began === null ? {} : { startedAt: began };
42953
43037
  if (process.platform === "linux") {
42954
- const raw = readFileSync21(`/proc/${pid}/cmdline`, "utf8").replaceAll("\x00", " ").trim();
43038
+ const raw = readFileSync22(`/proc/${pid}/cmdline`, "utf8").replaceAll("\x00", " ").trim();
42955
43039
  return raw === "" ? null : { kind: "commandLine", text: raw, ...when };
42956
43040
  }
42957
43041
  const args = (await execFileAsync("ps", ["-p", String(pid), "-o", "args="], { encoding: "utf8", timeout: 15000 })).stdout.trim();
@@ -43057,7 +43141,7 @@ function whyNotTheRun(record, running, needed) {
43057
43141
  if (after > SAME_RUN_WINDOW_MS) {
43058
43142
  return `that process started ${Math.round(after / 1000)} seconds after the run had`;
43059
43143
  }
43060
- const engine = record.command === undefined ? null : basename6(record.command);
43144
+ const engine = record.command === undefined ? null : basename7(record.command);
43061
43145
  const said = process.platform === "win32" ? running.text.toLowerCase() : running.text;
43062
43146
  const wanted = engine === null || process.platform !== "win32" ? engine : engine.toLowerCase();
43063
43147
  if (wanted !== null && !said.includes(wanted)) {
@@ -43096,7 +43180,7 @@ function judgeAnnouncedGame(running, projectPath, announcedAt) {
43096
43180
  }
43097
43181
  function clearRunRecord(projectPath) {
43098
43182
  for (const directory of runtimeDirectories()) {
43099
- const shared = join25(directory, "runs", SHARED_NOTE);
43183
+ const shared = join26(directory, "runs", SHARED_NOTE);
43100
43184
  const sharedRecord = recordAt(shared);
43101
43185
  const ours = sharedRecord !== null && sharedRecord.projectPath !== "" && isSameDirectory(sharedRecord.projectPath, projectPath);
43102
43186
  for (const path of [runRecordPath(projectPath, directory), ...ours ? [shared] : []]) {
@@ -43121,7 +43205,7 @@ function sweepTranscripts(keepMs = 24 * 60 * 60 * 1000, now = Date.now()) {
43121
43205
  ]));
43122
43206
  let entries;
43123
43207
  try {
43124
- entries = readdirSync10(directory);
43208
+ entries = readdirSync11(directory);
43125
43209
  } catch {
43126
43210
  return;
43127
43211
  }
@@ -43130,7 +43214,7 @@ function sweepTranscripts(keepMs = 24 * 60 * 60 * 1000, now = Date.now()) {
43130
43214
  if (!transcript && !entry.endsWith(".partial")) {
43131
43215
  continue;
43132
43216
  }
43133
- const path = join25(directory, entry);
43217
+ const path = join26(directory, entry);
43134
43218
  if (kept.has(path) || ageMs(path, now) < keepMs) {
43135
43219
  continue;
43136
43220
  }
@@ -43151,7 +43235,7 @@ var init_run_record = __esm(() => {
43151
43235
  });
43152
43236
 
43153
43237
  // src/script-origins.ts
43154
- import { join as join26 } from "node:path";
43238
+ import { join as join27 } from "node:path";
43155
43239
  function unresolvedClassIn(message) {
43156
43240
  return UNRESOLVED.exec(message)?.[1] ?? null;
43157
43241
  }
@@ -43204,7 +43288,7 @@ function checkWithEngine(godotPath, projectPath) {
43204
43288
  const ran = await runEngine(godotPath, [
43205
43289
  "--headless",
43206
43290
  "--log-file",
43207
- join26(logDir, "engine.log"),
43291
+ join27(logDir, "engine.log"),
43208
43292
  "--path",
43209
43293
  projectPath,
43210
43294
  "--check-only",
@@ -43249,10 +43333,10 @@ var init_script_origins = __esm(() => {
43249
43333
  });
43250
43334
 
43251
43335
  // src/script-profile.ts
43252
- import { readFileSync as readFileSync22 } from "node:fs";
43336
+ import { readFileSync as readFileSync23 } from "node:fs";
43253
43337
  function readProfileFile(path) {
43254
43338
  try {
43255
- const parsed = JSON.parse(readFileSync22(path, "utf8"));
43339
+ const parsed = JSON.parse(readFileSync23(path, "utf8"));
43256
43340
  return typeof parsed === "object" && typeof parsed.connected === "boolean" ? parsed : null;
43257
43341
  } catch {
43258
43342
  return null;
@@ -43430,17 +43514,17 @@ import {
43430
43514
  closeSync as closeSync2,
43431
43515
  constants,
43432
43516
  copyFileSync,
43433
- existsSync as existsSync20,
43517
+ existsSync as existsSync21,
43434
43518
  fstatSync,
43435
43519
  mkdirSync as mkdirSync16,
43436
43520
  openSync as openSync3,
43437
- readdirSync as readdirSync11,
43438
- readFileSync as readFileSync23,
43521
+ readdirSync as readdirSync12,
43522
+ readFileSync as readFileSync24,
43439
43523
  readSync as readSync2,
43440
43524
  rmdirSync as rmdirSync2,
43441
43525
  statSync as statSync10
43442
43526
  } from "node:fs";
43443
- import { basename as basename7, dirname as dirname20, extname as extname3, isAbsolute as isAbsolute3, join as join27, normalize as normalize2, relative as relative5 } from "node:path";
43527
+ import { basename as basename8, dirname as dirname21, extname as extname3, isAbsolute as isAbsolute4, join as join28, normalize as normalize2, relative as relative5 } from "node:path";
43444
43528
  import { setTimeout as delay4 } from "node:timers/promises";
43445
43529
  import { fileURLToPath as fileURLToPath5 } from "node:url";
43446
43530
  import { promisify as promisify5 } from "node:util";
@@ -43471,22 +43555,22 @@ function writtenFileRefusal(path, rule) {
43471
43555
  if (/^(res|user):\/\//.test(path)) {
43472
43556
  return `${named} ${path} is a Godot path; ${what} is saved outside the project, at an absolute path on this machine.`;
43473
43557
  }
43474
- if (!isAbsolute3(path)) {
43558
+ if (!isAbsolute4(path)) {
43475
43559
  return `${named} ${path} is not absolute; name the whole path, so where it is written does not depend on this server's directory.`;
43476
43560
  }
43477
43561
  if (!rule.extensions.includes(extname3(path).toLowerCase())) {
43478
43562
  return `${named} ${path} does not end in ${rule.extensions.join(" or ")}, which is what ${what} is.`;
43479
43563
  }
43480
- const directory = realPathOr(dirname20(path));
43481
- if (!existsSync20(directory)) {
43482
- return `${named}'s directory ${dirname20(path)} does not exist; ${what} creates no directories.`;
43564
+ const directory = realPathOr(dirname21(path));
43565
+ if (!existsSync21(directory)) {
43566
+ return `${named}'s directory ${dirname21(path)} does not exist; ${what} creates no directories.`;
43483
43567
  }
43484
- const where = join27(directory, basename7(path));
43568
+ const where = join28(directory, basename8(path));
43485
43569
  const inside = rule.projects.find((project) => project !== "" && isWithinRoot(realPathOr(project), where));
43486
43570
  if (inside !== undefined) {
43487
43571
  return `${named} ${path} is inside the project at ${inside}; ${what} is saved outside it, so it never becomes part of the game.`;
43488
43572
  }
43489
- const taken = rule.writes(where).find((file) => existsSync20(file));
43573
+ const taken = rule.writes(where).find((file) => existsSync21(file));
43490
43574
  if (taken !== undefined) {
43491
43575
  return taken === where ? `${named} ${path} already exists; ${what} is not written over a file. Name a new one.` : `${named} ${path} would write ${taken}, which already exists; ${what} is not written over a file. Name a new one.`;
43492
43576
  }
@@ -43982,6 +44066,7 @@ function notRunNote(notRun, failFast) {
43982
44066
  function exportAnswer(ending, asked, before, after) {
43983
44067
  const { log } = ending;
43984
44068
  const written = after !== null && after !== before;
44069
+ const notCopied = librariesNotCopied(log.all);
43985
44070
  const verdict = {
43986
44071
  exported: ending.exitCode === 0 && ending.failure === null && log.count("error") === 0 && written,
43987
44072
  ...asked,
@@ -43990,6 +44075,10 @@ function exportAnswer(ending, asked, before, after) {
43990
44075
  failure: ending.failure ?? undefined,
43991
44076
  errors: log.count("error"),
43992
44077
  warnings: log.count("warning"),
44078
+ ...notCopied.length === 0 ? {} : {
44079
+ librariesNotLoaded: notCopied,
44080
+ extensionNote: `The export could not load ${notCopied.join(" and ")}: on Windows an engine running as the editor, which an export is, loads a GDExtension from a ~ copy beside the library, and another engine on this project held that copy: it could not be moved aside before the export, or another engine made a new one in between. Export again once that engine has loaded or exited.`
44081
+ },
43993
44082
  ...foldedEntriesAnswer(log)
43994
44083
  };
43995
44084
  if (verdict.exported) {
@@ -44027,7 +44116,7 @@ function engineFlagsNote(args) {
44027
44116
  return `${named} ${among.length === 1 ? "is a flag" : "are flags"} the engine reads, and args goes to the game after --, where the engine does not read ${among.length === 1 ? "it" : "them"}. engineArgs hands the engine its own flags; ${among.length === 1 ? "leave it" : "leave them"} in args only for a game that reads ${among.length === 1 ? "that flag" : "those flags"} itself.`;
44028
44117
  }
44029
44118
  function editorAddonMissing(projectPath) {
44030
- if (!existsSync20(join27(projectPath, "project.godot"))) {
44119
+ if (!existsSync21(join28(projectPath, "project.godot"))) {
44031
44120
  return null;
44032
44121
  }
44033
44122
  const report = inspectProject(projectPath);
@@ -44263,7 +44352,7 @@ function profileFrameFunctions(projectPath) {
44263
44352
  let projectGodot = null;
44264
44353
  if (projectPath !== null) {
44265
44354
  try {
44266
- projectGodot = readFileSync23(join27(projectPath, "project.godot"), "utf8");
44355
+ projectGodot = readFileSync24(join28(projectPath, "project.godot"), "utf8");
44267
44356
  } catch {}
44268
44357
  }
44269
44358
  return frameFunctionsFor(queuedMessagesOf(projectGodot));
@@ -44287,7 +44376,7 @@ function projectSourceOf(projectPath) {
44287
44376
  let source = null;
44288
44377
  if (inside.ok) {
44289
44378
  try {
44290
- source = readFileSync23(inside.absolutePath, "utf8");
44379
+ source = readFileSync24(inside.absolutePath, "utf8");
44291
44380
  } catch {}
44292
44381
  }
44293
44382
  read.set(script, source);
@@ -44437,7 +44526,7 @@ function foldedEntriesAnswer(log, floor = "warning") {
44437
44526
  }
44438
44527
  function gdUnitFailsOnScriptErrors(projectPath) {
44439
44528
  try {
44440
- return settingKeys(readFileSync23(join27(projectPath, "project.godot"), "utf8")).get("gdunit4/report/godot/script_error") !== false;
44529
+ return settingKeys(readFileSync24(join28(projectPath, "project.godot"), "utf8")).get("gdunit4/report/godot/script_error") !== false;
44441
44530
  } catch {
44442
44531
  return true;
44443
44532
  }
@@ -44452,13 +44541,13 @@ function uncaughtNote(uncaught) {
44452
44541
  }
44453
44542
  function elsewhereIn(projectPath, asked) {
44454
44543
  const wanted = asked.replace(/^res:\/\//, "").replace(/\/+$/, "");
44455
- if (wanted === "" || existsSync20(join27(projectPath, wanted))) {
44544
+ if (wanted === "" || existsSync21(join28(projectPath, wanted))) {
44456
44545
  return "";
44457
44546
  }
44458
44547
  const root = (wanted.split("/")[0] ?? "").toLowerCase();
44459
44548
  let near = [];
44460
44549
  try {
44461
- near = readdirSync11(projectPath, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name.toLowerCase() !== root).map((entry) => entry.name).filter((name) => name.toLowerCase().startsWith("test") || root.startsWith(name.toLowerCase()));
44550
+ near = readdirSync12(projectPath, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name.toLowerCase() !== root).map((entry) => entry.name).filter((name) => name.toLowerCase().startsWith("test") || root.startsWith(name.toLowerCase()));
44462
44551
  } catch {
44463
44552
  return "";
44464
44553
  }
@@ -44548,17 +44637,17 @@ function isDirectory(path) {
44548
44637
  }
44549
44638
  }
44550
44639
  function scriptsUnder(directory, prefix) {
44551
- if (existsSync20(join27(directory, ".gdignore"))) {
44640
+ if (existsSync21(join28(directory, ".gdignore"))) {
44552
44641
  return [];
44553
44642
  }
44554
44643
  const found = [];
44555
- for (const entry of readdirSync11(directory, { withFileTypes: true }).sort((a, b) => a.name < b.name ? -1 : 1)) {
44644
+ for (const entry of readdirSync12(directory, { withFileTypes: true }).sort((a, b) => a.name < b.name ? -1 : 1)) {
44556
44645
  if (entry.name.startsWith(".")) {
44557
44646
  continue;
44558
44647
  }
44559
44648
  const path = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
44560
44649
  if (entry.isDirectory()) {
44561
- found.push(...scriptsUnder(join27(directory, entry.name), path));
44650
+ found.push(...scriptsUnder(join28(directory, entry.name), path));
44562
44651
  } else if (entry.isFile() && entry.name.endsWith(".gd")) {
44563
44652
  found.push(path);
44564
44653
  }
@@ -44566,7 +44655,7 @@ function scriptsUnder(directory, prefix) {
44566
44655
  return found;
44567
44656
  }
44568
44657
  function hasMainScene(projectFile) {
44569
- const scene = parseProjectGodot(readFileSync23(projectFile, "utf8"))["application"]?.["run/main_scene"];
44658
+ const scene = parseProjectGodot(readFileSync24(projectFile, "utf8"))["application"]?.["run/main_scene"];
44570
44659
  return typeof scene === "string" && scene !== "";
44571
44660
  }
44572
44661
  function runtimeAddonAnnouncesEditors(projectPath) {
@@ -44574,13 +44663,13 @@ function runtimeAddonAnnouncesEditors(projectPath) {
44574
44663
  return false;
44575
44664
  }
44576
44665
  try {
44577
- return readFileSync23(join27(projectPath, RUNTIME_AUTOLOAD.path), "utf8").includes('"editor_pid"');
44666
+ return readFileSync24(join28(projectPath, RUNTIME_AUTOLOAD.path), "utf8").includes('"editor_pid"');
44578
44667
  } catch {
44579
44668
  return false;
44580
44669
  }
44581
44670
  }
44582
44671
  function editorPlaysHeadless(projectFile) {
44583
- const runArgs = parseProjectGodot(readFileSync23(projectFile, "utf8"))["editor"]?.["run/main_run_args"];
44672
+ const runArgs = parseProjectGodot(readFileSync24(projectFile, "utf8"))["editor"]?.["run/main_run_args"];
44584
44673
  return typeof runArgs === "string" && /(?:^|\s)--headless(?:\s|$)/.test(runArgs);
44585
44674
  }
44586
44675
  function camelCased(params) {
@@ -44599,7 +44688,7 @@ function needsItsOwnProcess(asked) {
44599
44688
  class GodotServer {
44600
44689
  stdio = null;
44601
44690
  locator = new GodotLocator;
44602
- operationsScript = join27(__dirname2, "godot", "operations", "godot_operations.gd");
44691
+ operationsScript = join28(__dirname2, "godot", "operations", "godot_operations.gd");
44603
44692
  godotBridge;
44604
44693
  tools = buildToolDefinitions();
44605
44694
  activeProcess = null;
@@ -44624,6 +44713,7 @@ class GodotServer {
44624
44713
  launchedDropped = null;
44625
44714
  closedHere = null;
44626
44715
  inFlight = new Map;
44716
+ callsOnGames = new Set;
44627
44717
  callsTaken = 0;
44628
44718
  clientNoted = null;
44629
44719
  parentCommand = null;
@@ -44855,14 +44945,14 @@ class GodotServer {
44855
44945
  }
44856
44946
  noteTheStart() {
44857
44947
  const cwd = process.cwd();
44858
- const mcpJson = join27(cwd, ".mcp.json");
44948
+ const mcpJson = join28(cwd, ".mcp.json");
44859
44949
  const announced = this.ownProject === null ? null : readAnnouncement(announcementPath(this.ownProject));
44860
44950
  const predecessor = announced !== null && announced.pid !== process.pid && alive(announced.pid) ? announced.pid : null;
44861
44951
  writeLifecycle("started", {
44862
44952
  argv: process.argv.slice(1),
44863
44953
  cwd,
44864
44954
  project: this.ownProject,
44865
- mcpJson: existsSync20(mcpJson) ? mcpJson : null
44955
+ mcpJson: existsSync21(mcpJson) ? mcpJson : null
44866
44956
  });
44867
44957
  if (predecessor !== null) {
44868
44958
  writeLifecycle("replaced", { project: this.ownProject, predecessor });
@@ -45003,7 +45093,7 @@ class GodotServer {
45003
45093
  return checked.response;
45004
45094
  }
45005
45095
  const named = args["projectPath"];
45006
- if (typeof named === "string" && existsSync20(join27(named, "project.godot"))) {
45096
+ if (typeof named === "string" && existsSync21(join28(named, "project.godot"))) {
45007
45097
  this.lastProjectPath = named;
45008
45098
  }
45009
45099
  this.updates.refresh();
@@ -45561,8 +45651,8 @@ class GodotServer {
45561
45651
  if (path === undefined) {
45562
45652
  return { ok: false, response: this.createErrorResponse("projectPath is required.") };
45563
45653
  }
45564
- const file = join27(path, "project.godot");
45565
- if (!existsSync20(file)) {
45654
+ const file = join28(path, "project.godot");
45655
+ if (!existsSync21(file)) {
45566
45656
  return {
45567
45657
  ok: false,
45568
45658
  response: this.createErrorResponse(`Not a Godot project: ${path}`, [
@@ -45608,7 +45698,7 @@ class GodotServer {
45608
45698
  }
45609
45699
  runEnvironment(args) {
45610
45700
  const savesIn = readNonEmptyString(args, "savesIn");
45611
- if (savesIn !== undefined && !isAbsolute3(savesIn)) {
45701
+ if (savesIn !== undefined && !isAbsolute4(savesIn)) {
45612
45702
  return {
45613
45703
  ok: false,
45614
45704
  response: this.createErrorResponse(`savesIn must be an absolute path, not "${savesIn}".`, [
@@ -46192,11 +46282,11 @@ class GodotServer {
46192
46282
  if (!engine.ok) {
46193
46283
  return engine.response;
46194
46284
  }
46195
- const application = parseProjectGodot(readFileSync23(project.value.file, "utf8"))["application"] ?? {};
46285
+ const application = parseProjectGodot(readFileSync24(project.value.file, "utf8"))["application"] ?? {};
46196
46286
  const name = application["config/name"];
46197
46287
  const mainScene = application["run/main_scene"];
46198
46288
  const info = {
46199
- name: typeof name === "string" && name !== "" ? name : basename7(project.value.path),
46289
+ name: typeof name === "string" && name !== "" ? name : basename8(project.value.path),
46200
46290
  path: project.value.path,
46201
46291
  mainScene: typeof mainScene === "string" && mainScene !== "" ? mainScene : null,
46202
46292
  godotVersion: await this.godotVersion(engine.value),
@@ -46248,18 +46338,18 @@ class GodotServer {
46248
46338
  if (!engine.ok) {
46249
46339
  return engine.response;
46250
46340
  }
46251
- const folder = dirname20(output.absolutePath);
46341
+ const folder = dirname21(output.absolutePath);
46252
46342
  try {
46253
46343
  mkdirSync16(folder, { recursive: true });
46254
46344
  } catch (error) {
46255
- return this.createErrorResponse(`Cannot create ${dirname20(output.relativePath)} to export into: ${errorMessage(error)}`);
46345
+ return this.createErrorResponse(`Cannot create ${dirname21(output.relativePath)} to export into: ${errorMessage(error)}`);
46256
46346
  }
46257
46347
  const debug = readBoolean(args, "debug") ?? false;
46258
46348
  const exportLogs = scratchDirectory("export");
46259
46349
  const exportArgs = [
46260
46350
  "--headless",
46261
46351
  "--log-file",
46262
- join27(exportLogs, "engine.log"),
46352
+ join28(exportLogs, "engine.log"),
46263
46353
  "--path",
46264
46354
  project.value.path,
46265
46355
  debug ? "--export-debug" : "--export-release",
@@ -46267,8 +46357,12 @@ class GodotServer {
46267
46357
  output.absolutePath
46268
46358
  ];
46269
46359
  this.logDebug(`Exporting: ${engine.value} ${exportArgs.join(" ")}`);
46270
- const writtenAt = () => existsSync20(output.absolutePath) ? statSync10(output.absolutePath).mtimeMs : null;
46360
+ const writtenAt = () => existsSync21(output.absolutePath) ? statSync10(output.absolutePath).mtimeMs : null;
46271
46361
  const before = writtenAt();
46362
+ const freed = freeLibraryCopies(project.value.path);
46363
+ if (freed.setAside.length > 0 || freed.swept.length > 0) {
46364
+ this.logDebug(`Before the export: set aside ${freed.setAside.join(", ")}; swept ${freed.swept.join(", ")}`);
46365
+ }
46272
46366
  let ending;
46273
46367
  try {
46274
46368
  ending = await runEngine(engine.value, exportArgs, { timeout: { ms: 300000, said: "five minutes" } });
@@ -46331,10 +46425,10 @@ class GodotServer {
46331
46425
  if (movie !== null) {
46332
46426
  return this.createErrorResponse(movie);
46333
46427
  }
46334
- const missing = paths.value.filter((path) => !existsSync20(join27(project.value.path, path.slice("res://".length))));
46428
+ const missing = paths.value.filter((path) => !existsSync21(join28(project.value.path, path.slice("res://".length))));
46335
46429
  const asked = paths.value.filter((path) => !missing.includes(path));
46336
46430
  const runner = "addons/gdUnit4/bin/GdUnitCmdTool.gd";
46337
- const noRunner = !existsSync20(join27(project.value.path, runner));
46431
+ const noRunner = !existsSync21(join28(project.value.path, runner));
46338
46432
  if (asked.length === 0) {
46339
46433
  const verdict = `nothing at ${missing.join(" and ")}`;
46340
46434
  const andNoRunner = noRunner ? `, and gdUnit4 is not installed in this project: no ${runner}` : "";
@@ -46425,21 +46519,21 @@ class GodotServer {
46425
46519
  await untilGone([run.process.pid]);
46426
46520
  }
46427
46521
  await run.settled;
46428
- const reportsDir = join27(project.value.path, ".godot", ...ours.split("/"));
46522
+ const reportsDir = join28(project.value.path, ".godot", ...ours.split("/"));
46429
46523
  let report = null;
46430
46524
  let reportProblem = null;
46431
46525
  try {
46432
- const written = existsSync20(reportsDir) ? readdirSync11(reportsDir).filter((name) => name.startsWith("report_")).sort((a, b) => Number(a.slice("report_".length)) - Number(b.slice("report_".length))) : [];
46526
+ const written = existsSync21(reportsDir) ? readdirSync12(reportsDir).filter((name) => name.startsWith("report_")).sort((a, b) => Number(a.slice("report_".length)) - Number(b.slice("report_".length))) : [];
46433
46527
  const newest = written.at(-1);
46434
46528
  if (newest !== undefined) {
46435
- report = parseJUnit(readFileSync23(join27(reportsDir, newest, "results.xml"), "utf8"));
46529
+ report = parseJUnit(readFileSync24(join28(reportsDir, newest, "results.xml"), "utf8"));
46436
46530
  }
46437
46531
  } catch (error) {
46438
46532
  reportProblem = errorMessage(error);
46439
46533
  } finally {
46440
46534
  discard(reportsDir, userData);
46441
46535
  try {
46442
- rmdirSync2(dirname20(reportsDir));
46536
+ rmdirSync2(dirname21(reportsDir));
46443
46537
  } catch {}
46444
46538
  }
46445
46539
  const engineEntries = foldedRepeats(run.log.select({
@@ -46699,11 +46793,11 @@ class GodotServer {
46699
46793
  const cached = cachedClasses(projectPath);
46700
46794
  const sourceOf = (resourcePath) => {
46701
46795
  const contained = resolveWithinProject(projectPath, resourcePath);
46702
- if (!contained.ok || !existsSync20(contained.absolutePath)) {
46796
+ if (!contained.ok || !existsSync21(contained.absolutePath)) {
46703
46797
  return null;
46704
46798
  }
46705
46799
  try {
46706
- return readFileSync23(contained.absolutePath, "utf8");
46800
+ return readFileSync24(contained.absolutePath, "utf8");
46707
46801
  } catch {
46708
46802
  return null;
46709
46803
  }
@@ -46768,11 +46862,11 @@ class GodotServer {
46768
46862
  }
46769
46863
  sourceOfClass(projectPath, resourcePath) {
46770
46864
  const contained = resolveWithinProject(projectPath, resourcePath);
46771
- if (!contained.ok || !existsSync20(contained.absolutePath)) {
46865
+ if (!contained.ok || !existsSync21(contained.absolutePath)) {
46772
46866
  return null;
46773
46867
  }
46774
46868
  try {
46775
- return readFileSync23(contained.absolutePath, "utf8");
46869
+ return readFileSync24(contained.absolutePath, "utf8");
46776
46870
  } catch {
46777
46871
  return null;
46778
46872
  }
@@ -46920,7 +47014,7 @@ class GodotServer {
46920
47014
  ...Array.isArray(stack) ? {
46921
47015
  stack: stack.map((frame) => ({
46922
47016
  ...frame,
46923
- ...projectPath !== "" && typeof frame["script"] === "string" && frame["script"].startsWith("res://") ? { path: join27(projectPath, frame["script"].slice("res://".length)) } : {}
47017
+ ...projectPath !== "" && typeof frame["script"] === "string" && frame["script"].startsWith("res://") ? { path: join28(projectPath, frame["script"].slice("res://".length)) } : {}
46924
47018
  }))
46925
47019
  } : {}
46926
47020
  });
@@ -47190,7 +47284,7 @@ class GodotServer {
47190
47284
  return null;
47191
47285
  }
47192
47286
  try {
47193
- return settingKeys(readFileSync23(join27(projectPath, "project.godot"), "utf8"));
47287
+ return settingKeys(readFileSync24(join28(projectPath, "project.godot"), "utf8"));
47194
47288
  } catch {
47195
47289
  return null;
47196
47290
  }
@@ -47455,6 +47549,7 @@ class GodotServer {
47455
47549
  }
47456
47550
  async openAnEditor(engine, projectPath, ports, hidden) {
47457
47551
  clearEditorLog(projectPath);
47552
+ freeLibraryCopies(projectPath);
47458
47553
  const launched = await launchOutsideTheTree({
47459
47554
  command: engine,
47460
47555
  args: editorArguments(projectPath, ports, editorLogPath(projectPath), hidden),
@@ -47613,7 +47708,7 @@ class GodotServer {
47613
47708
  }
47614
47709
  async runtimeUp(projectPath, before, budget, withArgs) {
47615
47710
  const budgetMs = budget.ms;
47616
- if (!existsSync20(join27(projectPath, RUNTIME_AUTOLOAD.path))) {
47711
+ if (!existsSync21(join28(projectPath, RUNTIME_AUTOLOAD.path))) {
47617
47712
  return runtimeVerdict(null, {
47618
47713
  addon: false,
47619
47714
  budgetMs,
@@ -47892,7 +47987,7 @@ class GodotServer {
47892
47987
  return 0;
47893
47988
  }
47894
47989
  try {
47895
- return reportAlreadyInTranscript(readFileSync23(game.transcript), readFileSync23(report));
47990
+ return reportAlreadyInTranscript(readFileSync24(game.transcript), readFileSync24(report));
47896
47991
  } catch (error) {
47897
47992
  this.logDebug(`Could not compare a picked-up run's report with its transcript: ${errorMessage(error)}`);
47898
47993
  return 0;
@@ -48572,7 +48667,7 @@ class GodotServer {
48572
48667
  }
48573
48668
  const going = await this.runStillGoing(run);
48574
48669
  const projectPath = run.projectPath;
48575
- const projectGone = projectPath !== null && !existsSync20(join27(projectPath, "project.godot"));
48670
+ const projectGone = projectPath !== null && !existsSync21(join28(projectPath, "project.godot"));
48576
48671
  if (projectGone && callersName !== undefined) {
48577
48672
  return this.createErrorResponse(`callersOf reads the project's scripts, and the run's project at ${projectPath} has no project.godot any more.`, ["editor_run profile without callersOf answers with the profile itself"]);
48578
48673
  }
@@ -48860,7 +48955,7 @@ class GodotServer {
48860
48955
  notes.push(endedWithoutACode({
48861
48956
  throughEditor: run.throughEditor,
48862
48957
  announced: announced !== undefined || run.announcedPid !== undefined,
48863
- couldAnnounce: run.projectPath !== null && existsSync20(join27(run.projectPath, RUNTIME_AUTOLOAD.path)),
48958
+ couldAnnounce: run.projectPath !== null && existsSync21(join28(run.projectPath, RUNTIME_AUTOLOAD.path)),
48864
48959
  endedHere: typeof run.endedHere === "string",
48865
48960
  pickedUp: run.pickedUp === true
48866
48961
  }));
@@ -49425,7 +49520,7 @@ class GodotServer {
49425
49520
  if (!scriptPath.ok) {
49426
49521
  return this.createErrorResponse(scriptPath.reason);
49427
49522
  }
49428
- if (!existsSync20(scriptPath.absolutePath)) {
49523
+ if (!existsSync21(scriptPath.absolutePath)) {
49429
49524
  return this.createErrorResponse(`Script file does not exist: ${scriptPath.absolutePath}`);
49430
49525
  }
49431
49526
  const asked = readNonEmptyString(args, "newScriptPath");
@@ -49753,7 +49848,7 @@ class GodotServer {
49753
49848
  async ownRunOnItsWay(projectPath, budgetMs) {
49754
49849
  const run = await this.currentRun();
49755
49850
  const project = projectPath ?? run?.projectPath ?? null;
49756
- if (run === null || project === null || run.announcedPid !== undefined || run.projectPath !== null && !isSameDirectory(run.projectPath, project) || !existsSync20(join27(project, RUNTIME_AUTOLOAD.path)) || !await this.runStillGoing(run)) {
49851
+ if (run === null || project === null || run.announcedPid !== undefined || run.projectPath !== null && !isSameDirectory(run.projectPath, project) || !existsSync21(join28(project, RUNTIME_AUTOLOAD.path)) || !await this.runStillGoing(run)) {
49757
49852
  return null;
49758
49853
  }
49759
49854
  const already = runtimesAnnounced().running;
@@ -49838,12 +49933,25 @@ class GodotServer {
49838
49933
  }
49839
49934
  }
49840
49935
  const screenshotDir = expectsScreenshot ? scratchDirectory("runtime-screenshot") : null;
49841
- const screenshotPath = screenshotDir ? join27(screenshotDir, "capture.png") : null;
49936
+ const screenshotPath = screenshotDir ? join28(screenshotDir, "capture.png") : null;
49842
49937
  const staleNote = this.runtimeStaleNote(choice.endpoint);
49843
49938
  const stale = staleNote === undefined ? {} : { addonIsStale: true, staleNote };
49844
49939
  const refused = (message) => this.createErrorResponse(staleNote === undefined ? message : `${message}
49845
49940
 
49846
49941
  ${staleNote}`);
49942
+ const onGame = {
49943
+ pid: choice.endpoint.pid,
49944
+ what: `${asked.tool} ${asked.op}`,
49945
+ alongside: new Set
49946
+ };
49947
+ for (const other of this.callsOnGames) {
49948
+ if (other.pid === onGame.pid) {
49949
+ other.alongside.add(onGame.what);
49950
+ onGame.alongside.add(other.what);
49951
+ }
49952
+ }
49953
+ this.callsOnGames.add(onGame);
49954
+ const alongside = () => onGame.alongside.size === 0 ? {} : { ranAlongside: [...onGame.alongside], alongsideNote: ALONGSIDE_NOTE };
49847
49955
  try {
49848
49956
  const reply = await runtimeRequest(choice.endpoint, command, screenshotPath ? { ...params, output_path: screenshotPath } : params, timeoutMs, !expectsScreenshot);
49849
49957
  if (!reply.ok && reply.requestId !== undefined) {
@@ -49854,7 +49962,8 @@ ${staleNote}`);
49854
49962
  waitedMs: timeoutMs,
49855
49963
  note: `${reply.message} ${longerWaitNote(asked.tool, asked.op, timeoutMs)}`,
49856
49964
  ...answeredBy,
49857
- ...stale
49965
+ ...stale,
49966
+ ...alongside()
49858
49967
  });
49859
49968
  }
49860
49969
  if (!reply.ok) {
@@ -49862,7 +49971,7 @@ ${staleNote}`);
49862
49971
  }
49863
49972
  const { id: _id, ...payload } = reply.payload;
49864
49973
  if (!expectsScreenshot) {
49865
- return this.jsonTextResponse({ ...payload, ...answeredBy, ...stale });
49974
+ return this.jsonTextResponse({ ...payload, ...answeredBy, ...stale, ...alongside() });
49866
49975
  }
49867
49976
  const returnedPath = readString(payload, "path");
49868
49977
  if (readString(payload, "type") !== "screenshot_file" || !returnedPath || !screenshotPath) {
@@ -49879,18 +49988,22 @@ ${staleNote}`);
49879
49988
  return this.createErrorResponse(`The capture was taken and could not be saved at ${saveTo}: ${errorMessage(error)}`);
49880
49989
  }
49881
49990
  }
49991
+ const ranWith = [...onGame.alongside];
49882
49992
  return {
49883
49993
  content: [
49884
49994
  {
49885
49995
  type: "text",
49886
- text: `Screenshot captured: ${dimensions}${chosenAmongSeveral ? ` from pid ${own}, the game this server holds` : ""}${captureDetail(payload)}${saveTo === null ? "" : `. Saved to ${saveTo}`}${staleNote === undefined ? "" : `
49996
+ text: `Screenshot captured: ${dimensions}${chosenAmongSeveral ? ` from pid ${own}, the game this server holds` : ""}${captureDetail(payload)}${saveTo === null ? "" : `. Saved to ${saveTo}`}${ranWith.length === 0 ? "" : `
49997
+
49998
+ Ran alongside ${ranWith.join(", ")}. ${ALONGSIDE_NOTE}`}${staleNote === undefined ? "" : `
49887
49999
 
49888
50000
  ${staleNote}`}`
49889
50001
  },
49890
- { type: "image", data: readFileSync23(screenshotPath).toString("base64"), mimeType: "image/png" }
50002
+ { type: "image", data: readFileSync24(screenshotPath).toString("base64"), mimeType: "image/png" }
49891
50003
  ]
49892
50004
  };
49893
50005
  } finally {
50006
+ this.callsOnGames.delete(onGame);
49894
50007
  discard(screenshotDir);
49895
50008
  }
49896
50009
  }
@@ -49989,7 +50102,7 @@ async function runGodotServer() {
49989
50102
  const server = new GodotServer;
49990
50103
  await server.run();
49991
50104
  }
49992
- var UPDATE_NOTICE_EVERY = 500, FEEDBACK_NOTICE_EVERY = 250, run4, __dirname2, PARENT_ASKED_AFTER_MS = 5000, EDITOR_SERVERS_START_MS = 20000, HOLD_PING_MS = 2000, SLOWEST_FRAME_RATE = 20, OWN_DEBUGGER = " Its debugger is the keeper holding it rather than the editor, so the debug_* tools answer for it all the same: breakpoints stop it, and debug_control pause breaks into it wherever it is, which the editor offers no way to do.", PLAY_STARTS_WITHIN_MS = 30000, KEEPER_ANSWER_MS = 15000, KEEPER_DEBUG_MS, EXIT_REPORTED_WITHIN_MS = 2000, CONSOLE_WRAPPER, LONGEST_BOOT_NOTED_MS, PROFILE_SEND_MS = 5000, MOST_GROUPS = 20, RUNTIME_LISTENING_LINE = "[gdharness] runtime listening on ", VALUE_NAMES, BRIDGE_RETRY_MS = 2000, SUCCESSOR_CHECK_MS = 1e4, CONSOLE_WATCH_MS = 250, BOUNDARY_WAIT_MS = 2000, HANDOVER_MS, HANDOVER_POLL_MS = 250, CACHE_WRITE_MS = 2000, STREAMS_GRACE_MS = 2000, PLAYING_STATUS_MS = 1000, SCAN_START_MS = 2000, SCAN_SETTLED_MS = 500, REIMPORT_TIMEOUT_MS = 300000, EDITOR_FREE_INSTEAD, OPS_ELSEWHERE, AUDIO_BUS_OPS, STALLED_AFTER_MS = 30000, TEST_HEARTBEAT_MS = 20000, DEPENDED_FAILED = "Failed to compile depended scripts", WAIT_FOR_RUN_MS = 600000, RUN_POLL_MS = 250, PATH_SOLUTIONS, PROJECT_FILE_ARGUMENTS, DEBUG_STATE_CALLS, PROJECT_INFO_SECTIONS2, RUNNER_DIRECTORY = "addons/gdUnit4/", PROFILE_FUNCTIONS = 30, PROFILER_ON_MS = 3000, PROPERTY_PLACES = "It is a property, so each place says whether it writes it (access write), which runs its set block, or reads it (access read), which runs its get block: withinCalls on a read says nothing of the set block, nor on a write of the get block unless the write is a compound one such as +=. Inside the property's own blocks its name is the value stored, which the engine reads and writes without running them, so those lines are not listed.", CALLERS_FOUND_BY_NAME = "Found by name in the project's scripts, since the engine's profiler counts calls and time per function and records nothing about who made them: a method of the same name on another class is listed too, unless one class was asked about and it is called through another class by name or through a variable declared as one. Each place carries the profile's numbers for the function it is written in.", MOST_ENGINE_ENTRIES = 200, ERROR_ASSERT = "GdUnitGodotErrorAssertImpl.gd", EXTERNAL_MEMBER = 'Could not resolve external class member "', STATIC_LIST_HINT;
50105
+ var UPDATE_NOTICE_EVERY = 500, FEEDBACK_NOTICE_EVERY = 250, run4, __dirname2, PARENT_ASKED_AFTER_MS = 5000, EDITOR_SERVERS_START_MS = 20000, HOLD_PING_MS = 2000, SLOWEST_FRAME_RATE = 20, ALONGSIDE_NOTE = "Another call on this game was running while this one was: calls sent together run together and are answered as each finishes, so this answer is not from after the other. Send a call meant to come after another once that one has answered.", OWN_DEBUGGER = " Its debugger is the keeper holding it rather than the editor, so the debug_* tools answer for it all the same: breakpoints stop it, and debug_control pause breaks into it wherever it is, which the editor offers no way to do.", PLAY_STARTS_WITHIN_MS = 30000, KEEPER_ANSWER_MS = 15000, KEEPER_DEBUG_MS, EXIT_REPORTED_WITHIN_MS = 2000, CONSOLE_WRAPPER, LONGEST_BOOT_NOTED_MS, PROFILE_SEND_MS = 5000, MOST_GROUPS = 20, RUNTIME_LISTENING_LINE = "[gdharness] runtime listening on ", VALUE_NAMES, BRIDGE_RETRY_MS = 2000, SUCCESSOR_CHECK_MS = 1e4, CONSOLE_WATCH_MS = 250, BOUNDARY_WAIT_MS = 2000, HANDOVER_MS, HANDOVER_POLL_MS = 250, CACHE_WRITE_MS = 2000, STREAMS_GRACE_MS = 2000, PLAYING_STATUS_MS = 1000, SCAN_START_MS = 2000, SCAN_SETTLED_MS = 500, REIMPORT_TIMEOUT_MS = 300000, EDITOR_FREE_INSTEAD, OPS_ELSEWHERE, AUDIO_BUS_OPS, STALLED_AFTER_MS = 30000, TEST_HEARTBEAT_MS = 20000, DEPENDED_FAILED = "Failed to compile depended scripts", WAIT_FOR_RUN_MS = 600000, RUN_POLL_MS = 250, PATH_SOLUTIONS, PROJECT_FILE_ARGUMENTS, DEBUG_STATE_CALLS, PROJECT_INFO_SECTIONS2, RUNNER_DIRECTORY = "addons/gdUnit4/", PROFILE_FUNCTIONS = 30, PROFILER_ON_MS = 3000, PROPERTY_PLACES = "It is a property, so each place says whether it writes it (access write), which runs its set block, or reads it (access read), which runs its get block: withinCalls on a read says nothing of the set block, nor on a write of the get block unless the write is a compound one such as +=. Inside the property's own blocks its name is the value stored, which the engine reads and writes without running them, so those lines are not listed.", CALLERS_FOUND_BY_NAME = "Found by name in the project's scripts, since the engine's profiler counts calls and time per function and records nothing about who made them: a method of the same name on another class is listed too, unless one class was asked about and it is called through another class by name or through a variable declared as one. Each place carries the profile's numbers for the function it is written in.", MOST_ENGINE_ENTRIES = 200, ERROR_ASSERT = "GdUnitGodotErrorAssertImpl.gd", EXTERNAL_MEMBER = 'Could not resolve external class member "', STATIC_LIST_HINT;
49993
50106
  var init_server = __esm(() => {
49994
50107
  init_dist();
49995
50108
  init_stdio();
@@ -50019,6 +50132,7 @@ var init_server = __esm(() => {
50019
50132
  init_junit();
50020
50133
  init_keeper_channel();
50021
50134
  init_launch();
50135
+ init_library_copies();
50022
50136
  init_lifecycle_log();
50023
50137
  init_lsp_client();
50024
50138
  init_outside();
@@ -50045,7 +50159,7 @@ var init_server = __esm(() => {
50045
50159
  init_tool_reference();
50046
50160
  init_update_check();
50047
50161
  run4 = promisify5(execFile5);
50048
- __dirname2 = dirname20(fileURLToPath5(import.meta.url));
50162
+ __dirname2 = dirname21(fileURLToPath5(import.meta.url));
50049
50163
  KEEPER_DEBUG_MS = PAUSE_MS + STEP_MS + 2 * READ_MS;
50050
50164
  CONSOLE_WRAPPER = /_console(\.exe)?$/i;
50051
50165
  LONGEST_BOOT_NOTED_MS = 5 * 60000;
@@ -50104,8 +50218,8 @@ var init_server = __esm(() => {
50104
50218
  init_bridge_announce();
50105
50219
  init_errors();
50106
50220
  init_godot_path();
50107
- import { existsSync as existsSync21 } from "node:fs";
50108
- import { join as join28, resolve as resolve8 } from "node:path";
50221
+ import { existsSync as existsSync22 } from "node:fs";
50222
+ import { join as join29, resolve as resolve8 } from "node:path";
50109
50223
 
50110
50224
  // src/harnesses.ts
50111
50225
  import { existsSync as existsSync4, mkdirSync as mkdirSync2, readdirSync as readdirSync2, readFileSync as readFileSync2, rmSync as rmSync2, statSync as statSync2, writeFileSync as writeFileSync2 } from "node:fs";
@@ -51070,10 +51184,10 @@ init_setup();
51070
51184
  init_setup();
51071
51185
  init_tool_definitions();
51072
51186
  init_tool_reference();
51073
- import { existsSync as existsSync12, mkdirSync as mkdirSync5, readdirSync as readdirSync7, rmSync as rmSync6, writeFileSync as writeFileSync7 } from "node:fs";
51074
- import { basename as basename4, dirname as dirname6, join as join12 } from "node:path";
51187
+ import { existsSync as existsSync13, mkdirSync as mkdirSync5, readdirSync as readdirSync8, rmSync as rmSync6, writeFileSync as writeFileSync7 } from "node:fs";
51188
+ import { basename as basename5, dirname as dirname7, join as join13 } from "node:path";
51075
51189
  var SKILL_NAME = "gdharness";
51076
- var SHARED_SKILLS = join12(".agents", "skills");
51190
+ var SHARED_SKILLS = join13(".agents", "skills");
51077
51191
  function skillMarkdown(version) {
51078
51192
  return `---
51079
51193
  name: ${SKILL_NAME}
@@ -51279,27 +51393,27 @@ comes back separately, under \`engine_messages_after_answer\`: none of it change
51279
51393
  function skillFiles(version) {
51280
51394
  return new Map([
51281
51395
  ["SKILL.md", skillMarkdown(version)],
51282
- [join12("references", "tools.md"), renderToolsMarkdown()]
51396
+ [join13("references", "tools.md"), renderToolsMarkdown()]
51283
51397
  ]);
51284
51398
  }
51285
51399
  function skillDirectories(harnesses, projectPath, exists) {
51286
- const directories = [join12(projectPath, SHARED_SKILLS, SKILL_NAME)];
51400
+ const directories = [join13(projectPath, SHARED_SKILLS, SKILL_NAME)];
51287
51401
  for (const harness of harnesses) {
51288
51402
  if (harness.skills === undefined) {
51289
51403
  continue;
51290
51404
  }
51291
- const own = join12(projectPath, harness.skills.dir);
51292
- if ((!harness.skills.shared || exists(own)) && !directories.includes(join12(own, SKILL_NAME))) {
51293
- directories.push(join12(own, SKILL_NAME));
51405
+ const own = join13(projectPath, harness.skills.dir);
51406
+ if ((!harness.skills.shared || exists(own)) && !directories.includes(join13(own, SKILL_NAME))) {
51407
+ directories.push(join13(own, SKILL_NAME));
51294
51408
  }
51295
51409
  }
51296
51410
  return directories;
51297
51411
  }
51298
51412
  function everySkillDirectory(projectPath, harnesses) {
51299
- const directories = new Set([join12(projectPath, SHARED_SKILLS, SKILL_NAME)]);
51413
+ const directories = new Set([join13(projectPath, SHARED_SKILLS, SKILL_NAME)]);
51300
51414
  for (const harness of harnesses) {
51301
51415
  if (harness.skills !== undefined) {
51302
- directories.add(join12(projectPath, harness.skills.dir, SKILL_NAME));
51416
+ directories.add(join13(projectPath, harness.skills.dir, SKILL_NAME));
51303
51417
  }
51304
51418
  }
51305
51419
  return [...directories];
@@ -51307,33 +51421,33 @@ function everySkillDirectory(projectPath, harnesses) {
51307
51421
  function removeSkill(directories, projectPath) {
51308
51422
  const removed = [];
51309
51423
  for (const directory of directories) {
51310
- if (existsSync12(directory)) {
51424
+ if (existsSync13(directory)) {
51311
51425
  rmSync6(directory, { recursive: true, force: true });
51312
- pruneEmpty(dirname6(directory), projectPath);
51426
+ pruneEmpty(dirname7(directory), projectPath);
51313
51427
  removed.push(directory);
51314
51428
  }
51315
51429
  }
51316
51430
  return removed;
51317
51431
  }
51318
51432
  function pruneEmpty(directory, projectPath) {
51319
- const shared = join12(projectPath, ".agents");
51433
+ const shared = join13(projectPath, ".agents");
51320
51434
  let path = directory;
51321
- while (path !== projectPath && (basename4(path) === "skills" || path === shared)) {
51322
- if (readdirSync7(path).length > 0) {
51435
+ while (path !== projectPath && (basename5(path) === "skills" || path === shared)) {
51436
+ if (readdirSync8(path).length > 0) {
51323
51437
  return;
51324
51438
  }
51325
51439
  rmSync6(path, { recursive: true, force: true });
51326
- path = dirname6(path);
51440
+ path = dirname7(path);
51327
51441
  }
51328
51442
  }
51329
51443
  function writeSkill(directories, version) {
51330
51444
  const files = skillFiles(version);
51331
51445
  return directories.map((directory) => {
51332
- const replaced = existsSync12(directory);
51446
+ const replaced = existsSync13(directory);
51333
51447
  rmSync6(directory, { recursive: true, force: true });
51334
51448
  for (const [name, contents] of files) {
51335
- const path = join12(directory, name);
51336
- mkdirSync5(dirname6(path), { recursive: true });
51449
+ const path = join13(directory, name);
51450
+ mkdirSync5(dirname7(path), { recursive: true });
51337
51451
  writeFileSync7(path, contents, "utf8");
51338
51452
  }
51339
51453
  return { path: directory, replaced };
@@ -51341,18 +51455,18 @@ function writeSkill(directories, version) {
51341
51455
  }
51342
51456
 
51343
51457
  // src/version.ts
51344
- import { readFileSync as readFileSync11 } from "node:fs";
51345
- import { dirname as dirname7, join as join13 } from "node:path";
51458
+ import { readFileSync as readFileSync12 } from "node:fs";
51459
+ import { dirname as dirname8, join as join14 } from "node:path";
51346
51460
  import { fileURLToPath as fileURLToPath2 } from "node:url";
51347
51461
  function getLocalVersion() {
51348
- const currentDirectory = dirname7(fileURLToPath2(import.meta.url));
51462
+ const currentDirectory = dirname8(fileURLToPath2(import.meta.url));
51349
51463
  const packageCandidates = [
51350
- join13(currentDirectory, "..", "package.json"),
51351
- join13(currentDirectory, "..", "..", "package.json")
51464
+ join14(currentDirectory, "..", "package.json"),
51465
+ join14(currentDirectory, "..", "..", "package.json")
51352
51466
  ];
51353
51467
  for (const packagePath of packageCandidates) {
51354
51468
  try {
51355
- const value = JSON.parse(readFileSync11(packagePath, "utf-8"));
51469
+ const value = JSON.parse(readFileSync12(packagePath, "utf-8"));
51356
51470
  if (typeof value === "object" && value !== null && "name" in value && value.name === "gdharness" && "version" in value && typeof value.version === "string") {
51357
51471
  return value.version;
51358
51472
  }
@@ -51370,7 +51484,7 @@ class UsageError extends Refusal {
51370
51484
  function projectArgument(at) {
51371
51485
  const named = args.slice(at).find((value) => !value.startsWith("--"));
51372
51486
  const projectPath = resolve8(named ?? ".");
51373
- if (!existsSync21(join28(projectPath, "project.godot"))) {
51487
+ if (!existsSync22(join29(projectPath, "project.godot"))) {
51374
51488
  throw new UsageError(`Not a Godot project: ${projectPath} holds no project.godot.`);
51375
51489
  }
51376
51490
  return projectPath;
@@ -51415,7 +51529,7 @@ async function registerRuntime(godot, projectPath) {
51415
51529
  return;
51416
51530
  }
51417
51531
  console.log(`${RUNTIME_AUTOLOAD.name} autoload left at ${named}, which this project registers itself`);
51418
- if (!existsSync21(join28(projectPath, (named ?? "").replace(/^res:\/\//, "")))) {
51532
+ if (!existsSync22(join29(projectPath, (named ?? "").replace(/^res:\/\//, "")))) {
51419
51533
  console.log(` that file is not in this project, so it boots with a missing script: restore it, or point the entry at res://${RUNTIME_AUTOLOAD.path}`);
51420
51534
  }
51421
51535
  }
@@ -51547,7 +51661,7 @@ async function setup() {
51547
51661
  console.log("no harness configured; name one yourself, and gdharness harnesses lists them all");
51548
51662
  }
51549
51663
  if (!args.includes("--no-skill")) {
51550
- const directories = skillDirectories(chosen.harnesses, projectPath, existsSync21);
51664
+ const directories = skillDirectories(chosen.harnesses, projectPath, existsSync22);
51551
51665
  for (const written of writeSkill(directories, getLocalVersion())) {
51552
51666
  console.log(`skill: ${written.replaced ? "replaced" : "written"} ${written.path}`);
51553
51667
  }
@@ -51661,7 +51775,7 @@ async function upgrade() {
51661
51775
  moved.push(took.from);
51662
51776
  }
51663
51777
  }
51664
- for (const written of writeSkill(skillDirectories(already, projectPath, existsSync21), version)) {
51778
+ for (const written of writeSkill(skillDirectories(already, projectPath, existsSync22), version)) {
51665
51779
  console.log(`skill: ${written.replaced ? "replaced" : "written"} ${written.path}`);
51666
51780
  }
51667
51781
  console.log(`