gdharness 1.3.53 → 1.3.55

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
@@ -800,6 +800,30 @@ public static class GdhProcessTable
800
800
  return "";
801
801
  }
802
802
 
803
+ /// <summary>"pid seconds" on a line for each running one of the comma-separated pids, from one snapshot.</summary>
804
+ public static string ProcessorSecondsOf(string pids)
805
+ {
806
+ HashSet<int> wanted = new HashSet<int>();
807
+ foreach (string one in pids.Split(','))
808
+ {
809
+ int pid;
810
+ if (int.TryParse(one, NumberStyles.Integer, CultureInfo.InvariantCulture, out pid))
811
+ {
812
+ wanted.Add(pid);
813
+ }
814
+ }
815
+ StringBuilder said = new StringBuilder();
816
+ foreach (Entry entry in Snapshot())
817
+ {
818
+ if (wanted.Contains(entry.Pid))
819
+ {
820
+ said.Append(Number(entry.Pid)).Append(' ')
821
+ .Append((entry.Processor / 1e7).ToString("R", CultureInfo.InvariantCulture)).Append('\n');
822
+ }
823
+ }
824
+ return said.ToString();
825
+ }
826
+
803
827
  /// <summary>How many more bytes Windows will commit.</summary>
804
828
  public static string CommitLeft()
