gdharness 1.2.38 → 1.2.39

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
@@ -11424,7 +11424,7 @@ var init_tool_definitions = __esm(() => {
11424
11424
  name: "project_info",
11425
11425
  title: "Project info",
11426
11426
  effect: "read",
11427
- description: "What a project is: its name and main scene from project.godot, the Godot that answers, and how many scenes, scripts and assets it holds, with optional sections on top.",
11427
+ description: "What a project is: its name and main scene from project.godot, the Godot that answers, and how many scenes, scripts and assets it holds, other counting every remaining file, leaving out the addons gdharness installed and any folder holding a .gdignore, with optional sections on top.",
11428
11428
  parameters: {
11429
11429
  projectPath: PROJECT_PATH,
11430
11430
  include: {
@@ -11666,7 +11666,7 @@ var init_tool_definitions = __esm(() => {
11666
11666
  },
11667
11667
  reimport: {
11668
11668
  effect: "destructive",
11669
- summary: "reimport what status says is outdated or failed, one resource with resourcePath, or what is current as well with force. Through the open editor when one serves the project, so it reloads what it holds, waiting for it to finish; otherwise through the engine's own import pass. The editor scans first, since its reimport passes over a file it has not taken in, such as one an installer has just written; what that scan imported is named under importedByTheScan and is not imported again unless force asks. Answered from a second status read: reimported lists what is current now, notReimported what is not, one entry per status and reason with the paths sharing them, and via says which of the two did the work. A whole-project reimport also answers missingSource, the .import files whose source is not beside them, empty when there are none, whether or not anything needed reimporting; one resourcePath is not a walk of the project and leaves it out. stillImporting is an editor that had not finished within timeoutMs",
11669
+ summary: "reimport what status says is outdated or failed, one resource with resourcePath, or what is current as well with force. Through the open editor when one serves the project, so it reloads what it holds, waiting for it to finish; otherwise through the engine's own import pass. The editor scans first, since its reimport passes over a file it has not taken in, such as one an installer has just written; what that scan imported is named under importedByTheScan and is not imported again unless force asks. Answered from a second status read: reimported lists what is current now, notReimported what is not, one entry per status and reason with the paths sharing them, and via says which of the two did the work. When nothing needs reimporting, nothing runs, and the answer has the two lists empty, a note saying so, and no via. A whole-project reimport also answers missingSource, the .import files whose source is not beside them, empty when there are none, whether or not anything needed reimporting; one resourcePath is not a walk of the project and leaves it out. stillImporting is an editor that had not finished within timeoutMs",
11670
11670
  requires: []
11671
11671
  },
11672
11672
  uid: {
@@ -11750,7 +11750,7 @@ var init_tool_definitions = __esm(() => {
11750
11750
  timeoutMs: {
11751
11751
  type: "integer",
11752
11752
  minimum: 1,
11753
- description: "How long the run may take before it is killed. Default 600000. A run killed at the limit answers timedOut and silentForMs, how long it had printed nothing; it is called hung only when that was half the limit, or thirty seconds of a longer one, and otherwise it was still running and wants a longer timeoutMs or a narrower path."
11753
+ description: "How long the run may take before it is killed. Default 600000. A run killed at the limit answers timedOut and silentForMs, how long it had printed nothing; it is called hung only when that was half the limit, or thirty seconds of a longer one, and otherwise it was still running and wants a longer timeoutMs or a narrower path, unless it ended repeating one script error, which the verdict names instead. A run whose autoload did not compile is stopped as soon as the engine says so, and names the script that did not under scriptErrors."
11754
11754
  },
11755
11755
  printed: {
11756
11756
  type: "string",
@@ -12421,7 +12421,7 @@ var init_tool_definitions = __esm(() => {
12421
12421
  requires: []
12422
12422
  }
12423
12423
  },
12424
- answer: "stop: endedPid is, for a run the editor plays, the number its game announced under, read at the stop rather than remembered, and the operating system hands a freed number to the next process, so it can equal the game before it. stopped is false, with the number under notSignalled, when that number no longer answers as the process the run was started as and nothing was signalled, the note naming which property it failed on, and false with the number under stillGoing when what was told to end was still running ten seconds later. endedThrough says how the stop ended a run that was going: close (the game was asked to close and exited, so exitCode is its own), keeper (the process that started the game ended it through the handle it holds), signal (its pid was signalled once shown to still be the run), or editor (the editor playing it was asked). A start that ends the run before it says how under endedPreviousRunThrough, with the same values, and one that refused to signal that run names the property it failed on under previousRunLeftBecause. check: hung is a run killed at timeoutMs, and errors and warnings count what it printed, which comes back under entries with repeats folded together. entries holds the errors and warnings and not the ordinary lines the run printed, which printedLinesNotShown counts; a check given --verbose holds those too, and editor_run start then editor_output reads everything. An engine crash is an error whatever it prints after: the crash handler's report is the entry, and the engine version and the native and GDScript backtraces are its detail, so a crashed run is never booted or clean. arguments is the engine command line the check ran, as a start's is. profile: per function its script, the function (Class.name when the script declares a class_name), the line its body's first statement is on, for a lambda the named function it is written in (within), calls, and self and total milliseconds, with functionsCounted, scriptMs (the self time of them all) and coveredMs. complete is true when the engine's own totals arrived, which it sends only when the profiler is switched off: the runtime addon does that as a game quits, so a scene that does its work in _ready and quits is covered whole; otherwise the answer is the frames summed so far and the note says what that misses. Self time counts a call into the engine's own methods as the caller's, and a call through super too, while the overridden function's row counts it again: such a row carries selfIncludesSuper, so self times along an override chain overlap. The profiler's own cost of timing a call lands in the self time of the function called; with the runtime addon it is measured each run on an empty function, as perCallOverheadUs, and a row with at least half its selfMs in it carries overheadMs, since a small function called often costs that much less without the profiler. A function the profiler started inside is left out, since its time would be counted from the engine's start. The milliseconds are time passed while the function ran, not processor time given to it, so they grow when other processes compete for the processor: compare two profiles taken under the same load. They are also summed over every thread: a function run by WorkerThreadPool or a Thread on several threads at once counts each thread's time, the engine does not say which thread a call ran on, and a function waiting for them has the wait in its own self time, so selfMs is not time taken from frames, and scriptMs can exceed coveredMs, which the note then says. A property's get and set blocks are named Class.@name_getter and Class.@name_setter, as the engine names them, and their rows carry property, the property's name. A game the editor plays that was not started with profile: true says, in the answer, that it was profiled from the moment it was asked. A profile with mark answers marked: true; one with since answers since: \"mark\" and markSetMsAgo, and lessThanAtMark counts the functions left out for reading less than at the mark, which the engine's own totals, capped, can do when they replace the frames' sum.",
12424
+ answer: "stop: endedPid is, for a run the editor plays, the number its game announced under, read at the stop rather than remembered, and the operating system hands a freed number to the next process, so it can equal the game before it. stopped is false, with the number under notSignalled, when that number no longer answers as the process the run was started as and nothing was signalled, the note naming which property it failed on, and false with the number under stillGoing when what was told to end was still running ten seconds later. endedThrough says how the stop ended a run that was going: close (the game was asked to close and exited, so exitCode is its own), keeper (the process that started the game ended it through the handle it holds), signal (its pid was signalled once shown to still be the run), or editor (the editor playing it was asked). A start that ends the run before it says how under endedPreviousRunThrough, with the same values, and one that refused to signal that run names the property it failed on under previousRunLeftBecause. check: hung is a run killed at timeoutMs, and errors and warnings count what it printed, which comes back under entries with repeats folded together. entries holds the errors and warnings and not the ordinary lines the run printed, which printedLinesNotShown counts; a check given --verbose holds those too, and editor_run start then editor_output reads everything. An engine crash is an error whatever it prints after: the crash handler's report is the entry, and the engine version and the native and GDScript backtraces are its detail, so a crashed run is never booted or clean. arguments is the engine command line the check ran, as a start's is. profile: per function its script, the function (Class.name when the script declares a class_name), the line its body's first statement is on, for a lambda the named function it is written in (within), calls, and self and total milliseconds, with functionsCounted, scriptMs (the self time of them all) and coveredMs. The runtime addon's own functions, gdharness answering its tools inside the game, are not among them: their self time is runtimeAddonMs, over runtimeAddonFunctions functions. complete is true when the engine's own totals arrived, which it sends only when the profiler is switched off: the runtime addon does that as a game quits, so a scene that does its work in _ready and quits is covered whole; otherwise the answer is the frames summed so far and the note says what that misses. frames counts the frames of the profile that arrived, and the engine's totals also hold the work after the last of them, so once complete a function run every frame can show a call more than frames. Self time counts a call into the engine's own methods as the caller's, and a call through super too, while the overridden function's row counts it again: such a row carries selfIncludesSuper, so self times along an override chain overlap. The profiler's own cost of timing a call lands in the self time of the function called; with the runtime addon it is measured each run on an empty function, as perCallOverheadUs, and a row with at least half its selfMs in it carries overheadMs, since a small function called often costs that much less without the profiler. A function the profiler started inside is left out, since its time would be counted from the engine's start. The milliseconds are time passed while the function ran, not processor time given to it, so they grow when other processes compete for the processor: compare two profiles taken under the same load. They are also summed over every thread: a function run by WorkerThreadPool or a Thread on several threads at once counts each thread's time, the engine does not say which thread a call ran on, and a function waiting for them has the wait in its own self time, so selfMs is not time taken from frames, and scriptMs can exceed coveredMs, which the note then says. A property's get and set blocks are named Class.@name_getter and Class.@name_setter, as the engine names them, and their rows carry property, the property's name. A game the editor plays that was not started with profile: true says, in the answer, that it was profiled from the moment it was asked. A profile with mark answers marked: true; one with since answers since: \"mark\" and markSetMsAgo, and lessThanAtMark counts the functions left out for reading less than at the mark, which the engine's own totals, capped, can do when they replace the frames' sum.",
12425
12425
  defaultOperation: "start"
12426
12426
  },
12427
12427
  {
@@ -34899,11 +34899,51 @@ var init_stdio = __esm(() => {
34899
34899
  swallowsErrorsAfterClose = Symbol("swallowsErrorsAfterClose");
34900
34900
  });
34901
34901
 
34902
+ // src/autoload-failure.ts
34903
+ import { existsSync as existsSync15, readFileSync as readFileSync13 } from "node:fs";
34904
+ import { join as join16 } from "node:path";
34905
+ function scriptsAutoloaded(projectPath) {
34906
+ const scripts = new Map;
34907
+ let settings;
34908
+ try {
34909
+ settings = parseProjectGodot(readFileSync13(join16(projectPath, "project.godot"), "utf8"));
34910
+ } catch {
34911
+ return scripts;
34912
+ }
34913
+ for (const [name, path] of autoloadPaths(settings)) {
34914
+ if (!path.endsWith(".tscn")) {
34915
+ scripts.set(`res://${path}`, name);
34916
+ continue;
34917
+ }
34918
+ const scene = join16(projectPath, path);
34919
+ if (!existsSync15(scene)) {
34920
+ continue;
34921
+ }
34922
+ try {
34923
+ for (const named of readFileSync13(scene, "utf8").matchAll(/^\[ext_resource\b[^\]]*\btype="Script"[^\]]*\bpath="(res:\/\/[^"]+)"/gm)) {
34924
+ scripts.set(named[1] ?? "", name);
34925
+ }
34926
+ } catch {}
34927
+ }
34928
+ return scripts;
34929
+ }
34930
+ function autoloadNotCompiled(entry, autoloaded) {
34931
+ const path = NOT_COMPILED.exec(entry.text)?.[1];
34932
+ const name = path === undefined ? undefined : autoloaded.get(path);
34933
+ return path === undefined || name === undefined ? null : { name, path };
34934
+ }
34935
+ var NOT_COMPILED;
34936
+ var init_autoload_failure = __esm(() => {
34937
+ init_resources();
34938
+ init_setup();
34939
+ NOT_COMPILED = /^Failed to load script "(res:\/\/[^"]+)" with error "Compilation failed"/;
34940
+ });
34941
+
34902
34942
  // src/runtime-client.ts
34903
- import { existsSync as existsSync15, readdirSync as readdirSync10, readFileSync as readFileSync13, statSync as statSync8, unlinkSync as unlinkSync2 } from "node:fs";
34943
+ import { existsSync as existsSync16, readdirSync as readdirSync10, readFileSync as readFileSync14, statSync as statSync8, unlinkSync as unlinkSync2 } from "node:fs";
34904
34944
  import { createConnection } from "node:net";
34905
34945
  import { tmpdir as tmpdir4 } from "node:os";
34906
- import { dirname as dirname10, join as join16, resolve as resolve4 } from "node:path";
34946
+ import { dirname as dirname10, join as join17, resolve as resolve4 } from "node:path";
34907
34947
  import { setTimeout as delay } from "node:timers/promises";
34908
34948
  function runtimeDirectory(variables = process.env) {
34909
34949
  const explicit = envValue("GDHARNESS_RUNTIME_DIR", variables);
@@ -34911,20 +34951,20 @@ function runtimeDirectory(variables = process.env) {
34911
34951
  return explicit;
34912
34952
  }
34913
34953
  const perUser = envValue("XDG_RUNTIME_DIR", variables);
34914
- return join16(perUser ?? tmpdir4(), "gdharness");
34954
+ return join17(perUser ?? tmpdir4(), "gdharness");
34915
34955
  }
34916
34956
  function runtimeDirectories(variables = process.env) {
34917
34957
  const candidates = [runtimeDirectory(variables)];
34918
34958
  const fallbacks = process.platform === "win32" ? [envValue("SystemRoot", variables) ?? envValue("windir", variables) ?? "C:\\Windows"] : ["/tmp", "/var/tmp"];
34919
34959
  for (const base of fallbacks) {
34920
- candidates.push(join16(base, process.platform === "win32" ? "Temp" : "", "gdharness"));
34960
+ candidates.push(join17(base, process.platform === "win32" ? "Temp" : "", "gdharness"));
34921
34961
  }
34922
34962
  return [...new Set(candidates.map((path) => resolve4(path)))];
34923
34963
  }
34924
34964
  function errorReportOf(pid, directories = runtimeDirectories()) {
34925
34965
  for (const directory of directories) {
34926
- const path = join16(directory, `runtime-${pid}.log`);
34927
- if (existsSync15(path)) {
34966
+ const path = join17(directory, `runtime-${pid}.log`);
34967
+ if (existsSync16(path)) {
34928
34968
  return path;
34929
34969
  }
34930
34970
  }
@@ -34955,7 +34995,7 @@ function reportAlreadyInTranscript(transcript, report) {
34955
34995
  function parseAnnouncement(file, pid) {
34956
34996
  let fields;
34957
34997
  try {
34958
- fields = asParams(JSON.parse(readFileSync13(file, "utf8")));
34998
+ fields = asParams(JSON.parse(readFileSync14(file, "utf8")));
34959
34999
  } catch {
34960
35000
  return { kind: "rubbish" };
34961
35001
  }
@@ -35021,7 +35061,7 @@ function discoverRuntimes(directories = runtimeDirectories()) {
35021
35061
  function wentAway(file, pid) {
35022
35062
  let project = "";
35023
35063
  try {
35024
- const fields = asParams(JSON.parse(readFileSync13(file, "utf8")));
35064
+ const fields = asParams(JSON.parse(readFileSync14(file, "utf8")));
35025
35065
  project = readString(readParams(fields, "project") ?? {}, "path") ?? "";
35026
35066
  } catch {
35027
35067
  return;
@@ -35064,7 +35104,7 @@ function strangersAmong(candidates, now, startTimes = startTimesOf) {
35064
35104
  }).map((one) => one.file));
35065
35105
  }
35066
35106
  function announcedIn(directory) {
35067
- if (!existsSync15(directory)) {
35107
+ if (!existsSync16(directory)) {
35068
35108
  return [];
35069
35109
  }
35070
35110
  const found = [];
@@ -35073,14 +35113,14 @@ function announcedIn(directory) {
35073
35113
  for (const entry of readdirSync10(directory)) {
35074
35114
  const report = ERROR_REPORT_PATTERN.exec(entry);
35075
35115
  if (report) {
35076
- sweepErrorReport(join16(directory, entry), Number.parseInt(report[1] ?? "", 10));
35116
+ sweepErrorReport(join17(directory, entry), Number.parseInt(report[1] ?? "", 10));
35077
35117
  continue;
35078
35118
  }
35079
35119
  const match = ANNOUNCEMENT_PATTERN.exec(entry);
35080
35120
  if (!match) {
35081
35121
  continue;
35082
35122
  }
35083
- const file = join16(directory, entry);
35123
+ const file = join17(directory, entry);
35084
35124
  const pid = Number.parseInt(match[1] ?? "", 10);
35085
35125
  const running = alive(pid);
35086
35126
  entries.push({ file, pid, running });
@@ -35108,7 +35148,7 @@ function announcedIn(directory) {
35108
35148
  found.push(announced);
35109
35149
  }
35110
35150
  const held = new Set(entries.map((one) => one.file));
35111
- const here = join16(directory, ".");
35151
+ const here = join17(directory, ".");
35112
35152
  for (const file of verdicts.keys()) {
35113
35153
  if (dirname10(file) === here && !held.has(file)) {
35114
35154
  verdicts.delete(file);
@@ -35117,7 +35157,7 @@ function announcedIn(directory) {
35117
35157
  return found;
35118
35158
  }
35119
35159
  async function announcementEnded(pid, directories = runtimeDirectories()) {
35120
- const files = directories.map((directory) => join16(directory, `runtime-${pid}.json`)).filter((file) => existsSync15(file));
35160
+ const files = directories.map((directory) => join17(directory, `runtime-${pid}.json`)).filter((file) => existsSync16(file));
35121
35161
  const first = files[0];
35122
35162
  if (first === undefined) {
35123
35163
  return;
@@ -35281,7 +35321,36 @@ function keepLate(id, answer) {
35281
35321
  function lateAnswer(id) {
35282
35322
  return lateAnswers.get(id);
35283
35323
  }
35284
- function runtimeRequest(endpoint, command, params, timeoutMs, keepListening = false) {
35324
+ function placeOf(endpoint) {
35325
+ return `${dialAddress(endpoint.address)}:${endpoint.port}`;
35326
+ }
35327
+ function waitForAPlace(game, granted) {
35328
+ const line = untaken.get(game) ?? { held: 0, waiting: [] };
35329
+ untaken.set(game, line);
35330
+ const grant = () => {
35331
+ line.held += 1;
35332
+ let given = false;
35333
+ granted(() => {
35334
+ if (given) {
35335
+ return;
35336
+ }
35337
+ given = true;
35338
+ line.held -= 1;
35339
+ const next = line.waiting.shift();
35340
+ if (next !== undefined) {
35341
+ next();
35342
+ } else if (line.held === 0 && untaken.get(game) === line) {
35343
+ untaken.delete(game);
35344
+ }
35345
+ });
35346
+ };
35347
+ if (line.held < UNTAKEN_PER_GAME) {
35348
+ grant();
35349
+ } else {
35350
+ line.waiting.push(grant);
35351
+ }
35352
+ }
35353
+ function runtimeRequest(endpoint, command, params, timeoutMs, afterTheWait = "drop") {
35285
35354
  const id = nextRequestId++;
35286
35355
  const sentAt = Date.now();
35287
35356
  return new Promise((settle) => {
@@ -35289,16 +35358,32 @@ function runtimeRequest(endpoint, command, params, timeoutMs, keepListening = fa
35289
35358
  let late = false;
35290
35359
  let buffered = "";
35291
35360
  let welcomed = false;
35292
- const socket = createConnection({ port: endpoint.port, host: dialAddress(endpoint.address) });
35361
+ let socket = null;
35362
+ let refusals = 0;
35363
+ let redial = null;
35364
+ let giveBackThePlace = null;
35365
+ const taken = () => {
35366
+ giveBackThePlace?.();
35367
+ giveBackThePlace = null;
35368
+ };
35293
35369
  let lateTimer = null;
35294
35370
  const finish = (reply) => {
35371
+ taken();
35372
+ if (redial !== null) {
35373
+ clearTimeout(redial);
35374
+ redial = null;
35375
+ }
35295
35376
  if (late) {
35296
35377
  if (lateTimer !== null) {
35297
35378
  clearTimeout(lateTimer);
35298
35379
  }
35299
- socket.destroy();
35300
- keepLate(id, reply.ok || reply.reason === "error" ? { state: "answered", command, sentAt, answeredAt: Date.now(), reply } : { state: "never", command, sentAt, why: reply.message });
35380
+ socket?.destroy();
35301
35381
  late = false;
35382
+ if (typeof afterTheWait === "function") {
35383
+ afterTheWait(reply);
35384
+ return;
35385
+ }
35386
+ keepLate(id, reply.ok || reply.reason === "error" ? { state: "answered", command, sentAt, answeredAt: Date.now(), reply } : { state: "never", command, sentAt, why: reply.message });
35302
35387
  return;
35303
35388
  }
35304
35389
  if (done) {
@@ -35306,12 +35391,24 @@ function runtimeRequest(endpoint, command, params, timeoutMs, keepListening = fa
35306
35391
  }
35307
35392
  done = true;
35308
35393
  clearTimeout(timer);
35309
- socket.destroy();
35394
+ socket?.destroy();
35310
35395
  settle(reply);
35311
35396
  };
35312
35397
  const timer = setTimeout(() => {
35398
+ if (socket === null) {
35399
+ finish(refusals > 0 ? {
35400
+ ok: false,
35401
+ reason: "refused",
35402
+ message: `The game (${describe2(endpoint)}) is running but nothing answered on its port: it refused ${refusals === 1 ? "the connection" : `all ${refusals} connections tried`} within ${timeoutMs}ms. Nothing listens there while a game is starting or shutting down, and the engine refuses connections while more are waiting for the game than the eight it queues, which several servers calling one game at once, or a long frame or a breakpoint holding it, can make it do. Nothing was sent, so nothing ran.`
35403
+ } : {
35404
+ ok: false,
35405
+ reason: "busy",
35406
+ message: `'${command}' was not sent to the game (${describe2(endpoint)}) within ${timeoutMs}ms: ${UNTAKEN_PER_GAME} connections from this server were already waiting for the game to take them, which it does between frames, so a long frame or a breakpoint holds them. Nothing was sent, so nothing ran.`
35407
+ });
35408
+ return;
35409
+ }
35313
35410
  const still = welcomed ? `The game (${describe2(endpoint)}) has the request and did not answer '${command}' within ${timeoutMs}ms.` : `The game (${describe2(endpoint)}) did not answer '${command}' within ${timeoutMs}ms and had not taken the request: its frame has not ended since it was sent, which a long frame or a breakpoint does, and the request runs when it does.`;
35314
- if (!keepListening) {
35411
+ if (afterTheWait === "drop") {
35315
35412
  finish({
35316
35413
  ok: false,
35317
35414
  reason: "busy",
@@ -35321,7 +35418,9 @@ function runtimeRequest(endpoint, command, params, timeoutMs, keepListening = fa
35321
35418
  }
35322
35419
  done = true;
35323
35420
  late = true;
35324
- keepLate(id, { state: "waiting", command, sentAt });
35421
+ if (afterTheWait === "keep") {
35422
+ keepLate(id, { state: "waiting", command, sentAt });
35423
+ }
35325
35424
  lateTimer = setTimeout(() => {
35326
35425
  finish({
35327
35426
  ok: false,
@@ -35331,6 +35430,15 @@ function runtimeRequest(endpoint, command, params, timeoutMs, keepListening = fa
35331
35430
  }, LATE_ANSWER_MS);
35332
35431
  lateTimer.unref();
35333
35432
  socket.unref();
35433
+ if (afterTheWait !== "keep") {
35434
+ settle({
35435
+ ok: false,
35436
+ reason: "busy",
35437
+ stillWithTheGame: true,
35438
+ message: `${still} Nothing was cancelled: the game runs it when it gets to it, and what it answers then is not kept, so ask again once the game answers. A game held at a breakpoint instead is one debug_state stack answers about.`
35439
+ });
35440
+ return;
35441
+ }
35334
35442
  settle({
35335
35443
  ok: false,
35336
35444
  reason: "busy",
@@ -35338,71 +35446,112 @@ function runtimeRequest(endpoint, command, params, timeoutMs, keepListening = fa
35338
35446
  message: `${still} Nothing was cancelled: a call that takes longer than the wait goes on and does everything it was asked, so read the state back rather than repeating it. runtime_invoke op result with requestId ${id} answers with its reply once it comes. A game held at a breakpoint instead is one debug_state stack answers about.`
35339
35447
  });
35340
35448
  }, timeoutMs);
35341
- socket.setEncoding("utf8");
35342
- socket.on("connect", () => {
35343
- socket.write(`${JSON.stringify({ id, command, params })}
35449
+ const open = () => {
35450
+ const opened = createConnection({ port: endpoint.port, host: dialAddress(endpoint.address) });
35451
+ socket = opened;
35452
+ opened.setEncoding("utf8");
35453
+ opened.on("connect", () => {
35454
+ opened.write(`${JSON.stringify({ id, command, params })}
35344
35455
  `);
35345
- });
35346
- socket.on("data", (chunk) => {
35347
- buffered += chunk;
35348
- let newline = buffered.indexOf(`
35456
+ });
35457
+ opened.on("data", (chunk) => {
35458
+ buffered += chunk;
35459
+ let newline = buffered.indexOf(`
35349
35460
  `);
35350
- while (newline !== -1 && (!done || late)) {
35351
- const line = buffered.slice(0, newline).trim();
35352
- buffered = buffered.slice(newline + 1);
35353
- newline = buffered.indexOf(`
35461
+ while (newline !== -1 && (!done || late)) {
35462
+ const line = buffered.slice(0, newline).trim();
35463
+ buffered = buffered.slice(newline + 1);
35464
+ newline = buffered.indexOf(`
35354
35465
  `);
35355
- if (line === "") {
35356
- continue;
35357
- }
35358
- let message;
35359
- try {
35360
- message = asParams(JSON.parse(line));
35361
- } catch {
35362
- continue;
35363
- }
35364
- if (!welcomed) {
35365
- welcomed = true;
35366
- if (readString(message, "type") === "welcome") {
35367
- const spoken = readNumber(message, "protocol");
35368
- if (spoken !== RUNTIME_PROTOCOL) {
35369
- finish({
35370
- ok: false,
35371
- reason: "protocol",
35372
- message: `The game (${describe2(endpoint)}) speaks runtime protocol ${spoken ?? "unknown"} and this server speaks ${RUNTIME_PROTOCOL}. Reinstall the runtime addon into the project.`
35373
- });
35374
- return;
35466
+ if (line === "") {
35467
+ continue;
35468
+ }
35469
+ let message;
35470
+ try {
35471
+ message = asParams(JSON.parse(line));
35472
+ } catch {
35473
+ continue;
35474
+ }
35475
+ if (!welcomed) {
35476
+ welcomed = true;
35477
+ taken();
35478
+ if (readString(message, "type") === "welcome") {
35479
+ const spoken = readNumber(message, "protocol");
35480
+ if (spoken !== RUNTIME_PROTOCOL) {
35481
+ finish({
35482
+ ok: false,
35483
+ reason: "protocol",
35484
+ message: `The game (${describe2(endpoint)}) speaks runtime protocol ${spoken ?? "unknown"} and this server speaks ${RUNTIME_PROTOCOL}. Reinstall the runtime addon into the project.`
35485
+ });
35486
+ return;
35487
+ }
35488
+ continue;
35375
35489
  }
35490
+ }
35491
+ if (message["id"] !== id) {
35376
35492
  continue;
35377
35493
  }
35494
+ if (readString(message, "type") === "error") {
35495
+ finish({
35496
+ ok: false,
35497
+ reason: "error",
35498
+ message: readString(message, "message") ?? "unknown error"
35499
+ });
35500
+ return;
35501
+ }
35502
+ finish({ ok: true, payload: message, endpoint });
35378
35503
  }
35379
- if (message["id"] !== id) {
35380
- continue;
35504
+ });
35505
+ opened.on("error", (error) => {
35506
+ if (socket !== opened) {
35507
+ return;
35381
35508
  }
35382
- if (readString(message, "type") === "error") {
35383
- finish({ ok: false, reason: "error", message: readString(message, "message") ?? "unknown error" });
35509
+ if (error.code === "ECONNREFUSED" && !done) {
35510
+ if (alive(endpoint.pid)) {
35511
+ refusals += 1;
35512
+ socket = null;
35513
+ opened.destroy();
35514
+ redial = setTimeout(() => {
35515
+ redial = null;
35516
+ open();
35517
+ }, REDIAL_MS);
35518
+ return;
35519
+ }
35520
+ finish({
35521
+ ok: false,
35522
+ reason: "refused",
35523
+ message: `The game (${describe2(endpoint)}) has ended: its port refused the connection and its process is gone.`
35524
+ });
35384
35525
  return;
35385
35526
  }
35386
- finish({ ok: true, payload: message, endpoint });
35387
- }
35388
- });
35389
- socket.on("error", (error) => {
35390
- finish({
35391
- ok: false,
35392
- reason: "refused",
35393
- message: `The game (${describe2(endpoint)}) is running but nothing answered on its port: ${error.code ?? error.message}. It may still be starting, or be shutting down.`
35527
+ finish({
35528
+ ok: false,
35529
+ reason: "refused",
35530
+ message: `The game (${describe2(endpoint)}) is running but nothing answered on its port: ${error.code ?? error.message}. It may still be starting or be shutting down.`
35531
+ });
35394
35532
  });
35395
- });
35396
- socket.on("close", () => {
35397
- finish({
35398
- ok: false,
35399
- reason: "refused",
35400
- message: `The game (${describe2(endpoint)}) closed the connection before answering '${command}'.`
35533
+ opened.on("close", () => {
35534
+ if (socket !== opened) {
35535
+ return;
35536
+ }
35537
+ finish({
35538
+ ok: false,
35539
+ reason: "refused",
35540
+ message: `The game (${describe2(endpoint)}) closed the connection before answering '${command}'.`
35541
+ });
35401
35542
  });
35543
+ };
35544
+ waitForAPlace(placeOf(endpoint), (giveBack) => {
35545
+ if (done) {
35546
+ giveBack();
35547
+ return;
35548
+ }
35549
+ giveBackThePlace = giveBack;
35550
+ open();
35402
35551
  });
35403
35552
  });
35404
35553
  }
35405
- var RUNTIME_PROTOCOL = 2, ANNOUNCEMENT_PATTERN, ERROR_REPORT_PATTERN, CONSOLE_BOUNDARY = "[gdharness] runtime listening on ", ERROR_REPORT_KEEP_MS, GONE_WINDOW_MS = 120000, GONE_KEPT = 32, gone, STARTED_AFTER_ANNOUNCING_MS = 2000, CONFIRMED_FOR_MS = 60000, JUDGED_AFTER_MS = 60000, verdicts, judging = null, ENDED_WITHIN_MS = 3000, LOOK_EVERY_MS = 50, ANNOUNCE_BUDGET_MS = 5000, nextRequestId = 1, LATE_ANSWER_MS, LATE_KEPT = 50, lateAnswers;
35554
+ var RUNTIME_PROTOCOL = 2, ANNOUNCEMENT_PATTERN, ERROR_REPORT_PATTERN, CONSOLE_BOUNDARY = "[gdharness] runtime listening on ", ERROR_REPORT_KEEP_MS, GONE_WINDOW_MS = 120000, GONE_KEPT = 32, gone, STARTED_AFTER_ANNOUNCING_MS = 2000, CONFIRMED_FOR_MS = 60000, JUDGED_AFTER_MS = 60000, verdicts, judging = null, ENDED_WITHIN_MS = 3000, LOOK_EVERY_MS = 50, ANNOUNCE_BUDGET_MS = 5000, nextRequestId = 1, LATE_ANSWER_MS, LATE_KEPT = 50, lateAnswers, UNTAKEN_PER_GAME = 4, REDIAL_MS = 50, untaken;
35406
35555
  var init_runtime_client = __esm(() => {
35407
35556
  init_alive();
35408
35557
  init_launch();
@@ -35415,17 +35564,18 @@ var init_runtime_client = __esm(() => {
35415
35564
  verdicts = new Map;
35416
35565
  LATE_ANSWER_MS = 30 * 60000;
35417
35566
  lateAnswers = new Map;
35567
+ untaken = new Map;
35418
35568
  });
35419
35569
 
35420
35570
  // src/boot-note.ts
35421
- import { mkdirSync as mkdirSync6, readFileSync as readFileSync14, writeFileSync as writeFileSync8 } from "node:fs";
35422
- import { dirname as dirname11, join as join17 } from "node:path";
35571
+ import { mkdirSync as mkdirSync6, readFileSync as readFileSync15, writeFileSync as writeFileSync8 } from "node:fs";
35572
+ import { dirname as dirname11, join as join18 } from "node:path";
35423
35573
  function bootNotePath(projectPath) {
35424
- return join17(projectPath, ".godot", "gdharness-boot.json");
35574
+ return join18(projectPath, ".godot", "gdharness-boot.json");
35425
35575
  }
35426
35576
  function readScenes(projectPath) {
35427
35577
  try {
35428
- return asParams(asParams(JSON.parse(readFileSync14(bootNotePath(projectPath), "utf8")))["scenes"]);
35578
+ return asParams(asParams(JSON.parse(readFileSync15(bootNotePath(projectPath), "utf8")))["scenes"]);
35429
35579
  } catch {
35430
35580
  return {};
35431
35581
  }
@@ -35467,10 +35617,10 @@ var init_boot_note = __esm(() => {
35467
35617
  });
35468
35618
 
35469
35619
  // src/breakpoint-note.ts
35470
- import { mkdirSync as mkdirSync7, readFileSync as readFileSync15, rmSync as rmSync7, writeFileSync as writeFileSync9 } from "node:fs";
35471
- import { dirname as dirname12, isAbsolute as isAbsolute4, join as join18, relative as relative3 } from "node:path";
35620
+ import { mkdirSync as mkdirSync7, readFileSync as readFileSync16, rmSync as rmSync7, writeFileSync as writeFileSync9 } from "node:fs";
35621
+ import { dirname as dirname12, isAbsolute as isAbsolute4, join as join19, relative as relative3 } from "node:path";
35472
35622
  function breakpointNotePath(projectPath) {
35473
- return join18(projectPath, ".godot", "gdharness-breakpoints.json");
35623
+ return join19(projectPath, ".godot", "gdharness-breakpoints.json");
35474
35624
  }
35475
35625
  function writeBreakpointNote(projectPath, held) {
35476
35626
  const path = breakpointNotePath(projectPath);
@@ -35496,7 +35646,7 @@ function writeBreakpointNote(projectPath, held) {
35496
35646
  }
35497
35647
  function readBreakpointNote(projectPath) {
35498
35648
  try {
35499
- const parsed = JSON.parse(readFileSync15(breakpointNotePath(projectPath), "utf8"));
35649
+ const parsed = JSON.parse(readFileSync16(breakpointNotePath(projectPath), "utf8"));
35500
35650
  if (typeof parsed !== "object" || parsed === null || !Array.isArray(parsed.breakpoints)) {
35501
35651
  return [];
35502
35652
  }
@@ -35511,7 +35661,7 @@ function readBreakpointNote(projectPath) {
35511
35661
  }
35512
35662
  const lines = Array.isArray(listed) ? listed.filter((line) => Number.isInteger(line) && line > 0) : [];
35513
35663
  if (lines.length > 0) {
35514
- held.push({ scriptPath: realPathOr(join18(projectPath, script)), lines });
35664
+ held.push({ scriptPath: realPathOr(join19(projectPath, script)), lines });
35515
35665
  }
35516
35666
  }
35517
35667
  return held;
@@ -35560,14 +35710,14 @@ var init_whole_file = __esm(() => {
35560
35710
  });
35561
35711
 
35562
35712
  // src/class-note.ts
35563
- import { mkdirSync as mkdirSync8, readFileSync as readFileSync16 } from "node:fs";
35564
- import { dirname as dirname13, join as join19 } from "node:path";
35713
+ import { mkdirSync as mkdirSync8, readFileSync as readFileSync17 } from "node:fs";
35714
+ import { dirname as dirname13, join as join20 } from "node:path";
35565
35715
  function classNotePath(projectPath) {
35566
- return join19(projectPath, ".godot", "gdharness-classes.json");
35716
+ return join20(projectPath, ".godot", "gdharness-classes.json");
35567
35717
  }
35568
35718
  function readClassNote(projectPath) {
35569
35719
  try {
35570
- const fields = asParams(JSON.parse(readFileSync16(classNotePath(projectPath), "utf8")));
35720
+ const fields = asParams(JSON.parse(readFileSync17(classNotePath(projectPath), "utf8")));
35571
35721
  const rebuiltWith = readArray(fields, "rebuiltWith");
35572
35722
  if (rebuiltWith === undefined || !rebuiltWith.every((name) => typeof name === "string")) {
35573
35723
  return null;
@@ -35609,16 +35759,16 @@ var init_class_note = __esm(() => {
35609
35759
  });
35610
35760
 
35611
35761
  // src/config-pin.ts
35612
- import { existsSync as existsSync16, readFileSync as readFileSync17 } from "node:fs";
35613
- import { join as join20 } from "node:path";
35762
+ import { existsSync as existsSync17, readFileSync as readFileSync18 } from "node:fs";
35763
+ import { join as join21 } from "node:path";
35614
35764
  function versionsNamedInConfig(projectPath) {
35615
- const config = join20(projectPath, ".mcp.json");
35616
- if (!existsSync16(config)) {
35765
+ const config = join21(projectPath, ".mcp.json");
35766
+ if (!existsSync17(config)) {
35617
35767
  return [];
35618
35768
  }
35619
35769
  let text;
35620
35770
  try {
35621
- text = readFileSync17(config, "utf8");
35771
+ text = readFileSync18(config, "utf8");
35622
35772
  JSON.parse(text);
35623
35773
  } catch {
35624
35774
  return [];
@@ -35639,10 +35789,10 @@ var init_config_pin = __esm(() => {
35639
35789
  });
35640
35790
 
35641
35791
  // src/cut-test-run.ts
35642
- import { existsSync as existsSync17, mkdirSync as mkdirSync9, readFileSync as readFileSync18, rmSync as rmSync9, writeFileSync as writeFileSync11 } from "node:fs";
35643
- import { dirname as dirname14, join as join21 } from "node:path";
35792
+ import { existsSync as existsSync18, mkdirSync as mkdirSync9, readFileSync as readFileSync19, rmSync as rmSync9, writeFileSync as writeFileSync11 } from "node:fs";
35793
+ import { dirname as dirname14, join as join22 } from "node:path";
35644
35794
  function keptAt(projectPath) {
35645
- return join21(projectPath, ".godot", "gdharness-reports", "cut-run.json");
35795
+ return join22(projectPath, ".godot", "gdharness-reports", "cut-run.json");
35646
35796
  }
35647
35797
  function keepCutTestRun(projectPath, run) {
35648
35798
  const path = keptAt(projectPath);
@@ -35654,11 +35804,11 @@ function keepCutTestRun(projectPath, run) {
35654
35804
  }
35655
35805
  function takeCutTestRun(projectPath) {
35656
35806
  const path = keptAt(projectPath);
35657
- if (!existsSync17(path)) {
35807
+ if (!existsSync18(path)) {
35658
35808
  return null;
35659
35809
  }
35660
35810
  try {
35661
- const run = JSON.parse(readFileSync18(path, "utf8"));
35811
+ const run = JSON.parse(readFileSync19(path, "utf8"));
35662
35812
  rmSync9(path, { force: true });
35663
35813
  return typeof run.cutAt === "string" && Array.isArray(run.paths) && run.paths.every((path) => typeof path === "string") ? run : null;
35664
35814
  } catch {
@@ -36471,27 +36621,27 @@ var init_dap_client = __esm(() => {
36471
36621
  import { createHash as createHash4 } from "node:crypto";
36472
36622
  import {
36473
36623
  closeSync,
36474
- existsSync as existsSync18,
36624
+ existsSync as existsSync19,
36475
36625
  mkdtempSync as mkdtempSync2,
36476
36626
  openSync,
36477
- readFileSync as readFileSync19,
36627
+ readFileSync as readFileSync20,
36478
36628
  rmSync as rmSync10,
36479
36629
  statSync as statSync9,
36480
36630
  writeFileSync as writeFileSync12,
36481
36631
  writeSync
36482
36632
  } from "node:fs";
36483
36633
  import { tmpdir as tmpdir5 } from "node:os";
36484
- import { join as join22 } from "node:path";
36634
+ import { join as join23 } from "node:path";
36485
36635
  import { setTimeout as delay3 } from "node:timers/promises";
36486
36636
  function helperAssembly(directory = tmpdir5()) {
36487
- return join22(directory, `gdharness-desktop-${DIGEST}.dll`);
36637
+ return join23(directory, `gdharness-desktop-${DIGEST}.dll`);
36488
36638
  }
36489
36639
  async function ensureHelperCompiled(directory = tmpdir5(), budgetMs = HELPER_COMPILE_MS, wait = true) {
36490
36640
  const assembly = helperAssembly(directory);
36491
36641
  const lock = `${assembly}.compiling`;
36492
36642
  const until = Date.now() + budgetMs;
36493
36643
  while (Date.now() < until) {
36494
- if (existsSync18(assembly)) {
36644
+ if (existsSync19(assembly)) {
36495
36645
  return null;
36496
36646
  }
36497
36647
  let mine = false;
@@ -36509,7 +36659,7 @@ async function ensureHelperCompiled(directory = tmpdir5(), budgetMs = HELPER_COM
36509
36659
  }
36510
36660
  }
36511
36661
  try {
36512
- const holder = Number(readFileSync19(lock, "utf8").trim());
36662
+ const holder = Number(readFileSync20(lock, "utf8").trim());
36513
36663
  const old = Date.now() - statSync9(lock).mtimeMs > budgetMs;
36514
36664
  if (old || Number.isInteger(holder) && holder > 0 && !alive(holder)) {
36515
36665
  rmSync10(lock, { force: true });
@@ -36524,8 +36674,8 @@ async function ensureHelperCompiled(directory = tmpdir5(), budgetMs = HELPER_COM
36524
36674
  return `the desktop helper was not compiled within ${String(budgetMs)} ms`;
36525
36675
  }
36526
36676
  async function compileHelper(directory, assembly, budgetMs) {
36527
- const scratch = mkdtempSync2(join22(directory, "gdharness-desktop-source-"));
36528
- const source = join22(scratch, "helper.cs");
36677
+ const scratch = mkdtempSync2(join23(directory, "gdharness-desktop-source-"));
36678
+ const source = join23(scratch, "helper.cs");
36529
36679
  writeFileSync12(source, SOURCE, "utf8");
36530
36680
  const literal = (path) => `'${path.replaceAll("'", "''")}'`;
36531
36681
  const script = [
@@ -36563,7 +36713,7 @@ async function compileHelper(directory, assembly, budgetMs) {
36563
36713
  });
36564
36714
  compiler.once("close", (code) => {
36565
36715
  clearTimeout(timer);
36566
- resolve(existsSync18(assembly) ? null : `Windows PowerShell could not compile it${code === null ? " in time" : ""}: ${complained.trim() || `exit code ${String(code)}`}`);
36716
+ resolve(existsSync19(assembly) ? null : `Windows PowerShell could not compile it${code === null ? " in time" : ""}: ${complained.trim() || `exit code ${String(code)}`}`);
36567
36717
  });
36568
36718
  });
36569
36719
  } finally {
@@ -36730,25 +36880,25 @@ var init_desktop = __esm(() => {
36730
36880
  // src/editor-log.ts
36731
36881
  import {
36732
36882
  closeSync as closeSync2,
36733
- existsSync as existsSync19,
36883
+ existsSync as existsSync20,
36734
36884
  mkdirSync as mkdirSync10,
36735
36885
  openSync as openSync2,
36736
- readFileSync as readFileSync20,
36886
+ readFileSync as readFileSync21,
36737
36887
  readSync,
36738
36888
  rmSync as rmSync11,
36739
36889
  statSync as statSync10,
36740
36890
  writeFileSync as writeFileSync13
36741
36891
  } from "node:fs";
36742
- import { dirname as dirname15, join as join23 } from "node:path";
36892
+ import { dirname as dirname15, join as join24 } from "node:path";
36743
36893
  function editorLogPath(projectPath) {
36744
- return join23(projectPath, ".godot", "gdharness-editor.log");
36894
+ return join24(projectPath, ".godot", "gdharness-editor.log");
36745
36895
  }
36746
36896
  function editorLogNotePath(projectPath) {
36747
- return join23(projectPath, ".godot", "gdharness-editor.json");
36897
+ return join24(projectPath, ".godot", "gdharness-editor.json");
36748
36898
  }
36749
36899
  function readEditorLogNote(projectPath) {
36750
36900
  try {
36751
- const fields = asParams(JSON.parse(readFileSync20(editorLogNotePath(projectPath), "utf8")));
36901
+ const fields = asParams(JSON.parse(readFileSync21(editorLogNotePath(projectPath), "utf8")));
36752
36902
  const pid = readNumber(fields, "pid");
36753
36903
  const startedAt = readString(fields, "startedAt");
36754
36904
  return pid !== undefined && Number.isInteger(pid) && pid > 0 && startedAt !== undefined ? { pid, startedAt } : null;
@@ -36789,7 +36939,7 @@ function editorConsole(projectPath, editorPid, openedByAServer) {
36789
36939
  return { kind: "older server", notedPid: noted?.pid ?? null };
36790
36940
  }
36791
36941
  const path = editorLogPath(projectPath);
36792
- if (!existsSync19(path)) {
36942
+ if (!existsSync20(path)) {
36793
36943
  return { kind: "not written" };
36794
36944
  }
36795
36945
  const log = new GameLog;
@@ -36847,7 +36997,7 @@ function foldProgress(text) {
36847
36997
  function theFirstOf(path) {
36848
36998
  const whole = statSync10(path).size;
36849
36999
  if (whole <= MOST_CONSOLE_BYTES) {
36850
- return { text: readFileSync20(path, "utf8") };
37000
+ return { text: readFileSync21(path, "utf8") };
36851
37001
  }
36852
37002
  const handle = openSync2(path, "r");
36853
37003
  try {
@@ -37008,7 +37158,7 @@ var init_editor_log = __esm(() => {
37008
37158
  });
37009
37159
 
37010
37160
  // src/rename.ts
37011
- import { existsSync as existsSync20, mkdirSync as mkdirSync11, readFileSync as readFileSync21, renameSync as renameSync3, unlinkSync as unlinkSync3, writeFileSync as writeFileSync14 } from "node:fs";
37161
+ import { existsSync as existsSync21, mkdirSync as mkdirSync11, readFileSync as readFileSync22, renameSync as renameSync3, unlinkSync as unlinkSync3, writeFileSync as writeFileSync14 } from "node:fs";
37012
37162
  import { dirname as dirname16, posix as posix2 } from "node:path";
37013
37163
 
37014
37164
  class Script {
@@ -37713,11 +37863,11 @@ function rewritesOf(plan, texts, move) {
37713
37863
  }
37714
37864
  function applyRewrites(rewrites) {
37715
37865
  for (const rewrite of rewrites) {
37716
- const now = readFileSync21(rewrite.absolute, "utf8");
37866
+ const now = readFileSync22(rewrite.absolute, "utf8");
37717
37867
  if (now !== rewrite.before) {
37718
37868
  throw new Error(`${rewrite.path} changed on disk while the rename was being worked out; nothing was written`);
37719
37869
  }
37720
- if (rewrite.writeTo !== rewrite.absolute && existsSync20(rewrite.writeTo)) {
37870
+ if (rewrite.writeTo !== rewrite.absolute && existsSync21(rewrite.writeTo)) {
37721
37871
  throw new Error(`${rewrite.writeTo} already exists; nothing was written`);
37722
37872
  }
37723
37873
  }
@@ -37731,7 +37881,7 @@ function applyRewrites(rewrites) {
37731
37881
  let movedUid = false;
37732
37882
  if (rewrite.writeTo !== rewrite.absolute) {
37733
37883
  unlinkSync3(rewrite.absolute);
37734
- if (existsSync20(`${rewrite.absolute}.uid`)) {
37884
+ if (existsSync21(`${rewrite.absolute}.uid`)) {
37735
37885
  renameSync3(`${rewrite.absolute}.uid`, `${rewrite.writeTo}.uid`);
37736
37886
  movedUid = true;
37737
37887
  }
@@ -37777,10 +37927,10 @@ var init_rename = __esm(() => {
37777
37927
  });
37778
37928
 
37779
37929
  // src/lsp_client.ts
37780
- import { existsSync as existsSync21, realpathSync as realpathSync4 } from "node:fs";
37930
+ import { existsSync as existsSync22, realpathSync as realpathSync4 } from "node:fs";
37781
37931
  import { readFile, realpath } from "node:fs/promises";
37782
37932
  import { createConnection as createConnection3 } from "node:net";
37783
- import { dirname as dirname17, join as join24, resolve as resolve5 } from "node:path";
37933
+ import { dirname as dirname17, join as join25, resolve as resolve5 } from "node:path";
37784
37934
  import { setTimeout as delay4 } from "node:timers/promises";
37785
37935
  import { fileURLToPath as fileURLToPath3, pathToFileURL } from "node:url";
37786
37936
  function initializeTimeoutMs() {
@@ -38458,7 +38608,7 @@ async function resolveLSPPaths(projectPathValue, scriptPathValue) {
38458
38608
  } catch {
38459
38609
  throw new ArgumentRefusal(`Project path does not exist: ${requestedProjectPath}`);
38460
38610
  }
38461
- if (!existsSync21(join24(projectPath, "project.godot"))) {
38611
+ if (!existsSync22(join25(projectPath, "project.godot"))) {
38462
38612
  throw new ArgumentRefusal(`Not a Godot project: ${projectPath}. Point projectPath at the directory holding project.godot.`);
38463
38613
  }
38464
38614
  const contained = resolveWithinProject(projectPath, scriptPathValue);
@@ -38618,8 +38768,8 @@ var init_lsp_client = __esm(() => {
38618
38768
  });
38619
38769
 
38620
38770
  // src/rename-symbol.ts
38621
- import { existsSync as existsSync22, mkdirSync as mkdirSync12, readdirSync as readdirSync11, realpathSync as realpathSync5, rmSync as rmSync12, writeFileSync as writeFileSync15 } from "node:fs";
38622
- import { basename as basename7, dirname as dirname18, join as join25, relative as relative4, resolve as resolve6 } from "node:path";
38771
+ import { existsSync as existsSync23, mkdirSync as mkdirSync12, readdirSync as readdirSync11, realpathSync as realpathSync5, rmSync as rmSync12, writeFileSync as writeFileSync15 } from "node:fs";
38772
+ import { basename as basename7, dirname as dirname18, join as join26, relative as relative4, resolve as resolve6 } from "node:path";
38623
38773
  function resourcePathOf(projectPath, absolute) {
38624
38774
  const inside = relative4(onDiskSpelling(projectPath), onDiskSpelling(absolute));
38625
38775
  if (inside === "" || inside.startsWith("..") || /^[A-Za-z]:/.test(inside)) {
@@ -38632,7 +38782,7 @@ function onDiskSpelling(path) {
38632
38782
  let at = resolve6(path);
38633
38783
  for (;; ) {
38634
38784
  try {
38635
- return join25(realpathSync5.native(at), ...missing.reverse());
38785
+ return join26(realpathSync5.native(at), ...missing.reverse());
38636
38786
  } catch {
38637
38787
  const parent = dirname18(at);
38638
38788
  if (parent === at) {
@@ -38685,7 +38835,7 @@ async function renameSymbol(request, services) {
38685
38835
  reason: `newScriptPath must be a .gd path inside the project, and ${request.newScriptPath} is not.`
38686
38836
  };
38687
38837
  }
38688
- if (existsSync22(request.newScriptPath)) {
38838
+ if (existsSync23(request.newScriptPath)) {
38689
38839
  return { ok: false, reason: `${moveTo} already exists, so the script cannot be moved there.` };
38690
38840
  }
38691
38841
  }
@@ -38861,7 +39011,7 @@ async function renameSymbol(request, services) {
38861
39011
  scripts,
38862
39012
  moves: new Map([[declaringPath, moveTo]]),
38863
39013
  named: [declaringPath],
38864
- exists: (path) => existsSync22(`${projectPath}/${path.slice("res://".length)}`)
39014
+ exists: (path) => existsSync23(`${projectPath}/${path.slice("res://".length)}`)
38865
39015
  }, plan);
38866
39016
  }
38867
39017
  plan = mentionIgnored(plan, ignoredTexts(projectPath), symbol, moveTo === null ? [] : [declaringPath]);
@@ -39060,15 +39210,15 @@ ${mention.kind}`;
39060
39210
  };
39061
39211
  }
39062
39212
  function writeLeftAloneDetail(projectPath, symbol, detail) {
39063
- const directory = join25(projectPath, ".godot", "gdharness-renames");
39213
+ const directory = join26(projectPath, ".godot", "gdharness-renames");
39064
39214
  mkdirSync12(directory, { recursive: true });
39065
39215
  detailWrites += 1;
39066
- const path = join25(directory, `${String(Date.now())}-${String(detailWrites).padStart(6, "0")}-${String(process.pid)}-${symbol}.json`);
39216
+ const path = join26(directory, `${String(Date.now())}-${String(detailWrites).padStart(6, "0")}-${String(process.pid)}-${symbol}.json`);
39067
39217
  writeFileSync15(path, `${JSON.stringify(detail, null, 2)}
39068
39218
  `, "utf8");
39069
39219
  const kept = readdirSync11(directory).filter((name) => name.endsWith(".json")).sort().reverse();
39070
39220
  for (const old of kept.slice(10)) {
39071
- rmSync12(join25(directory, old), { force: true });
39221
+ rmSync12(join26(directory, old), { force: true });
39072
39222
  }
39073
39223
  return path;
39074
39224
  }
@@ -39109,18 +39259,18 @@ var init_rename_symbol = __esm(() => {
39109
39259
 
39110
39260
  // src/file-move.ts
39111
39261
  import {
39112
- existsSync as existsSync23,
39262
+ existsSync as existsSync24,
39113
39263
  mkdirSync as mkdirSync13,
39114
39264
  readdirSync as readdirSync12,
39115
- readFileSync as readFileSync22,
39265
+ readFileSync as readFileSync23,
39116
39266
  renameSync as renameSync4,
39117
39267
  statSync as statSync11,
39118
39268
  writeFileSync as writeFileSync16
39119
39269
  } from "node:fs";
39120
- import { basename as basename8, dirname as dirname19, extname as extname3, join as join26, relative as relative5 } from "node:path";
39270
+ import { basename as basename8, dirname as dirname19, extname as extname3, join as join27, relative as relative5 } from "node:path";
39121
39271
  function filesUnder(directory) {
39122
39272
  return readdirSync12(directory, { withFileTypes: true }).flatMap((entry) => {
39123
- const path = join26(directory, entry.name);
39273
+ const path = join27(directory, entry.name);
39124
39274
  return entry.isDirectory() ? filesUnder(path) : [path];
39125
39275
  });
39126
39276
  }
@@ -39141,8 +39291,8 @@ function unseenByTheEngine(projectPath, path) {
39141
39291
  return `${path} is under ${step}, and the engine passes over every directory whose name starts with a dot`;
39142
39292
  }
39143
39293
  if (at < steps.length - 1) {
39144
- directory = join26(directory, step);
39145
- if (existsSync23(join26(directory, ".gdignore"))) {
39294
+ directory = join27(directory, step);
39295
+ if (existsSync24(join27(directory, ".gdignore"))) {
39146
39296
  return `${path} is under a directory holding a .gdignore, which the engine does not load`;
39147
39297
  }
39148
39298
  }
@@ -39154,12 +39304,12 @@ function installedAddonOf(projectPath, path) {
39154
39304
  if (steps[0] !== "addons" || steps[1] === undefined) {
39155
39305
  return null;
39156
39306
  }
39157
- return existsSync23(join26(projectPath, "addons", steps[1], VERSION_MARKER)) ? `res://addons/${steps[1]}` : null;
39307
+ return existsSync24(join27(projectPath, "addons", steps[1], VERSION_MARKER)) ? `res://addons/${steps[1]}` : null;
39158
39308
  }
39159
39309
  function binaryResources(projectPath) {
39160
39310
  const found = [];
39161
39311
  const visit = (directory, prefix) => {
39162
- if (existsSync23(join26(directory, ".gdignore"))) {
39312
+ if (existsSync24(join27(directory, ".gdignore"))) {
39163
39313
  return;
39164
39314
  }
39165
39315
  for (const entry of readdirSync12(directory, { withFileTypes: true })) {
@@ -39168,7 +39318,7 @@ function binaryResources(projectPath) {
39168
39318
  }
39169
39319
  const path = `${prefix}${entry.name}`;
39170
39320
  if (entry.isDirectory()) {
39171
- visit(join26(directory, entry.name), `${path}/`);
39321
+ visit(join27(directory, entry.name), `${path}/`);
39172
39322
  } else if (/\.(scn|res)$/i.test(entry.name)) {
39173
39323
  found.push(path);
39174
39324
  }
@@ -39178,7 +39328,7 @@ function binaryResources(projectPath) {
39178
39328
  return found;
39179
39329
  }
39180
39330
  function targetPathOf(projectPath, absolute) {
39181
- const inside = relative5(onDiskSpelling(projectPath), join26(onDiskSpelling(dirname19(absolute)), basename8(absolute)));
39331
+ const inside = relative5(onDiskSpelling(projectPath), join27(onDiskSpelling(dirname19(absolute)), basename8(absolute)));
39182
39332
  if (inside === "" || inside.startsWith("..") || /^[A-Za-z]:/.test(inside)) {
39183
39333
  return null;
39184
39334
  }
@@ -39196,7 +39346,7 @@ function planFileMove(projectPath, absoluteFrom, absoluteTo) {
39196
39346
  if (to === null) {
39197
39347
  return { ok: false, reason: `newPath must be a path inside the project, and ${absoluteTo} is not.` };
39198
39348
  }
39199
- if (!existsSync23(absoluteFrom)) {
39349
+ if (!existsSync24(absoluteFrom)) {
39200
39350
  return { ok: false, reason: `There is nothing at ${from} to move.` };
39201
39351
  }
39202
39352
  if (from === to) {
@@ -39225,7 +39375,7 @@ function planFileMove(projectPath, absoluteFrom, absoluteTo) {
39225
39375
  if (isDirectory && to.startsWith(`${from}/`)) {
39226
39376
  return { ok: false, reason: `${from} cannot be moved into itself.` };
39227
39377
  }
39228
- if (existsSync23(absoluteTo) && !sameFile(absoluteFrom, absoluteTo)) {
39378
+ if (existsSync24(absoluteTo) && !sameFile(absoluteFrom, absoluteTo)) {
39229
39379
  return { ok: false, reason: `${to} already exists, so nothing was moved there.` };
39230
39380
  }
39231
39381
  const unseen = unseenByTheEngine(projectPath, to);
@@ -39233,7 +39383,7 @@ function planFileMove(projectPath, absoluteFrom, absoluteTo) {
39233
39383
  return { ok: false, reason: `${unseen}, so nothing there could load what was moved.` };
39234
39384
  }
39235
39385
  if (!isDirectory) {
39236
- const owner = SIDECARS.map((sidecar) => from.endsWith(sidecar) ? from.slice(0, -sidecar.length) : null).find((path) => path !== null && existsSync23(join26(projectPath, path.slice("res://".length))));
39386
+ const owner = SIDECARS.map((sidecar) => from.endsWith(sidecar) ? from.slice(0, -sidecar.length) : null).find((path) => path !== null && existsSync24(join27(projectPath, path.slice("res://".length))));
39237
39387
  if (owner !== undefined && owner !== null) {
39238
39388
  return { ok: false, reason: `${from} belongs to ${owner} and moves with it: move ${owner} instead.` };
39239
39389
  }
@@ -39255,7 +39405,7 @@ function planFileMove(projectPath, absoluteFrom, absoluteTo) {
39255
39405
  } else {
39256
39406
  moves.set(from, to);
39257
39407
  for (const sidecar of SIDECARS) {
39258
- if (existsSync23(`${absoluteFrom}${sidecar}`)) {
39408
+ if (existsSync24(`${absoluteFrom}${sidecar}`)) {
39259
39409
  moves.set(`${from}${sidecar}`, `${to}${sidecar}`);
39260
39410
  }
39261
39411
  }
@@ -39267,12 +39417,12 @@ function planFileMove(projectPath, absoluteFrom, absoluteTo) {
39267
39417
  scripts,
39268
39418
  moves,
39269
39419
  named: [from],
39270
- exists: (path) => existsSync23(join26(projectPath, path.slice("res://".length)))
39420
+ exists: (path) => existsSync24(join27(projectPath, path.slice("res://".length)))
39271
39421
  }, { edits: [], mentions: [] });
39272
39422
  plan = mentionIgnored(plan, ignoredTexts(projectPath), null, [from]);
39273
39423
  const binaries = [];
39274
39424
  for (const path of binaryResources(projectPath)) {
39275
- const bytes = readFileSync22(join26(projectPath, path.slice("res://".length)));
39425
+ const bytes = readFileSync23(join27(projectPath, path.slice("res://".length)));
39276
39426
  const names = [...moves.keys()].find((moving) => bytes.includes(moving));
39277
39427
  if (names !== undefined) {
39278
39428
  binaries.push({
@@ -39291,7 +39441,7 @@ function planFileMove(projectPath, absoluteFrom, absoluteTo) {
39291
39441
  }));
39292
39442
  const left = leftAloneReport(mentions);
39293
39443
  const rewrites = rewritesOf(plan, texts, null);
39294
- const absoluteOf = (path) => join26(projectPath, path.slice("res://".length));
39444
+ const absoluteOf = (path) => join27(projectPath, path.slice("res://".length));
39295
39445
  const rewritten = new Map(rewrites.map((rewrite) => [rewrite.path, rewrite]));
39296
39446
  const moved = [...moves].map(([source, target]) => {
39297
39447
  const rewrite = rewritten.get(source);
@@ -39333,18 +39483,18 @@ function planFileMove(projectPath, absoluteFrom, absoluteTo) {
39333
39483
  }
39334
39484
  function applyFileMove(outcome) {
39335
39485
  for (const rewrite of outcome.inPlace) {
39336
- if (readFileSync22(rewrite.absolute, "utf8") !== rewrite.before) {
39486
+ if (readFileSync23(rewrite.absolute, "utf8") !== rewrite.before) {
39337
39487
  throw new Error(`${rewrite.path} changed on disk while the move was being worked out; nothing was written`);
39338
39488
  }
39339
39489
  }
39340
39490
  for (const file of outcome.moved) {
39341
- if (!existsSync23(file.absoluteFrom)) {
39491
+ if (!existsSync24(file.absoluteFrom)) {
39342
39492
  throw new Error(`${file.from} is gone from disk; nothing was written`);
39343
39493
  }
39344
- if (file.text !== null && readFileSync22(file.absoluteFrom, "utf8") !== file.text.before) {
39494
+ if (file.text !== null && readFileSync23(file.absoluteFrom, "utf8") !== file.text.before) {
39345
39495
  throw new Error(`${file.from} changed on disk while the move was being worked out; nothing was written`);
39346
39496
  }
39347
- if (existsSync23(file.absoluteTo) && !sameFile(file.absoluteFrom, file.absoluteTo)) {
39497
+ if (existsSync24(file.absoluteTo) && !sameFile(file.absoluteFrom, file.absoluteTo)) {
39348
39498
  throw new Error(`${file.to} already exists; nothing was written`);
39349
39499
  }
39350
39500
  }
@@ -43537,10 +43687,10 @@ var init_keeper_channel = () => {};
43537
43687
 
43538
43688
  // src/lifecycle-log.ts
43539
43689
  import { appendFileSync, mkdirSync as mkdirSync14, renameSync as renameSync5, statSync as statSync12 } from "node:fs";
43540
- import { dirname as dirname20, join as join27 } from "node:path";
43690
+ import { dirname as dirname20, join as join28 } from "node:path";
43541
43691
  import process11 from "node:process";
43542
43692
  function lifecycleLogPath() {
43543
- return join27(runtimeDirectory(), "servers.log");
43693
+ return join28(runtimeDirectory(), "servers.log");
43544
43694
  }
43545
43695
  function writeLifecycle(event, fields = {}) {
43546
43696
  const path = lifecycleLogPath();
@@ -43860,10 +44010,10 @@ var init_prompts = __esm(() => {
43860
44010
  });
43861
44011
 
43862
44012
  // src/restart-note.ts
43863
- import { mkdirSync as mkdirSync15, readFileSync as readFileSync23, rmSync as rmSync13, writeFileSync as writeFileSync17 } from "node:fs";
43864
- import { dirname as dirname21, join as join28 } from "node:path";
44013
+ import { mkdirSync as mkdirSync15, readFileSync as readFileSync24, rmSync as rmSync13, writeFileSync as writeFileSync17 } from "node:fs";
44014
+ import { dirname as dirname21, join as join29 } from "node:path";
43865
44015
  function restartNotePath(projectPath) {
43866
- return join28(projectPath, ".godot", "gdharness-restart.json");
44016
+ return join29(projectPath, ".godot", "gdharness-restart.json");
43867
44017
  }
43868
44018
  function noteRestartBegun(note) {
43869
44019
  const path = restartNotePath(note.projectPath);
@@ -43877,7 +44027,7 @@ function noteRestartBegun(note) {
43877
44027
  }
43878
44028
  function restartOwed(projectPath) {
43879
44029
  try {
43880
- const parsed = JSON.parse(readFileSync23(restartNotePath(projectPath), "utf8"));
44030
+ const parsed = JSON.parse(readFileSync24(restartNotePath(projectPath), "utf8"));
43881
44031
  if (typeof parsed !== "object" || parsed === null) {
43882
44032
  return null;
43883
44033
  }
@@ -43903,17 +44053,17 @@ var init_restart_note = __esm(() => {
43903
44053
 
43904
44054
  // src/run-record.ts
43905
44055
  import { createHash as createHash5 } from "node:crypto";
43906
- import { mkdirSync as mkdirSync16, openSync as openSync3, readdirSync as readdirSync13, readFileSync as readFileSync24, realpathSync as realpathSync6, rmSync as rmSync14, statSync as statSync13 } from "node:fs";
43907
- import { basename as basename9, dirname as dirname22, join as join29, resolve as resolve7 } from "node:path";
44056
+ import { mkdirSync as mkdirSync16, openSync as openSync3, readdirSync as readdirSync13, readFileSync as readFileSync25, realpathSync as realpathSync6, rmSync as rmSync14, statSync as statSync13 } from "node:fs";
44057
+ import { basename as basename9, dirname as dirname22, join as join30, resolve as resolve7 } from "node:path";
43908
44058
  function runsDirectory() {
43909
- return join29(runtimeDirectory(), "runs");
44059
+ return join30(runtimeDirectory(), "runs");
43910
44060
  }
43911
44061
  function realAsFarAsItExists(path) {
43912
44062
  const missing = [];
43913
44063
  let at = path;
43914
44064
  for (;; ) {
43915
44065
  try {
43916
- return join29(realpathSync6.native(at), ...missing);
44066
+ return join30(realpathSync6.native(at), ...missing);
43917
44067
  } catch {
43918
44068
  const parent = dirname22(at);
43919
44069
  if (parent === at) {
@@ -43930,10 +44080,10 @@ function projectKey(projectPath) {
43930
44080
  return createHash5("sha256").update(folded.replaceAll("\\", "/")).digest("hex").slice(0, 16);
43931
44081
  }
43932
44082
  function runRecordPath(projectPath, directory = runtimeDirectory()) {
43933
- return join29(directory, "runs", `note-${projectKey(projectPath)}.json`);
44083
+ return join30(directory, "runs", `note-${projectKey(projectPath)}.json`);
43934
44084
  }
43935
44085
  function latestRunPath(servedBy, directory = runtimeDirectory()) {
43936
- return join29(directory, "runs", `latest-${projectKey(servedBy)}.json`);
44086
+ return join30(directory, "runs", `latest-${projectKey(servedBy)}.json`);
43937
44087
  }
43938
44088
  function writeLatestRun(servedBy, latest) {
43939
44089
  try {
@@ -43943,7 +44093,7 @@ function writeLatestRun(servedBy, latest) {
43943
44093
  }
43944
44094
  function readLatestRun(servedBy, directory = runtimeDirectory()) {
43945
44095
  try {
43946
- const fields = JSON.parse(readFileSync24(latestRunPath(servedBy, directory), "utf8"));
44096
+ const fields = JSON.parse(readFileSync25(latestRunPath(servedBy, directory), "utf8"));
43947
44097
  if (typeof fields !== "object" || fields === null) {
43948
44098
  return null;
43949
44099
  }
@@ -43958,20 +44108,20 @@ function readLatestRun(servedBy, directory = runtimeDirectory()) {
43958
44108
  }
43959
44109
  function openTranscript(startedAt) {
43960
44110
  mkdirSync16(runsDirectory(), { recursive: true });
43961
- const path = join29(runsDirectory(), `run-${startedAt}.log`);
44111
+ const path = join30(runsDirectory(), `run-${startedAt}.log`);
43962
44112
  return { path, fd: openSync3(path, "a") };
43963
44113
  }
43964
44114
  function profilePath(startedAt) {
43965
- return join29(runsDirectory(), `run-${startedAt}.profile.json`);
44115
+ return join30(runsDirectory(), `run-${startedAt}.profile.json`);
43966
44116
  }
43967
44117
  function profileMarkPath(startedAt) {
43968
- return join29(runsDirectory(), `run-${startedAt}.profile-mark.json`);
44118
+ return join30(runsDirectory(), `run-${startedAt}.profile-mark.json`);
43969
44119
  }
43970
44120
  function noteIsAbout(note, playing) {
43971
44121
  return note.play !== undefined && playing !== null && note.play.play === playing.play && note.play.editorPid === playing.editorPid;
43972
44122
  }
43973
44123
  function editorNotePath(projectPath, directory = runtimeDirectory()) {
43974
- return join29(directory, "runs", `editor-${projectKey(projectPath)}.json`);
44124
+ return join30(directory, "runs", `editor-${projectKey(projectPath)}.json`);
43975
44125
  }
43976
44126
  function writeEditorRunNote(note) {
43977
44127
  mkdirSync16(runsDirectory(), { recursive: true });
@@ -43982,7 +44132,7 @@ function writeEditorRunNote(note) {
43982
44132
  }
43983
44133
  function readEditorRunNote(projectPath) {
43984
44134
  for (const directory of runtimeDirectories()) {
43985
- const note = editorNoteAt(editorNotePath(projectPath, directory)) ?? editorNoteAt(join29(directory, "runs", "editor-run.json"));
44135
+ const note = editorNoteAt(editorNotePath(projectPath, directory)) ?? editorNoteAt(join30(directory, "runs", "editor-run.json"));
43986
44136
  if (note !== null && isSameDirectory(note.projectPath, projectPath)) {
43987
44137
  return note;
43988
44138
  }
@@ -43992,7 +44142,7 @@ function readEditorRunNote(projectPath) {
43992
44142
  function editorNoteAt(path) {
43993
44143
  let parsed;
43994
44144
  try {
43995
- parsed = JSON.parse(readFileSync24(path, "utf8"));
44145
+ parsed = JSON.parse(readFileSync25(path, "utf8"));
43996
44146
  } catch {
43997
44147
  return null;
43998
44148
  }
@@ -44016,7 +44166,7 @@ function readRunRecord(projectPath) {
44016
44166
  if (own !== null) {
44017
44167
  return own;
44018
44168
  }
44019
- const shared = recordAt(join29(directory, "runs", SHARED_NOTE));
44169
+ const shared = recordAt(join30(directory, "runs", SHARED_NOTE));
44020
44170
  if (shared !== null && shared.projectPath !== "" && isSameDirectory(shared.projectPath, projectPath)) {
44021
44171
  return shared;
44022
44172
  }
@@ -44028,13 +44178,13 @@ function everyRunRecord() {
44028
44178
  for (const directory of runtimeDirectories()) {
44029
44179
  let entries;
44030
44180
  try {
44031
- entries = readdirSync13(join29(directory, "runs"));
44181
+ entries = readdirSync13(join30(directory, "runs"));
44032
44182
  } catch {
44033
44183
  continue;
44034
44184
  }
44035
44185
  for (const entry of entries) {
44036
44186
  if (entry === SHARED_NOTE || entry.startsWith("note-") && entry.endsWith(".json")) {
44037
- const record = recordAt(join29(directory, "runs", entry));
44187
+ const record = recordAt(join30(directory, "runs", entry));
44038
44188
  if (record !== null) {
44039
44189
  found.push(record);
44040
44190
  }
@@ -44046,7 +44196,7 @@ function everyRunRecord() {
44046
44196
  function recordAt(path) {
44047
44197
  let parsed;
44048
44198
  try {
44049
- parsed = JSON.parse(readFileSync24(path, "utf8"));
44199
+ parsed = JSON.parse(readFileSync25(path, "utf8"));
44050
44200
  } catch {
44051
44201
  return null;
44052
44202
  }
@@ -44099,7 +44249,7 @@ async function runningAs(pid) {
44099
44249
  const began = await startedAt(pid, null);
44100
44250
  const when = began === null ? {} : { startedAt: began };
44101
44251
  if (process.platform === "linux") {
44102
- const raw = readFileSync24(`/proc/${pid}/cmdline`, "utf8").replaceAll("\x00", " ").trim();
44252
+ const raw = readFileSync25(`/proc/${pid}/cmdline`, "utf8").replaceAll("\x00", " ").trim();
44103
44253
  return raw === "" ? null : { kind: "commandLine", text: raw, ...when };
44104
44254
  }
44105
44255
  const args = (await runHidden("ps", ["-p", String(pid), "-o", "args="], { timeout: 15000 })).stdout.trim();
@@ -44242,7 +44392,7 @@ function judgeAnnouncedGame(running, projectPath, announcedAt) {
44242
44392
  }
44243
44393
  function clearRunRecord(projectPath) {
44244
44394
  for (const directory of runtimeDirectories()) {
44245
- const shared = join29(directory, "runs", SHARED_NOTE);
44395
+ const shared = join30(directory, "runs", SHARED_NOTE);
44246
44396
  const sharedRecord = recordAt(shared);
44247
44397
  const ours = sharedRecord !== null && sharedRecord.projectPath !== "" && isSameDirectory(sharedRecord.projectPath, projectPath);
44248
44398
  for (const path of [runRecordPath(projectPath, directory), ...ours ? [shared] : []]) {
@@ -44277,7 +44427,7 @@ function sweepTranscripts(keepMs = 24 * 60 * 60 * 1000, now = Date.now()) {
44277
44427
  if (!transcript && !entry.endsWith(".partial")) {
44278
44428
  continue;
44279
44429
  }
44280
- const path = join29(directory, entry);
44430
+ const path = join30(directory, entry);
44281
44431
  if (kept.has(path) || ageMs(path, now) < keepMs) {
44282
44432
  continue;
44283
44433
  }
@@ -44298,7 +44448,7 @@ var init_run_record = __esm(() => {
44298
44448
  });
44299
44449
 
44300
44450
  // src/script-origins.ts
44301
- import { join as join30 } from "node:path";
44451
+ import { join as join31 } from "node:path";
44302
44452
  function unresolvedClassIn(message) {
44303
44453
  return UNRESOLVED.exec(message)?.[1] ?? null;
44304
44454
  }
@@ -44335,6 +44485,12 @@ async function originsOf(errors, declared, check) {
44335
44485
  }
44336
44486
  return { origins, unconfirmed };
44337
44487
  }
44488
+ function compileErrorsIn(entries) {
44489
+ return entries.flatMap((entry) => {
44490
+ const at = entry.severity === "error" ? reloadedAt(entry) : null;
44491
+ return at === null ? [] : [{ ...at, message: entry.text }];
44492
+ });
44493
+ }
44338
44494
  function reloadedAt(entry) {
44339
44495
  for (const line of entry.detail) {
44340
44496
  const at = RELOADED_AT.exec(line.trim());
@@ -44351,7 +44507,7 @@ function checkWithEngine(godotPath, projectPath) {
44351
44507
  const ran = await runEngine(godotPath, [
44352
44508
  "--headless",
44353
44509
  "--log-file",
44354
- join30(logDir, "engine.log"),
44510
+ join31(logDir, "engine.log"),
44355
44511
  "--path",
44356
44512
  projectPath,
44357
44513
  "--check-only",
@@ -44396,10 +44552,10 @@ var init_script_origins = __esm(() => {
44396
44552
  });
44397
44553
 
44398
44554
  // src/script-profile.ts
44399
- import { readFileSync as readFileSync25 } from "node:fs";
44555
+ import { readFileSync as readFileSync26 } from "node:fs";
44400
44556
  function readProfileFile(path) {
44401
44557
  try {
44402
- const parsed = JSON.parse(readFileSync25(path, "utf8"));
44558
+ const parsed = JSON.parse(readFileSync26(path, "utf8"));
44403
44559
  return typeof parsed === "object" && typeof parsed.connected === "boolean" ? parsed : null;
44404
44560
  } catch {
44405
44561
  return null;
@@ -44427,12 +44583,16 @@ function signatureParts(signature) {
44427
44583
  function profiledFunctions(totals, limit, sourceOf = () => null) {
44428
44584
  const perCallUs = perCallCostOf(totals);
44429
44585
  const rows = rowsOf(totals, sourceOf).map((row) => withOverhead(row, perCallUs));
44430
- const scriptMs = ms(Object.entries(totals.functions).reduce((sum, [signature, one]) => sum + (one.calls > 0 && !isCalibration(signature) ? one.selfSeconds : 0), 0));
44586
+ const called = Object.entries(totals.functions).filter(([signature, one]) => one.calls > 0 && !isCalibration(signature));
44587
+ const addon = called.filter(([signature]) => isRuntimeAddon(signature));
44588
+ const selfOf = (entries) => ms(entries.reduce((sum, [, one]) => sum + one.selfSeconds, 0));
44431
44589
  return {
44432
44590
  functions: rows.slice(0, limit).map((row) => placedLambda(row, sourceOf)),
44433
44591
  omitted: Math.max(0, rows.length - limit),
44434
- scriptMs,
44592
+ scriptMs: selfOf(called.filter(([signature]) => !isRuntimeAddon(signature))),
44435
44593
  counted: rows.length,
44594
+ addonMs: selfOf(addon),
44595
+ addonFunctions: addon.length,
44436
44596
  superCallers: rows.filter((row) => row.selfIncludesSuper === true).length,
44437
44597
  perCallUs,
44438
44598
  overheadDominated: rows.filter((row) => row.overheadMs !== undefined).length,
@@ -44440,6 +44600,9 @@ function profiledFunctions(totals, limit, sourceOf = () => null) {
44440
44600
  overheadReach: rows.findLastIndex((row) => row.overheadMs !== undefined) + 1
44441
44601
  };
44442
44602
  }
44603
+ function isRuntimeAddon(signature) {
44604
+ return signatureParts(signature).script.startsWith(RUNTIME_ADDON2);
44605
+ }
44443
44606
  function isCalibration(signature) {
44444
44607
  const parts = signatureParts(signature);
44445
44608
  return parts.script.endsWith(RUNTIME_SCRIPT) && (parts.function === PROFILER_PROBE || parts.function === PROFILER_CALIBRATOR);
@@ -44449,7 +44612,7 @@ function writeProfileMark(path, mark) {
44449
44612
  }
44450
44613
  function readProfileMark(path) {
44451
44614
  try {
44452
- const parsed = JSON.parse(readFileSync25(path, "utf8"));
44615
+ const parsed = JSON.parse(readFileSync26(path, "utf8"));
44453
44616
  return typeof parsed === "object" && typeof parsed.totals === "object" ? parsed : null;
44454
44617
  } catch {
44455
44618
  return null;
@@ -44515,7 +44678,7 @@ function rowsOf(totals, sourceOf) {
44515
44678
  }
44516
44679
  return codes.get(script) ?? null;
44517
44680
  };
44518
- return Object.entries(totals.functions).filter(([signature, sum]) => sum.calls > 0 && !isCalibration(signature)).map(([signature, sum]) => {
44681
+ return Object.entries(totals.functions).filter(([signature, sum]) => sum.calls > 0 && !isRuntimeAddon(signature) && !isCalibration(signature)).map(([signature, sum]) => {
44519
44682
  const parts = signatureParts(signature);
44520
44683
  const code = codeOfScript(parts.script);
44521
44684
  const property = propertyOf(parts.function);
@@ -44591,7 +44754,7 @@ function callersOf(totals, name, scripts) {
44591
44754
  property
44592
44755
  };
44593
44756
  }
44594
- var QUEUE_SETTING = "limits/debugger/max_queued_messages", QUEUE_DEFAULT = 2048, QUEUE_HEADROOM = 512, UNNAMED_PREFIX = "#", PROFILE_FROM_START = "GDHARNESS_PROFILE_FROM_START", ms = (seconds) => Math.round(seconds * 1e6) / 1000, RUNTIME_SCRIPT = "gdharness_runtime/runtime_autoload.gd", PROFILER_PROBE = "_profiler_probe", PROFILER_CALIBRATOR = "_calibrate_the_profiler", CALL_SITES = 40;
44757
+ var QUEUE_SETTING = "limits/debugger/max_queued_messages", QUEUE_DEFAULT = 2048, QUEUE_HEADROOM = 512, UNNAMED_PREFIX = "#", PROFILE_FROM_START = "GDHARNESS_PROFILE_FROM_START", ms = (seconds) => Math.round(seconds * 1e6) / 1000, RUNTIME_SCRIPT = "gdharness_runtime/runtime_autoload.gd", PROFILER_PROBE = "_profiler_probe", PROFILER_CALIBRATOR = "_calibrate_the_profiler", RUNTIME_ADDON2 = "res://addons/gdharness_runtime/", CALL_SITES = 40;
44595
44758
  var init_script_profile = __esm(() => {
44596
44759
  init_gdscript_source();
44597
44760
  init_godot_variant();
@@ -44622,18 +44785,18 @@ import {
44622
44785
  closeSync as closeSync3,
44623
44786
  constants,
44624
44787
  copyFileSync,
44625
- existsSync as existsSync24,
44788
+ existsSync as existsSync25,
44626
44789
  fstatSync,
44627
44790
  mkdirSync as mkdirSync17,
44628
44791
  openSync as openSync4,
44629
44792
  readdirSync as readdirSync14,
44630
- readFileSync as readFileSync26,
44793
+ readFileSync as readFileSync27,
44631
44794
  readSync as readSync2,
44632
44795
  rmdirSync as rmdirSync2,
44633
44796
  statSync as statSync14,
44634
44797
  writeFileSync as writeFileSync18
44635
44798
  } from "node:fs";
44636
- import { basename as basename10, dirname as dirname23, extname as extname4, isAbsolute as isAbsolute5, join as join31, normalize as normalize2, relative as relative6 } from "node:path";
44799
+ import { basename as basename10, dirname as dirname23, extname as extname4, isAbsolute as isAbsolute5, join as join32, normalize as normalize2, relative as relative6 } from "node:path";
44637
44800
  import { setTimeout as delay5 } from "node:timers/promises";
44638
44801
  import { fileURLToPath as fileURLToPath5 } from "node:url";
44639
44802
  function editorRestartTimeoutMs() {
@@ -44670,15 +44833,15 @@ function writtenFileRefusal(path, rule) {
44670
44833
  return `${named} ${path} does not end in ${rule.extensions.join(" or ")}, which is what ${what} is.`;
44671
44834
  }
44672
44835
  const directory = realPathOr(dirname23(path));
44673
- if (!existsSync24(directory)) {
44836
+ if (!existsSync25(directory)) {
44674
44837
  return `${named}'s directory ${dirname23(path)} does not exist; ${what} creates no directories.`;
44675
44838
  }
44676
- const where = join31(directory, basename10(path));
44839
+ const where = join32(directory, basename10(path));
44677
44840
  const inside = rule.projects.find((project) => project !== "" && isWithinRoot(realPathOr(project), where));
44678
44841
  if (inside !== undefined) {
44679
44842
  return `${named} ${path} is inside the project at ${inside}; ${what} is saved outside it, so it never becomes part of the game.`;
44680
44843
  }
44681
- const taken = rule.writes(where).find((file) => existsSync24(file));
44844
+ const taken = rule.writes(where).find((file) => existsSync25(file));
44682
44845
  if (taken !== undefined) {
44683
44846
  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.`;
44684
44847
  }
@@ -45150,7 +45313,12 @@ function unlistedValues(schema, value, path) {
45150
45313
  const fields = schema;
45151
45314
  const listed = fields["enum"];
45152
45315
  if (Array.isArray(listed) && typeof value === "string") {
45153
- return listed.includes(value) ? [] : [`${path} as one of ${listed.join(", ")}, not ${JSON.stringify(value)}`];
45316
+ if (listed.includes(value)) {
45317
+ return [];
45318
+ }
45319
+ return [
45320
+ listed.length === 1 ? `${path} only as ${String(listed[0])}, not ${JSON.stringify(value)}` : `${path} as one of ${listed.join(", ")}, not ${JSON.stringify(value)}`
45321
+ ];
45154
45322
  }
45155
45323
  if (Array.isArray(value)) {
45156
45324
  return value.flatMap((element, index) => unlistedValues(fields["items"], element, `${path}[${index}]`));
@@ -45214,7 +45382,7 @@ function notRunNote(notRun, failFast) {
45214
45382
  function exportPresetRefusal(projectPath, preset) {
45215
45383
  let text;
45216
45384
  try {
45217
- text = readFileSync26(join31(projectPath, "export_presets.cfg"), "utf8");
45385
+ text = readFileSync27(join32(projectPath, "export_presets.cfg"), "utf8");
45218
45386
  } catch {
45219
45387
  return `This project has no export_presets.cfg, so it has no preset '${preset}' to export with. project_export add_preset writes one, as the editor's Project > Export does.`;
45220
45388
  }
@@ -45280,7 +45448,7 @@ function filesIn(folder) {
45280
45448
  try {
45281
45449
  for (const entry of readdirSync14(folder, { withFileTypes: true })) {
45282
45450
  if (entry.isFile()) {
45283
- found.set(entry.name, statSync14(join31(folder, entry.name)).mtimeMs);
45451
+ found.set(entry.name, statSync14(join32(folder, entry.name)).mtimeMs);
45284
45452
  }
45285
45453
  }
45286
45454
  } catch {}
@@ -45310,7 +45478,7 @@ function unsavedInEditorNote(scripts) {
45310
45478
  return `${named} ${one ? "is" : "are"} open in the editor's script ${one ? "tab" : "tabs"} with unsaved edits, and saving ${one ? "that tab" : "them"} there writes ${one ? "its" : "their"} text over what was written here: close ${one ? "it" : "them"} without saving, or save first and edit again.`;
45311
45479
  }
45312
45480
  function editorAddonMissing(projectPath) {
45313
- if (!existsSync24(join31(projectPath, "project.godot"))) {
45481
+ if (!existsSync25(join32(projectPath, "project.godot"))) {
45314
45482
  return null;
45315
45483
  }
45316
45484
  const report = inspectProject(projectPath);
@@ -45476,8 +45644,34 @@ function scriptErrorsNote(errors, {
45476
45644
  const opening = unnamed === 0 ? `No tests ran: gdUnit4 could not load ${scripts === 1 ? "one script" : `${scripts} scripts`} while looking for suites, and it runs no suite at all when one fails to load.` : `No tests ran: ${scripts + unnamed} scripts did not compile, and gdUnit4 runs no suite at all when one fails to load. It could not load ${scripts === 1 ? "one" : scripts} of them while looking for suites, and ${unnamed === 1 ? "the one it did not name comes" : `the ${unnamed} it did not name come`} first.`;
45477
45645
  return [opening, named, following, rest, after].filter((part) => part !== "").join(" ");
45478
45646
  }
45479
- function timedOutVerdict(timeoutMs, silentForMs) {
45647
+ function autoloadNotCompiledNote(at, errors, stoppedEarly) {
45648
+ const first = errors.scriptErrors[0];
45649
+ const where = first === undefined ? "" : `, and the first error the engine raised compiling was at ${first.path}${first.line === null ? "" : `:${first.line}`}: ${first.message}`;
45650
+ const others = errors.scriptErrors.length - 1;
45651
+ const more = others > 0 ? ` ${others === 1 ? "One more error is" : `${others} more errors are`} under scriptErrors.` : "";
45652
+ const through = errors.failedThroughDependency.length;
45653
+ const dependents = through > 0 ? ` ${through === 1 ? "One script" : `${through} scripts`} failed only through ${first === undefined ? "it" : "that"}, listed under failedThroughDependency.` : "";
45654
+ const ended = stoppedEarly ? "The run was stopped as soon as the engine said so rather than left to its timeout" : "The run ended before that was read, and what its suites said is set aside";
45655
+ const stop = /[.!?]\)?$/.test(where) ? "" : ".";
45656
+ return `No result counts: the autoload ${at.name} (${at.path}) did not compile${where}${stop}${more}${dependents} Every suite runs with the autoloads, so a case calling into a script that did not compile fails the same way on every pass, and one waiting in a loop for the call to succeed never ends. ${ended}. script_diagnostics checks a script without running anything.`;
45657
+ }
45658
+ function repeatingScriptError(entries, byScript) {
45659
+ const last = entries.at(-1);
45660
+ if (last === undefined || !byScript(last)) {
45661
+ return null;
45662
+ }
45663
+ const where = last.detail[0] ?? null;
45664
+ const times = entries.filter((entry) => entry.text === last.text && (entry.detail[0] ?? null) === where && byScript(entry)).length;
45665
+ return times >= LOOPING_AFTER ? { text: last.text, where: where?.replace(/^at: /, "") ?? null, times } : null;
45666
+ }
45667
+ function timedOutVerdict(timeoutMs, silentForMs, repeating = null) {
45480
45668
  const quiet = Math.round(silentForMs);
45669
+ if (repeating !== null) {
45670
+ return {
45671
+ verdict: `timed out after ${timeoutMs} ms repeating one script error, printed ${repeating.times} times: ${repeating.text}${repeating.where === null ? "" : ` (${repeating.where})`}. A line that fails on every pass of a loop does this, and a longer timeoutMs would not end it`,
45672
+ hung: false
45673
+ };
45674
+ }
45481
45675
  return quiet >= Math.min(STALLED_AFTER_MS, timeoutMs / 2) ? {
45482
45676
  verdict: `hung: killed after ${timeoutMs} ms, having printed nothing for the last ${quiet} ms`,
45483
45677
  hung: true
@@ -45548,7 +45742,7 @@ function profileFrameFunctions(projectPath) {
45548
45742
  let projectGodot = null;
45549
45743
  if (projectPath !== null) {
45550
45744
  try {
45551
- projectGodot = readFileSync26(join31(projectPath, "project.godot"), "utf8");
45745
+ projectGodot = readFileSync27(join32(projectPath, "project.godot"), "utf8");
45552
45746
  } catch {}
45553
45747
  }
45554
45748
  return frameFunctionsFor(queuedMessagesOf(projectGodot));
@@ -45572,7 +45766,7 @@ function projectSourceOf(projectPath) {
45572
45766
  let source = null;
45573
45767
  if (inside.ok) {
45574
45768
  try {
45575
- source = readFileSync26(inside.absolutePath, "utf8");
45769
+ source = readFileSync27(inside.absolutePath, "utf8");
45576
45770
  } catch {}
45577
45771
  }
45578
45772
  read.set(script, source);
@@ -45623,6 +45817,9 @@ function profileAnswer(reading, limit) {
45623
45817
  if (reading.coveredMs > 0 && table.scriptMs > reading.coveredMs) {
45624
45818
  notes.push(`scriptMs, ${table.scriptMs}ms, is more than the ${reading.coveredMs}ms the profile covers.${table.superCallers > 0 ? " The super calls above, counted twice, can account for some or all of that; whatever they do not is" : " One thread cannot spend more time than passes, so that is"} functions running on several threads at once, through WorkerThreadPool or Thread. The engine adds every thread's time to the same function and does not say which thread a call ran on, so a function's selfMs is summed over every thread it ran on rather than taken out of frames, and a function that waits for the others, through wait_for_group_task_completion or wait_to_finish, has the wait in its own selfMs as well.`);
45625
45819
  }
45820
+ if (table.addonFunctions > 0) {
45821
+ notes.push(`The gdharness runtime addon's own functions took ${table.addonMs}ms of self time over ${table.addonFunctions === 1 ? "one function" : `${table.addonFunctions} functions`}, answering tools such as runtime_invoke and sending the profile. They are left out of functions, functionsCounted and scriptMs and summed under runtimeAddonMs, since they are not the game's work, though that time was spent in the game's frames.`);
45822
+ }
45626
45823
  notes.push(...cappedNotes(totals));
45627
45824
  if (totals.unreadable > 0) {
45628
45825
  notes.push(`${totals.unreadable} message${totals.unreadable === 1 ? "" : "s"} from the game could not be read, so the totals have that many gaps.`);
@@ -45637,6 +45834,8 @@ function profileAnswer(reading, limit) {
45637
45834
  scriptMs: table.scriptMs,
45638
45835
  perCallOverheadUs: table.perCallUs ?? undefined,
45639
45836
  functionsCounted: table.counted,
45837
+ runtimeAddonMs: table.addonFunctions > 0 ? table.addonMs : undefined,
45838
+ runtimeAddonFunctions: table.addonFunctions > 0 ? table.addonFunctions : undefined,
45640
45839
  omitted: table.omitted > 0 ? table.omitted : undefined,
45641
45840
  functions: table.functions,
45642
45841
  callers: reading.callersOf === undefined ? undefined : callersAnswer(totals, reading.callersOf),
@@ -45722,7 +45921,7 @@ function foldedEntriesAnswer(log, floor = "warning") {
45722
45921
  }
45723
45922
  function gdUnitFailsOnScriptErrors(projectPath) {
45724
45923
  try {
45725
- return settingKeys(readFileSync26(join31(projectPath, "project.godot"), "utf8")).get("gdunit4/report/godot/script_error") !== false;
45924
+ return settingKeys(readFileSync27(join32(projectPath, "project.godot"), "utf8")).get("gdunit4/report/godot/script_error") !== false;
45726
45925
  } catch {
45727
45926
  return true;
45728
45927
  }
@@ -45737,7 +45936,7 @@ function uncaughtNote(uncaught) {
45737
45936
  }
45738
45937
  function elsewhereIn(projectPath, asked) {
45739
45938
  const wanted = asked.replace(/^res:\/\//, "").replace(/\/+$/, "");
45740
- if (wanted === "" || existsSync24(join31(projectPath, wanted))) {
45939
+ if (wanted === "" || existsSync25(join32(projectPath, wanted))) {
45741
45940
  return "";
45742
45941
  }
45743
45942
  const root = (wanted.split("/")[0] ?? "").toLowerCase();
@@ -45840,7 +46039,7 @@ function isDirectory(path) {
45840
46039
  }
45841
46040
  }
45842
46041
  function scriptsUnder(directory, prefix) {
45843
- if (existsSync24(join31(directory, ".gdignore"))) {
46042
+ if (existsSync25(join32(directory, ".gdignore"))) {
45844
46043
  return [];
45845
46044
  }
45846
46045
  const found = [];
@@ -45850,7 +46049,7 @@ function scriptsUnder(directory, prefix) {
45850
46049
  }
45851
46050
  const path = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
45852
46051
  if (entry.isDirectory()) {
45853
- found.push(...scriptsUnder(join31(directory, entry.name), path));
46052
+ found.push(...scriptsUnder(join32(directory, entry.name), path));
45854
46053
  } else if (entry.isFile() && entry.name.endsWith(".gd")) {
45855
46054
  found.push(path);
45856
46055
  }
@@ -45858,7 +46057,7 @@ function scriptsUnder(directory, prefix) {
45858
46057
  return found;
45859
46058
  }
45860
46059
  function hasMainScene(projectFile) {
45861
- const scene = parseProjectGodot(readFileSync26(projectFile, "utf8"))["application"]?.["run/main_scene"];
46060
+ const scene = parseProjectGodot(readFileSync27(projectFile, "utf8"))["application"]?.["run/main_scene"];
45862
46061
  return typeof scene === "string" && scene !== "";
45863
46062
  }
45864
46063
  function runtimeAddonAnnouncesEditors(projectPath) {
@@ -45866,13 +46065,13 @@ function runtimeAddonAnnouncesEditors(projectPath) {
45866
46065
  return false;
45867
46066
  }
45868
46067
  try {
45869
- return readFileSync26(join31(projectPath, RUNTIME_AUTOLOAD.path), "utf8").includes('"editor_pid"');
46068
+ return readFileSync27(join32(projectPath, RUNTIME_AUTOLOAD.path), "utf8").includes('"editor_pid"');
45870
46069
  } catch {
45871
46070
  return false;
45872
46071
  }
45873
46072
  }
45874
46073
  function editorPlaysHeadless(projectFile) {
45875
- const runArgs = parseProjectGodot(readFileSync26(projectFile, "utf8"))["editor"]?.["run/main_run_args"];
46074
+ const runArgs = parseProjectGodot(readFileSync27(projectFile, "utf8"))["editor"]?.["run/main_run_args"];
45876
46075
  return typeof runArgs === "string" && /(?:^|\s)--headless(?:\s|$)/.test(runArgs);
45877
46076
  }
45878
46077
  function camelCased(params) {
@@ -45891,7 +46090,7 @@ function needsItsOwnProcess(asked) {
45891
46090
  class GodotServer {
45892
46091
  stdio = null;
45893
46092
  locator = new GodotLocator;
45894
- operationsScript = join31(__dirname2, "godot", "operations", "godot_operations.gd");
46093
+ operationsScript = join32(__dirname2, "godot", "operations", "godot_operations.gd");
45895
46094
  godotBridge;
45896
46095
  tools = buildToolDefinitions();
45897
46096
  activeProcess = null;
@@ -46152,14 +46351,14 @@ class GodotServer {
46152
46351
  }
46153
46352
  noteTheStart() {
46154
46353
  const cwd = process.cwd();
46155
- const mcpJson = join31(cwd, ".mcp.json");
46354
+ const mcpJson = join32(cwd, ".mcp.json");
46156
46355
  const announced = this.ownProject === null ? null : readAnnouncement(announcementPath(this.ownProject));
46157
46356
  const predecessor = announced !== null && announced.pid !== process.pid && alive(announced.pid) ? announced.pid : null;
46158
46357
  writeLifecycle("started", {
46159
46358
  argv: process.argv.slice(1),
46160
46359
  cwd,
46161
46360
  project: this.ownProject,
46162
- mcpJson: existsSync24(mcpJson) ? mcpJson : null
46361
+ mcpJson: existsSync25(mcpJson) ? mcpJson : null
46163
46362
  });
46164
46363
  if (predecessor !== null) {
46165
46364
  writeLifecycle("replaced", { project: this.ownProject, predecessor });
@@ -46300,7 +46499,7 @@ class GodotServer {
46300
46499
  return checked.response;
46301
46500
  }
46302
46501
  const named = args["projectPath"];
46303
- if (typeof named === "string" && existsSync24(join31(named, "project.godot"))) {
46502
+ if (typeof named === "string" && existsSync25(join32(named, "project.godot"))) {
46304
46503
  this.lastProjectPath = named;
46305
46504
  }
46306
46505
  this.updates.refresh();
@@ -46336,7 +46535,7 @@ class GodotServer {
46336
46535
  let notSaved;
46337
46536
  try {
46338
46537
  savedAt = answer.content.map((item, index) => {
46339
- const file = join31(directory, item.type === "text" ? `${stem}-${index}.txt` : `${stem}-${index}.${item.mimeType.split("/")[1] ?? "bin"}`);
46538
+ const file = join32(directory, item.type === "text" ? `${stem}-${index}.txt` : `${stem}-${index}.${item.mimeType.split("/")[1] ?? "bin"}`);
46340
46539
  writeFileSync18(file, item.type === "text" ? item.text : Buffer.from(item.data, "base64"));
46341
46540
  return file;
46342
46541
  });
@@ -46954,8 +47153,8 @@ class GodotServer {
46954
47153
  if (path === undefined) {
46955
47154
  return { ok: false, response: this.createErrorResponse("projectPath is required.") };
46956
47155
  }
46957
- const file = join31(path, "project.godot");
46958
- if (!existsSync24(file)) {
47156
+ const file = join32(path, "project.godot");
47157
+ if (!existsSync25(file)) {
46959
47158
  return {
46960
47159
  ok: false,
46961
47160
  response: this.createErrorResponse(`Not a Godot project: ${path}`, [
@@ -47615,7 +47814,7 @@ class GodotServer {
47615
47814
  if (!engine.ok) {
47616
47815
  return engine.response;
47617
47816
  }
47618
- const application = parseProjectGodot(readFileSync26(project.value.file, "utf8"))["application"] ?? {};
47817
+ const application = parseProjectGodot(readFileSync27(project.value.file, "utf8"))["application"] ?? {};
47619
47818
  const name = application["config/name"];
47620
47819
  const mainScene = application["run/main_scene"];
47621
47820
  const info = {
@@ -47713,7 +47912,7 @@ class GodotServer {
47713
47912
  const exportArgs = [
47714
47913
  "--headless",
47715
47914
  "--log-file",
47716
- join31(exportLogs, "engine.log"),
47915
+ join32(exportLogs, "engine.log"),
47717
47916
  "--path",
47718
47917
  project.value.path,
47719
47918
  debug ? "--export-debug" : "--export-release",
@@ -47721,7 +47920,7 @@ class GodotServer {
47721
47920
  output.absolutePath
47722
47921
  ];
47723
47922
  this.logDebug(`Exporting: ${engine.value} ${exportArgs.join(" ")}`);
47724
- const writtenAt = () => existsSync24(output.absolutePath) ? statSync14(output.absolutePath).mtimeMs : null;
47923
+ const writtenAt = () => existsSync25(output.absolutePath) ? statSync14(output.absolutePath).mtimeMs : null;
47725
47924
  const caches = editorCacheDirectory(engine.value);
47726
47925
  const exportOnce = async () => {
47727
47926
  const before = writtenAt();
@@ -47804,10 +48003,10 @@ class GodotServer {
47804
48003
  if (movie !== null) {
47805
48004
  return this.createErrorResponse(movie);
47806
48005
  }
47807
- const missing = paths.value.filter((path) => !existsSync24(join31(project.value.path, path.slice("res://".length))));
48006
+ const missing = paths.value.filter((path) => !existsSync25(join32(project.value.path, path.slice("res://".length))));
47808
48007
  const asked = paths.value.filter((path) => !missing.includes(path));
47809
48008
  const runner = "addons/gdUnit4/bin/GdUnitCmdTool.gd";
47810
- const noRunner = !existsSync24(join31(project.value.path, runner));
48009
+ const noRunner = !existsSync25(join32(project.value.path, runner));
47811
48010
  if (asked.length === 0) {
47812
48011
  const verdict = `nothing at ${missing.join(" and ")}`;
47813
48012
  const andNoRunner = noRunner ? `, and gdUnit4 is not installed in this project: no ${runner}` : "";
@@ -47877,35 +48076,50 @@ class GodotServer {
47877
48076
  lastSaid = Date.now();
47878
48077
  });
47879
48078
  const reporting = this.reportTestProgress(run.log);
48079
+ const autoloaded = scriptsAutoloaded(project.value.path);
47880
48080
  let silentForMs = 0;
48081
+ const stopped = { for: null };
47881
48082
  const timedOut = await new Promise((resolve) => {
48083
+ let read = 0;
48084
+ const watching = setInterval(() => {
48085
+ const entries = run.log.all;
48086
+ for (const entry of entries.slice(read)) {
48087
+ stopped.for ??= autoloadNotCompiled(entry, autoloaded);
48088
+ }
48089
+ read = entries.length;
48090
+ if (stopped.for !== null) {
48091
+ clearInterval(watching);
48092
+ run.process.kill();
48093
+ }
48094
+ }, 100);
47882
48095
  const timer = setTimeout(() => {
48096
+ clearInterval(watching);
47883
48097
  silentForMs = Date.now() - lastSaid;
47884
48098
  run.process.kill();
47885
48099
  resolve(true);
47886
48100
  }, timeoutMs);
47887
- run.process.once("exit", () => {
47888
- clearTimeout(timer);
47889
- resolve(false);
47890
- });
47891
- run.process.once("error", () => {
48101
+ const ended = () => {
48102
+ clearInterval(watching);
47892
48103
  clearTimeout(timer);
47893
48104
  resolve(false);
47894
- });
48105
+ };
48106
+ run.process.once("exit", ended);
48107
+ run.process.once("error", ended);
47895
48108
  });
47896
48109
  clearInterval(reporting);
47897
- if (timedOut && run.process.pid !== undefined) {
48110
+ if ((timedOut || stopped.for !== null) && run.process.pid !== undefined) {
47898
48111
  await untilGone([run.process.pid]);
47899
48112
  }
47900
48113
  await run.settled;
47901
- const reportsDir = join31(project.value.path, ".godot", ...ours.split("/"));
48114
+ const notCompiled = stopped.for ?? run.log.all.map((entry) => autoloadNotCompiled(entry, autoloaded)).find((found) => found !== null) ?? null;
48115
+ const reportsDir = join32(project.value.path, ".godot", ...ours.split("/"));
47902
48116
  let report = null;
47903
48117
  let reportProblem = null;
47904
48118
  try {
47905
- const written = existsSync24(reportsDir) ? readdirSync14(reportsDir).filter((name) => name.startsWith("report_")).sort((a, b) => Number(a.slice("report_".length)) - Number(b.slice("report_".length))) : [];
48119
+ const written = existsSync25(reportsDir) && notCompiled === null ? readdirSync14(reportsDir).filter((name) => name.startsWith("report_")).sort((a, b) => Number(a.slice("report_".length)) - Number(b.slice("report_".length))) : [];
47906
48120
  const newest = written.at(-1);
47907
48121
  if (newest !== undefined) {
47908
- report = parseJUnit(readFileSync26(join31(reportsDir, newest, "results.xml"), "utf8"));
48122
+ report = parseJUnit(readFileSync27(join32(reportsDir, newest, "results.xml"), "utf8"));
47909
48123
  }
47910
48124
  } catch (error) {
47911
48125
  reportProblem = errorMessage(error);
@@ -47937,14 +48151,14 @@ class GodotServer {
47937
48151
  limit: Number.POSITIVE_INFINITY
47938
48152
  }).entries;
47939
48153
  const said = printed.map((entry) => entry.text);
47940
- const nothingRan = timedOut ? null : whyNoReport(said, asked.join(" and "));
47941
- const cutShort = timedOut ? timedOutVerdict(timeoutMs, silentForMs) : null;
48154
+ const nothingRan = timedOut || notCompiled !== null ? null : whyNoReport(said, asked.join(" and "));
48155
+ const cutShort = timedOut && notCompiled === null ? timedOutVerdict(timeoutMs, silentForMs, repeatingScriptError(run.log.all, (entry) => run.log.raisedByScript(entry))) : null;
47942
48156
  const hung = cutShort?.hung ?? false;
47943
- const printedErrors = timedOut || exitCode !== 105 ? [] : scriptErrorsPrinted(said);
48157
+ const printedErrors = notCompiled !== null ? compileErrorsIn(run.log.all) : timedOut || exitCode !== 105 ? [] : scriptErrorsPrinted(said);
47944
48158
  const declaring = printedErrors.some((error) => unresolvedClassIn(error.message) !== null) ? await originsOf(printedErrors, declaredClasses(project.value.path), checkWithEngine(engine.value, project.value.path)) : { origins: [], unconfirmed: [] };
47945
48159
  const scriptErrors = [...declaring.origins, ...printedErrors];
47946
48160
  const brokenScripts = new Set(scriptErrors.map((error) => error.path)).size;
47947
- const ranAs = cutShort !== null ? cutShort.verdict : nothingRan ?? (brokenScripts > 0 ? `script errors in ${brokenScripts === 1 ? "one script" : `${brokenScripts} scripts`}, so no suite ran` : undefined) ?? (uncaught.length > 0 ? `${uncaught.length === 1 ? "one script error" : `${uncaught.length} script errors`} that failed no case` : undefined) ?? (exitCode === null ? undefined : verdicts[exitCode]) ?? howItExited(run);
48161
+ const ranAs = notCompiled !== null ? `the autoload ${notCompiled.name} (${notCompiled.path}) did not compile, so no result counts` : cutShort !== null ? cutShort.verdict : nothingRan ?? (brokenScripts > 0 ? `script errors in ${brokenScripts === 1 ? "one script" : `${brokenScripts} scripts`}, so no suite ran` : undefined) ?? (uncaught.length > 0 ? `${uncaught.length === 1 ? "one script error" : `${uncaught.length} script errors`} that failed no case` : undefined) ?? (exitCode === null ? undefined : verdicts[exitCode]) ?? howItExited(run);
47948
48162
  const nothingAt = missing.join(" and ");
47949
48163
  const verdict = missing.length === 0 ? ranAs : `${ranAs}; nothing at ${nothingAt}`;
47950
48164
  const missingNote = missing.length === 0 ? undefined : `Nothing at ${nothingAt}, so ${missing.length === 1 ? "it was" : "they were"} left out and only ${asked.join(", ")} ran; a run of fewer paths than were asked for is not called a pass.${elsewhereIn(project.value.path, missing[0] ?? "")}`;
@@ -47972,7 +48186,7 @@ class GodotServer {
47972
48186
  }),
47973
48187
  ...running === null ? {} : { suiteRunning: running }
47974
48188
  };
47975
- const said = nothingRan !== null ? `No tests ran: ${ranAs}.` : scriptErrors.length > 0 ? scriptErrorsNote(printedErrors, declaring) : reportProblem !== null ? `The test run's report could not be read (${reportProblem}); the engine's own verdict was ${ranAs}.` : `The test run wrote no report (${ranAs}).${finishedBeforeTheEnd(finished.length, failing.length, running)}`;
48189
+ const said = notCompiled !== null ? autoloadNotCompiledNote(notCompiled, foldedScriptErrors(scriptErrors), stopped.for !== null) : nothingRan !== null ? `No tests ran: ${ranAs}.` : scriptErrors.length > 0 ? scriptErrorsNote(printedErrors, declaring) : reportProblem !== null ? `The test run's report could not be read (${reportProblem}); the engine's own verdict was ${ranAs}.` : `The test run wrote no report (${ranAs}).${finishedBeforeTheEnd(finished.length, failing.length, running)}`;
47976
48190
  const note = missingNote === undefined ? said : `${said} ${missingNote}`;
47977
48191
  if (callSignal()?.aborted === true) {
47978
48192
  keepCutTestRun(project.value.path, { cutAt: new Date().toISOString(), paths: asked, ...atTheEnd });
@@ -48271,11 +48485,11 @@ class GodotServer {
48271
48485
  const cached = cachedClasses(projectPath);
48272
48486
  const sourceOf = (resourcePath) => {
48273
48487
  const contained = resolveWithinProject(projectPath, resourcePath);
48274
- if (!contained.ok || !existsSync24(contained.absolutePath)) {
48488
+ if (!contained.ok || !existsSync25(contained.absolutePath)) {
48275
48489
  return null;
48276
48490
  }
48277
48491
  try {
48278
- return readFileSync26(contained.absolutePath, "utf8");
48492
+ return readFileSync27(contained.absolutePath, "utf8");
48279
48493
  } catch {
48280
48494
  return null;
48281
48495
  }
@@ -48365,11 +48579,11 @@ class GodotServer {
48365
48579
  }
48366
48580
  sourceOfClass(projectPath, resourcePath) {
48367
48581
  const contained = resolveWithinProject(projectPath, resourcePath);
48368
- if (!contained.ok || !existsSync24(contained.absolutePath)) {
48582
+ if (!contained.ok || !existsSync25(contained.absolutePath)) {
48369
48583
  return null;
48370
48584
  }
48371
48585
  try {
48372
- return readFileSync26(contained.absolutePath, "utf8");
48586
+ return readFileSync27(contained.absolutePath, "utf8");
48373
48587
  } catch {
48374
48588
  return null;
48375
48589
  }
@@ -48527,7 +48741,7 @@ class GodotServer {
48527
48741
  ...Array.isArray(stack) ? {
48528
48742
  stack: stack.map((frame) => ({
48529
48743
  ...frame,
48530
- ...projectPath !== "" && typeof frame["script"] === "string" && frame["script"].startsWith("res://") ? { path: join31(projectPath, frame["script"].slice("res://".length)) } : {}
48744
+ ...projectPath !== "" && typeof frame["script"] === "string" && frame["script"].startsWith("res://") ? { path: join32(projectPath, frame["script"].slice("res://".length)) } : {}
48531
48745
  }))
48532
48746
  } : {}
48533
48747
  });
@@ -48806,7 +49020,7 @@ class GodotServer {
48806
49020
  return null;
48807
49021
  }
48808
49022
  try {
48809
- return settingKeys(readFileSync26(join31(projectPath, "project.godot"), "utf8"));
49023
+ return settingKeys(readFileSync27(join32(projectPath, "project.godot"), "utf8"));
48810
49024
  } catch {
48811
49025
  return null;
48812
49026
  }
@@ -49243,7 +49457,7 @@ class GodotServer {
49243
49457
  }
49244
49458
  async runtimeUp(projectPath, before, budget, withArgs) {
49245
49459
  const budgetMs = budget.ms;
49246
- if (!existsSync24(join31(projectPath, RUNTIME_AUTOLOAD.path))) {
49460
+ if (!existsSync25(join32(projectPath, RUNTIME_AUTOLOAD.path))) {
49247
49461
  return runtimeVerdict(null, {
49248
49462
  addon: false,
49249
49463
  budgetMs,
@@ -49523,7 +49737,7 @@ class GodotServer {
49523
49737
  return 0;
49524
49738
  }
49525
49739
  try {
49526
- return reportAlreadyInTranscript(readFileSync26(game.transcript), readFileSync26(report));
49740
+ return reportAlreadyInTranscript(readFileSync27(game.transcript), readFileSync27(report));
49527
49741
  } catch (error) {
49528
49742
  this.logDebug(`Could not compare a picked-up run's report with its transcript: ${errorMessage(error)}`);
49529
49743
  return 0;
@@ -50225,7 +50439,7 @@ class GodotServer {
50225
50439
  };
50226
50440
  const going = await this.runStillGoing(run);
50227
50441
  const projectPath = run.projectPath;
50228
- const projectGone = projectPath !== null && !existsSync24(join31(projectPath, "project.godot"));
50442
+ const projectGone = projectPath !== null && !existsSync25(join32(projectPath, "project.godot"));
50229
50443
  if (projectGone && callersName !== undefined) {
50230
50444
  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"]);
50231
50445
  }
@@ -50572,7 +50786,7 @@ class GodotServer {
50572
50786
  notes.push(endedWithoutACode({
50573
50787
  throughEditor: run.throughEditor,
50574
50788
  announced: announced !== undefined || run.announcedPid !== undefined,
50575
- couldAnnounce: run.projectPath !== null && existsSync24(join31(run.projectPath, RUNTIME_AUTOLOAD.path)),
50789
+ couldAnnounce: run.projectPath !== null && existsSync25(join32(run.projectPath, RUNTIME_AUTOLOAD.path)),
50576
50790
  endedHere: typeof run.endedHere === "string",
50577
50791
  pickedUp: run.pickedUp === true
50578
50792
  }));
@@ -51202,7 +51416,7 @@ class GodotServer {
51202
51416
  if (!scriptPath.ok) {
51203
51417
  return this.createErrorResponse(scriptPath.reason);
51204
51418
  }
51205
- if (!existsSync24(scriptPath.absolutePath)) {
51419
+ if (!existsSync25(scriptPath.absolutePath)) {
51206
51420
  return this.createErrorResponse(`Script file does not exist: ${scriptPath.absolutePath}`);
51207
51421
  }
51208
51422
  const asked = readNonEmptyString(args, "newScriptPath");
@@ -51616,7 +51830,7 @@ class GodotServer {
51616
51830
  }
51617
51831
  const escaped = symbol.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
51618
51832
  const declared = new RegExp(`^\\s*(?:@\\w+(?:\\([^)]*\\))?\\s+)*class_name\\s+${escaped}\\b`, "m");
51619
- if (declared.test(readFileSync26(scriptPath, "utf8"))) {
51833
+ if (declared.test(readFileSync27(scriptPath, "utf8"))) {
51620
51834
  return null;
51621
51835
  }
51622
51836
  const checked = await this.classesTheEditorCannotSee(args);
@@ -51784,7 +51998,7 @@ class GodotServer {
51784
51998
  async ownRunOnItsWay(projectPath, budgetMs) {
51785
51999
  const run = await this.currentRun();
51786
52000
  const project = projectPath ?? run?.projectPath ?? null;
51787
- if (run === null || project === null || run.announcedPid !== undefined || run.projectPath !== null && !isSameDirectory(run.projectPath, project) || !existsSync24(join31(project, RUNTIME_AUTOLOAD.path)) || !await this.runStillGoing(run)) {
52001
+ if (run === null || project === null || run.announcedPid !== undefined || run.projectPath !== null && !isSameDirectory(run.projectPath, project) || !existsSync25(join32(project, RUNTIME_AUTOLOAD.path)) || !await this.runStillGoing(run)) {
51788
52002
  return null;
51789
52003
  }
51790
52004
  const already = runtimesAnnounced().running;
@@ -51874,7 +52088,7 @@ class GodotServer {
51874
52088
  }
51875
52089
  }
51876
52090
  const screenshotDir = expectsScreenshot ? scratchDirectory("runtime-screenshot") : null;
51877
- const screenshotPath = screenshotDir ? join31(screenshotDir, "capture.png") : null;
52091
+ const screenshotPath = screenshotDir ? join32(screenshotDir, "capture.png") : null;
51878
52092
  const staleNote = this.runtimeStaleNote(choice.endpoint);
51879
52093
  const stale = staleNote === undefined ? {} : { addonIsStale: true, staleNote };
51880
52094
  const refused = (message) => this.createErrorResponse(staleNote === undefined ? message : `${message}
@@ -51893,8 +52107,10 @@ ${staleNote}`);
51893
52107
  }
51894
52108
  this.callsOnGames.add(onGame);
51895
52109
  const alongside = () => onGame.alongside.size === 0 ? {} : { ranAlongside: [...onGame.alongside], alongsideNote: ALONGSIDE_NOTE };
52110
+ let savedLater = false;
51896
52111
  try {
51897
- const reply = await runtimeRequest(choice.endpoint, command, screenshotPath ? { ...params, output_path: screenshotPath } : params, timeoutMs, !expectsScreenshot);
52112
+ const reply = await runtimeRequest(choice.endpoint, command, screenshotPath ? { ...params, output_path: screenshotPath } : params, timeoutMs, expectsScreenshot ? () => discard(screenshotDir) : "keep");
52113
+ savedLater = !reply.ok && reply.stillWithTheGame === true;
51898
52114
  if (!reply.ok && reply.requestId !== undefined) {
51899
52115
  return this.jsonTextResponse({
51900
52116
  pending: true,
@@ -51940,12 +52156,14 @@ Ran alongside ${ranWith.join(", ")}. ${ALONGSIDE_NOTE}`}${staleNote === undefine
51940
52156
 
51941
52157
  ${staleNote}`}`
51942
52158
  },
51943
- { type: "image", data: readFileSync26(screenshotPath).toString("base64"), mimeType: "image/png" }
52159
+ { type: "image", data: readFileSync27(screenshotPath).toString("base64"), mimeType: "image/png" }
51944
52160
  ]
51945
52161
  };
51946
52162
  } finally {
51947
52163
  this.callsOnGames.delete(onGame);
51948
- discard(screenshotDir);
52164
+ if (!savedLater) {
52165
+ discard(screenshotDir);
52166
+ }
51949
52167
  }
51950
52168
  }
51951
52169
  handleLateAnswer(args) {
@@ -52055,12 +52273,13 @@ async function runGodotServer() {
52055
52273
  const server = new GodotServer;
52056
52274
  await server.run();
52057
52275
  }
52058
- var UPDATE_NOTICE_EVERY = 500, FEEDBACK_NOTICE_EVERY = 250, FAILED_BUDGET = 1e6, ANSWER_BUDGET = 4000000, __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, CLOSE_ASK_MS = 3000, 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, 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, OUTPUT_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;
52276
+ var UPDATE_NOTICE_EVERY = 500, FEEDBACK_NOTICE_EVERY = 250, FAILED_BUDGET = 1e6, ANSWER_BUDGET = 4000000, __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, CLOSE_ASK_MS = 3000, 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, OPS_ELSEWHERE, AUDIO_BUS_OPS, STALLED_AFTER_MS = 30000, TEST_HEARTBEAT_MS = 20000, DEPENDED_FAILED = "Failed to compile depended scripts", LOOPING_AFTER = 100, WAIT_FOR_RUN_MS = 600000, RUN_POLL_MS = 250, PATH_SOLUTIONS, OUTPUT_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;
52059
52277
  var init_server = __esm(() => {
52060
52278
  init_dist();
52061
52279
  init_stdio();
52062
52280
  init_alive();
52063
52281
  init_answer_json();
52282
+ init_autoload_failure();
52064
52283
  init_boot_note();
52065
52284
  init_breakpoint_note();
52066
52285
  init_bridge_announce();
@@ -52174,8 +52393,8 @@ var init_server = __esm(() => {
52174
52393
  init_bridge_announce();
52175
52394
  init_errors();
52176
52395
  init_godot_path();
52177
- import { existsSync as existsSync25 } from "node:fs";
52178
- import { join as join32, resolve as resolve8 } from "node:path";
52396
+ import { existsSync as existsSync26 } from "node:fs";
52397
+ import { join as join33, resolve as resolve8 } from "node:path";
52179
52398
 
52180
52399
  // src/harnesses.ts
52181
52400
  import { existsSync as existsSync4, mkdirSync as mkdirSync2, readdirSync as readdirSync2, readFileSync as readFileSync2, rmSync as rmSync2, statSync as statSync3, writeFileSync as writeFileSync2 } from "node:fs";
@@ -53459,7 +53678,7 @@ class UsageError extends Refusal {
53459
53678
  function projectArgument(at) {
53460
53679
  const named = args.slice(at).find((value) => !value.startsWith("--"));
53461
53680
  const projectPath = resolve8(named ?? ".");
53462
- if (!existsSync25(join32(projectPath, "project.godot"))) {
53681
+ if (!existsSync26(join33(projectPath, "project.godot"))) {
53463
53682
  throw new UsageError(`Not a Godot project: ${projectPath} holds no project.godot.`);
53464
53683
  }
53465
53684
  return projectPath;
@@ -53504,7 +53723,7 @@ async function registerRuntime(godot, projectPath) {
53504
53723
  return;
53505
53724
  }
53506
53725
  console.log(`${RUNTIME_AUTOLOAD.name} autoload left at ${named}, which this project registers itself`);
53507
- if (!existsSync25(join32(projectPath, (named ?? "").replace(/^res:\/\//, "")))) {
53726
+ if (!existsSync26(join33(projectPath, (named ?? "").replace(/^res:\/\//, "")))) {
53508
53727
  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}`);
53509
53728
  }
53510
53729
  }
@@ -53636,7 +53855,7 @@ async function setup() {
53636
53855
  console.log("no harness configured; name one yourself, and gdharness harnesses lists them all");
53637
53856
  }
53638
53857
  if (!args.includes("--no-skill")) {
53639
- const directories = skillDirectories(chosen.harnesses, projectPath, existsSync25);
53858
+ const directories = skillDirectories(chosen.harnesses, projectPath, existsSync26);
53640
53859
  for (const written of writeSkill(directories, getLocalVersion())) {
53641
53860
  console.log(`skill: ${written.replaced ? "replaced" : "written"} ${written.path}`);
53642
53861
  }
@@ -53750,7 +53969,7 @@ async function upgrade() {
53750
53969
  moved.push(took.from);
53751
53970
  }
53752
53971
  }
53753
- for (const written of writeSkill(skillDirectories(already, projectPath, existsSync25), version)) {
53972
+ for (const written of writeSkill(skillDirectories(already, projectPath, existsSync26), version)) {
53754
53973
  console.log(`skill: ${written.replaced ? "replaced" : "written"} ${written.path}`);
53755
53974
  }
53756
53975
  console.log(`