gdharness 1.1.19 → 1.1.21

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
@@ -7850,6 +7850,27 @@ var init_game_log = __esm(() => {
7850
7850
  };
7851
7851
  });
7852
7852
 
7853
+ // src/reimport.ts
7854
+ import { createHash } from "node:crypto";
7855
+ import { existsSync as existsSync5, readFileSync as readFileSync3, rmSync as rmSync3, statSync as statSync2, utimesSync, writeFileSync as writeFileSync3 } from "node:fs";
7856
+ import { basename as basename2, join as join5 } from "node:path";
7857
+ function forceNextImport(projectPath, resourcePath) {
7858
+ const relative = resourcePath.replace(/^res:\/\//, "");
7859
+ const sidecar = join5(projectPath, `${relative}.import`);
7860
+ if (!existsSync5(sidecar)) {
7861
+ return;
7862
+ }
7863
+ const { atime, mtimeMs } = statSync2(sidecar);
7864
+ writeFileSync3(sidecar, readFileSync3(sidecar, "utf8").replace(/^valid=false\r?\n/m, ""));
7865
+ utimesSync(sidecar, atime, new Date(mtimeMs + 2000));
7866
+ rmSync3(importRecord(projectPath, resourcePath), { force: true });
7867
+ }
7868
+ function importRecord(projectPath, resourcePath) {
7869
+ const digest = createHash("md5").update(resourcePath).digest("hex");
7870
+ return join5(projectPath, ".godot", "imported", `${basename2(resourcePath)}-${digest}.md5`);
7871
+ }
7872
+ var init_reimport = () => {};
7873
+
7853
7874
  // src/alive.ts
7854
7875
  import process5 from "node:process";
7855
7876
  function alive(pid) {
@@ -7866,12 +7887,12 @@ function alive(pid) {
7866
7887
  var init_alive = () => {};
7867
7888
 
7868
7889
  // src/scratch.ts
7869
- import { existsSync as existsSync5, mkdtempSync, readdirSync as readdirSync2, rmSync as rmSync3 } from "node:fs";
7890
+ import { existsSync as existsSync6, mkdtempSync, readdirSync as readdirSync2, rmSync as rmSync4 } from "node:fs";
7870
7891
  import { tmpdir } from "node:os";
7871
- import { join as join5 } from "node:path";
7892
+ import { join as join6 } from "node:path";
7872
7893
  import process6 from "node:process";
7873
7894
  function scratchDirectory(kind) {
7874
- return mkdtempSync(join5(tmpdir(), `gdharness-${kind}-${process6.pid}-`));
7895
+ return mkdtempSync(join6(tmpdir(), `gdharness-${kind}-${process6.pid}-`));
7875
7896
  }
7876
7897
  function sweepAbandonedScratch(directory = tmpdir()) {
7877
7898
  let entries;
@@ -7887,7 +7908,7 @@ function sweepAbandonedScratch(directory = tmpdir()) {
7887
7908
  continue;
7888
7909
  }
7889
7910
  try {
7890
- remove(join5(directory, entry));
7911
+ remove(join6(directory, entry));
7891
7912
  } catch {}
7892
7913
  }
7893
7914
  }
@@ -7913,7 +7934,7 @@ function discard(...paths) {
7913
7934
  return left;
7914
7935
  }
7915
7936
  function remove(path) {
7916
- rmSync3(path, SWEPT);
7937
+ rmSync4(path, SWEPT);
7917
7938
  }
7918
7939
  function tryAgain(path, attempt) {
7919
7940
  if (attempt === 0) {
@@ -7925,7 +7946,7 @@ function tryAgain(path, attempt) {
7925
7946
  lastTryOnExit();
7926
7947
  }
7927
7948
  const after = AGAIN_AFTER_MS[attempt];
7928
- if (after === undefined || !existsSync5(path)) {
7949
+ if (after === undefined || !existsSync6(path)) {
7929
7950
  waiting.delete(path);
7930
7951
  if (after === undefined) {
7931
7952
  atExit.add(path);
@@ -7973,8 +7994,8 @@ var init_scratch = __esm(() => {
7973
7994
 
7974
7995
  // src/headless.ts
7975
7996
  import { execFile as execFile3 } from "node:child_process";
7976
- import { writeFileSync as writeFileSync3 } from "node:fs";
7977
- import { join as join6 } from "node:path";
7997
+ import { writeFileSync as writeFileSync4 } from "node:fs";
7998
+ import { join as join7 } from "node:path";
7978
7999
  import { promisify as promisify3 } from "node:util";
7979
8000
  function snakeCased(params) {
7980
8001
  const result = emptyRecord();
@@ -8023,21 +8044,36 @@ function librariesNotCopied(messages) {
8023
8044
  }
8024
8045
  return [...found];
8025
8046
  }
8026
- function extensionRetryNote(libraries) {
8027
- return `The first import could not load ${libraries.join(" and ")}: on Windows an engine running as the editor ` + "loads a GDExtension from a ~ copy beside the library, and the editor open on this project holds its own " + "copy there, so the first engine started beside it cannot make one. That failed attempt moves the " + "editor's copy aside, so the import was run again and loaded the extension, and the messages are from " + "that second run. The editor's copy is left beside the library as a ~RF….TMP file, which can be deleted " + "once the editor has exited.";
8047
+ function extensionRetryNote(libraries, stillNotLoaded = []) {
8048
+ const second = stillNotLoaded.length === 0 ? "so the import was run again and loaded the extension, and the messages are from that second run." : `so the import was run again, and it still could not load ${stillNotLoaded.join(" and ")}: this answer is from an import without ${stillNotLoaded.length === 1 ? "it" : "them"}, and its messages say what the engine hit.`;
8049
+ return `The first import could not load ${libraries.join(" and ")}: on Windows an engine running as the editor ` + "loads a GDExtension from a ~ copy beside the library, and the editor open on this project holds its own " + "copy there, so the first engine started beside it cannot make one. That failed attempt moves the " + `editor's copy aside, ${second} The editor's copy is left beside the library as a ~RF….TMP file, which ` + "can be deleted once the editor has exited.";
8028
8050
  }
8029
- async function runImport(godotPath, projectPath, once = importOnce) {
8030
- const first = await once(godotPath, projectPath);
8051
+ async function runImport(godotPath, projectPath, options = {}) {
8052
+ const once = options.once ?? importOnce;
8053
+ const pass = () => {
8054
+ for (const target of options.forcing ?? []) {
8055
+ forceNextImport(projectPath, target);
8056
+ }
8057
+ return once(godotPath, projectPath);
8058
+ };
8059
+ const first = await pass();
8031
8060
  const collided = librariesNotCopied(first.messages);
8032
8061
  if (collided.length === 0) {
8033
8062
  return first;
8034
8063
  }
8035
- return { ...await once(godotPath, projectPath), librariesRetried: collided };
8064
+ const second = await pass();
8065
+ const still = librariesNotCopied(second.messages);
8066
+ return {
8067
+ ...second,
8068
+ librariesRetried: collided,
8069
+ ...still.length === 0 ? {} : { librariesNotLoaded: still },
8070
+ extensionNote: extensionRetryNote(collided, still)
8071
+ };
8036
8072
  }
8037
8073
  async function importOnce(godotPath, projectPath) {
8038
8074
  const logDir = scratchDirectory("import");
8039
8075
  try {
8040
- const { stderr } = await run3(godotPath, ["--headless", "--log-file", join6(logDir, "engine.log"), "--path", projectPath, "--import"], { signal: callSignal() });
8076
+ const { stderr } = await run3(godotPath, ["--headless", "--log-file", join7(logDir, "engine.log"), "--path", projectPath, "--import"], { signal: callSignal() });
8041
8077
  return { ok: true, messages: problems(stderr) };
8042
8078
  } catch (error) {
8043
8079
  if (error instanceof Error && "stdout" in error && "stderr" in error) {
@@ -8059,12 +8095,12 @@ async function importOnce(godotPath, projectPath) {
8059
8095
  }
8060
8096
  async function runOperation(engine, operation, params, projectPath) {
8061
8097
  const paramsDir = scratchDirectory("params");
8062
- const paramsFile = join6(paramsDir, `${operation}.json`);
8063
- writeFileSync3(paramsFile, JSON.stringify(snakeCased(params)), "utf8");
8098
+ const paramsFile = join7(paramsDir, `${operation}.json`);
8099
+ writeFileSync4(paramsFile, JSON.stringify(snakeCased(params)), "utf8");
8064
8100
  const args = [
8065
8101
  "--headless",
8066
8102
  "--log-file",
8067
- join6(paramsDir, "engine.log"),
8103
+ join7(paramsDir, "engine.log"),
8068
8104
  "--path",
8069
8105
  projectPath,
8070
8106
  "--script",
@@ -8108,14 +8144,15 @@ var run3;
8108
8144
  var init_headless = __esm(() => {
8109
8145
  init_call_signal();
8110
8146
  init_game_log();
8147
+ init_reimport();
8111
8148
  init_scratch();
8112
8149
  run3 = promisify3(execFile3);
8113
8150
  });
8114
8151
 
8115
8152
  // src/update-check.ts
8116
- import { existsSync as existsSync6, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync4 } from "node:fs";
8153
+ import { existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync5 } from "node:fs";
8117
8154
  import { homedir as homedir3, tmpdir as tmpdir2 } from "node:os";
8118
- import { join as join7 } from "node:path";
8155
+ import { join as join8 } from "node:path";
8119
8156
  function packageUrl(root) {
8120
8157
  return `${root.replace(/\/+$/, "")}/gdharness/latest`;
8121
8158
  }
@@ -8137,28 +8174,28 @@ function cacheDirectory(environment) {
8137
8174
  };
8138
8175
  const home = set("HOME") ?? homedir3();
8139
8176
  if (process.platform === "win32") {
8140
- return join7(set("LOCALAPPDATA") ?? home, "gdharness");
8177
+ return join8(set("LOCALAPPDATA") ?? home, "gdharness");
8141
8178
  }
8142
8179
  if (process.platform === "darwin") {
8143
- return join7(home, "Library", "Caches", "gdharness");
8180
+ return join8(home, "Library", "Caches", "gdharness");
8144
8181
  }
8145
- return join7(set("XDG_CACHE_HOME") ?? join7(home, ".cache"), "gdharness");
8182
+ return join8(set("XDG_CACHE_HOME") ?? join8(home, ".cache"), "gdharness");
8146
8183
  }
8147
8184
  function cacheFile(environment = process.env) {
8148
8185
  try {
8149
8186
  const directory = cacheDirectory(environment);
8150
8187
  mkdirSync3(directory, { recursive: true, mode: 448 });
8151
- return join7(directory, "update-check.json");
8188
+ return join8(directory, "update-check.json");
8152
8189
  } catch {
8153
- return join7(tmpdir2(), "gdharness-update-check.json");
8190
+ return join8(tmpdir2(), "gdharness-update-check.json");
8154
8191
  }
8155
8192
  }
8156
8193
  function readCache(path) {
8157
8194
  try {
8158
- if (!existsSync6(path)) {
8195
+ if (!existsSync7(path)) {
8159
8196
  return null;
8160
8197
  }
8161
- const parsed = JSON.parse(readFileSync3(path, "utf8"));
8198
+ const parsed = JSON.parse(readFileSync4(path, "utf8"));
8162
8199
  if (typeof parsed !== "object" || parsed === null) {
8163
8200
  return null;
8164
8201
  }
@@ -8175,7 +8212,7 @@ function readCache(path) {
8175
8212
  }
8176
8213
  function writeCache(path, entry) {
8177
8214
  try {
8178
- writeFileSync4(path, JSON.stringify(entry), { encoding: "utf8", mode: 384 });
8215
+ writeFileSync5(path, JSON.stringify(entry), { encoding: "utf8", mode: 384 });
8179
8216
  } catch {}
8180
8217
  }
8181
8218
  function parts(version) {
@@ -8306,7 +8343,7 @@ var init_update_check = __esm(() => {
8306
8343
  });
8307
8344
 
8308
8345
  // src/server-version.ts
8309
- import { readFileSync as readFileSync4 } from "node:fs";
8346
+ import { readFileSync as readFileSync5 } from "node:fs";
8310
8347
  function editorIsStale(addonVersion, serverVersion, addonDigest, shippedDigest) {
8311
8348
  if (addonDigest !== undefined && addonDigest !== "" && shippedDigest !== undefined) {
8312
8349
  return addonDigest !== shippedDigest;
@@ -8348,7 +8385,7 @@ var init_server_version = __esm(() => {
8348
8385
  GODOT_DEBUG_MODE_DEFAULT = process.env["GODOT_DEBUG"] === "true" || DEBUG_MODE;
8349
8386
  SERVER_VERSION = (() => {
8350
8387
  try {
8351
- const pkg = JSON.parse(readFileSync4(new URL("../package.json", import.meta.url), "utf8"));
8388
+ const pkg = JSON.parse(readFileSync5(new URL("../package.json", import.meta.url), "utf8"));
8352
8389
  return typeof pkg.version === "string" ? pkg.version : "0.0.0";
8353
8390
  } catch {
8354
8391
  return "0.0.0";
@@ -8357,7 +8394,7 @@ var init_server_version = __esm(() => {
8357
8394
  });
8358
8395
 
8359
8396
  // src/issues.ts
8360
- import { createHash } from "node:crypto";
8397
+ import { createHash as createHash2 } from "node:crypto";
8361
8398
  import process7 from "node:process";
8362
8399
  function feedbackNotice(every) {
8363
8400
  return JSON.stringify({
@@ -8376,7 +8413,7 @@ function runtime() {
8376
8413
  function defectSignature(where, message) {
8377
8414
  const normalised = `${where}
8378
8415
  ${message}`.toLowerCase().replaceAll("\\", "/").replaceAll(/(?:[a-z]:)?(?:\/+[\w.-]+){2,}/gu, "<path>").replaceAll(/\d+/gu, "0");
8379
- return createHash("sha256").update(normalised).digest("hex").slice(0, 8);
8416
+ return createHash2("sha256").update(normalised).digest("hex").slice(0, 8);
8380
8417
  }
8381
8418
  function facts(where, message, godotVersion) {
8382
8419
  return [
@@ -8482,8 +8519,8 @@ var init_issues = __esm(() => {
8482
8519
  });
8483
8520
 
8484
8521
  // src/class-cache.ts
8485
- import { existsSync as existsSync7, readdirSync as readdirSync3, readFileSync as readFileSync5, statSync as statSync2 } from "node:fs";
8486
- import { join as join8 } from "node:path";
8522
+ import { existsSync as existsSync8, readdirSync as readdirSync3, readFileSync as readFileSync6, statSync as statSync3 } from "node:fs";
8523
+ import { join as join9 } from "node:path";
8487
8524
  function declaredClasses(projectPath) {
8488
8525
  const declared = new Map;
8489
8526
  eachScript(projectPath, (script, source) => {
@@ -8496,18 +8533,18 @@ function declaredClasses(projectPath) {
8496
8533
  }
8497
8534
  function eachScript(projectPath, each) {
8498
8535
  const visit = (directory, prefix) => {
8499
- if (existsSync7(join8(directory, ".gdignore"))) {
8536
+ if (existsSync8(join9(directory, ".gdignore"))) {
8500
8537
  return;
8501
8538
  }
8502
8539
  for (const entry of readdirSync3(directory, { withFileTypes: true })) {
8503
8540
  if (entry.name.startsWith(".")) {
8504
8541
  continue;
8505
8542
  }
8506
- const path = join8(directory, entry.name);
8543
+ const path = join9(directory, entry.name);
8507
8544
  if (entry.isDirectory()) {
8508
8545
  visit(path, `${prefix}${entry.name}/`);
8509
8546
  } else if (entry.isFile() && entry.name.endsWith(".gd")) {
8510
- each(`res://${prefix}${entry.name}`, readFileSync5(path, "utf8"));
8547
+ each(`res://${prefix}${entry.name}`, readFileSync6(path, "utf8"));
8511
8548
  }
8512
8549
  }
8513
8550
  };
@@ -8556,12 +8593,12 @@ function codeOf(source) {
8556
8593
  return code;
8557
8594
  }
8558
8595
  function cachedClasses(projectPath) {
8559
- const cache = join8(projectPath, ".godot", "global_script_class_cache.cfg");
8560
- if (!existsSync7(cache)) {
8596
+ const cache = join9(projectPath, ".godot", "global_script_class_cache.cfg");
8597
+ if (!existsSync8(cache)) {
8561
8598
  return null;
8562
8599
  }
8563
8600
  const listed = new Map;
8564
- const text = readFileSync5(cache, "utf8");
8601
+ const text = readFileSync6(cache, "utf8");
8565
8602
  for (const entry of text.matchAll(/"class":\s*&"([^"]+)"[\s\S]*?"path":\s*"([^"]+)"/g)) {
8566
8603
  listed.set(entry[1] ?? "", entry[2] ?? "");
8567
8604
  }
@@ -8604,32 +8641,33 @@ function missingMemberIn(message) {
8604
8641
  if (called !== null) {
8605
8642
  return { kind: "static method", member: called[1] ?? "", type: called[2] ?? "" };
8606
8643
  }
8607
- const inBase = /Cannot find member "([A-Za-z_]\w*)" in base "([A-Za-z_]\w*)((?:\.[A-Za-z_]\w*)*)"/.exec(message);
8644
+ const inBase = /Cannot find member "([A-Za-z_]\w*)" in base "([A-Za-z_]\w*)(?:\.([A-Za-z_]\w*))?"/.exec(message);
8608
8645
  if (inBase === null) {
8609
8646
  return null;
8610
8647
  }
8611
8648
  const member = inBase[1] ?? "";
8612
8649
  const type = inBase[2] ?? "";
8613
- const within = (inBase[3] ?? "").split(".").filter((part) => part !== "");
8614
- const enumName = within.at(-1);
8650
+ const enumName = inBase[3];
8615
8651
  return enumName === undefined ? { kind: "member", member, type } : { kind: "enum member", member, type, enum: enumName };
8616
8652
  }
8617
8653
  function declaresMember(source, missing) {
8618
8654
  const name = missing.member.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
8655
+ const top = String.raw`^(?:@\w+(?:\([^)]*\))?\s+)*`;
8656
+ const declared = (kinds) => new RegExp(String.raw`${top}(?:${kinds})\s+${name}\b`, "m").test(source);
8619
8657
  if (missing.kind === "method") {
8620
- return new RegExp(String.raw`^\s*(?:static\s+)?func\s+${name}\s*\(`, "m").test(source);
8658
+ return new RegExp(String.raw`${top}(?:static\s+)?func\s+${name}\s*\(`, "m").test(source);
8621
8659
  }
8622
8660
  if (missing.kind === "static method") {
8623
- return new RegExp(String.raw`^\s*static\s+func\s+${name}\s*\(`, "m").test(source);
8661
+ return new RegExp(String.raw`${top}static\s+func\s+${name}\s*\(`, "m").test(source);
8624
8662
  }
8625
8663
  if (missing.kind === "enum member") {
8626
8664
  return enumValues(source, missing.enum ?? "").includes(missing.member);
8627
8665
  }
8628
- const kinds = missing.kind === "property" ? "var|const|enum|class|signal|func" : "var|const|enum|class|signal";
8629
- return new RegExp(String.raw`^\s*(?:static\s+)?(?:@\w+(?:\([^)]*\))?\s+)*(?:${kinds})\s+${name}\b`, "m").test(source) || enumValues(source, null).includes(missing.member);
8666
+ const kinds = missing.kind === "property" ? String.raw`(?:static\s+)?(?:var|func)|const|enum|class|signal` : String.raw`static\s+(?:var|func)|const|enum|class`;
8667
+ return declared(kinds) || enumValues(source, null).includes(missing.member);
8630
8668
  }
8631
8669
  function enumValues(source, named) {
8632
- const opening = named === null ? /^\s*enum\s*\{([^}]*)\}/gm : new RegExp(String.raw`^\s*enum\s+${named.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\s*\{([^}]*)\}`, "gm");
8670
+ const opening = named === null ? /^enum\s*\{([^}]*)\}/gm : new RegExp(String.raw`^enum\s+${named.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\s*\{([^}]*)\}`, "gm");
8633
8671
  const values = [];
8634
8672
  for (const [, body] of source.matchAll(opening)) {
8635
8673
  const uncommented = (body ?? "").split(`
@@ -8672,21 +8710,22 @@ function classesNamedIn(source, known) {
8672
8710
  return [...named].sort();
8673
8711
  }
8674
8712
  function contradictedDiagnostics(messages, classes, sourceOf) {
8713
+ const keyOf = (missing) => [missing.type, missing.enum ?? "", missing.member].join(".");
8714
+ const denials = messages.map((message) => missingMemberIn(message)).filter((missing) => missing !== null);
8715
+ const calledOnTheClass = new Set(denials.filter((missing) => missing.kind === "static method").map((missing) => keyOf(missing)));
8675
8716
  const read = new Map;
8676
8717
  const found = new Map;
8677
- for (const message of messages) {
8678
- const missing = missingMemberIn(message);
8679
- const declaredIn = missing === null ? undefined : classes.get(missing.type);
8680
- if (missing === null || declaredIn === undefined) {
8718
+ for (const missing of denials) {
8719
+ const declaredIn = classes.get(missing.type);
8720
+ const key = keyOf(missing);
8721
+ if (declaredIn === undefined || missing.kind === "method" && calledOnTheClass.has(key)) {
8681
8722
  continue;
8682
8723
  }
8683
8724
  if (!read.has(declaredIn)) {
8684
8725
  read.set(declaredIn, sourceOf(declaredIn));
8685
8726
  }
8686
8727
  const source = read.get(declaredIn) ?? null;
8687
- const key = [missing.type, missing.enum ?? "", missing.member].join(".");
8688
- const already = found.get(key);
8689
- if (source !== null && declaresMember(source, missing) && already?.kind !== "static method") {
8728
+ if (source !== null && declaresMember(source, missing)) {
8690
8729
  found.set(key, { ...missing, declaredIn });
8691
8730
  }
8692
8731
  }
@@ -8725,7 +8764,8 @@ function classRemedy(entries) {
8725
8764
  }
8726
8765
  const declaring = declaringScripts(entries);
8727
8766
  const reload = declaring.length === 1 ? `editor_rescan with reloadScript set to ${declaring[0]}` : `editor_rescan with reloadScript, a call each for ${declaring.join(" and ")} since it takes one script,`;
8728
- return `For ${entries.map(written).join(", ")}, run ${reload} first. In the one project that has ` + "reproduced a stale enum member, twice, the reload cleared it at once both times, and the one " + "plain editor_rescan tried left the diagnostic standing.";
8767
+ const oneAtATime = declaring.length === 1 ? "" : " Take them one at a time: a rescan that brings in a class also reloads every script naming it and lists them under dependentsReloaded, so a script listed there needs no call of its own.";
8768
+ return `For ${entries.map(written).join(", ")}, run ${reload} first.${oneAtATime} In the one project ` + "that has reproduced a stale enum member, three times, the reload cleared it at once each time, " + "and the one plain editor_rescan tried left the diagnostic standing.";
8729
8769
  }
8730
8770
  function failedReloadNote(failed) {
8731
8771
  const missing = (before, after) => before.filter((name) => !after.includes(name));
@@ -8735,9 +8775,9 @@ function failedReloadNote(failed) {
8735
8775
  ];
8736
8776
  const unchanged = lost.length === 0 && missing(failed.heldAfter, failed.heldBefore).length === 0 && missing(failed.constantsAfter, failed.constantsBefore).length === 0;
8737
8777
  const answered = failed.codeName === "" ? `error ${failed.code}` : `error ${failed.code}, ${failed.codeName}`;
8738
- const copy = unchanged ? "The copy the editor holds is as it was, with the members under heldBeforeReload and heldConstantsBeforeReload." : `The copy the editor holds changed even so${lost.length === 0 ? "" : `, and has lost ${lost.join(", ")}`}: anything diagnosed against it now is answered from what is left, and reloading it once it compiles rebuilds it.`;
8778
+ const copy = unchanged ? "Nothing was reloaded: the copy the editor holds is as it was, with the members under heldBeforeReload and heldConstantsBeforeReload." : `The copy the editor holds changed even so${lost.length === 0 ? "" : `, and has lost ${lost.join(", ")}`}: anything diagnosed against it now is answered from what is left, and reloading it once it compiles rebuilds it.`;
8739
8779
  const order = failed.names.length === 0 ? "" : ` If they deny a member of ${failed.names.join(" or ")} that its file declares, the editor's copy of that class is behind: reload that class first with reloadScript and then this script, the order that cleared it in the one project that has met this.`;
8740
- return `${failed.script} did not compile, so nothing was reloaded: Godot answered ${answered}. ${copy} ` + `script_diagnostics on it gives the errors.${order}`;
8780
+ return `${failed.script} did not compile: Godot answered ${answered}. ${copy} ` + `script_diagnostics on it gives the errors.${order}`;
8741
8781
  }
8742
8782
  function uncachedClassNote(types) {
8743
8783
  if (types.length === 0) {
@@ -8747,9 +8787,9 @@ function uncachedClassNote(types) {
8747
8787
  return `${types.join(", ")} ${one ? "is declared" : "are declared"} in this project and missing from ` + `.godot/global_script_class_cache.cfg, so a game launched now would not resolve ${one ? "it" : "them"} ` + "either. Try editor_rescan first: it asks the editor already running and starts nothing, and it " + "cleared this in 314ms in a second project with a 31-worker fan-out still importing. It is not " + "always enough, because a file another engine has already imported reads as settled and the " + "walk does not look inside it. project_import refresh_classes rewrites the cache from the " + "declarations on disk and does reach that case, at the cost of a short headless engine, which " + "is worth knowing when something else is already running against this project.";
8748
8788
  }
8749
8789
  function cacheWrittenAt(projectPath) {
8750
- const cache = join8(projectPath, ".godot", "global_script_class_cache.cfg");
8790
+ const cache = join9(projectPath, ".godot", "global_script_class_cache.cfg");
8751
8791
  try {
8752
- return statSync2(cache).mtimeMs;
8792
+ return statSync3(cache).mtimeMs;
8753
8793
  } catch {
8754
8794
  return null;
8755
8795
  }
@@ -8761,14 +8801,14 @@ function declaredSince(projectPath, paths, names, since) {
8761
8801
  return false;
8762
8802
  }
8763
8803
  try {
8764
- return statSync2(join8(projectPath, path.slice("res://".length))).mtimeMs > since;
8804
+ return statSync3(join9(projectPath, path.slice("res://".length))).mtimeMs > since;
8765
8805
  } catch {
8766
8806
  return false;
8767
8807
  }
8768
8808
  });
8769
8809
  }
8770
8810
  function cachedAtMissingPaths(cached, projectPath) {
8771
- return [...cached].filter(([, path]) => path.startsWith("res://") && !existsSync7(join8(projectPath, path.slice("res://".length)))).map(([name]) => name).sort();
8811
+ return [...cached].filter(([, path]) => path.startsWith("res://") && !existsSync8(join9(projectPath, path.slice("res://".length)))).map(([name]) => name).sort();
8772
8812
  }
8773
8813
  function staleAgainst(cached, projectPath) {
8774
8814
  return [...declaredClasses(projectPath)].filter(([name, path]) => cached.get(name) !== path).map(([name]) => name);
@@ -15955,7 +15995,7 @@ var init_paths = __esm(() => {
15955
15995
  });
15956
15996
 
15957
15997
  // src/resources.ts
15958
- import { readFileSync as readFileSync6 } from "node:fs";
15998
+ import { readFileSync as readFileSync7 } from "node:fs";
15959
15999
  import { extname, resolve as resolve2 } from "node:path";
15960
16000
  function ensureProjectPath(getProjectPath) {
15961
16001
  const projectPath = getProjectPath();
@@ -16121,7 +16161,7 @@ function readResourceText(uri, getProjectPath) {
16121
16161
  const parsedUri = parseGodotUri(uri);
16122
16162
  if (parsedUri.kind === "project-info") {
16123
16163
  const projectFilePath = resolveProjectFile(projectPath, "project.godot");
16124
- const rawProject = readFileSync6(projectFilePath, "utf-8");
16164
+ const rawProject = readFileSync7(projectFilePath, "utf-8");
16125
16165
  const parsedProject = parseProjectGodot(rawProject);
16126
16166
  return {
16127
16167
  mimeType: "application/json",
@@ -16130,7 +16170,7 @@ function readResourceText(uri, getProjectPath) {
16130
16170
  }
16131
16171
  const filePath = resolveProjectFile(projectPath, parsedUri.resourcePath);
16132
16172
  ensureAllowedExtension(parsedUri.kind, filePath);
16133
- const text = readFileSync6(filePath, "utf-8");
16173
+ const text = readFileSync7(filePath, "utf-8");
16134
16174
  return {
16135
16175
  mimeType: parsedUri.kind === "script" ? "text/x-gdscript" : "text/plain",
16136
16176
  text
@@ -16259,42 +16299,42 @@ function readParams(params, key) {
16259
16299
 
16260
16300
  // src/setup.ts
16261
16301
  import { spawnSync } from "node:child_process";
16262
- import { createHash as createHash2 } from "node:crypto";
16302
+ import { createHash as createHash3 } from "node:crypto";
16263
16303
  import {
16264
16304
  cpSync,
16265
- existsSync as existsSync8,
16305
+ existsSync as existsSync9,
16266
16306
  mkdirSync as mkdirSync4,
16267
16307
  readdirSync as readdirSync4,
16268
- readFileSync as readFileSync7,
16308
+ readFileSync as readFileSync8,
16269
16309
  rmdirSync,
16270
- rmSync as rmSync4,
16271
- statSync as statSync3,
16272
- writeFileSync as writeFileSync5
16310
+ rmSync as rmSync5,
16311
+ statSync as statSync4,
16312
+ writeFileSync as writeFileSync6
16273
16313
  } from "node:fs";
16274
- import { dirname as dirname4, join as join9 } from "node:path";
16314
+ import { dirname as dirname4, join as join10 } from "node:path";
16275
16315
  import { fileURLToPath } from "node:url";
16276
16316
  function editorAddonDigest(from = shippedAddonsDirectory()) {
16277
- const hash = createHash2("sha256");
16317
+ const hash = createHash3("sha256");
16278
16318
  const walk = (directory, relative) => {
16279
16319
  for (const name of readdirSync4(directory).sort()) {
16280
16320
  if (name === VERSION_MARKER || name === DIGEST_MARKER) {
16281
16321
  continue;
16282
16322
  }
16283
- const path = join9(directory, name);
16323
+ const path = join10(directory, name);
16284
16324
  const inside = `${relative}/${name}`;
16285
- if (statSync3(path).isDirectory()) {
16325
+ if (statSync4(path).isDirectory()) {
16286
16326
  walk(path, inside);
16287
16327
  } else {
16288
16328
  hash.update(`${inside}
16289
16329
  `);
16290
- hash.update(readFileSync7(path));
16330
+ hash.update(readFileSync8(path));
16291
16331
  hash.update(`
16292
16332
  `);
16293
16333
  }
16294
16334
  }
16295
16335
  };
16296
16336
  for (const name of EDITOR_PLUGINS) {
16297
- walk(join9(from, name), name);
16337
+ walk(join10(from, name), name);
16298
16338
  }
16299
16339
  return hash.digest("hex");
16300
16340
  }
@@ -16309,58 +16349,58 @@ function shippedEditorDigest() {
16309
16349
  return shippedDigest ?? undefined;
16310
16350
  }
16311
16351
  function installedEditorDigest(projectPath) {
16312
- const marker = join9(projectPath, "addons", ADDONS[0], DIGEST_MARKER);
16313
- if (!existsSync8(marker)) {
16352
+ const marker = join10(projectPath, "addons", ADDONS[0], DIGEST_MARKER);
16353
+ if (!existsSync9(marker)) {
16314
16354
  return null;
16315
16355
  }
16316
- const said = readFileSync7(marker, "utf8").trim();
16356
+ const said = readFileSync8(marker, "utf8").trim();
16317
16357
  return said === "" ? null : said;
16318
16358
  }
16319
16359
  function shippedAddonsDirectory() {
16320
- return join9(dirname4(fileURLToPath(import.meta.url)), "godot", "addons");
16360
+ return join10(dirname4(fileURLToPath(import.meta.url)), "godot", "addons");
16321
16361
  }
16322
16362
  function shippedOperationsScript() {
16323
- return join9(dirname4(fileURLToPath(import.meta.url)), "godot", "operations", "godot_operations.gd");
16363
+ return join10(dirname4(fileURLToPath(import.meta.url)), "godot", "operations", "godot_operations.gd");
16324
16364
  }
16325
16365
  function installAddons(projectPath, from = shippedAddonsDirectory()) {
16326
16366
  const installed = [];
16327
16367
  for (const name of ADDONS) {
16328
- const source = join9(from, name);
16329
- if (!existsSync8(join9(source, name === "gdharness_runtime" ? "runtime_autoload.gd" : "plugin.cfg"))) {
16368
+ const source = join10(from, name);
16369
+ if (!existsSync9(join10(source, name === "gdharness_runtime" ? "runtime_autoload.gd" : "plugin.cfg"))) {
16330
16370
  throw new Error(`The package holds no ${name} addon at ${source}.`);
16331
16371
  }
16332
- const target = join9(projectPath, "addons", name);
16333
- const replaced = existsSync8(target);
16334
- rmSync4(target, { recursive: true, force: true });
16372
+ const target = join10(projectPath, "addons", name);
16373
+ const replaced = existsSync9(target);
16374
+ rmSync5(target, { recursive: true, force: true });
16335
16375
  mkdirSync4(dirname4(target), { recursive: true });
16336
16376
  cpSync(source, target, { recursive: true });
16337
- writeFileSync5(join9(target, VERSION_MARKER), `${SERVER_VERSION}
16377
+ writeFileSync6(join10(target, VERSION_MARKER), `${SERVER_VERSION}
16338
16378
  `);
16339
16379
  installed.push({ name, path: target, replaced });
16340
16380
  }
16341
- writeFileSync5(join9(projectPath, "addons", ADDONS[0], DIGEST_MARKER), `${editorAddonDigest(from)}
16381
+ writeFileSync6(join10(projectPath, "addons", ADDONS[0], DIGEST_MARKER), `${editorAddonDigest(from)}
16342
16382
  `);
16343
16383
  return installed;
16344
16384
  }
16345
16385
  function installedAddonVersion(projectPath) {
16346
- const marker = join9(projectPath, "addons", ADDONS[0], VERSION_MARKER);
16347
- if (!existsSync8(marker)) {
16386
+ const marker = join10(projectPath, "addons", ADDONS[0], VERSION_MARKER);
16387
+ if (!existsSync9(marker)) {
16348
16388
  return null;
16349
16389
  }
16350
- const said = readFileSync7(marker, "utf8").trim();
16390
+ const said = readFileSync8(marker, "utf8").trim();
16351
16391
  return said === "" ? null : said;
16352
16392
  }
16353
16393
  function removeAddons(projectPath) {
16354
16394
  const removed = [];
16355
16395
  for (const name of ADDONS) {
16356
- const target = join9(projectPath, "addons", name);
16357
- if (existsSync8(target)) {
16358
- rmSync4(target, { recursive: true, force: true });
16396
+ const target = join10(projectPath, "addons", name);
16397
+ if (existsSync9(target)) {
16398
+ rmSync5(target, { recursive: true, force: true });
16359
16399
  removed.push(target);
16360
16400
  }
16361
16401
  }
16362
- const addons = join9(projectPath, "addons");
16363
- if (removed.length > 0 && existsSync8(addons) && readdirSync4(addons).length === 0) {
16402
+ const addons = join10(projectPath, "addons");
16403
+ if (removed.length > 0 && existsSync9(addons) && readdirSync4(addons).length === 0) {
16364
16404
  rmdirSync(addons);
16365
16405
  removed.push(addons);
16366
16406
  }
@@ -16394,7 +16434,7 @@ function loaderAmong(projectPath, named) {
16394
16434
  }
16395
16435
  let source = null;
16396
16436
  try {
16397
- source = readFileSync7(join9(projectPath, path), "utf8");
16437
+ source = readFileSync8(join10(projectPath, path), "utf8");
16398
16438
  } catch {}
16399
16439
  if (source?.includes(RUNTIME_AUTOLOAD.path)) {
16400
16440
  return { name: entry, path, exists: true };
@@ -16436,10 +16476,10 @@ function enabledPlugins(settings) {
16436
16476
  function inspectProject(projectPath) {
16437
16477
  const problems = [];
16438
16478
  const addons = ADDONS.map((name) => {
16439
- const directory = join9(projectPath, "addons", name);
16440
- const marker = join9(directory, VERSION_MARKER);
16441
- const installed = existsSync8(directory);
16442
- const version = existsSync8(marker) ? readFileSync7(marker, "utf8").trim() : null;
16479
+ const directory = join10(projectPath, "addons", name);
16480
+ const marker = join10(directory, VERSION_MARKER);
16481
+ const installed = existsSync9(directory);
16482
+ const version = existsSync9(marker) ? readFileSync8(marker, "utf8").trim() : null;
16443
16483
  const current = version === SERVER_VERSION;
16444
16484
  if (!installed) {
16445
16485
  problems.push(`addons/${name} is not installed; run gdharness setup`);
@@ -16448,7 +16488,7 @@ function inspectProject(projectPath) {
16448
16488
  }
16449
16489
  return { name, installed, version, current };
16450
16490
  });
16451
- const settings = parseProjectGodot(readFileSync7(join9(projectPath, "project.godot"), "utf8"));
16491
+ const settings = parseProjectGodot(readFileSync8(join10(projectPath, "project.godot"), "utf8"));
16452
16492
  const pluginsEnabled = enabledPlugins(settings);
16453
16493
  for (const name of EDITOR_PLUGINS) {
16454
16494
  if (!pluginsEnabled.includes(name)) {
@@ -16464,7 +16504,7 @@ function inspectProject(projectPath) {
16464
16504
  const tracked = trackedByGit(projectPath, [...named.values()]);
16465
16505
  const addonMissing = (path) => addons.some((addon) => !addon.installed && path.startsWith(`addons/${addon.name}/`));
16466
16506
  for (const [name, path] of named) {
16467
- if (!existsSync8(join9(projectPath, path))) {
16507
+ if (!existsSync9(join10(projectPath, path))) {
16468
16508
  if (!addonMissing(path)) {
16469
16509
  problems.push(`the ${name} autoload names res://${path}, which is not in this project, so it boots with a missing script; restore that file, or point the autoload at one that is here`);
16470
16510
  }
@@ -17754,7 +17794,7 @@ var init_tool_definitions = __esm(() => {
17754
17794
  },
17755
17795
  value: {
17756
17796
  ops: ["until"],
17757
- description: "until: the value to wait for, fitted to the property's type. On a property declared to hold an object, null, which the text null also means; any other value is refused, since the property can never hold it."
17797
+ description: "until: the value to wait for, fitted to the property's type. On a property holding an object, whether declared to or untyped and holding one or null, the text null means null. One declared to hold an object refuses any value other than null, since it can never hold it."
17758
17798
  },
17759
17799
  says: {
17760
17800
  type: "string",
@@ -32586,10 +32626,10 @@ var init_stdio2 = __esm(() => {
32586
32626
  });
32587
32627
 
32588
32628
  // src/runtime-client.ts
32589
- import { existsSync as existsSync10, readdirSync as readdirSync6, readFileSync as readFileSync9, statSync as statSync4, unlinkSync } from "node:fs";
32629
+ import { existsSync as existsSync11, readdirSync as readdirSync6, readFileSync as readFileSync10, statSync as statSync5, unlinkSync } from "node:fs";
32590
32630
  import { createConnection } from "node:net";
32591
32631
  import { tmpdir as tmpdir3 } from "node:os";
32592
- import { dirname as dirname7, join as join12, resolve as resolve3 } from "node:path";
32632
+ import { dirname as dirname7, join as join13, resolve as resolve3 } from "node:path";
32593
32633
  import { setTimeout as delay } from "node:timers/promises";
32594
32634
  function runtimeDirectory(variables = process.env) {
32595
32635
  const explicit = envValue("GDHARNESS_RUNTIME_DIR", variables);
@@ -32597,20 +32637,20 @@ function runtimeDirectory(variables = process.env) {
32597
32637
  return explicit;
32598
32638
  }
32599
32639
  const perUser = envValue("XDG_RUNTIME_DIR", variables);
32600
- return join12(perUser ?? tmpdir3(), "gdharness");
32640
+ return join13(perUser ?? tmpdir3(), "gdharness");
32601
32641
  }
32602
32642
  function runtimeDirectories(variables = process.env) {
32603
32643
  const candidates = [runtimeDirectory(variables)];
32604
32644
  const fallbacks = process.platform === "win32" ? [envValue("SystemRoot", variables) ?? envValue("windir", variables) ?? "C:\\Windows"] : ["/tmp", "/var/tmp"];
32605
32645
  for (const base of fallbacks) {
32606
- candidates.push(join12(base, process.platform === "win32" ? "Temp" : "", "gdharness"));
32646
+ candidates.push(join13(base, process.platform === "win32" ? "Temp" : "", "gdharness"));
32607
32647
  }
32608
32648
  return [...new Set(candidates.map((path) => resolve3(path)))];
32609
32649
  }
32610
32650
  function errorReportOf(pid, directories = runtimeDirectories()) {
32611
32651
  for (const directory of directories) {
32612
- const path = join12(directory, `runtime-${pid}.log`);
32613
- if (existsSync10(path)) {
32652
+ const path = join13(directory, `runtime-${pid}.log`);
32653
+ if (existsSync11(path)) {
32614
32654
  return path;
32615
32655
  }
32616
32656
  }
@@ -32641,7 +32681,7 @@ function reportAlreadyInTranscript(transcript, report) {
32641
32681
  function parseAnnouncement(file, pid) {
32642
32682
  let fields;
32643
32683
  try {
32644
- fields = asParams(JSON.parse(readFileSync9(file, "utf8")));
32684
+ fields = asParams(JSON.parse(readFileSync10(file, "utf8")));
32645
32685
  } catch {
32646
32686
  return { kind: "rubbish" };
32647
32687
  }
@@ -32658,7 +32698,7 @@ function parseAnnouncement(file, pid) {
32658
32698
  };
32659
32699
  let announcedAt;
32660
32700
  try {
32661
- announcedAt = statSync4(file).mtimeMs;
32701
+ announcedAt = statSync5(file).mtimeMs;
32662
32702
  } catch {}
32663
32703
  const when = announcedAt === undefined ? {} : { announcedAt };
32664
32704
  if (protocol !== RUNTIME_PROTOCOL) {
@@ -32699,7 +32739,7 @@ function discoverRuntimes(directories = runtimeDirectories()) {
32699
32739
  function wentAway(file, pid) {
32700
32740
  let project = "";
32701
32741
  try {
32702
- const fields = asParams(JSON.parse(readFileSync9(file, "utf8")));
32742
+ const fields = asParams(JSON.parse(readFileSync10(file, "utf8")));
32703
32743
  project = readString(readParams(fields, "project") ?? {}, "path") ?? "";
32704
32744
  } catch {
32705
32745
  return;
@@ -32742,7 +32782,7 @@ function strangersAmong(candidates, now, startTimes = startTimesOf) {
32742
32782
  }).map((one) => one.file));
32743
32783
  }
32744
32784
  function announcedIn(directory) {
32745
- if (!existsSync10(directory)) {
32785
+ if (!existsSync11(directory)) {
32746
32786
  return [];
32747
32787
  }
32748
32788
  const found = [];
@@ -32751,20 +32791,20 @@ function announcedIn(directory) {
32751
32791
  for (const entry of readdirSync6(directory)) {
32752
32792
  const report = ERROR_REPORT_PATTERN.exec(entry);
32753
32793
  if (report) {
32754
- sweepErrorReport(join12(directory, entry), Number.parseInt(report[1] ?? "", 10));
32794
+ sweepErrorReport(join13(directory, entry), Number.parseInt(report[1] ?? "", 10));
32755
32795
  continue;
32756
32796
  }
32757
32797
  const match = ANNOUNCEMENT_PATTERN.exec(entry);
32758
32798
  if (!match) {
32759
32799
  continue;
32760
32800
  }
32761
- const file = join12(directory, entry);
32801
+ const file = join13(directory, entry);
32762
32802
  const pid = Number.parseInt(match[1] ?? "", 10);
32763
32803
  const running = alive(pid);
32764
32804
  entries.push({ file, pid, running });
32765
32805
  if (running) {
32766
32806
  try {
32767
- candidates.push({ file, pid, writtenAt: statSync4(file).mtimeMs });
32807
+ candidates.push({ file, pid, writtenAt: statSync5(file).mtimeMs });
32768
32808
  } catch {}
32769
32809
  }
32770
32810
  }
@@ -32786,7 +32826,7 @@ function announcedIn(directory) {
32786
32826
  found.push(announced);
32787
32827
  }
32788
32828
  const held = new Set(entries.map((one) => one.file));
32789
- const here = join12(directory, ".");
32829
+ const here = join13(directory, ".");
32790
32830
  for (const file of verdicts.keys()) {
32791
32831
  if (dirname7(file) === here && !held.has(file)) {
32792
32832
  verdicts.delete(file);
@@ -32795,7 +32835,7 @@ function announcedIn(directory) {
32795
32835
  return found;
32796
32836
  }
32797
32837
  async function announcementEnded(pid, directories = runtimeDirectories()) {
32798
- const files = directories.map((directory) => join12(directory, `runtime-${pid}.json`)).filter((file) => existsSync10(file));
32838
+ const files = directories.map((directory) => join13(directory, `runtime-${pid}.json`)).filter((file) => existsSync11(file));
32799
32839
  const first = files[0];
32800
32840
  if (first === undefined) {
32801
32841
  return;
@@ -32821,7 +32861,7 @@ function sweepErrorReport(path, pid, now = Date.now()) {
32821
32861
  return;
32822
32862
  }
32823
32863
  try {
32824
- if (now - statSync4(path).mtimeMs >= ERROR_REPORT_KEEP_MS) {
32864
+ if (now - statSync5(path).mtimeMs >= ERROR_REPORT_KEEP_MS) {
32825
32865
  unlinkSync(path);
32826
32866
  }
32827
32867
  } catch {}
@@ -33095,14 +33135,14 @@ var init_runtime_client = __esm(() => {
33095
33135
  });
33096
33136
 
33097
33137
  // src/boot-note.ts
33098
- import { mkdirSync as mkdirSync6, readFileSync as readFileSync10, writeFileSync as writeFileSync7 } from "node:fs";
33099
- import { dirname as dirname8, join as join13 } from "node:path";
33138
+ import { mkdirSync as mkdirSync6, readFileSync as readFileSync11, writeFileSync as writeFileSync8 } from "node:fs";
33139
+ import { dirname as dirname8, join as join14 } from "node:path";
33100
33140
  function bootNotePath(projectPath) {
33101
- return join13(projectPath, ".godot", "gdharness-boot.json");
33141
+ return join14(projectPath, ".godot", "gdharness-boot.json");
33102
33142
  }
33103
33143
  function readBootNote(projectPath) {
33104
33144
  try {
33105
- const noted = readNumber(asParams(JSON.parse(readFileSync10(bootNotePath(projectPath), "utf8"))), "announcedAfterMs");
33145
+ const noted = readNumber(asParams(JSON.parse(readFileSync11(bootNotePath(projectPath), "utf8"))), "announcedAfterMs");
33106
33146
  return noted !== undefined && Number.isFinite(noted) && noted > 0 ? noted : null;
33107
33147
  } catch {
33108
33148
  return null;
@@ -33112,7 +33152,7 @@ function writeBootNote(projectPath, announcedAfterMs) {
33112
33152
  try {
33113
33153
  const path = bootNotePath(projectPath);
33114
33154
  mkdirSync6(dirname8(path), { recursive: true });
33115
- writeFileSync7(path, JSON.stringify({ announcedAfterMs: Math.round(announcedAfterMs), at: new Date().toISOString() }, null, 2), "utf8");
33155
+ writeFileSync8(path, JSON.stringify({ announcedAfterMs: Math.round(announcedAfterMs), at: new Date().toISOString() }, null, 2), "utf8");
33116
33156
  } catch {}
33117
33157
  }
33118
33158
  function halfAsLongAgain(lastBootMs) {
@@ -33130,10 +33170,10 @@ var init_boot_note = __esm(() => {
33130
33170
  });
33131
33171
 
33132
33172
  // src/breakpoint-note.ts
33133
- import { mkdirSync as mkdirSync7, readFileSync as readFileSync11, rmSync as rmSync6, writeFileSync as writeFileSync8 } from "node:fs";
33134
- import { dirname as dirname9, isAbsolute as isAbsolute2, join as join14, relative as relative2 } from "node:path";
33173
+ import { mkdirSync as mkdirSync7, readFileSync as readFileSync12, rmSync as rmSync7, writeFileSync as writeFileSync9 } from "node:fs";
33174
+ import { dirname as dirname9, isAbsolute as isAbsolute2, join as join15, relative as relative2 } from "node:path";
33135
33175
  function breakpointNotePath(projectPath) {
33136
- return join14(projectPath, ".godot", "gdharness-breakpoints.json");
33176
+ return join15(projectPath, ".godot", "gdharness-breakpoints.json");
33137
33177
  }
33138
33178
  function writeBreakpointNote(projectPath, held) {
33139
33179
  const path = breakpointNotePath(projectPath);
@@ -33141,7 +33181,7 @@ function writeBreakpointNote(projectPath, held) {
33141
33181
  const inside = held.map((one) => ({ scriptPath: realPathOr(one.scriptPath), lines: one.lines })).filter((one) => one.lines.length > 0 && withinProject(root, one.scriptPath));
33142
33182
  try {
33143
33183
  if (inside.length === 0) {
33144
- rmSync6(path, { force: true });
33184
+ rmSync7(path, { force: true });
33145
33185
  return;
33146
33186
  }
33147
33187
  const note = {
@@ -33151,7 +33191,7 @@ function writeBreakpointNote(projectPath, held) {
33151
33191
  }))
33152
33192
  };
33153
33193
  mkdirSync7(dirname9(path), { recursive: true });
33154
- writeFileSync8(path, `${JSON.stringify(note, null, 2)}
33194
+ writeFileSync9(path, `${JSON.stringify(note, null, 2)}
33155
33195
  `, "utf8");
33156
33196
  } catch (error) {
33157
33197
  console.error(`[SERVER] Could not record the breakpoints at ${path}: ${errorMessage(error)}`);
@@ -33159,7 +33199,7 @@ function writeBreakpointNote(projectPath, held) {
33159
33199
  }
33160
33200
  function readBreakpointNote(projectPath) {
33161
33201
  try {
33162
- const parsed = JSON.parse(readFileSync11(breakpointNotePath(projectPath), "utf8"));
33202
+ const parsed = JSON.parse(readFileSync12(breakpointNotePath(projectPath), "utf8"));
33163
33203
  if (typeof parsed !== "object" || parsed === null || !Array.isArray(parsed.breakpoints)) {
33164
33204
  return [];
33165
33205
  }
@@ -33174,7 +33214,7 @@ function readBreakpointNote(projectPath) {
33174
33214
  }
33175
33215
  const lines = Array.isArray(listed) ? listed.filter((line) => Number.isInteger(line) && line > 0) : [];
33176
33216
  if (lines.length > 0) {
33177
- held.push({ scriptPath: realPathOr(join14(projectPath, script)), lines });
33217
+ held.push({ scriptPath: realPathOr(join15(projectPath, script)), lines });
33178
33218
  }
33179
33219
  }
33180
33220
  return held;
@@ -33192,14 +33232,14 @@ var init_breakpoint_note = __esm(() => {
33192
33232
  });
33193
33233
 
33194
33234
  // src/class-note.ts
33195
- import { mkdirSync as mkdirSync8, readFileSync as readFileSync12, writeFileSync as writeFileSync9 } from "node:fs";
33196
- import { dirname as dirname10, join as join15 } from "node:path";
33235
+ import { mkdirSync as mkdirSync8, readFileSync as readFileSync13, writeFileSync as writeFileSync10 } from "node:fs";
33236
+ import { dirname as dirname10, join as join16 } from "node:path";
33197
33237
  function classNotePath(projectPath) {
33198
- return join15(projectPath, ".godot", "gdharness-classes.json");
33238
+ return join16(projectPath, ".godot", "gdharness-classes.json");
33199
33239
  }
33200
33240
  function readClassNote(projectPath) {
33201
33241
  try {
33202
- const fields = asParams(JSON.parse(readFileSync12(classNotePath(projectPath), "utf8")));
33242
+ const fields = asParams(JSON.parse(readFileSync13(classNotePath(projectPath), "utf8")));
33203
33243
  const rebuiltWith = readArray(fields, "rebuiltWith");
33204
33244
  if (rebuiltWith === undefined || !rebuiltWith.every((name) => typeof name === "string")) {
33205
33245
  return null;
@@ -33217,23 +33257,23 @@ function writeClassNote(projectPath, note) {
33217
33257
  try {
33218
33258
  const path = classNotePath(projectPath);
33219
33259
  mkdirSync8(dirname10(path), { recursive: true });
33220
- writeFileSync9(path, `${JSON.stringify(note, null, 2)}
33260
+ writeFileSync10(path, `${JSON.stringify(note, null, 2)}
33221
33261
  `, "utf8");
33222
33262
  } catch {}
33223
33263
  }
33224
33264
  var init_class_note = () => {};
33225
33265
 
33226
33266
  // src/config-pin.ts
33227
- import { existsSync as existsSync11, readFileSync as readFileSync13 } from "node:fs";
33228
- import { join as join16 } from "node:path";
33267
+ import { existsSync as existsSync12, readFileSync as readFileSync14 } from "node:fs";
33268
+ import { join as join17 } from "node:path";
33229
33269
  function versionsNamedInConfig(projectPath) {
33230
- const config = join16(projectPath, ".mcp.json");
33231
- if (!existsSync11(config)) {
33270
+ const config = join17(projectPath, ".mcp.json");
33271
+ if (!existsSync12(config)) {
33232
33272
  return [];
33233
33273
  }
33234
33274
  let text;
33235
33275
  try {
33236
- text = readFileSync13(config, "utf8");
33276
+ text = readFileSync14(config, "utf8");
33237
33277
  JSON.parse(text);
33238
33278
  } catch {
33239
33279
  return [];
@@ -33993,25 +34033,25 @@ var init_desktop = () => {};
33993
34033
  // src/editor-log.ts
33994
34034
  import {
33995
34035
  closeSync,
33996
- existsSync as existsSync12,
34036
+ existsSync as existsSync13,
33997
34037
  mkdirSync as mkdirSync9,
33998
34038
  openSync,
33999
- readFileSync as readFileSync14,
34039
+ readFileSync as readFileSync15,
34000
34040
  readSync,
34001
- rmSync as rmSync7,
34002
- statSync as statSync5,
34003
- writeFileSync as writeFileSync10
34041
+ rmSync as rmSync8,
34042
+ statSync as statSync6,
34043
+ writeFileSync as writeFileSync11
34004
34044
  } from "node:fs";
34005
- import { dirname as dirname11, join as join17 } from "node:path";
34045
+ import { dirname as dirname11, join as join18 } from "node:path";
34006
34046
  function editorLogPath(projectPath) {
34007
- return join17(projectPath, ".godot", "gdharness-editor.log");
34047
+ return join18(projectPath, ".godot", "gdharness-editor.log");
34008
34048
  }
34009
34049
  function editorLogNotePath(projectPath) {
34010
- return join17(projectPath, ".godot", "gdharness-editor.json");
34050
+ return join18(projectPath, ".godot", "gdharness-editor.json");
34011
34051
  }
34012
34052
  function readEditorLogNote(projectPath) {
34013
34053
  try {
34014
- const fields = asParams(JSON.parse(readFileSync14(editorLogNotePath(projectPath), "utf8")));
34054
+ const fields = asParams(JSON.parse(readFileSync15(editorLogNotePath(projectPath), "utf8")));
34015
34055
  const pid = readNumber(fields, "pid");
34016
34056
  const startedAt = readString(fields, "startedAt");
34017
34057
  return pid !== undefined && Number.isInteger(pid) && pid > 0 && startedAt !== undefined ? { pid, startedAt } : null;
@@ -34023,15 +34063,15 @@ function clearEditorLog(projectPath) {
34023
34063
  try {
34024
34064
  const path = editorLogPath(projectPath);
34025
34065
  mkdirSync9(dirname11(path), { recursive: true });
34026
- rmSync7(path, { force: true });
34027
- rmSync7(editorLogNotePath(projectPath), { force: true });
34066
+ rmSync8(path, { force: true });
34067
+ rmSync8(editorLogNotePath(projectPath), { force: true });
34028
34068
  } catch {}
34029
34069
  }
34030
34070
  function writeEditorLogNote(projectPath, pid) {
34031
34071
  try {
34032
34072
  const path = editorLogNotePath(projectPath);
34033
34073
  mkdirSync9(dirname11(path), { recursive: true });
34034
- writeFileSync10(path, JSON.stringify({ pid, startedAt: new Date().toISOString() }, null, 2), "utf8");
34074
+ writeFileSync11(path, JSON.stringify({ pid, startedAt: new Date().toISOString() }, null, 2), "utf8");
34035
34075
  } catch {}
34036
34076
  }
34037
34077
  function theConsoleWasNotCaptured(why, projectPath) {
@@ -34052,7 +34092,7 @@ function editorConsole(projectPath, editorPid, openedByAServer) {
34052
34092
  return { kind: "older server", notedPid: noted?.pid ?? null };
34053
34093
  }
34054
34094
  const path = editorLogPath(projectPath);
34055
- if (!existsSync12(path)) {
34095
+ if (!existsSync13(path)) {
34056
34096
  return { kind: "not written" };
34057
34097
  }
34058
34098
  const log = new GameLog;
@@ -34062,9 +34102,9 @@ function editorConsole(projectPath, editorPid, openedByAServer) {
34062
34102
  return { log, path, ...of === undefined ? {} : { readBytes: text.length, of } };
34063
34103
  }
34064
34104
  function theFirstOf(path) {
34065
- const whole = statSync5(path).size;
34105
+ const whole = statSync6(path).size;
34066
34106
  if (whole <= MOST_CONSOLE_BYTES) {
34067
- return { text: readFileSync14(path, "utf8") };
34107
+ return { text: readFileSync15(path, "utf8") };
34068
34108
  }
34069
34109
  const handle = openSync(path, "r");
34070
34110
  try {
@@ -38589,13 +38629,13 @@ var init_process_time = __esm(() => {
38589
38629
  });
38590
38630
 
38591
38631
  // src/project-scan.ts
38592
- import { existsSync as existsSync13, readdirSync as readdirSync7, readFileSync as readFileSync15 } from "node:fs";
38593
- import { join as join18 } from "node:path";
38632
+ import { existsSync as existsSync14, readdirSync as readdirSync7, readFileSync as readFileSync16 } from "node:fs";
38633
+ import { join as join19 } from "node:path";
38594
38634
  function skipped(name) {
38595
38635
  return name.startsWith(".") || SKIPPED.has(name);
38596
38636
  }
38597
38637
  function steppedOver(directory) {
38598
- return existsSync13(join18(directory, ".gdignore"));
38638
+ return existsSync14(join19(directory, ".gdignore"));
38599
38639
  }
38600
38640
  function projectStructure(projectPath) {
38601
38641
  const structure = { scenes: 0, scripts: 0, assets: 0, other: 0 };
@@ -38608,7 +38648,7 @@ function projectStructure(projectPath) {
38608
38648
  continue;
38609
38649
  }
38610
38650
  if (entry.isDirectory()) {
38611
- visit(join18(directory, entry.name));
38651
+ visit(join19(directory, entry.name));
38612
38652
  } else if (entry.isFile()) {
38613
38653
  const extension = entry.name.split(".").pop()?.toLowerCase() ?? "";
38614
38654
  if (extension === "tscn") {
@@ -38638,14 +38678,14 @@ function scriptsWithoutUid(projectPath) {
38638
38678
  }
38639
38679
  const spelled = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
38640
38680
  if (entry.isDirectory()) {
38641
- visit(join18(directory, entry.name), spelled);
38681
+ visit(join19(directory, entry.name), spelled);
38642
38682
  continue;
38643
38683
  }
38644
38684
  if (!entry.isFile()) {
38645
38685
  continue;
38646
38686
  }
38647
38687
  const extension = entry.name.split(".").pop()?.toLowerCase() ?? "";
38648
- if (CARRIES_A_UID.has(extension) && !existsSync13(join18(directory, `${entry.name}.uid`))) {
38688
+ if (CARRIES_A_UID.has(extension) && !existsSync14(join19(directory, `${entry.name}.uid`))) {
38649
38689
  found.push(spelled);
38650
38690
  }
38651
38691
  }
@@ -38680,7 +38720,7 @@ function searchProject(projectPath, options) {
38680
38720
  if (skipped(entry.name)) {
38681
38721
  continue;
38682
38722
  }
38683
- const entryPath = join18(directory, entry.name);
38723
+ const entryPath = join19(directory, entry.name);
38684
38724
  if (entry.isDirectory()) {
38685
38725
  visit(entryPath);
38686
38726
  continue;
@@ -38691,7 +38731,7 @@ function searchProject(projectPath, options) {
38691
38731
  }
38692
38732
  result.summary.files_searched += 1;
38693
38733
  const matches = [];
38694
- for (const [index, line] of readFileSync15(entryPath, "utf8").split(`
38734
+ for (const [index, line] of readFileSync16(entryPath, "utf8").split(`
38695
38735
  `).entries()) {
38696
38736
  if (full()) {
38697
38737
  break;
@@ -38719,27 +38759,6 @@ var init_project_scan = __esm(() => {
38719
38759
  CARRIES_A_UID = new Set(["gd", "shader", "gdshader"]);
38720
38760
  });
38721
38761
 
38722
- // src/reimport.ts
38723
- import { createHash as createHash3 } from "node:crypto";
38724
- import { existsSync as existsSync14, readFileSync as readFileSync16, rmSync as rmSync8, statSync as statSync6, utimesSync, writeFileSync as writeFileSync11 } from "node:fs";
38725
- import { basename as basename3, join as join19 } from "node:path";
38726
- function forceNextImport(projectPath, resourcePath) {
38727
- const relative = resourcePath.replace(/^res:\/\//, "");
38728
- const sidecar = join19(projectPath, `${relative}.import`);
38729
- if (!existsSync14(sidecar)) {
38730
- return;
38731
- }
38732
- const { atime, mtimeMs } = statSync6(sidecar);
38733
- writeFileSync11(sidecar, readFileSync16(sidecar, "utf8").replace(/^valid=false\r?\n/m, ""));
38734
- utimesSync(sidecar, atime, new Date(mtimeMs + 2000));
38735
- rmSync8(importRecord(projectPath, resourcePath), { force: true });
38736
- }
38737
- function importRecord(projectPath, resourcePath) {
38738
- const digest = createHash3("md5").update(resourcePath).digest("hex");
38739
- return join19(projectPath, ".godot", "imported", `${basename3(resourcePath)}-${digest}.md5`);
38740
- }
38741
- var init_reimport = () => {};
38742
-
38743
38762
  // src/restart-note.ts
38744
38763
  import { mkdirSync as mkdirSync10, readFileSync as readFileSync17, rmSync as rmSync9, writeFileSync as writeFileSync12 } from "node:fs";
38745
38764
  import { dirname as dirname13, join as join20 } from "node:path";
@@ -40650,7 +40669,7 @@ class GodotServer {
40650
40669
  uidsCreated: given,
40651
40670
  stillWithoutUid: after,
40652
40671
  note: uidsLeftNote(after.length),
40653
- ...imported.librariesRetried === undefined ? {} : { extensionNote: extensionRetryNote(imported.librariesRetried) }
40672
+ ...imported.extensionNote === undefined ? {} : { extensionNote: imported.extensionNote }
40654
40673
  }
40655
40674
  }, scanned));
40656
40675
  }
@@ -40795,19 +40814,16 @@ class GodotServer {
40795
40814
  if (!engine.ok) {
40796
40815
  return { ok: false, response: engine.response };
40797
40816
  }
40798
- for (const target of targets) {
40799
- forceNextImport(projectPath, target);
40800
- }
40801
40817
  const scanned = await this.untilTheEditorsScanIsWritten(projectPath);
40802
- const imported = await runImport(engine.value, projectPath);
40818
+ const imported = await runImport(engine.value, projectPath, { forcing: targets });
40803
40819
  if (!imported.ok) {
40804
40820
  return { ok: false, response: this.answer(withScanWait(imported, scanned)) };
40805
40821
  }
40806
40822
  if (imported.messages.length > 0) {
40807
40823
  extra["engine_messages"] = imported.messages;
40808
40824
  }
40809
- if (imported.librariesRetried !== undefined) {
40810
- extra["extensionNote"] = extensionRetryNote(imported.librariesRetried);
40825
+ if (imported.extensionNote !== undefined) {
40826
+ extra["extensionNote"] = imported.extensionNote;
40811
40827
  }
40812
40828
  }
40813
40829
  const after = await this.operation("get_import_status", resourcePath === undefined ? { includeUpToDate: true } : { resourcePath }, projectPath);
@@ -41298,7 +41314,7 @@ class GodotServer {
41298
41314
  const notes = [];
41299
41315
  if (contradicted.length > 0) {
41300
41316
  const dependsOn = new Set;
41301
- for (const one of contradicted) {
41317
+ for (const one of contradicted.filter((entry) => entry.kind === "method" || entry.kind === "property")) {
41302
41318
  const source = cached === null ? null : this.sourceOfClass(projectPath, one.declaredIn);
41303
41319
  if (source === null || cached === null) {
41304
41320
  continue;
@@ -41338,9 +41354,10 @@ class GodotServer {
41338
41354
  });
41339
41355
  }
41340
41356
  classesAScriptNames(projectPath, scriptPath) {
41341
- const source = this.sourceOfClass(projectPath, scriptPath);
41357
+ const resourcePath = scriptPath.startsWith("res://") ? scriptPath : `res://${scriptPath.replace(/^\/+/, "")}`;
41358
+ const source = this.sourceOfClass(projectPath, resourcePath);
41342
41359
  const known = cachedClasses(projectPath) ?? declaredClasses(projectPath);
41343
- return source === null ? [] : classesNamedIn(source, known).filter((name) => known.get(name) !== scriptPath);
41360
+ return source === null ? [] : classesNamedIn(source, known).filter((name) => known.get(name) !== resourcePath);
41344
41361
  }
41345
41362
  sourceOfClass(projectPath, resourcePath) {
41346
41363
  const contained = resolveWithinProject(projectPath, resourcePath);
@@ -43626,7 +43643,6 @@ var init_server3 = __esm(() => {
43626
43643
  init_process_children();
43627
43644
  init_process_time();
43628
43645
  init_project_scan();
43629
- init_reimport();
43630
43646
  init_resources();
43631
43647
  init_restart_note();
43632
43648
  init_run_record();
@@ -44586,8 +44602,8 @@ init_setup();
44586
44602
  // src/skill.ts
44587
44603
  init_setup();
44588
44604
  init_tool_definitions();
44589
- import { existsSync as existsSync9, mkdirSync as mkdirSync5, readdirSync as readdirSync5, rmSync as rmSync5, writeFileSync as writeFileSync6 } from "node:fs";
44590
- import { basename as basename2, dirname as dirname5, join as join10 } from "node:path";
44605
+ import { existsSync as existsSync10, mkdirSync as mkdirSync5, readdirSync as readdirSync5, rmSync as rmSync6, writeFileSync as writeFileSync7 } from "node:fs";
44606
+ import { basename as basename3, dirname as dirname5, join as join11 } from "node:path";
44591
44607
 
44592
44608
  // src/tool-reference.ts
44593
44609
  init_headless_operations();
@@ -44683,7 +44699,7 @@ function renderToolsMarkdown() {
44683
44699
 
44684
44700
  // src/skill.ts
44685
44701
  var SKILL_NAME = "gdharness";
44686
- var SHARED_SKILLS = join10(".agents", "skills");
44702
+ var SHARED_SKILLS = join11(".agents", "skills");
44687
44703
  function skillMarkdown(version) {
44688
44704
  return `---
44689
44705
  name: ${SKILL_NAME}
@@ -44874,27 +44890,27 @@ whatever the engine said on stderr comes back under \`engine_messages\`.
44874
44890
  function skillFiles(version) {
44875
44891
  return new Map([
44876
44892
  ["SKILL.md", skillMarkdown(version)],
44877
- [join10("references", "tools.md"), renderToolsMarkdown()]
44893
+ [join11("references", "tools.md"), renderToolsMarkdown()]
44878
44894
  ]);
44879
44895
  }
44880
44896
  function skillDirectories(harnesses, projectPath, exists) {
44881
- const directories = [join10(projectPath, SHARED_SKILLS, SKILL_NAME)];
44897
+ const directories = [join11(projectPath, SHARED_SKILLS, SKILL_NAME)];
44882
44898
  for (const harness of harnesses) {
44883
44899
  if (harness.skills === undefined) {
44884
44900
  continue;
44885
44901
  }
44886
- const own = join10(projectPath, harness.skills.dir);
44887
- if ((!harness.skills.shared || exists(own)) && !directories.includes(join10(own, SKILL_NAME))) {
44888
- directories.push(join10(own, SKILL_NAME));
44902
+ const own = join11(projectPath, harness.skills.dir);
44903
+ if ((!harness.skills.shared || exists(own)) && !directories.includes(join11(own, SKILL_NAME))) {
44904
+ directories.push(join11(own, SKILL_NAME));
44889
44905
  }
44890
44906
  }
44891
44907
  return directories;
44892
44908
  }
44893
44909
  function everySkillDirectory(projectPath, harnesses) {
44894
- const directories = new Set([join10(projectPath, SHARED_SKILLS, SKILL_NAME)]);
44910
+ const directories = new Set([join11(projectPath, SHARED_SKILLS, SKILL_NAME)]);
44895
44911
  for (const harness of harnesses) {
44896
44912
  if (harness.skills !== undefined) {
44897
- directories.add(join10(projectPath, harness.skills.dir, SKILL_NAME));
44913
+ directories.add(join11(projectPath, harness.skills.dir, SKILL_NAME));
44898
44914
  }
44899
44915
  }
44900
44916
  return [...directories];
@@ -44902,8 +44918,8 @@ function everySkillDirectory(projectPath, harnesses) {
44902
44918
  function removeSkill(directories, projectPath) {
44903
44919
  const removed = [];
44904
44920
  for (const directory of directories) {
44905
- if (existsSync9(directory)) {
44906
- rmSync5(directory, { recursive: true, force: true });
44921
+ if (existsSync10(directory)) {
44922
+ rmSync6(directory, { recursive: true, force: true });
44907
44923
  pruneEmpty(dirname5(directory), projectPath);
44908
44924
  removed.push(directory);
44909
44925
  }
@@ -44911,43 +44927,43 @@ function removeSkill(directories, projectPath) {
44911
44927
  return removed;
44912
44928
  }
44913
44929
  function pruneEmpty(directory, projectPath) {
44914
- const shared = join10(projectPath, ".agents");
44930
+ const shared = join11(projectPath, ".agents");
44915
44931
  let path = directory;
44916
- while (path !== projectPath && (basename2(path) === "skills" || path === shared)) {
44932
+ while (path !== projectPath && (basename3(path) === "skills" || path === shared)) {
44917
44933
  if (readdirSync5(path).length > 0) {
44918
44934
  return;
44919
44935
  }
44920
- rmSync5(path, { recursive: true, force: true });
44936
+ rmSync6(path, { recursive: true, force: true });
44921
44937
  path = dirname5(path);
44922
44938
  }
44923
44939
  }
44924
44940
  function writeSkill(directories, version) {
44925
44941
  const files = skillFiles(version);
44926
44942
  return directories.map((directory) => {
44927
- const replaced = existsSync9(directory);
44928
- rmSync5(directory, { recursive: true, force: true });
44943
+ const replaced = existsSync10(directory);
44944
+ rmSync6(directory, { recursive: true, force: true });
44929
44945
  for (const [name, contents] of files) {
44930
- const path = join10(directory, name);
44946
+ const path = join11(directory, name);
44931
44947
  mkdirSync5(dirname5(path), { recursive: true });
44932
- writeFileSync6(path, contents, "utf8");
44948
+ writeFileSync7(path, contents, "utf8");
44933
44949
  }
44934
44950
  return { path: directory, replaced };
44935
44951
  });
44936
44952
  }
44937
44953
 
44938
44954
  // src/version.ts
44939
- import { readFileSync as readFileSync8 } from "node:fs";
44940
- import { dirname as dirname6, join as join11 } from "node:path";
44955
+ import { readFileSync as readFileSync9 } from "node:fs";
44956
+ import { dirname as dirname6, join as join12 } from "node:path";
44941
44957
  import { fileURLToPath as fileURLToPath2 } from "node:url";
44942
44958
  function getLocalVersion() {
44943
44959
  const currentDirectory = dirname6(fileURLToPath2(import.meta.url));
44944
44960
  const packageCandidates = [
44945
- join11(currentDirectory, "..", "package.json"),
44946
- join11(currentDirectory, "..", "..", "package.json")
44961
+ join12(currentDirectory, "..", "package.json"),
44962
+ join12(currentDirectory, "..", "..", "package.json")
44947
44963
  ];
44948
44964
  for (const packagePath of packageCandidates) {
44949
44965
  try {
44950
- const value = JSON.parse(readFileSync8(packagePath, "utf-8"));
44966
+ const value = JSON.parse(readFileSync9(packagePath, "utf-8"));
44951
44967
  if (typeof value === "object" && value !== null && "name" in value && value.name === "gdharness" && "version" in value && typeof value.version === "string") {
44952
44968
  return value.version;
44953
44969
  }