805
829
  {
@@ -2245,6 +2269,184 @@ var init_engine_slots = __esm(() => {
2245
2269
  init_whole_file();
2246
2270
  });
2247
2271
 
2272
+ // src/process-time.ts
2273
+ async function cpuSecondsOf(pid) {
2274
+ try {
2275
+ return process.platform === "win32" ? await secondsOnWindows(pid) : await askPosix2(pid);
2276
+ } catch {
2277
+ return;
2278
+ }
2279
+ }
2280
+ async function secondsOnWindows(pid) {
2281
+ const said = (await askProcessTable(`ProcessorSeconds(${pid})`, ASK_TIMEOUT_MS2)).trim();
2282
+ const seconds = Number(said);
2283
+ return said !== "" && Number.isFinite(seconds) ? seconds : undefined;
2284
+ }
2285
+ async function askPosix2(pid) {
2286
+ const { stdout } = await runHidden("ps", ["-o", "time=", "-p", String(pid)], { timeout: ASK_TIMEOUT_MS2 });
2287
+ return secondsFromClock(stdout.trim());
2288
+ }
2289
+ function secondsFromClock(printed) {
2290
+ if (printed === "") {
2291
+ return;
2292
+ }
2293
+ const dashed = printed.includes("-");
2294
+ const days = dashed ? printed.slice(0, printed.indexOf("-")) : null;
2295
+ const clock = dashed ? printed.slice(printed.indexOf("-") + 1) : printed;
2296
+ const parts = clock.split(":").map((part) => Number(part));
2297
+ if (parts.length === 0 || parts.some((part) => !Number.isFinite(part))) {
2298
+ return;
2299
+ }
2300
+ const seconds = parts.reduce((total, part) => total * 60 + part, 0);
2301
+ const extra = days === null ? 0 : Number(days) * 86400;
2302
+ return Number.isFinite(extra) ? seconds + extra : undefined;
2303
+ }
2304
+ var ASK_TIMEOUT_MS2 = 6000;
2305
+ var init_process_time = __esm(() => {
2306
+ init_process_table();
2307
+ init_spawn();
2308
+ });
2309
+
2310
+ // src/engine-spent.ts
2311
+ import { readFileSync as readFileSync7 } from "node:fs";
2312
+ import { availableParallelism as availableParallelism2, cpus } from "node:os";
2313
+ import { performance as performance2 } from "node:perf_hooks";
2314
+ function crowded(stretch, work, busy, cores) {
2315
+ return stretch > 0 && Math.max(0, busy - work) / (stretch * cores) >= BUSY;
2316
+ }
2317
+
2318
+ class Spent {
2319
+ byClock = 0;
2320
+ byWork = 0;
2321
+ since;
2322
+ cores;
2323
+ last;
2324
+ constructor(since, first, pid, cores = availableParallelism2()) {
2325
+ this.since = since;
2326
+ this.cores = cores;
2327
+ this.last = {
2328
+ at: since,
2329
+ machine: first?.machine ?? Number.NaN,
2330
+ processor: pid === undefined ? undefined : first?.processors?.get(pid)
2331
+ };
2332
+ }
2333
+ counted(sample, pid) {
2334
+ if (sample.at <= this.last.at) {
2335
+ return;
2336
+ }
2337
+ const stretch = sample.at - this.last.at;
2338
+ const processor = pid === undefined ? undefined : sample.processors?.get(pid);
2339
+ const work = processor === undefined || this.last.processor === undefined ? undefined : Math.max(0, processor - this.last.processor);
2340
+ if (work !== undefined && crowded(stretch, work, sample.machine - this.last.machine, this.cores)) {
2341
+ this.byWork += Math.min(work, stretch);
2342
+ } else {
2343
+ this.byClock += stretch;
2344
+ }
2345
+ this.last = { at: sample.at, machine: sample.machine, processor };
2346
+ }
2347
+ restart(at) {
2348
+ this.since = at;
2349
+ this.byClock = 0;
2350
+ this.byWork = 0;
2351
+ this.last = { ...this.last, at: Math.max(this.last.at, at) };
2352
+ }
2353
+ clock(now) {
2354
+ return now - this.since;
2355
+ }
2356
+ past(limit, now) {
2357
+ if (this.clock(now) > limit * STARVED) {
2358
+ return "clock";
2359
+ }
2360
+ return this.byClock + this.byWork > limit ? "own" : null;
2361
+ }
2362
+ }
2363
+ function nowSeconds() {
2364
+ return performance2.now() / 1000;
2365
+ }
2366
+ function machineSeconds() {
2367
+ return cpus().reduce((sum, core) => sum + core.times.user + core.times.nice + core.times.sys + core.times.irq, 0) / 1000;
2368
+ }
2369
+ function watching(pid, on) {
2370
+ if (on) {
2371
+ watched.add(pid);
2372
+ } else {
2373
+ watched.delete(pid);
2374
+ }
2375
+ }
2376
+ function sampled() {
2377
+ const now = Date.now();
2378
+ if (latest === null || now - latest.taken >= SAMPLE_MS) {
2379
+ latest = { taken: now, sample: take() };
2380
+ }
2381
+ return latest.sample;
2382
+ }
2383
+ async function take() {
2384
+ const at = nowSeconds();
2385
+ const machine = machineSeconds();
2386
+ const previous = quietAt;
2387
+ quietAt = { at, machine, processors: null };
2388
+ const share = previous === null || at <= previous.at ? 1 : (machine - previous.machine) / ((at - previous.at) * availableParallelism2());
2389
+ if (share < BUSY || watched.size === 0) {
2390
+ return quietAt;
2391
+ }
2392
+ return { at, machine, processors: await processorSecondsOf([...watched]) };
2393
+ }
2394
+ async function processorSecondsOf(pids) {
2395
+ const found = new Map;
2396
+ try {
2397
+ if (process.platform === "win32") {
2398
+ const said = await askProcessTable(`ProcessorSecondsOf('${pids.join(",")}')`, ASK_TIMEOUT_MS3);
2399
+ for (const line of said.split(/\r?\n/)) {
2400
+ const [pid, seconds] = line.trim().split(" ").map(Number);
2401
+ if (pid !== undefined && seconds !== undefined && Number.isFinite(pid) && Number.isFinite(seconds)) {
2402
+ found.set(pid, seconds);
2403
+ }
2404
+ }
2405
+ } else if (process.platform === "linux") {
2406
+ const ticks = await ticksASecond();
2407
+ for (const pid of pids) {
2408
+ try {
2409
+ const fields = (readFileSync7(`/proc/${String(pid)}/stat`, "utf8").split(")").pop() ?? "").trim().split(/\s+/);
2410
+ const used = Number(fields[11]) + Number(fields[12]);
2411
+ if (Number.isFinite(used)) {
2412
+ found.set(pid, used / ticks);
2413
+ }
2414
+ } catch {}
2415
+ }
2416
+ } else {
2417
+ const asked = (some) => runHidden("ps", ["-o", "pid=,time=", "-p", some.join(",")], { timeout: ASK_TIMEOUT_MS3 }).then((done) => done.stdout, (error) => {
2418
+ const printed = error.stdout;
2419
+ return typeof printed === "string" ? printed : "";
2420
+ });
2421
+ let stdout = await asked(pids);
2422
+ if (stdout.trim() === "" && pids.length > 1) {
2423
+ stdout = (await Promise.all(pids.map((pid) => asked([pid])))).join(`
2424
+ `);
2425
+ }
2426
+ for (const line of stdout.split(`
2427
+ `)) {
2428
+ const [pid, time] = line.trim().split(/\s+/);
2429
+ const seconds = time === undefined ? undefined : secondsFromClock(time);
2430
+ if (pid !== undefined && seconds !== undefined) {
2431
+ found.set(Number(pid), seconds);
2432
+ }
2433
+ }
2434
+ }
2435
+ } catch {}
2436
+ return found;
2437
+ }
2438
+ function ticksASecond() {
2439
+ ticks ??= runHidden("getconf", ["CLK_TCK"], { timeout: ASK_TIMEOUT_MS3 }).then(({ stdout }) => Number(stdout.trim()) || 100).catch(() => 100);
2440
+ return ticks;
2441
+ }
2442
+ var STARVED = 8, BUSY = 0.25, SAMPLE_MS = 1000, ASK_TIMEOUT_MS3 = 3000, watched, latest = null, quietAt = null, ticks = null;
2443
+ var init_engine_spent = __esm(() => {
2444
+ init_process_table();
2445
+ init_process_time();
2446
+ init_spawn();
2447
+ watched = new Set;
2448
+ });
2449
+
2248
2450
  // node_modules/yaml/dist/nodes/identity.js
2249
2451
  var require_identity = __commonJS(function(exports) {
2250
2452
  var ALIAS = Symbol.for("yaml.alias");
@@ -9381,7 +9583,7 @@ function alongside(execPath, name) {
9381
9583
  var init_runner = () => {};
9382
9584
 
9383
9585
  // src/harnesses.ts
9384
- import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync2, readFileSync as readFileSync7, rmSync as rmSync5, statSync as statSync4, writeFileSync as writeFileSync5 } from "node:fs";
9586
+ import { existsSync as existsSync4, mkdirSync as mkdirSync3, readdirSync as readdirSync2, readFileSync as readFileSync8, rmSync as rmSync5, statSync as statSync4, writeFileSync as writeFileSync5 } from "node:fs";
9385
9587
  import { homedir as homedir3 } from "node:os";
9386
9588
  import { basename as basename4, dirname as dirname4, isAbsolute as isAbsolute2, join as join7, relative as relative2, resolve as resolve4 } from "node:path";
9387
9589
  import process6 from "node:process";
@@ -9518,7 +9720,7 @@ function registered(harness, projectPath) {
9518
9720
  if (harness.snippet !== undefined || !existsSync4(path)) {
9519
9721
  return false;
9520
9722
  }
9521
- const text = readFileSync7(path, "utf8");
9723
+ const text = readFileSync8(path, "utf8");
9522
9724
  if (harness.toml !== undefined) {
9523
9725
  return text.includes(harness.toml.identity ?? harness.toml.header);
9524
9726
  }
@@ -9563,7 +9765,7 @@ function mainCheckoutOf(projectPath) {
9563
9765
  if (!statSync4(marker).isFile()) {
9564
9766
  return null;
9565
9767
  }
9566
- const pointer = /^gitdir:\s*(.+)$/m.exec(readFileSync7(marker, "utf8"))?.[1]?.trim();
9768
+ const pointer = /^gitdir:\s*(.+)$/m.exec(readFileSync8(marker, "utf8"))?.[1]?.trim();
9567
9769
  if (pointer === undefined) {
9568
9770
  return null;
9569
9771
  }
@@ -9572,7 +9774,7 @@ function mainCheckoutOf(projectPath) {
9572
9774
  if (!existsSync4(commonFile)) {
9573
9775
  return null;
9574
9776
  }
9575
- const common = resolve4(entry, readFileSync7(commonFile, "utf8").trim());
9777
+ const common = resolve4(entry, readFileSync8(commonFile, "utf8").trim());
9576
9778
  if (basename4(common) !== ".git") {
9577
9779
  return null;
9578
9780
  }
@@ -9598,7 +9800,7 @@ function worktreeCheckoutsOf(projectPath) {
9598
9800
  if (!existsSync4(pointer)) {
9599
9801
  return [];
9600
9802
  }
9601
- const checkout = dirname4(resolve4(listed, name, readFileSync7(pointer, "utf8").trim()));
9803
+ const checkout = dirname4(resolve4(listed, name, readFileSync8(pointer, "utf8").trim()));
9602
9804
  const project = join7(checkout, below);
9603
9805
  return existsSync4(join7(project, "project.godot")) ? [project] : [];
9604
9806
  });
@@ -9613,7 +9815,7 @@ function entryEnvironment(harness, projectPath) {
9613
9815
  }
9614
9816
  let parsed;
9615
9817
  try {
9616
- parsed = JSON.parse(readFileSync7(path, "utf8"));
9818
+ parsed = JSON.parse(readFileSync8(path, "utf8"));
9617
9819
  } catch {
9618
9820
  return;
9619
9821
  }
@@ -9635,7 +9837,7 @@ function disconnect(harness, projectPath) {
9635
9837
  if (harness.snippet !== undefined) {
9636
9838
  return { harness, path, action: "manual" };
9637
9839
  }
9638
- const text = readFileSync7(path, "utf8");
9840
+ const text = readFileSync8(path, "utf8");
9639
9841
  if (harness.toml !== undefined) {
9640
9842
  return taken(harness, path, text, removeToml(text, harness.toml));
9641
9843
  }
@@ -9644,7 +9846,7 @@ function disconnect(harness, projectPath) {
9644
9846
  }
9645
9847
  let existing;
9646
9848
  try {
9647
- existing = JSON.parse(readFileSync7(path, "utf8"));
9849
+ existing = JSON.parse(readFileSync8(path, "utf8"));
9648
9850
  } catch {
9649
9851
  return { harness, path, action: "absent" };
9650
9852
  }
@@ -9716,7 +9918,7 @@ function connect(harness, projectPath, launch) {
9716
9918
  if (harness.snippet !== undefined) {
9717
9919
  return { harness, path, action: "snippet", snippet: harness.snippet(launch) };
9718
9920
  }
9719
- const held = existsSync4(path) ? readFileSync7(path, "utf8") : "";
9921
+ const held = existsSync4(path) ? readFileSync8(path, "utf8") : "";
9720
9922
  if (harness.toml !== undefined) {
9721
9923
  const already = held.includes(harness.toml.identity ?? harness.toml.header);
9722
9924
  return put(harness, path, writeToml(held, harness.toml, launch), already);
@@ -10882,7 +11084,7 @@ var init_gdscript_source = __esm(() => {
10882
11084
  });
10883
11085
 
10884
11086
  // src/class-cache.ts
10885
- import { existsSync as existsSync5, readdirSync as readdirSync3, readFileSync as readFileSync8, statSync as statSync5 } from "node:fs";
11087
+ import { existsSync as existsSync5, readdirSync as readdirSync3, readFileSync as readFileSync9, statSync as statSync5 } from "node:fs";
10886
11088
  import { join as join8, posix } from "node:path";
10887
11089
  function isToolScript(source) {
10888
11090
  return TOOL.test(codeOf(source));
@@ -10957,7 +11159,7 @@ function eachScript(projectPath, each) {
10957
11159
  if (entry.isDirectory()) {
10958
11160
  visit(path, `${prefix}${entry.name}/`);
10959
11161
  } else if (entry.isFile() && entry.name.endsWith(".gd")) {
10960
- each(`res://${prefix}${entry.name}`, readFileSync8(path, "utf8"));
11162
+ each(`res://${prefix}${entry.name}`, readFileSync9(path, "utf8"));
10961
11163
  }
10962
11164
  }
10963
11165
  };
@@ -12095,7 +12297,7 @@ var init_junit = __esm(() => {
12095
12297
  });
12096
12298
 
12097
12299
  // src/suite-judge.ts
12098
- import { readFileSync as readFileSync9 } from "node:fs";
12300
+ import { readFileSync as readFileSync10 } from "node:fs";
12099
12301
  import { stripVTControlCharacters } from "node:util";
12100
12302
  function secondsOf(time) {
12101
12303
  let ms = 0;
@@ -12272,7 +12474,7 @@ function readRun(printed) {
12272
12474
  settleAfterCase("after");
12273
12475
  const joined = lines.join(`
12274
12476
  `);
12275
- const starved = STARVED.flatMap((pattern) => [...joined.matchAll(pattern)].map((found) => found[0]));
12477
+ const starved = STARVED2.flatMap((pattern) => [...joined.matchAll(pattern)].map((found) => found[0]));
12276
12478
  const unloadable = scriptErrorsPrinted(lines).map((error) => `${error.path}${error.line === null ? "" : `:${String(error.line)}`}: ${error.message}`);
12277
12479
  return {
12278
12480
  lines,
@@ -12306,7 +12508,7 @@ function versionFault(said, wanted) {
12306
12508
  function readExpected(file) {
12307
12509
  const declared = (value) => typeof value === "object" && value !== null ? value : {};
12308
12510
  try {
12309
- const parsed = JSON.parse(readFileSync9(file, "utf8"));
12511
+ const parsed = JSON.parse(readFileSync10(file, "utf8"));
12310
12512
  const read = typeof parsed === "object" && parsed !== null ? parsed : {};
12311
12513
  return { errors: declared(read["errors"]), warnings: declared(read["warnings"]) };
12312
12514
  } catch {
@@ -12652,7 +12854,7 @@ ${String(error.warning)}`);
12652
12854
  const neverPrinted = [...owed.values()].flatMap((debt) => debt.texts.map((text) => ({ suite: debt.suite, during: debt.during, text, warning: debt.warning })));
12653
12855
  return { printed: unexpected, neverPrinted };
12654
12856
  }
12655
- var SUITE_OPENED, TIME, CASE_LINE, STATISTICS, ERROR_LINE, WARNING_LINE, EXECUTED, MISMATCHED_ROW, UNRUNNABLE_CASE, BANNER, LEFT_OUT, DRIVER_FALLBACK, MACHINE_WARNED, ORPHANS_REPORTED, STARVED, NESTED_ERROR = "While attempting to print an error, another error was printed:", WINDOWS_EXITS, MEMORY_EXITS, GDUNIT_FAILED = 100, GDUNIT_ORPHANS = 101, GDUNIT_EXITS, GODOT_EXITS, CRASH_HEADER, CRASH_BUILD, QUEUE_OVERFLOW_LINE = "Object was deleted while awaiting a callback.", QUEUE_OVERFLOW_FRAMES, TAIL = 60, MOST_NAMED = 8, COMPILED_SAID = "gdharness compiled every script together";
12857
+ var SUITE_OPENED, TIME, CASE_LINE, STATISTICS, ERROR_LINE, WARNING_LINE, EXECUTED, MISMATCHED_ROW, UNRUNNABLE_CASE, BANNER, LEFT_OUT, DRIVER_FALLBACK, MACHINE_WARNED, ORPHANS_REPORTED, STARVED2, NESTED_ERROR = "While attempting to print an error, another error was printed:", WINDOWS_EXITS, MEMORY_EXITS, GDUNIT_FAILED = 100, GDUNIT_ORPHANS = 101, GDUNIT_EXITS, GODOT_EXITS, CRASH_HEADER, CRASH_BUILD, QUEUE_OVERFLOW_LINE = "Object was deleted while awaiting a callback.", QUEUE_OVERFLOW_FRAMES, TAIL = 60, MOST_NAMED = 8, COMPILED_SAID = "gdharness compiled every script together";
12656
12858
  var init_suite_judge = __esm(() => {
12657
12859
  init_engine_leaks();
12658
12860
  init_junit();
@@ -12670,7 +12872,7 @@ var init_suite_judge = __esm(() => {
12670
12872
  DRIVER_FALLBACK = /^\s*WARNING: Your video card drivers .*, switching to /;
12671
12873
  MACHINE_WARNED = /^\s*at: .*\((?:\.\/)?(?:platform|drivers)\//;
12672
12874
  ORPHANS_REPORTED = /^\s*WARNING: Detected \d+ (?:possible )?orphan nodes\.\s*$/;
12673
- STARVED = [
12875
+ STARVED2 = [
12674
12876
  /Parameter "mem(?:_new)?" is null\.?\s*\n\s*at: \w+ \((?:\.\/)?core\/(?:os\/memory\.cpp|templates\/cowdata\.h):/g,
12675
12877
  /ERROR: Failed decoding WebP image\.\s*\n\s*at: _webp_unpack/g
12676
12878
  ];
@@ -12706,16 +12908,24 @@ import {
12706
12908
  fstatSync as fstatSync2,
12707
12909
  openSync as openSync4,
12708
12910
  readdirSync as readdirSync4,
12911
+ readFileSync as readFileSync11,
12709
12912
  readSync as readSync2,
12710
12913
  rmSync as rmSync7,
12711
12914
  statSync as statSync6,
12712
12915
  writeFileSync as writeFileSync6,
12713
12916
  writeSync as writeSync4
12714
12917
  } from "node:fs";
12715
- import { availableParallelism as availableParallelism2, freemem, totalmem } from "node:os";
12918
+ import { availableParallelism as availableParallelism3, freemem, totalmem } from "node:os";
12716
12919
  import { isAbsolute as isAbsolute3, join as join9, relative as relative3, resolve as resolve5, sep as sep2 } from "node:path";
12717
12920
  import process7 from "node:process";
12718
12921
  import { setTimeout as delay3 } from "node:timers/promises";
12922
+ function countedBy(spent, past, at) {
12923
+ const clock = Math.round(spent.clock(at));
12924
+ if (past === "clock") {
12925
+ return `, ${String(STARVED)} times over on the clock (${String(clock)} s), the machine kept busy by other work`;
12926
+ }
12927
+ return spent.byWork === 0 ? "" : ` of its own, counted by its processor time while other work kept the machine busy (${String(clock)} s on the clock)`;
12928
+ }
12719
12929
  function plural(count, noun) {
12720
12930
  return `${String(count)} ${noun}${count === 1 ? "" : "s"}`;
12721
12931
  }
@@ -12741,7 +12951,7 @@ function godotNamed(value, cwd) {
12741
12951
  }
12742
12952
  function coresNamed(value) {
12743
12953
  const cores = wholeFlag(value, "--cores");
12744
- const most = Math.min(availableParallelism2(), 64);
12954
+ const most = Math.min(availableParallelism3(), 64);
12745
12955
  if (cores !== null && cores > most) {
12746
12956
  throw new EngineUsageError(`--cores ${String(cores)} is more than the ${String(most)} processors a run can be held to here.`);
12747
12957
  }
@@ -12932,19 +13142,49 @@ async function watchedEngine(launch, lock, args, directory, log, silenceSeconds,
12932
13142
  exited.then(() => {
12933
13143
  finished.now = true;
12934
13144
  });
13145
+ const hasFinished = () => finished.now;
12935
13146
  const reading = openSync4(log, "r");
13147
+ const engineAt = () => {
13148
+ if (helper === null) {
13149
+ return child.pid;
13150
+ }
13151
+ try {
13152
+ const pid = Number(readFileSync11(helper.pid, "utf8").trim());
13153
+ return Number.isInteger(pid) && pid > 0 ? pid : undefined;
13154
+ } catch {
13155
+ return;
13156
+ }
13157
+ };
13158
+ let pid = engineAt();
13159
+ if (pid !== undefined) {
13160
+ watching(pid, true);
13161
+ }
12936
13162
  try {
12937
13163
  let size = 0;
12938
- const started = Date.now();
12939
- let lastGrowth = started;
13164
+ const sample = launch.sample ?? sampled;
13165
+ const first = await sample();
13166
+ const running = new Spent(nowSeconds(), first, pid);
13167
+ const quiet = new Spent(nowSeconds(), first, pid);
12940
13168
  let carried = "";
12941
13169
  let allowed = { size: -1, seconds: silenceSeconds };
12942
13170
  while (!finished.now) {
12943
13171
  await delay3(LOOK_MS);
13172
+ if (pid === undefined) {
13173
+ pid = engineAt();
13174
+ if (pid !== undefined) {
13175
+ watching(pid, true);
13176
+ }
13177
+ }
13178
+ const taken = await sample();
13179
+ running.counted(taken, pid);
13180
+ quiet.counted(taken, pid);
13181
+ const at = nowSeconds();
13182
+ const gone = hasFinished() || pid !== undefined && !alive(pid);
12944
13183
  const deadline = launch.deadlineSeconds;
12945
- if (ended === null && deadline !== null && Date.now() - started > deadline * 1000) {
13184
+ const over = deadline === null || gone ? null : running.past(deadline, at);
13185
+ if (ended === null && deadline !== null && over !== null) {
12946
13186
  const stoppedIn = readRun(readLog(log)).stoppedIn;
12947
- ended = `the engine ran past the ${String(deadline)} s deadline${stoppedIn === null ? "" : ` while running ${stoppedIn}`}, so it was ended`;
13187
+ ended = `the engine ran past the ${String(deadline)} s deadline${countedBy(running, over, at)}${stoppedIn === null ? "" : ` while running ${stoppedIn}`}, so it was ended`;
12948
13188
  stop();
12949
13189
  }
12950
13190
  const now = fstatSync2(reading).size;
@@ -12964,24 +13204,25 @@ async function watchedEngine(launch, lock, args, directory, log, silenceSeconds,
12964
13204
  at += got;
12965
13205
  }
12966
13206
  size = now;
12967
- lastGrowth = Date.now();
13207
+ quiet.restart(at);
12968
13208
  if (ended === null && size > LOG_LIMIT_BYTES) {
12969
13209
  ended = `the engine printed more than the ${String(LOG_LIMIT_BYTES / 1024 / 1024)} MiB a log may hold, so it was ended`;
12970
13210
  stop();
12971
13211
  }
12972
- } else if (ended === null && Date.now() - lastGrowth > silenceSeconds * 1000) {
13212
+ } else if (ended === null && !gone && quiet.past(silenceSeconds, at) !== null) {
12973
13213
  if (allowed.size !== size) {
12974
13214
  allowed = {
12975
13215
  size,
12976
13216
  seconds: silenceIn === undefined ? silenceSeconds : Math.max(silenceSeconds, silenceIn(readRun(readLog(log))))
12977
13217
  };
12978
13218
  }
12979
- if (Date.now() - lastGrowth <= allowed.seconds * 1000) {
13219
+ const silent = quiet.past(allowed.seconds, at);
13220
+ if (silent === null) {
12980
13221
  continue;
12981
13222
  }
12982
13223
  const { stoppedIn } = readRun(readLog(log));
12983
13224
  const unstarted = helper !== null && !existsSync6(helper.pid) ? `, and it never started ${helper.how}: ${helperStep(helper.step)}` : "";
12984
- ended = `the engine printed nothing for ${String(allowed.seconds)} s${stoppedIn === null ? "" : ` while running ${stoppedIn}`}, so it was ended${unstarted}`;
13225
+ ended = `the engine printed nothing for ${String(allowed.seconds)} s${countedBy(quiet, silent, at)}${stoppedIn === null ? "" : ` while running ${stoppedIn}`}, so it was ended${unstarted}`;
12985
13226
  stop();
12986
13227
  }
12987
13228
  }
@@ -12991,6 +13232,9 @@ async function watchedEngine(launch, lock, args, directory, log, silenceSeconds,
12991
13232
  } finally {
12992
13233
  closeSync4(reading);
12993
13234
  launch.running.delete(stop);
13235
+ if (pid !== undefined) {
13236
+ watching(pid, false);
13237
+ }
12994
13238
  }
12995
13239
  const { code, signal } = await exited;
12996
13240
  const freeBytes = code !== null && code >>> 0 >= WINDOWS_EXCEPTION ? freemem() : undefined;
@@ -13047,6 +13291,7 @@ var init_engine_launch = __esm(() => {
13047
13291
  init_alive();
13048
13292
  init_desktop();
13049
13293
  init_engine_slots();
13294
+ init_engine_spent();
13050
13295
  init_errors();
13051
13296
  init_harnesses();
13052
13297
  init_log_read();
@@ -13857,7 +14102,7 @@ var init_doc_cache = __esm(() => {
13857
14102
  });
13858
14103
 
13859
14104
  // src/project-scan.ts
13860
- import { existsSync as existsSync10, readdirSync as readdirSync7, readFileSync as readFileSync10, statSync as statSync9 } from "node:fs";
14105
+ import { existsSync as existsSync10, readdirSync as readdirSync7, readFileSync as readFileSync12, statSync as statSync9 } from "node:fs";
13861
14106
  import { basename as basename6, extname as extname2, join as join12, relative as relative4, sep as sep3 } from "node:path";
13862
14107
  function skipped(name) {
13863
14108
  return name.startsWith(".") || SKIPPED.has(name);
@@ -13993,7 +14238,7 @@ function scriptsWithoutUid(projectPath) {
13993
14238
  function pathsInUidCache(projectPath) {
13994
14239
  let data;
13995
14240
  try {
13996
- data = readFileSync10(join12(projectPath, ".godot", "uid_cache.bin"));
14241
+ data = readFileSync12(join12(projectPath, ".godot", "uid_cache.bin"));
13997
14242
  } catch {
13998
14243
  return null;
13999
14244
  }
@@ -14055,7 +14300,7 @@ function projectTexts(projectPath) {
14055
14300
  if (!entry.isFile() || BINARY_EXTENSIONS.has(extension)) {
14056
14301
  continue;
14057
14302
  }
14058
- const bytes = readFileSync10(absolute);
14303
+ const bytes = readFileSync12(absolute);
14059
14304
  if (!isBinary(bytes)) {
14060
14305
  found.push({ path: `res://${spelled}`, absolute, text: bytes.toString("utf8") });
14061
14306
  }
@@ -14085,7 +14330,7 @@ function ignoredTexts(projectPath) {
14085
14330
  if (!under || !entry.isFile() || BINARY_EXTENSIONS.has(extension) || entry.name === ".gdignore") {
14086
14331
  continue;
14087
14332
  }
14088
- const bytes = readFileSync10(absolute);
14333
+ const bytes = readFileSync12(absolute);
14089
14334
  if (!isBinary(bytes)) {
14090
14335
  found.push({ path: `res://${spelled}`, absolute, text: bytes.toString("utf8") });
14091
14336
  }
@@ -14133,7 +14378,7 @@ function searchProject(projectPath, options) {
14133
14378
  if (extensions !== null && !extensions.has(extension)) {
14134
14379
  return;
14135
14380
  }
14136
- const bytes = readFileSync10(entryPath);
14381
+ const bytes = readFileSync12(entryPath);
14137
14382
  if (extensions === null && isBinary(bytes)) {
14138
14383
  return;
14139
14384
  }
@@ -14249,7 +14494,7 @@ var init_project_scan = __esm(() => {
14249
14494
 
14250
14495
  // src/import-freshness.ts
14251
14496
  import { createHash as createHash2 } from "node:crypto";
14252
- import { existsSync as existsSync11, readdirSync as readdirSync8, readFileSync as readFileSync11, statSync as statSync10 } from "node:fs";
14497
+ import { existsSync as existsSync11, readdirSync as readdirSync8, readFileSync as readFileSync13, statSync as statSync10 } from "node:fs";
14253
14498
  import { join as join13 } from "node:path";
14254
14499
  function md5Of(file) {
14255
14500
  let stats;
@@ -14264,7 +14509,7 @@ function md5Of(file) {
14264
14509
  }
14265
14510
  let md5;
14266
14511
  try {
14267
- md5 = createHash2("md5").update(readFileSync11(file)).digest("hex");
14512
+ md5 = createHash2("md5").update(readFileSync13(file)).digest("hex");
14268
14513
  } catch {
14269
14514
  return null;
14270
14515
  }
@@ -14298,7 +14543,7 @@ function importedIntoTheProject(projectPath, resPaths) {
14298
14543
  for (const resPath of resPaths) {
14299
14544
  let sidecar;
14300
14545
  try {
14301
- sidecar = readFileSync11(`${onDisk(projectPath, resPath)}.import`, "utf8");
14546
+ sidecar = readFileSync13(`${onDisk(projectPath, resPath)}.import`, "utf8");
14302
14547
  } catch {
14303
14548
  continue;
14304
14549
  }
@@ -14312,21 +14557,81 @@ function importedIntoTheProject(projectPath, resPaths) {
14312
14557
  function onDisk(projectPath, resPath) {
14313
14558
  return join13(projectPath, resPath.slice("res://".length));
14314
14559
  }
14315
- function behind(projectPath, resPath, sidecar) {
14560
+ function importedSidecarMd5s(projectPath) {
14561
+ const found = new Map;
14562
+ const editor = join13(projectPath, ".godot", "editor");
14563
+ let newest = -1;
14564
+ try {
14565
+ for (const name of readdirSync8(editor)) {
14566
+ const version = /^filesystem_cache(\d+)$/.exec(name)?.[1];
14567
+ if (version !== undefined) {
14568
+ newest = Math.max(newest, Number(version));
14569
+ }
14570
+ }
14571
+ } catch {
14572
+ return found;
14573
+ }
14574
+ if (newest < 0) {
14575
+ return found;
14576
+ }
14577
+ let text;
14578
+ try {
14579
+ text = readFileSync13(join13(editor, `filesystem_cache${newest}`), "utf8");
14580
+ } catch {
14581
+ return found;
14582
+ }
14583
+ let directory = "res://";
14584
+ for (const line of text.split(/\r?\n/).slice(1).map((one) => one.trim())) {
14585
+ if (line === "") {
14586
+ continue;
14587
+ }
14588
+ const fields = line.split("::");
14589
+ if (line.startsWith("::")) {
14590
+ if (fields.length === 3) {
14591
+ directory = fields[1] ?? directory;
14592
+ }
14593
+ continue;
14594
+ }
14595
+ const slices = fields.length < 9 ? [] : (fields[7] ?? "").split("<>");
14596
+ const md5 = slices[5];
14597
+ if (slices.length >= 7 && md5 !== undefined && md5 !== "") {
14598
+ found.set(`${directory}${directory.endsWith("/") ? "" : "/"}${fields[0]}`, md5);
14599
+ }
14600
+ }
14601
+ return found;
14602
+ }
14603
+ function behind(projectPath, resPath, sidecarFile, sidecar, importedFrom) {
14604
+ const imported = importedFrom.get(resPath);
14605
+ if (imported !== undefined && md5Of(sidecarFile) !== imported) {
14606
+ return "its import file changed after it was imported, as setting an option does";
14607
+ }
14316
14608
  const remap = sectionOf(sidecar, "remap");
14317
14609
  const importer = unquoted(remap.get("importer"));
14318
14610
  if (importer === "keep" || importer === "skip") {
14319
14611
  return null;
14320
14612
  }
14321
- const outputs = listed2(sectionOf(sidecar, "deps").get("dest_files"));
14613
+ const deps = sectionOf(sidecar, "deps");
14614
+ const outputs = listed2(deps.get("dest_files"));
14615
+ if (remap.get("valid") !== "false") {
14616
+ if (!remap.has("uid")) {
14617
+ return "its import file has no uid, which the import writes";
14618
+ }
14619
+ if (outputs.length === 0 && ![...remap.keys()].some((key) => key.startsWith("path"))) {
14620
+ return "its import file names no imported output, as one written before the engine imports from it does";
14621
+ }
14622
+ }
14322
14623
  if (outputs.some((output) => !existsSync11(onDisk(projectPath, output)))) {
14323
14624
  return "its imported output is not on disk";
14324
14625
  }
14626
+ const source = unquoted(deps.get("source_file"));
14627
+ if (source !== "" && source !== resPath) {
14628
+ return `its import file was written for ${source}`;
14629
+ }
14325
14630
  const name = resPath.slice(resPath.lastIndexOf("/") + 1);
14326
14631
  const record = join13(projectPath, ".godot", "imported", `${name}-${createHash2("md5").update(resPath, "utf8").digest("hex")}.md5`);
14327
14632
  let recorded = "";
14328
14633
  try {
14329
- recorded = /^source_md5="([0-9a-f]*)"/m.exec(readFileSync11(record, "utf8"))?.[1] ?? "";
14634
+ recorded = /^source_md5="([0-9a-f]*)"/m.exec(readFileSync13(record, "utf8"))?.[1] ?? "";
14330
14635
  } catch {
14331
14636
  recorded = "";
14332
14637
  }
@@ -14338,6 +14643,7 @@ function behind(projectPath, resPath, sidecar) {
14338
14643
  }
14339
14644
  function staleImports(projectPath) {
14340
14645
  const stale = [];
14646
+ const importedFrom = importedSidecarMd5s(projectPath);
14341
14647
  const visit = (directory, resDirectory) => {
14342
14648
  if (existsSync11(join13(directory, ".gdignore")) || existsSync11(join13(directory, VERSION_MARKER))) {
14343
14649
  return;
@@ -14366,13 +14672,14 @@ function staleImports(projectPath) {
14366
14672
  if (!names.has(entry.name.slice(0, -".import".length))) {
14367
14673
  continue;
14368
14674
  }
14675
+ const sidecarFile = join13(directory, entry.name);
14369
14676
  let sidecar;
14370
14677
  try {
14371
- sidecar = readFileSync11(join13(directory, entry.name), "utf8");
14678
+ sidecar = readFileSync13(sidecarFile, "utf8");
14372
14679
  } catch {
14373
14680
  continue;
14374
14681
  }
14375
- const reason = behind(projectPath, source, sidecar);
14682
+ const reason = behind(projectPath, source, sidecarFile, sidecar, importedFrom);
14376
14683
  if (reason !== null) {
14377
14684
  stale.push({ path: source, reason });
14378
14685
  }
@@ -14432,7 +14739,7 @@ import {
14432
14739
  existsSync as existsSync12,
14433
14740
  mkdirSync as mkdirSync4,
14434
14741
  readdirSync as readdirSync9,
14435
- readFileSync as readFileSync12,
14742
+ readFileSync as readFileSync14,
14436
14743
  renameSync as renameSync3,
14437
14744
  unlinkSync,
14438
14745
  writeFileSync as writeFileSync7
@@ -14548,7 +14855,7 @@ function resolveResource(projectPath, relativeTo, path) {
14548
14855
  }
14549
14856
  function readOrNull(path) {
14550
14857
  try {
14551
- return readFileSync12(path, "utf8");
14858
+ return readFileSync14(path, "utf8");
14552
14859
  } catch {
14553
14860
  return null;
14554
14861
  }
@@ -14557,7 +14864,7 @@ var init_library_copies = () => {};
14557
14864
 
14558
14865
  // src/reimport.ts
14559
14866
  import { createHash as createHash3 } from "node:crypto";
14560
- import { existsSync as existsSync13, readFileSync as readFileSync13, rmSync as rmSync8, statSync as statSync11, utimesSync, writeFileSync as writeFileSync8 } from "node:fs";
14867
+ import { existsSync as existsSync13, readFileSync as readFileSync15, rmSync as rmSync8, statSync as statSync11, utimesSync, writeFileSync as writeFileSync8 } from "node:fs";
14561
14868
  import { basename as basename8, join as join15 } from "node:path";
14562
14869
  function forceNextImport(projectPath, resourcePath) {
14563
14870
  const relative = resourcePath.replace(/^res:\/\//, "");
@@ -14566,7 +14873,7 @@ function forceNextImport(projectPath, resourcePath) {
14566
14873
  return;
14567
14874
  }
14568
14875
  const { atime, mtimeMs } = statSync11(sidecar);
14569
- writeFileSync8(sidecar, readFileSync13(sidecar, "utf8").replace(/^valid=false\r?\n/m, ""));
14876
+ writeFileSync8(sidecar, readFileSync15(sidecar, "utf8").replace(/^valid=false\r?\n/m, ""));
14570
14877
  utimesSync(sidecar, atime, new Date(mtimeMs + 2000));
14571
14878
  rmSync8(importRecord(projectPath, resourcePath), { force: true });
14572
14879
  }
@@ -14792,7 +15099,7 @@ var init_script_origins = __esm(() => {
14792
15099
  });
14793
15100
 
14794
15101
  // src/headless.ts
14795
- import { existsSync as existsSync15, readFileSync as readFileSync14, writeFileSync as writeFileSync9 } from "node:fs";
15102
+ import { existsSync as existsSync15, readFileSync as readFileSync16, writeFileSync as writeFileSync9 } from "node:fs";
14796
15103
  import { dirname as dirname7, join as join18 } from "node:path";
14797
15104
  function snakeCased(params) {
14798
15105
  const result = emptyRecord();
@@ -15084,7 +15391,7 @@ function answerIn(file) {
15084
15391
  if (!existsSync15(file)) {
15085
15392
  return { kind: "none" };
15086
15393
  }
15087
- const text = readFileSync14(file, "utf8");
15394
+ const text = readFileSync16(file, "utf8");
15088
15395
  try {
15089
15396
  const parsed = JSON.parse(text);
15090
15397
  return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? { kind: "answer", payload: parsed } : { kind: "unreadable", why: `it is JSON but not an object: ${text.slice(0, 200)}` };
@@ -15110,7 +15417,7 @@ var init_headless = __esm(() => {
15110
15417
  });
15111
15418
 
15112
15419
  // src/update-check.ts
15113
- import { existsSync as existsSync16, mkdirSync as mkdirSync5, readFileSync as readFileSync15, writeFileSync as writeFileSync10 } from "node:fs";
15420
+ import { existsSync as existsSync16, mkdirSync as mkdirSync5, readFileSync as readFileSync17, writeFileSync as writeFileSync10 } from "node:fs";
15114
15421
  import { homedir as homedir6, tmpdir as tmpdir6 } from "node:os";
15115
15422
  import { join as join19 } from "node:path";
15116
15423
  function packageUrl(root) {
@@ -15155,7 +15462,7 @@ function readCache(path) {
15155
15462
  if (!existsSync16(path)) {
15156
15463
  return null;
15157
15464
  }
15158
- const parsed = JSON.parse(readFileSync15(path, "utf8"));
15465
+ const parsed = JSON.parse(readFileSync17(path, "utf8"));
15159
15466
  if (typeof parsed !== "object" || parsed === null) {
15160
15467
  return null;
15161
15468
  }
@@ -15303,7 +15610,7 @@ var init_update_check = __esm(() => {
15303
15610
  });
15304
15611
 
15305
15612
  // src/server-version.ts
15306
- import { readFileSync as readFileSync16 } from "node:fs";
15613
+ import { readFileSync as readFileSync18 } from "node:fs";
15307
15614
  function staleAddon(addonDigest, shippedDigest) {
15308
15615
  return addonDigest !== shippedDigest;
15309
15616
  }
@@ -15362,7 +15669,7 @@ var init_server_version = __esm(() => {
15362
15669
  DEBUG_MODE = process.env["GDHARNESS_DEBUG"] === "1";
15363
15670
  SERVER_VERSION = (() => {
15364
15671
  try {
15365
- const pkg = JSON.parse(readFileSync16(new URL("../package.json", import.meta.url), "utf8"));
15672
+ const pkg = JSON.parse(readFileSync18(new URL("../package.json", import.meta.url), "utf8"));
15366
15673
  return typeof pkg.version === "string" ? pkg.version : "0.0.0";
15367
15674
  } catch {
15368
15675
  return "0.0.0";
@@ -16478,7 +16785,7 @@ var init_lsp_client = __esm(() => {
16478
16785
  });
16479
16786
 
16480
16787
  // src/resources.ts
16481
- import { readFileSync as readFileSync18, realpathSync as realpathSync4 } from "node:fs";
16788
+ import { readFileSync as readFileSync20, realpathSync as realpathSync4 } from "node:fs";
16482
16789
  import { extname as extname4, resolve as resolve9 } from "node:path";
16483
16790
  function ensureProjectPath(getProjectPath) {
16484
16791
  const projectPath = getProjectPath();
@@ -16674,7 +16981,7 @@ function readResourceText(uri, getProjectPath) {
16674
16981
  const parsedUri = parseGodotUri(uri);
16675
16982
  if (parsedUri.kind === "project-info") {
16676
16983
  const projectFilePath = resolveProjectFile(projectPath, "project.godot");
16677
- const rawProject = readFileSync18(projectFilePath, "utf-8");
16984
+ const rawProject = readFileSync20(projectFilePath, "utf-8");
16678
16985
  const parsedProject = parseProjectGodot(rawProject);
16679
16986
  return {
16680
16987
  mimeType: "application/json",
@@ -16683,7 +16990,7 @@ function readResourceText(uri, getProjectPath) {
16683
16990
  }
16684
16991
  const filePath = resolveProjectFile(projectPath, parsedUri.resourcePath);
16685
16992
  ensureAllowedExtension(parsedUri.kind, filePath);
16686
- const text = readFileSync18(filePath, "utf-8");
16993
+ const text = readFileSync20(filePath, "utf-8");
16687
16994
  return {
16688
16995
  mimeType: parsedUri.kind === "script" ? "text/x-gdscript" : "text/plain",
16689
16996
  text
@@ -16830,7 +17137,7 @@ import {
16830
17137
  existsSync as existsSync19,
16831
17138
  mkdirSync as mkdirSync6,
16832
17139
  readdirSync as readdirSync12,
16833
- readFileSync as readFileSync19,
17140
+ readFileSync as readFileSync21,
16834
17141
  rmdirSync,
16835
17142
  rmSync as rmSync10,
16836
17143
  statSync as statSync12,
@@ -16864,7 +17171,7 @@ function addonsDigest(names, from) {
16864
17171
  } else {
16865
17172
  hash.update(`${inside}
16866
17173
  `);
16867
- hash.update(readFileSync19(path));
17174
+ hash.update(readFileSync21(path));
16868
17175
  hash.update(`
16869
17176
  `);
16870
17177
  }
@@ -16913,7 +17220,7 @@ function digestMarker(projectPath, addon) {
16913
17220
  if (!existsSync19(marker)) {
16914
17221
  return null;
16915
17222
  }
16916
- const said = readFileSync19(marker, "utf8").trim();
17223
+ const said = readFileSync21(marker, "utf8").trim();
16917
17224
  return said === "" ? null : said;
16918
17225
  }
16919
17226
  function shippedAddonsDirectory() {
@@ -16930,7 +17237,7 @@ function loaderState(projectPath) {
16930
17237
  if (!existsSync19(copy)) {
16931
17238
  return "missing";
16932
17239
  }
16933
- const lines = (path) => readFileSync19(path, "utf8").replaceAll(`\r
17240
+ const lines = (path) => readFileSync21(path, "utf8").replaceAll(`\r
16934
17241
  `, `
16935
17242
  `);
16936
17243
  return lines(copy) === lines(shippedLoader()) ? "current" : "differs";
@@ -16941,7 +17248,7 @@ function writeLoader(projectPath) {
16941
17248
  }
16942
17249
  const copy = join22(projectPath, ...RUNTIME_AUTOLOAD.path.split("/"));
16943
17250
  mkdirSync6(dirname9(copy), { recursive: true });
16944
- writeFileSync11(copy, readFileSync19(shippedLoader()));
17251
+ writeFileSync11(copy, readFileSync21(shippedLoader()));
16945
17252
  return true;
16946
17253
  }
16947
17254
  function removeLoader(projectPath) {
@@ -17000,7 +17307,7 @@ function installedAddonVersion(projectPath) {
17000
17307
  if (!existsSync19(marker)) {
17001
17308
  return null;
17002
17309
  }
17003
- const said = readFileSync19(marker, "utf8").trim();
17310
+ const said = readFileSync21(marker, "utf8").trim();
17004
17311
  return said === "" ? null : said;
17005
17312
  }
17006
17313
  function removeAddons(projectPath) {
@@ -17055,7 +17362,7 @@ function loaderAmong(projectPath, named) {
17055
17362
  }
17056
17363
  let source = null;
17057
17364
  try {
17058
- source = readFileSync19(join22(projectPath, path), "utf8");
17365
+ source = readFileSync21(join22(projectPath, path), "utf8");
17059
17366
  } catch {}
17060
17367
  if (source?.includes(RUNTIME_SCRIPT)) {
17061
17368
  return { name: entry, path, exists: true };
@@ -17114,7 +17421,7 @@ function inspectProject(projectPath) {
17114
17421
  const directory = join22(projectPath, "addons", name);
17115
17422
  const marker = join22(directory, VERSION_MARKER);
17116
17423
  const installed = existsSync19(directory);
17117
- const version = existsSync19(marker) ? readFileSync19(marker, "utf8").trim() : null;
17424
+ const version = existsSync19(marker) ? readFileSync21(marker, "utf8").trim() : null;
17118
17425
  const current = version === SERVER_VERSION;
17119
17426
  if (!installed) {
17120
17427
  problems.push(`addons/${name} is not installed; run gdharness setup`);
@@ -17123,7 +17430,7 @@ function inspectProject(projectPath) {
17123
17430
  }
17124
17431
  return { name, installed, version, current };
17125
17432
  });
17126
- const settings = parseProjectGodot(readFileSync19(join22(projectPath, "project.godot"), "utf8"));
17433
+ const settings = parseProjectGodot(readFileSync21(join22(projectPath, "project.godot"), "utf8"));
17127
17434
  const pluginsEnabled = enabledPlugins(settings);
17128
17435
  for (const name of EDITOR_PLUGINS) {
17129
17436
  if (!pluginsEnabled.includes(name)) {
@@ -17843,7 +18150,7 @@ var init_tool_definitions = __esm(() => {
17843
18150
  {
17844
18151
  name: "project_export",
17845
18152
  title: "Export",
17846
- description: "Export presets and exports. add_preset writes a preset to export_presets.cfg with the keys the editor's Project > Export writes for a new one and every option a new preset of its platform shows, each at its default, and makes it the platform's runnable preset when the platform has none. set_preset changes a preset's export path, its resource filter and the files it lists, its include and exclude filters, its custom features and its platform's options, and answers under changed what each held before and holds now, and under unchanged what already held the value given, with saved false when that is all of it, since the file is then not written; nothing is written when any of it is refused. The option names are the exporter's own, as the Export dialog's Options tab shows them by their paths, such as binary_format/embed_pck, and a name the platform does not have is refused with the nearest it has, or all of them when none is near. An option the dialog shows only while another is on, such as codesign/identity behind codesign/enable, is taken when the preset has that one on or the same call turns it on, and otherwise refused naming what shows it. A secret option, such as a signing password, is written to .godot/export_credentials.cfg, where the editor keeps secrets out of the committed file, and its value is never answered. A file an option names that is not there is written and listed under missing_files, or for a secret its option under secretsNamingNoFile. remove_preset takes a preset out, and when it was its platform's runnable preset the next of that platform becomes it, under runnableNow, which is null when the platform has no other; a secret it held is taken out with it. It answers where this engine's export templates belong and whether the preset's platform has them, since an export needs them; list says the same for every preset. An editor open on the project does not see a preset added this way until it restarts. run answers bytes, the size of the output it wrote, and names, as alsoWritten, what else the export wrote beside its output, such as the .pck a Windows or Linux build cannot start without. Its entries are the errors and warnings the export printed, the newest two hundred distinct ones, one printed more than once carrying times, and errors and warnings count them; exported is true only when this run wrote the output and printed no error.",
18153
+ description: "Export presets and exports. add_preset writes a preset to export_presets.cfg with the keys the editor's Project > Export writes for a new one and every option a new preset of its platform shows, each at its default, and makes it the platform's runnable preset when the platform has none. set_preset changes a preset's export path, its resource filter and the files it lists, its include and exclude filters, its custom features and its platform's options, and answers under changed what each held before and holds now, and under unchanged what already held the value given, with saved false when that is all of it, since the file is then not written; nothing is written when any of it is refused. The option names are the exporter's own, as the Export dialog's Options tab shows them by their paths, such as binary_format/embed_pck, and a name the platform does not have is refused with the nearest it has, or all of them when none is near. An option the dialog shows only while another is on, such as codesign/identity behind codesign/enable, is taken when the preset has that one on or the same call turns it on, and otherwise refused naming what shows it. A secret option, such as a signing password, is written to .godot/export_credentials.cfg, where the editor keeps secrets out of the committed file, and its value is never answered. A file an option names that is not there is written and listed under missing_files, or for a secret its option under secretsNamingNoFile. remove_preset takes a preset out, and when it was its platform's runnable preset the next of that platform becomes it, under runnableNow, which is null when the platform has no other; a secret it held is taken out with it. It answers where this engine's export templates belong and whether the preset's platform has them, since an export needs them; list says the same for every preset. An editor open on the project does not see a preset added this way until it restarts. run first imports whatever changed on disk since it was imported, named under importedFirst, so the build carries each file as it is now. It answers bytes, the size of the output it wrote, and names, as alsoWritten, what else the export wrote beside its output, such as the .pck a Windows or Linux build cannot start without. Its entries are the errors and warnings the export printed, the newest two hundred distinct ones, one printed more than once carrying times, and errors and warnings count them; exported is true only when this run wrote the output and printed no error.",
17847
18154
  parameters: {
17848
18155
  projectPath: PROJECT_PATH,
17849
18156
  preset: {
@@ -19718,7 +20025,7 @@ var init_tool_reference = __esm(() => {
19718
20025
  });
19719
20026
 
19720
20027
  // src/suite-plan.ts
19721
- import { existsSync as existsSync23, mkdirSync as mkdirSync9, readFileSync as readFileSync22 } from "node:fs";
20028
+ import { existsSync as existsSync23, mkdirSync as mkdirSync9, readFileSync as readFileSync24 } from "node:fs";
19722
20029
  import { dirname as dirname11, join as join26, posix as posix3 } from "node:path";
19723
20030
  function within(path, folder) {
19724
20031
  return path === folder || path.startsWith(`${folder}/`);
@@ -19735,7 +20042,7 @@ class ProjectScripts {
19735
20042
  this.projectPath = projectPath;
19736
20043
  let settings = {};
19737
20044
  try {
19738
- settings = parseProjectGodot(readFileSync22(join26(projectPath, "project.godot"), "utf8"))["gdharness"] ?? {};
20045
+ settings = parseProjectGodot(readFileSync24(join26(projectPath, "project.godot"), "utf8"))["gdharness"] ?? {};
19739
20046
  } catch {}
19740
20047
  const [section, key] = [NOT_SEARCHED_SETTING, GUESS_WINDOW_SETTING].map((setting) => setting.slice("gdharness/".length));
19741
20048
  const listed = settings[section ?? ""];
@@ -19850,7 +20157,7 @@ class ProjectScripts {
19850
20157
  }
19851
20158
  function readTimes(file) {
19852
20159
  try {
19853
- const parsed = JSON.parse(readFileSync22(file, "utf8"));
20160
+ const parsed = JSON.parse(readFileSync24(file, "utf8"));
19854
20161
  return typeof parsed === "object" && parsed !== null ? parsed : {};
19855
20162
  } catch {
19856
20163
  return {};
@@ -20133,7 +20440,7 @@ function capsFrames(projectPath) {
20133
20440
  if (!existsSync23(file)) {
20134
20441
  return false;
20135
20442
  }
20136
- const cap = parseProjectGodot(readFileSync22(file, "utf8"))["application"]?.["run/max_fps"];
20443
+ const cap = parseProjectGodot(readFileSync24(file, "utf8"))["application"]?.["run/max_fps"];
20137
20444
  return typeof cap === "number" && cap > 0;
20138
20445
  }
20139
20446
  function gdUnitFailsOnPushErrors(projectPath) {
@@ -20141,14 +20448,14 @@ function gdUnitFailsOnPushErrors(projectPath) {
20141
20448
  if (!existsSync23(file)) {
20142
20449
  return false;
20143
20450
  }
20144
- return parseProjectGodot(readFileSync22(file, "utf8"))["gdunit4"]?.["report/godot/push_error"] === true;
20451
+ return parseProjectGodot(readFileSync24(file, "utf8"))["gdunit4"]?.["report/godot/push_error"] === true;
20145
20452
  }
20146
20453
  function viewportOf(projectPath) {
20147
20454
  const file = `${projectPath}/project.godot`;
20148
20455
  if (!existsSync23(file)) {
20149
20456
  return null;
20150
20457
  }
20151
- const text = readFileSync22(file, "utf8");
20458
+ const text = readFileSync24(file, "utf8");
20152
20459
  const width = /^window\/size\/viewport_width\s*=\s*(\d+)/m.exec(text)?.[1];
20153
20460
  const height = /^window\/size\/viewport_height\s*=\s*(\d+)/m.exec(text)?.[1];
20154
20461
  return width === undefined || height === undefined ? null : { width: Number(width), height: Number(height) };
@@ -41767,13 +42074,13 @@ var init_stdio = __esm(() => {
41767
42074
  });
41768
42075
 
41769
42076
  // src/autoload-failure.ts
41770
- import { existsSync as existsSync25, readFileSync as readFileSync25 } from "node:fs";
42077
+ import { existsSync as existsSync25, readFileSync as readFileSync27 } from "node:fs";
41771
42078
  import { join as join29 } from "node:path";
41772
42079
  function scriptsAutoloaded(projectPath) {
41773
42080
  const scripts = new Map;
41774
42081
  let settings;
41775
42082
  try {
41776
- settings = parseProjectGodot(readFileSync25(join29(projectPath, "project.godot"), "utf8"));
42083
+ settings = parseProjectGodot(readFileSync27(join29(projectPath, "project.godot"), "utf8"));
41777
42084
  } catch {
41778
42085
  return scripts;
41779
42086
  }
@@ -41787,7 +42094,7 @@ function scriptsAutoloaded(projectPath) {
41787
42094
  continue;
41788
42095
  }
41789
42096
  try {
41790
- for (const named of readFileSync25(scene, "utf8").matchAll(/^\[ext_resource\b[^\]]*\btype="Script"[^\]]*\bpath="(res:\/\/[^"]+)"/gm)) {
42097
+ for (const named of readFileSync27(scene, "utf8").matchAll(/^\[ext_resource\b[^\]]*\btype="Script"[^\]]*\bpath="(res:\/\/[^"]+)"/gm)) {
41791
42098
  scripts.set(named[1] ?? "", name);
41792
42099
  }
41793
42100
  } catch {}
@@ -41806,7 +42113,7 @@ var init_autoload_failure = __esm(() => {
41806
42113
  });
41807
42114
 
41808
42115
  // src/runtime-client.ts
41809
- import { existsSync as existsSync26, readdirSync as readdirSync16, readFileSync as readFileSync26, statSync as statSync15, unlinkSync as unlinkSync3 } from "node:fs";
42116
+ import { existsSync as existsSync26, readdirSync as readdirSync16, readFileSync as readFileSync28, statSync as statSync15, unlinkSync as unlinkSync3 } from "node:fs";
41810
42117
  import { createConnection as createConnection2 } from "node:net";
41811
42118
  import { tmpdir as tmpdir7 } from "node:os";
41812
42119
  import { dirname as dirname14, join as join30, resolve as resolve12 } from "node:path";
@@ -41861,7 +42168,7 @@ function reportAlreadyInTranscript(transcript, report) {
41861
42168
  function parseAnnouncement(file, pid) {
41862
42169
  let fields;
41863
42170
  try {
41864
- fields = asParams(JSON.parse(readFileSync26(file, "utf8")));
42171
+ fields = asParams(JSON.parse(readFileSync28(file, "utf8")));
41865
42172
  } catch {
41866
42173
  return { kind: "rubbish" };
41867
42174
  }
@@ -41933,7 +42240,7 @@ function discoverRuntimes(directories = runtimeDirectories()) {
41933
42240
  function wentAway(file, pid) {
41934
42241
  let project = "";
41935
42242
  try {
41936
- const fields = asParams(JSON.parse(readFileSync26(file, "utf8")));
42243
+ const fields = asParams(JSON.parse(readFileSync28(file, "utf8")));
41937
42244
  project = readString(readParams(fields, "project") ?? {}, "path") ?? "";
41938
42245
  } catch {
41939
42246
  return;
@@ -42448,14 +42755,14 @@ var init_runtime_client = __esm(() => {
42448
42755
  });
42449
42756
 
42450
42757
  // src/boot-note.ts
42451
- import { mkdirSync as mkdirSync11, readFileSync as readFileSync27, writeFileSync as writeFileSync14 } from "node:fs";
42758
+ import { mkdirSync as mkdirSync11, readFileSync as readFileSync29, writeFileSync as writeFileSync14 } from "node:fs";
42452
42759
  import { dirname as dirname15, join as join31 } from "node:path";
42453
42760
  function bootNotePath(projectPath) {
42454
42761
  return join31(projectPath, ".godot", "gdharness-boot.json");
42455
42762
  }
42456
42763
  function readScenes(projectPath) {
42457
42764
  try {
42458
- return asParams(asParams(JSON.parse(readFileSync27(bootNotePath(projectPath), "utf8")))["scenes"]);
42765
+ return asParams(asParams(JSON.parse(readFileSync29(bootNotePath(projectPath), "utf8")))["scenes"]);
42459
42766
  } catch {
42460
42767
  return {};
42461
42768
  }
@@ -42497,7 +42804,7 @@ var init_boot_note = __esm(() => {
42497
42804
  });
42498
42805
 
42499
42806
  // src/breakpoint-note.ts
42500
- import { mkdirSync as mkdirSync12, readFileSync as readFileSync28, rmSync as rmSync14, writeFileSync as writeFileSync15 } from "node:fs";
42807
+ import { mkdirSync as mkdirSync12, readFileSync as readFileSync30, rmSync as rmSync14, writeFileSync as writeFileSync15 } from "node:fs";
42501
42808
  import { dirname as dirname16, isAbsolute as isAbsolute7, join as join32, relative as relative6 } from "node:path";
42502
42809
  function breakpointNotePath(projectPath) {
42503
42810
  return join32(projectPath, ".godot", "gdharness-breakpoints.json");
@@ -42526,7 +42833,7 @@ function writeBreakpointNote(projectPath, held) {
42526
42833
  }
42527
42834
  function readBreakpointNote(projectPath) {
42528
42835
  try {
42529
- const parsed = JSON.parse(readFileSync28(breakpointNotePath(projectPath), "utf8"));
42836
+ const parsed = JSON.parse(readFileSync30(breakpointNotePath(projectPath), "utf8"));
42530
42837
  if (typeof parsed !== "object" || parsed === null || !Array.isArray(parsed.breakpoints)) {
42531
42838
  return [];
42532
42839
  }
@@ -42559,14 +42866,14 @@ var init_breakpoint_note = __esm(() => {
42559
42866
  });
42560
42867
 
42561
42868
  // src/class-note.ts
42562
- import { mkdirSync as mkdirSync13, readFileSync as readFileSync29 } from "node:fs";
42869
+ import { mkdirSync as mkdirSync13, readFileSync as readFileSync31 } from "node:fs";
42563
42870
  import { dirname as dirname17, join as join33 } from "node:path";
42564
42871
  function classNotePath(projectPath) {
42565
42872
  return join33(projectPath, ".godot", "gdharness-classes.json");
42566
42873
  }
42567
42874
  function readClassNote(projectPath) {
42568
42875
  try {
42569
- const fields = asParams(JSON.parse(readFileSync29(classNotePath(projectPath), "utf8")));
42876
+ const fields = asParams(JSON.parse(readFileSync31(classNotePath(projectPath), "utf8")));
42570
42877
  const rebuiltWith = readArray(fields, "rebuiltWith");
42571
42878
  if (rebuiltWith === undefined || !rebuiltWith.every((name) => typeof name === "string")) {
42572
42879
  return null;
@@ -42608,7 +42915,7 @@ var init_class_note = __esm(() => {
42608
42915
  });
42609
42916
 
42610
42917
  // src/config-pin.ts
42611
- import { existsSync as existsSync27, readFileSync as readFileSync30 } from "node:fs";
42918
+ import { existsSync as existsSync27, readFileSync as readFileSync32 } from "node:fs";
42612
42919
  import { join as join34 } from "node:path";
42613
42920
  function versionsNamedInConfig(projectPath) {
42614
42921
  const config = join34(projectPath, ".mcp.json");
@@ -42617,7 +42924,7 @@ function versionsNamedInConfig(projectPath) {
42617
42924
  }
42618
42925
  let text;
42619
42926
  try {
42620
- text = readFileSync30(config, "utf8");
42927
+ text = readFileSync32(config, "utf8");
42621
42928
  JSON.parse(text);
42622
42929
  } catch {
42623
42930
  return [];
@@ -42638,7 +42945,7 @@ var init_config_pin = __esm(() => {
42638
42945
  });
42639
42946
 
42640
42947
  // src/cut-test-run.ts
42641
- import { existsSync as existsSync28, mkdirSync as mkdirSync14, readFileSync as readFileSync31, rmSync as rmSync15, writeFileSync as writeFileSync16 } from "node:fs";
42948
+ import { existsSync as existsSync28, mkdirSync as mkdirSync14, readFileSync as readFileSync33, rmSync as rmSync15, writeFileSync as writeFileSync16 } from "node:fs";
42642
42949
  import { dirname as dirname18, join as join35 } from "node:path";
42643
42950
  function keptAt(projectPath) {
42644
42951
  return join35(projectPath, ".godot", "gdharness-reports", "cut-run.json");
@@ -42657,7 +42964,7 @@ function takeCutTestRun(projectPath) {
42657
42964
  return null;
42658
42965
  }
42659
42966
  try {
42660
- const run = JSON.parse(readFileSync31(path, "utf8"));
42967
+ const run = JSON.parse(readFileSync33(path, "utf8"));
42661
42968
  rmSync15(path, { force: true });
42662
42969
  return typeof run.cutAt === "string" && Array.isArray(run.paths) && run.paths.every((path) => typeof path === "string") ? run : null;
42663
42970
  } catch {
@@ -43424,7 +43731,7 @@ import {
43424
43731
  existsSync as existsSync29,
43425
43732
  mkdirSync as mkdirSync15,
43426
43733
  openSync as openSync5,
43427
- readFileSync as readFileSync32,
43734
+ readFileSync as readFileSync34,
43428
43735
  readSync as readSync3,
43429
43736
  rmSync as rmSync16,
43430
43737
  statSync as statSync16,
@@ -43439,7 +43746,7 @@ function editorLogNotePath(projectPath) {
43439
43746
  }
43440
43747
  function readEditorLogNote(projectPath) {
43441
43748
  try {
43442
- const fields = asParams(JSON.parse(readFileSync32(editorLogNotePath(projectPath), "utf8")));
43749
+ const fields = asParams(JSON.parse(readFileSync34(editorLogNotePath(projectPath), "utf8")));
43443
43750
  const pid = readNumber(fields, "pid");
43444
43751
  const startedAt = readString(fields, "startedAt");
43445
43752
  const processStartedAt = readNumber(fields, "processStartedAt");
@@ -43543,7 +43850,7 @@ function foldProgress(text) {
43543
43850
  function theFirstOf(path) {
43544
43851
  const whole = statSync16(path).size;
43545
43852
  if (whole <= MOST_CONSOLE_BYTES) {
43546
- return { text: readFileSync32(path, "utf8") };
43853
+ return { text: readFileSync34(path, "utf8") };
43547
43854
  }
43548
43855
  const handle = openSync5(path, "r");
43549
43856
  try {
@@ -43704,7 +44011,7 @@ var init_editor_log = __esm(() => {
43704
44011
  });
43705
44012
 
43706
44013
  // src/rename.ts
43707
- import { existsSync as existsSync30, mkdirSync as mkdirSync16, readFileSync as readFileSync33, renameSync as renameSync4, unlinkSync as unlinkSync4, writeFileSync as writeFileSync18 } from "node:fs";
44014
+ import { existsSync as existsSync30, mkdirSync as mkdirSync16, readFileSync as readFileSync35, renameSync as renameSync4, unlinkSync as unlinkSync4, writeFileSync as writeFileSync18 } from "node:fs";
43708
44015
  import { dirname as dirname20, posix as posix4 } from "node:path";
43709
44016
 
43710
44017
  class Script {
@@ -44425,7 +44732,7 @@ function rewritesOf(plan, texts, move) {
44425
44732
  }
44426
44733
  function applyRewrites(rewrites) {
44427
44734
  for (const rewrite of rewrites) {
44428
- const now = readFileSync33(rewrite.absolute, "utf8");
44735
+ const now = readFileSync35(rewrite.absolute, "utf8");
44429
44736
  if (now !== rewrite.before) {
44430
44737
  throw new Error(`${rewrite.path} changed on disk while the rename was being worked out; nothing was written`);
44431
44738
  }
@@ -45012,7 +45319,7 @@ import {
45012
45319
  existsSync as existsSync32,
45013
45320
  mkdirSync as mkdirSync18,
45014
45321
  readdirSync as readdirSync18,
45015
- readFileSync as readFileSync34,
45322
+ readFileSync as readFileSync36,
45016
45323
  renameSync as renameSync5,
45017
45324
  statSync as statSync17,
45018
45325
  writeFileSync as writeFileSync20
@@ -45172,7 +45479,7 @@ function planFileMove(projectPath, absoluteFrom, absoluteTo) {
45172
45479
  plan = mentionIgnored(plan, ignoredTexts(projectPath), null, [from]);
45173
45480
  const binaries = [];
45174
45481
  for (const path of binaryResources(projectPath)) {
45175
- const bytes = readFileSync34(join38(projectPath, path.slice("res://".length)));
45482
+ const bytes = readFileSync36(join38(projectPath, path.slice("res://".length)));
45176
45483
  const names = [...moves.keys()].find((moving) => bytes.includes(moving));
45177
45484
  if (names !== undefined) {
45178
45485
  binaries.push({
@@ -45233,7 +45540,7 @@ function planFileMove(projectPath, absoluteFrom, absoluteTo) {
45233
45540
  }
45234
45541
  function applyFileMove(outcome) {
45235
45542
  for (const rewrite of outcome.inPlace) {
45236
- if (readFileSync34(rewrite.absolute, "utf8") !== rewrite.before) {
45543
+ if (readFileSync36(rewrite.absolute, "utf8") !== rewrite.before) {
45237
45544
  throw new Error(`${rewrite.path} changed on disk while the move was being worked out; nothing was written`);
45238
45545
  }
45239
45546
  }
@@ -45241,7 +45548,7 @@ function applyFileMove(outcome) {
45241
45548
  if (!existsSync32(file.absoluteFrom)) {
45242
45549
  throw new Error(`${file.from} is gone from disk; nothing was written`);
45243
45550
  }
45244
- if (file.text !== null && readFileSync34(file.absoluteFrom, "utf8") !== file.text.before) {
45551
+ if (file.text !== null && readFileSync36(file.absoluteFrom, "utf8") !== file.text.before) {
45245
45552
  throw new Error(`${file.from} changed on disk while the move was being worked out; nothing was written`);
45246
45553
  }
45247
45554
  if (existsSync32(file.absoluteTo) && !sameFile(file.absoluteFrom, file.absoluteTo)) {
@@ -49156,44 +49463,6 @@ function notListeningNote(servers, greetedAgoMs) {
49156
49463
  var STARTING_MS = 20000;
49157
49464
  var init_port_probe = () => {};
49158
49465
 
49159
- // src/process-time.ts
49160
- async function cpuSecondsOf(pid) {
49161
- try {
49162
- return process.platform === "win32" ? await secondsOnWindows(pid) : await askPosix2(pid);
49163
- } catch {
49164
- return;
49165
- }
49166
- }
49167
- async function secondsOnWindows(pid) {
49168
- const said = (await askProcessTable(`ProcessorSeconds(${pid})`, ASK_TIMEOUT_MS2)).trim();
49169
- const seconds = Number(said);
49170
- return said !== "" && Number.isFinite(seconds) ? seconds : undefined;
49171
- }
49172
- async function askPosix2(pid) {
49173
- const { stdout } = await runHidden("ps", ["-o", "time=", "-p", String(pid)], { timeout: ASK_TIMEOUT_MS2 });
49174
- return secondsFromClock(stdout.trim());
49175
- }
49176
- function secondsFromClock(printed) {
49177
- if (printed === "") {
49178
- return;
49179
- }
49180
- const dashed = printed.includes("-");
49181
- const days = dashed ? printed.slice(0, printed.indexOf("-")) : null;
49182
- const clock = dashed ? printed.slice(printed.indexOf("-") + 1) : printed;
49183
- const parts = clock.split(":").map((part) => Number(part));
49184
- if (parts.length === 0 || parts.some((part) => !Number.isFinite(part))) {
49185
- return;
49186
- }
49187
- const seconds = parts.reduce((total, part) => total * 60 + part, 0);
49188
- const extra = days === null ? 0 : Number(days) * 86400;
49189
- return Number.isFinite(extra) ? seconds + extra : undefined;
49190
- }
49191
- var ASK_TIMEOUT_MS2 = 6000;
49192
- var init_process_time = __esm(() => {
49193
- init_process_table();
49194
- init_spawn();
49195
- });
49196
-
49197
49466
  // src/prompts.ts
49198
49467
  function listedPrompts() {
49199
49468
  return PROMPT_SPECS.map((spec) => ({
@@ -49331,7 +49600,7 @@ var init_prompts = __esm(() => {
49331
49600
  });
49332
49601
 
49333
49602
  // src/restart-note.ts
49334
- import { mkdirSync as mkdirSync20, readFileSync as readFileSync35, rmSync as rmSync18, writeFileSync as writeFileSync21 } from "node:fs";
49603
+ import { mkdirSync as mkdirSync20, readFileSync as readFileSync37, rmSync as rmSync18, writeFileSync as writeFileSync21 } from "node:fs";
49335
49604
  import { dirname as dirname24, join as join40 } from "node:path";
49336
49605
  function restartNotePath(projectPath) {
49337
49606
  return join40(projectPath, ".godot", "gdharness-restart.json");
@@ -49348,7 +49617,7 @@ function noteRestartBegun(note) {
49348
49617
  }
49349
49618
  function restartOwed(projectPath) {
49350
49619
  try {
49351
- const parsed = JSON.parse(readFileSync35(restartNotePath(projectPath), "utf8"));
49620
+ const parsed = JSON.parse(readFileSync37(restartNotePath(projectPath), "utf8"));
49352
49621
  if (typeof parsed !== "object" || parsed === null) {
49353
49622
  return null;
49354
49623
  }
@@ -49374,7 +49643,7 @@ var init_restart_note = __esm(() => {
49374
49643
 
49375
49644
  // src/run-record.ts
49376
49645
  import { createHash as createHash7 } from "node:crypto";
49377
- import { mkdirSync as mkdirSync21, openSync as openSync6, readdirSync as readdirSync19, readFileSync as readFileSync36, rmSync as rmSync19, statSync as statSync19 } from "node:fs";
49646
+ import { mkdirSync as mkdirSync21, openSync as openSync6, readdirSync as readdirSync19, readFileSync as readFileSync38, rmSync as rmSync19, statSync as statSync19 } from "node:fs";
49378
49647
  import { basename as basename12, join as join41, resolve as resolve14 } from "node:path";
49379
49648
  function runsDirectory() {
49380
49649
  return join41(runtimeDirectory(), "runs");
@@ -49398,7 +49667,7 @@ function writeLatestRun(servedBy, latest) {
49398
49667
  }
49399
49668
  function readLatestRun(servedBy, directory = runtimeDirectory()) {
49400
49669
  try {
49401
- const fields = JSON.parse(readFileSync36(latestRunPath(servedBy, directory), "utf8"));
49670
+ const fields = JSON.parse(readFileSync38(latestRunPath(servedBy, directory), "utf8"));
49402
49671
  if (typeof fields !== "object" || fields === null) {
49403
49672
  return null;
49404
49673
  }
@@ -49447,7 +49716,7 @@ function readEditorRunNote(projectPath) {
49447
49716
  function editorNoteAt(path) {
49448
49717
  let parsed;
49449
49718
  try {
49450
- parsed = JSON.parse(readFileSync36(path, "utf8"));
49719
+ parsed = JSON.parse(readFileSync38(path, "utf8"));
49451
49720
  } catch {
49452
49721
  return null;
49453
49722
  }
@@ -49497,7 +49766,7 @@ function everyRunRecord() {
49497
49766
  function recordAt(path) {
49498
49767
  let parsed;
49499
49768
  try {
49500
- parsed = JSON.parse(readFileSync36(path, "utf8"));
49769
+ parsed = JSON.parse(readFileSync38(path, "utf8"));
49501
49770
  } catch {
49502
49771
  return null;
49503
49772
  }
@@ -49578,7 +49847,7 @@ async function runningAs(pid) {
49578
49847
  const began = await startedAt(pid, null);
49579
49848
  const when = began === null ? {} : { startedAt: began };
49580
49849
  if (process.platform === "linux") {
49581
- const raw = readFileSync36(`/proc/${pid}/cmdline`, "utf8").replaceAll("\x00", " ").trim();
49850
+ const raw = readFileSync38(`/proc/${pid}/cmdline`, "utf8").replaceAll("\x00", " ").trim();
49582
49851
  return raw === "" ? null : { kind: "commandLine", text: raw, ...when };
49583
49852
  }
49584
49853
  const args = (await runHidden("ps", ["-p", String(pid), "-o", "args="], { timeout: 15000 })).stdout.trim();
@@ -49774,10 +50043,10 @@ var init_run_record = __esm(() => {
49774
50043
  });
49775
50044
 
49776
50045
  // src/script-profile.ts
49777
- import { readFileSync as readFileSync37 } from "node:fs";
50046
+ import { readFileSync as readFileSync39 } from "node:fs";
49778
50047
  function readProfileFile(path) {
49779
50048
  try {
49780
- const parsed = JSON.parse(readFileSync37(path, "utf8"));
50049
+ const parsed = JSON.parse(readFileSync39(path, "utf8"));
49781
50050
  return typeof parsed === "object" && typeof parsed.connected === "boolean" ? parsed : null;
49782
50051
  } catch {
49783
50052
  return null;
@@ -49835,7 +50104,7 @@ function writeProfileMark(path, mark) {
49835
50104
  }
49836
50105
  function readProfileMark(path) {
49837
50106
  try {
49838
- const parsed = JSON.parse(readFileSync37(path, "utf8"));
50107
+ const parsed = JSON.parse(readFileSync39(path, "utf8"));
49839
50108
  return typeof parsed === "object" && typeof parsed.totals === "object" ? parsed : null;
49840
50109
  } catch {
49841
50110
  return null;
@@ -50167,7 +50436,7 @@ import {
50167
50436
  mkdirSync as mkdirSync23,
50168
50437
  openSync as openSync7,
50169
50438
  readdirSync as readdirSync20,
50170
- readFileSync as readFileSync38,
50439
+ readFileSync as readFileSync40,
50171
50440
  readSync as readSync4,
50172
50441
  realpathSync as realpathSync6,
50173
50442
  rmdirSync as rmdirSync2,
@@ -50188,9 +50457,9 @@ function greetingWaitMs() {
50188
50457
  return waitFromEnv("GDHARNESS_GREETING_WAIT_MS", 1e4);
50189
50458
  }
50190
50459
  function pictureToSend(pngPath, jpegPath) {
50191
- const png = readFileSync38(pngPath);
50460
+ const png = readFileSync40(pngPath);
50192
50461
  const beside = pngPath.replace(/\.png$/, ".jpg");
50193
- const jpeg = jpegPath !== undefined && normalize2(jpegPath) === normalize2(beside) && existsSync35(beside) ? readFileSync38(beside) : null;
50462
+ const jpeg = jpegPath !== undefined && normalize2(jpegPath) === normalize2(beside) && existsSync35(beside) ? readFileSync40(beside) : null;
50194
50463
  const sent = jpeg !== null && jpeg.length < png.length ? jpeg : png;
50195
50464
  return {
50196
50465
  data: sent.toString("base64"),
@@ -50666,7 +50935,7 @@ function endedToStartThis(ended) {
50666
50935
  return ` ${which} was ended to start this one; its output is no longer what editor_output answers about.`;
50667
50936
  }
50668
50937
  function lineNothingStopsOn(absolutePath, relativePath, line) {
50669
- const text = readFileSync38(absolutePath, "utf8");
50938
+ const text = readFileSync40(absolutePath, "utf8");
50670
50939
  const lines = text === "" ? 0 : text.split(`
50671
50940
  `).length - (text.endsWith(`
50672
50941
  `) ? 1 : 0);
@@ -50877,7 +51146,7 @@ function notRunNote(notRun, failFast) {
50877
51146
  function presetsIn(projectPath) {
50878
51147
  let text;
50879
51148
  try {
50880
- text = readFileSync38(join44(projectPath, "export_presets.cfg"), "utf8");
51149
+ text = readFileSync40(join44(projectPath, "export_presets.cfg"), "utf8");
50881
51150
  } catch {
50882
51151
  return null;
50883
51152
  }
@@ -50921,7 +51190,7 @@ function exportPresetRefusal(projectPath, preset) {
50921
51190
  }
50922
51191
  return names.length === 0 ? `export_presets.cfg names no preset, so there is no '${preset}' to export with. project_export add_preset adds one.` : `This project has no export preset named '${preset}'. Its presets are ${names.map((name) => `'${name}'`).join(", ")}.`;
50923
51192
  }
50924
- function exportAnswer(ending, asked, before, after, beside = [], docCacheNote = null, bytes = null) {
51193
+ function exportAnswer(ending, asked, before, after, beside = [], docCacheNote = null, bytes = null, imports = {}) {
50925
51194
  const { log } = ending;
50926
51195
  const written = after !== null && after !== before;
50927
51196
  const notCopied = librariesNotCopied(log.all);
@@ -50935,6 +51204,7 @@ function exportAnswer(ending, asked, before, after, beside = [], docCacheNote =
50935
51204
  warnings: log.count("warning"),
50936
51205
  ...written && bytes !== null ? { bytes } : {},
50937
51206
  ...beside.length === 0 ? {} : { alsoWritten: beside },
51207
+ ...imports,
50938
51208
  ...notCopied.length === 0 ? {} : {
50939
51209
  librariesNotLoaded: notCopied,
50940
51210
  extensionNote: `The export could not load ${notCopied.join(" and ")}: on Windows an engine running as the editor, which an export is, loads a GDExtension from a ~ copy beside the library, and another engine on this project held that copy: it could not be moved aside before the export, or another engine made a new one in between. Export again once that engine has loaded or exited.`
@@ -51052,7 +51322,7 @@ function rewriteBusNamed(projectPath, from, to) {
51052
51322
  const rewritten = [];
51053
51323
  for (const file of projectFiles(projectPath, [".tscn", ".tres"])) {
51054
51324
  const absolute = join44(projectPath, file);
51055
- const lines = readFileSync38(absolute, "utf8").split(`
51325
+ const lines = readFileSync40(absolute, "utf8").split(`
51056
51326
  `);
51057
51327
  const at = [];
51058
51328
  const after = lines.map((line, index) => {
@@ -51071,7 +51341,7 @@ function rewriteBusNamed(projectPath, from, to) {
51071
51341
  }
51072
51342
  const namedInScripts = [];
51073
51343
  for (const file of projectFiles(projectPath, [".gd"])) {
51074
- readFileSync38(join44(projectPath, file), "utf8").split(`
51344
+ readFileSync40(join44(projectPath, file), "utf8").split(`
51075
51345
  `).forEach((line, index) => {
51076
51346
  if (line.includes(`"${from}"`)) {
51077
51347
  namedInScripts.push({ file: `res://${file}`, line: index + 1, text: line.trim() });
@@ -51356,7 +51626,7 @@ function profileFrameFunctions(projectPath) {
51356
51626
  let projectGodot = null;
51357
51627
  if (projectPath !== null) {
51358
51628
  try {
51359
- projectGodot = readFileSync38(join44(projectPath, "project.godot"), "utf8");
51629
+ projectGodot = readFileSync40(join44(projectPath, "project.godot"), "utf8");
51360
51630
  } catch {}
51361
51631
  }
51362
51632
  return frameFunctionsFor(queuedMessagesOf(projectGodot));
@@ -51380,7 +51650,7 @@ function projectSourceOf(projectPath) {
51380
51650
  let source = null;
51381
51651
  if (inside.ok) {
51382
51652
  try {
51383
- source = readFileSync38(inside.absolutePath, "utf8");
51653
+ source = readFileSync40(inside.absolutePath, "utf8");
51384
51654
  } catch {}
51385
51655
  }
51386
51656
  read.set(script, source);
@@ -51552,7 +51822,7 @@ function foldedEntriesAnswer(log, floor = "warning") {
51552
51822
  }
51553
51823
  function gdUnitFailsOnScriptErrors(projectPath) {
51554
51824
  try {
51555
- return settingKeys(readFileSync38(join44(projectPath, "project.godot"), "utf8")).get("gdunit4/report/godot/script_error") !== false;
51825
+ return settingKeys(readFileSync40(join44(projectPath, "project.godot"), "utf8")).get("gdunit4/report/godot/script_error") !== false;
51556
51826
  } catch {
51557
51827
  return true;
51558
51828
  }
@@ -51699,11 +51969,11 @@ function isDirectory(path) {
51699
51969
  }
51700
51970
  }
51701
51971
  function mainSceneOf(projectFile) {
51702
- const scene = parseProjectGodot(readFileSync38(projectFile, "utf8"))["application"]?.["run/main_scene"];
51972
+ const scene = parseProjectGodot(readFileSync40(projectFile, "utf8"))["application"]?.["run/main_scene"];
51703
51973
  return typeof scene === "string" && scene !== "" ? scene : null;
51704
51974
  }
51705
51975
  function editorPlaysHeadless(projectFile) {
51706
- const runArgs = parseProjectGodot(readFileSync38(projectFile, "utf8"))["editor"]?.["run/main_run_args"];
51976
+ const runArgs = parseProjectGodot(readFileSync40(projectFile, "utf8"))["editor"]?.["run/main_run_args"];
51707
51977
  return typeof runArgs === "string" && /(?:^|\s)--headless(?:\s|$)/.test(runArgs);
51708
51978
  }
51709
51979
  function ownArguments(params) {
@@ -53645,7 +53915,7 @@ class GodotServer {
53645
53915
  if (!engine.ok) {
53646
53916
  return engine.response;
53647
53917
  }
53648
- const application = parseProjectGodot(readFileSync38(project.value.file, "utf8"))["application"] ?? {};
53918
+ const application = parseProjectGodot(readFileSync40(project.value.file, "utf8"))["application"] ?? {};
53649
53919
  const name = application["config/name"];
53650
53920
  const mainScene = application["run/main_scene"];
53651
53921
  const info = {
@@ -53902,6 +54172,7 @@ ${platform}`;
53902
54172
  if (pack && extension !== ".pck" && extension !== ".zip") {
53903
54173
  return this.createErrorResponse(`A pack is written as a .pck or a .zip, and ${output.relativePath} is neither: the exporter picks the format by the extension.`, ["Give outputPath ending in .pck, or .zip for a zip of the resources"]);
53904
54174
  }
54175
+ const imported = await this.importWhatChanged(project.value.path);
53905
54176
  const exportLogs = scratchDirectory("export");
53906
54177
  const exportArgs = [
53907
54178
  "--headless",
@@ -53944,7 +54215,7 @@ ${platform}`;
53944
54215
  return this.createErrorResponse(`Export could not be run: ${ending.failure ?? "the engine gave no exit status"}`);
53945
54216
  }
53946
54217
  const beside = writtenBeside(listedBefore, filesIn(folder), basename14(output.absolutePath), dirname25(output.relativePath).replaceAll("\\", "/"));
53947
- return exportAnswer(ending, { preset, outputPath: output.relativePath, debug, ...pack ? { pack } : {} }, before, writtenAt(), beside, docCacheNote, existsSync35(output.absolutePath) ? statSync20(output.absolutePath).size : null);
54218
+ return exportAnswer(ending, { preset, outputPath: output.relativePath, debug, ...pack ? { pack } : {} }, before, writtenAt(), beside, docCacheNote, existsSync35(output.absolutePath) ? statSync20(output.absolutePath).size : null, importedFirstAnswer(imported));
53948
54219
  }
53949
54220
  reportTestProgress(log) {
53950
54221
  const progress = callProgress();
@@ -54213,13 +54484,13 @@ ${platform}`;
54213
54484
  const reportsDir = join44(project.value.path, ".godot", ...ours.split("/"));
54214
54485
  let report = null;
54215
54486
  let reportProblem = null;
54216
- const casePrinted = withNestedErrors(readRun(existsSync35(engineLog) ? readFileSync38(engineLog, "utf8") : ""), run.log.all);
54487
+ const casePrinted = withNestedErrors(readRun(existsSync35(engineLog) ? readFileSync40(engineLog, "utf8") : ""), run.log.all);
54217
54488
  const declaredErrors = readExpected(expectedFile);
54218
54489
  try {
54219
54490
  const written = existsSync35(reportsDir) && notCompiled === null ? readdirSync20(reportsDir).filter((name) => name.startsWith("report_")).sort((a, b) => Number(a.slice("report_".length)) - Number(b.slice("report_".length))) : [];
54220
54491
  const newest = written.at(-1);
54221
54492
  if (newest !== undefined) {
54222
- report = parseJUnit(readFileSync38(join44(reportsDir, newest, "results.xml"), "utf8"));
54493
+ report = parseJUnit(readFileSync40(join44(reportsDir, newest, "results.xml"), "utf8"));
54223
54494
  }
54224
54495
  } catch (error) {
54225
54496
  reportProblem = errorMessage(error);
@@ -54765,7 +55036,7 @@ ${error.text}`;
54765
55036
  return null;
54766
55037
  }
54767
55038
  try {
54768
- return readFileSync38(contained.absolutePath, "utf8");
55039
+ return readFileSync40(contained.absolutePath, "utf8");
54769
55040
  } catch {
54770
55041
  return null;
54771
55042
  }
@@ -54859,7 +55130,7 @@ ${error.text}`;
54859
55130
  return null;
54860
55131
  }
54861
55132
  try {
54862
- return readFileSync38(contained.absolutePath, "utf8");
55133
+ return readFileSync40(contained.absolutePath, "utf8");
54863
55134
  } catch {
54864
55135
  return null;
54865
55136
  }
@@ -55378,7 +55649,7 @@ ${error.text}`;
55378
55649
  return null;
55379
55650
  }
55380
55651
  try {
55381
- return settingKeys(readFileSync38(join44(projectPath, "project.godot"), "utf8"));
55652
+ return settingKeys(readFileSync40(join44(projectPath, "project.godot"), "utf8"));
55382
55653
  } catch {
55383
55654
  return null;
55384
55655
  }
@@ -56214,7 +56485,7 @@ ${error.text}`;
56214
56485
  return 0;
56215
56486
  }
56216
56487
  try {
56217
- return reportAlreadyInTranscript(readFileSync38(game.transcript), readFileSync38(report));
56488
+ return reportAlreadyInTranscript(readFileSync40(game.transcript), readFileSync40(report));
56218
56489
  } catch (error) {
56219
56490
  this.logDebug(`Could not compare a picked-up run's report with its transcript: ${errorMessage(error)}`);
56220
56491
  return 0;
@@ -58685,7 +58956,7 @@ ${error.text}`;
58685
58956
  }
58686
58957
  const escaped = symbol.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
58687
58958
  const declared = new RegExp(`^\\s*(?:@\\w+(?:\\([^)]*\\))?\\s+)*class_name\\s+${escaped}\\b`, "m");
58688
- if (declared.test(readFileSync38(scriptPath, "utf8"))) {
58959
+ if (declared.test(readFileSync40(scriptPath, "utf8"))) {
58689
58960
  return null;
58690
58961
  }
58691
58962
  const checked = await this.classesTheEditorCannotSee(args);
@@ -59374,7 +59645,7 @@ init_alive();
59374
59645
  init_engine_launch();
59375
59646
  init_lsp_client();
59376
59647
  init_process_children();
59377
- import { existsSync as existsSync18, readdirSync as readdirSync11, readFileSync as readFileSync17, unlinkSync as unlinkSync2 } from "node:fs";
59648
+ import { existsSync as existsSync18, readdirSync as readdirSync11, readFileSync as readFileSync19, unlinkSync as unlinkSync2 } from "node:fs";
59378
59649
  import { connect as connect2 } from "node:net";
59379
59650
  import { join as join21, resolve as resolve8 } from "node:path";
59380
59651
  var EDITOR_RECORD_PROTOCOL = 1;
@@ -59393,7 +59664,7 @@ function editorRecordsIn(projectPath) {
59393
59664
  for (const name of names) {
59394
59665
  const file = join21(directory, name);
59395
59666
  try {
59396
- const said = JSON.parse(readFileSync17(file, "utf8"));
59667
+ const said = JSON.parse(readFileSync19(file, "utf8"));
59397
59668
  const { protocol, pid, lsp_port: lspPort, written_at_ms: writtenAtMs } = said;
59398
59669
  if (protocol === EDITOR_RECORD_PROTOCOL && typeof pid === "number" && typeof lspPort === "number" && lspPort > 0 && typeof writtenAtMs === "number") {
59399
59670
  records.push({ file, pid, lspPort, writtenAtMs });
@@ -59904,8 +60175,8 @@ init_engine_slots();
59904
60175
  init_log_read();
59905
60176
  init_setup();
59906
60177
  init_suite_judge();
59907
- import { existsSync as existsSync21, mkdirSync as mkdirSync8, readFileSync as readFileSync20, rmSync as rmSync12 } from "node:fs";
59908
- import { availableParallelism as availableParallelism3 } from "node:os";
60178
+ import { existsSync as existsSync21, mkdirSync as mkdirSync8, readFileSync as readFileSync22, rmSync as rmSync12 } from "node:fs";
60179
+ import { availableParallelism as availableParallelism4 } from "node:os";
59909
60180
  import { join as join24, resolve as resolve10 } from "node:path";
59910
60181
  import process13 from "node:process";
59911
60182
  var SHARE = "--gdh-share=";
@@ -60061,7 +60332,7 @@ async function runSpreadCommand(options, godotPath) {
60061
60332
  let workers = options.workers;
60062
60333
  if (workers === null) {
60063
60334
  const memory = await memoryAvailable();
60064
- const processors = options.cores ?? availableParallelism3();
60335
+ const processors = options.cores ?? availableParallelism4();
60065
60336
  const room = roomFor(slotCount, processors, memory.bytes, memory.said);
60066
60337
  workers = room.engines;
60067
60338
  console.log(`${options.scene} over ${plural(workers, "worker")}${room.held === undefined ? "" : `, since ${room.held}`}.`);
@@ -60092,7 +60363,7 @@ async function runSpreadCommand(options, godotPath) {
60092
60363
  const faults = left.flatMap((worker) => engineFaults(worker, options.godotVersion));
60093
60364
  const arms = left.map((_, index) => {
60094
60365
  const file = join24(shareDirectory, `worker-${String(index)}.arms`);
60095
- return existsSync21(file) ? armsPlayed(readFileSync20(file, "utf8")) : [];
60366
+ return existsSync21(file) ? armsPlayed(readFileSync22(file, "utf8")) : [];
60096
60367
  });
60097
60368
  faults.push(...shareFaults(arms));
60098
60369
  if (faults.length > 0) {
@@ -60102,7 +60373,7 @@ async function runSpreadCommand(options, godotPath) {
60102
60373
  console.log(readLog(gather.log).replace(CONTROL_SEQUENCE, "").trimEnd());
60103
60374
  const gatherFaults = engineFaults(gather, options.godotVersion);
60104
60375
  const gatheredFile = join24(shareDirectory, "gathered.arms");
60105
- const gathered = new Set((existsSync21(gatheredFile) ? armsPlayed(readFileSync20(gatheredFile, "utf8")) ?? [] : []).map((arm) => arm.arm));
60376
+ const gathered = new Set((existsSync21(gatheredFile) ? armsPlayed(readFileSync22(gatheredFile, "utf8")) ?? [] : []).map((arm) => arm.arm));
60106
60377
  const ungathered = (arms[0] ?? []).filter((arm) => !gathered.has(arm.arm)).map((arm) => arm.arm);
60107
60378
  if (ungathered.length > 0) {
60108
60379
  gatherFaults.push(`the gathering run never asked for ${ungathered.join(", ")}, which the workers played: it went another way through the arms than they did`);
@@ -60197,18 +60468,19 @@ init_import_freshness();
60197
60468
  init_launch();
60198
60469
  init_library_copies();
60199
60470
  init_log_read();
60471
+ init_reimport();
60200
60472
  init_setup();
60201
60473
  import {
60202
60474
  copyFileSync,
60203
60475
  existsSync as existsSync24,
60204
60476
  mkdirSync as mkdirSync10,
60205
60477
  readdirSync as readdirSync15,
60206
- readFileSync as readFileSync24,
60478
+ readFileSync as readFileSync26,
60207
60479
  rmSync as rmSync13,
60208
60480
  statSync as statSync14,
60209
60481
  writeFileSync as writeFileSync13
60210
60482
  } from "node:fs";
60211
- import { availableParallelism as availableParallelism4 } from "node:os";
60483
+ import { availableParallelism as availableParallelism5 } from "node:os";
60212
60484
  import { dirname as dirname13, join as join28, relative as relative5, resolve as resolve11, sep as sep4 } from "node:path";
60213
60485
  import process14 from "node:process";
60214
60486
 
@@ -60216,7 +60488,7 @@ import process14 from "node:process";
60216
60488
  init_class_cache();
60217
60489
  init_gdscript_source();
60218
60490
  import { createHash as createHash6 } from "node:crypto";
60219
- import { existsSync as existsSync22, readdirSync as readdirSync14, readFileSync as readFileSync21, statSync as statSync13 } from "node:fs";
60491
+ import { existsSync as existsSync22, readdirSync as readdirSync14, readFileSync as readFileSync23, statSync as statSync13 } from "node:fs";
60220
60492
  import { join as join25, posix as posix2 } from "node:path";
60221
60493
  var EVERY_FILE = "res://";
60222
60494
  var CACHE_HELD = "cache:";
@@ -60240,7 +60512,7 @@ function reachedBySuite(file) {
60240
60512
  const paths = (value) => Array.isArray(value) ? value.filter((path) => typeof path === "string") : [];
60241
60513
  let autoloads = [];
60242
60514
  const resident = new Map;
60243
- for (const line of readFileSync21(file, "utf8").split(`
60515
+ for (const line of readFileSync23(file, "utf8").split(`
60244
60516
  `)) {
60245
60517
  if (line.trim() === "") {
60246
60518
  continue;
@@ -60335,7 +60607,7 @@ ${this.read(path).digest}
60335
60607
  let modified = 0;
60336
60608
  walkSeen(this.scripts.projectPath, (file, under, stat) => {
60337
60609
  hash.update(`${under}
60338
- `).update(readFileSync21(file));
60610
+ `).update(readFileSync23(file));
60339
60611
  modified = Math.max(modified, stat.mtimeMs);
60340
60612
  });
60341
60613
  return { digest: hash.digest("hex"), follows: [], also: [], modified };
@@ -60347,7 +60619,7 @@ ${this.read(path).digest}
60347
60619
  if (existsSync22(directory)) {
60348
60620
  walkSeen(directory, (file, under, stat) => {
60349
60621
  hash.update(`${under}
60350
- `).update(readFileSync21(file));
60622
+ `).update(readFileSync23(file));
60351
60623
  modified = Math.max(modified, stat.mtimeMs);
60352
60624
  });
60353
60625
  }
@@ -60364,7 +60636,7 @@ ${this.read(path).digest}
60364
60636
  return { digest: inside.join(`
60365
60637
  `), follows: [], also: [], modified: stat.mtimeMs };
60366
60638
  }
60367
- const bytes = readFileSync21(file);
60639
+ const bytes = readFileSync23(file);
60368
60640
  const digest = createHash6("sha256").update(bytes).digest("hex");
60369
60641
  const also = [`${path}.import`, `${path}.uid`].filter((beside) => existsSync22(join25(this.scripts.projectPath, beside.slice("res://".length))));
60370
60642
  const follows = [];
@@ -60576,7 +60848,7 @@ function walkSeen(root, seen) {
60576
60848
  }
60577
60849
  function extensionsOf(projectPath) {
60578
60850
  const list = join25(projectPath, ".godot", "extension_list.cfg");
60579
- return existsSync22(list) ? readFileSync21(list, "utf8").split(`
60851
+ return existsSync22(list) ? readFileSync23(list, "utf8").split(`
60580
60852
  `).map((line) => line.trim()).filter((line) => line.startsWith("res://")) : [];
60581
60853
  }
60582
60854
 
@@ -60585,7 +60857,7 @@ init_suite_judge();
60585
60857
  init_suite_plan();
60586
60858
 
60587
60859
  // src/version.ts
60588
- import { readFileSync as readFileSync23 } from "node:fs";
60860
+ import { readFileSync as readFileSync25 } from "node:fs";
60589
60861
  import { dirname as dirname12, join as join27 } from "node:path";
60590
60862
  import { fileURLToPath as fileURLToPath3 } from "node:url";
60591
60863
  function getLocalVersion() {
@@ -60596,7 +60868,7 @@ function getLocalVersion() {
60596
60868
  ];
60597
60869
  for (const packagePath of packageCandidates) {
60598
60870
  try {
60599
- const value = JSON.parse(readFileSync23(packagePath, "utf-8"));
60871
+ const value = JSON.parse(readFileSync25(packagePath, "utf-8"));
60600
60872
  if (typeof value === "object" && value !== null && "name" in value && value.name === "gdharness" && "version" in value && typeof value.version === "string") {
60601
60873
  return value.version;
60602
60874
  }
@@ -60865,10 +61137,10 @@ async function runTestCommand(options, godotPath) {
60865
61137
  const suitesBegan = Date.now();
60866
61138
  const always = [
60867
61139
  getLocalVersion(),
60868
- readFileSync24(shippedListedRunner(), "utf8"),
61140
+ readFileSync26(shippedListedRunner(), "utf8"),
60869
61141
  engineSaid(godotPath),
60870
61142
  ...options.reachAlso.map((path) => `${path}
60871
- ${existsSync24(path) ? readFileSync24(path, "latin1") : "missing"}`)
61143
+ ${existsSync24(path) ? readFileSync26(path, "latin1") : "missing"}`)
60872
61144
  ];
60873
61145
  const everySuite = suites;
60874
61146
  if (options.changedOnly) {
@@ -60907,7 +61179,7 @@ ${existsSync24(path) ? readFileSync24(path, "latin1") : "missing"}`)
60907
61179
  if (memory.unread !== undefined) {
60908
61180
  console.log(`Windows did not say how much memory it can still commit (${memory.unread}), so engines are sized by the physical memory free.`);
60909
61181
  }
60910
- const room = options.cores === null ? roomFor(options.engines, availableParallelism4(), memory.bytes, memory.said) : roomFor(options.engines, options.cores, memory.bytes, memory.said, `--cores holds it to ${plural(options.cores, "processor")}`);
61182
+ const room = options.cores === null ? roomFor(options.engines, availableParallelism5(), memory.bytes, memory.said) : roomFor(options.engines, options.cores, memory.bytes, memory.said, `--cores holds it to ${plural(options.cores, "processor")}`);
60911
61183
  if (options.cores !== null && process14.platform === "darwin") {
60912
61184
  console.log("macOS offers no way to hold a process to some processors, so --cores holds only the number of engines here.");
60913
61185
  }
@@ -60998,7 +61270,7 @@ function roomSaid(engines, lanes, unheld, held, asked) {
60998
61270
  function readPassed(file) {
60999
61271
  let parsed;
61000
61272
  try {
61001
- parsed = JSON.parse(readFileSync24(file, "utf8"));
61273
+ parsed = JSON.parse(readFileSync26(file, "utf8"));
61002
61274
  } catch {
61003
61275
  return {};
61004
61276
  }
@@ -61114,7 +61386,7 @@ async function compileCheck(launch, suites, handed) {
61114
61386
  } finally {
61115
61387
  launch.slots.release(lock);
61116
61388
  }
61117
- return { faults: judgeCompile(readRun(readFileSync24(log, "utf8")), end), log };
61389
+ return { faults: judgeCompile(readRun(readFileSync26(log, "utf8")), end), log };
61118
61390
  }
61119
61391
  function printCompiled(compiled, logs) {
61120
61392
  if (compiled.faults.length === 0) {
@@ -61146,7 +61418,8 @@ async function importProject(launch) {
61146
61418
  console.log(`Wrote .godot/extension_list.cfg naming ${named(seeded)}, so the import loads them.`);
61147
61419
  }
61148
61420
  const cached = cachedClasses(launch.projectPath);
61149
- if ((seeded === null || seeded.length === 0) && cached !== null && staleAgainst(cached, launch.projectPath).length === 0 && staleImports(launch.projectPath).length === 0) {
61421
+ const stale = staleImports(launch.projectPath);
61422
+ if ((seeded === null || seeded.length === 0) && cached !== null && staleAgainst(cached, launch.projectPath).length === 0 && stale.length === 0) {
61150
61423
  console.log("The import was skipped: every imported file and the class cache are current.");
61151
61424
  return null;
61152
61425
  }
@@ -61156,6 +61429,9 @@ async function importProject(launch) {
61156
61429
  const lock = await launch.slots.take();
61157
61430
  try {
61158
61431
  freeLibraryCopies(launch.projectPath);
61432
+ for (const one of stale) {
61433
+ forceNextImport(launch.projectPath, one.path);
61434
+ }
61159
61435
  const ended = await watchedEngine(launch, lock, ["--headless", "--audio-driver", "Dummy", "--path", launch.projectPath, "--import"], directory, log, launch.silenceFloorSeconds);
61160
61436
  if (ended.ended !== null || ended.code !== 0) {
61161
61437
  return `${ended.ended ?? `the engine exited ${exitNamed(ended.code ?? 0)}`}; everything it printed is in ${launch.logs.named(log)}`;