gdharness 1.0.15 → 1.0.17

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.
Files changed (3) hide show
  1. package/build/cli.js +679 -600
  2. package/build/index.js +35 -12
  3. package/package.json +1 -1
package/build/cli.js CHANGED
@@ -47,10 +47,233 @@ var __esm = (fn, res, err) => () => {
47
47
  };
48
48
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
49
49
 
50
+ // src/errors.ts
51
+ function errorMessage(error, fallback = "Unknown error") {
52
+ if (error instanceof Error) {
53
+ return error.message || fallback;
54
+ }
55
+ if (typeof error === "string") {
56
+ return error || fallback;
57
+ }
58
+ if (typeof error === "number" || typeof error === "boolean") {
59
+ return String(error);
60
+ }
61
+ return fallback;
62
+ }
63
+ function toError(error) {
64
+ return error instanceof Error ? error : new Error(errorMessage(error), { cause: error });
65
+ }
66
+ var Refusal;
67
+ var init_errors = __esm(() => {
68
+ Refusal = class Refusal extends Error {
69
+ };
70
+ });
71
+
72
+ // src/process-children.ts
73
+ import { execFile, execFileSync, spawnSync } from "node:child_process";
74
+ import { basename } from "node:path";
75
+ import { promisify } from "node:util";
76
+ function startTimesOf(pids) {
77
+ const began = new Map;
78
+ if (pids.length === 0) {
79
+ return began;
80
+ }
81
+ try {
82
+ if (process.platform === "win32") {
83
+ const filter = pids.map((pid) => `ProcessId=${pid}`).join(" OR ");
84
+ const said = execFileSync("powershell", [
85
+ "-NoProfile",
86
+ "-NonInteractive",
87
+ "-Command",
88
+ `Get-CimInstance Win32_Process -Filter "${filter}" | ForEach-Object { "$($_.ProcessId) $(([DateTimeOffset]$_.CreationDate).ToUnixTimeMilliseconds())" }`
89
+ ], { encoding: "utf8", timeout: ASK_TIMEOUT_MS, windowsHide: true });
90
+ for (const line of said.split(`
91
+ `)) {
92
+ const found = /^\s*(\d+)\s+(\d+)\s*$/.exec(line);
93
+ if (found?.[1] !== undefined && found[2] !== undefined) {
94
+ began.set(Number(found[1]), Number(found[2]));
95
+ }
96
+ }
97
+ return began;
98
+ }
99
+ const said = spawnSync("ps", ["-p", pids.join(","), "-o", "pid=,lstart="], {
100
+ encoding: "utf8",
101
+ timeout: ASK_TIMEOUT_MS,
102
+ env: { ...process.env, LC_ALL: "C" }
103
+ }).stdout;
104
+ for (const line of said.split(`
105
+ `)) {
106
+ const found = /^\s*(\d+)\s+(.+)$/.exec(line);
107
+ const at = Date.parse(found?.[2]?.trim() ?? "");
108
+ if (found?.[1] !== undefined && !Number.isNaN(at)) {
109
+ began.set(Number(found[1]), at);
110
+ }
111
+ }
112
+ } catch {}
113
+ return began;
114
+ }
115
+ async function processTree() {
116
+ try {
117
+ if (process.platform === "win32") {
118
+ return parseProcessTable(await askWindows(), true);
119
+ }
120
+ return parseProcessTable(await askPosix());
121
+ } catch {
122
+ return;
123
+ }
124
+ }
125
+ function parseProcessTable(printed, started = false) {
126
+ const tree = new Map;
127
+ for (const line of printed.split(/\r?\n/)) {
128
+ const fields = /^\s*(\d+)\s+(\d+)(?:\s+(.*))?$/.exec(line);
129
+ if (fields === null) {
130
+ continue;
131
+ }
132
+ const pid = Number(fields[1]);
133
+ const parent = Number(fields[2]);
134
+ const rest = (fields[3] ?? "").trim();
135
+ if (pid <= 0) {
136
+ continue;
137
+ }
138
+ if (!started) {
139
+ tree.set(pid, { parent, command: rest });
140
+ continue;
141
+ }
142
+ const timed = /^(\d+)(?:\s+(.*))?$/.exec(rest);
143
+ if (timed === null) {
144
+ continue;
145
+ }
146
+ const began = Number(timed[1]);
147
+ tree.set(pid, { parent, command: (timed[2] ?? "").trim(), ...began > 0 ? { startedAt: began } : {} });
148
+ }
149
+ return tree;
150
+ }
151
+ function linked(tree, child, parent) {
152
+ const from = tree.get(child);
153
+ const to = tree.get(parent);
154
+ if (from === undefined || from.parent !== parent) {
155
+ return false;
156
+ }
157
+ if (from.startedAt === undefined || to?.startedAt === undefined) {
158
+ return true;
159
+ }
160
+ return from.startedAt >= to.startedAt;
161
+ }
162
+ function childrenIn(tree, pid) {
163
+ const children = [];
164
+ for (const child of tree.keys()) {
165
+ if (linked(tree, child, pid)) {
166
+ children.push(child);
167
+ }
168
+ }
169
+ return children;
170
+ }
171
+ function descendantsIn(tree, pid) {
172
+ const found = [];
173
+ const queue = [pid];
174
+ const seen = new Set([pid]);
175
+ while (queue.length > 0) {
176
+ const next = queue.shift();
177
+ if (next === undefined) {
178
+ break;
179
+ }
180
+ for (const child of childrenIn(tree, next)) {
181
+ if (!seen.has(child)) {
182
+ seen.add(child);
183
+ found.push(child);
184
+ queue.push(child);
185
+ }
186
+ }
187
+ }
188
+ return found;
189
+ }
190
+ function ancestorsIn(tree, pid, root) {
191
+ const chain = [];
192
+ const seen = new Set([pid]);
193
+ let child = pid;
194
+ let parent = tree.get(pid)?.parent;
195
+ while (parent !== undefined && !seen.has(parent) && linked(tree, child, parent)) {
196
+ chain.push(parent);
197
+ if (parent === root) {
198
+ break;
199
+ }
200
+ seen.add(parent);
201
+ child = parent;
202
+ parent = tree.get(parent)?.parent;
203
+ }
204
+ return chain;
205
+ }
206
+ function readCommandLine(command) {
207
+ const words = commandWords(command);
208
+ const executable = words[0] === undefined ? "" : basename(words[0].replaceAll("\\", "/"));
209
+ const at = words.indexOf("--path");
210
+ let projectPath = null;
211
+ if (at !== -1 && at + 1 < words.length) {
212
+ const taken = [];
213
+ for (const word of words.slice(at + 1)) {
214
+ if (taken.length > 0 && (word.startsWith("-") || /^[a-z]+:\/\//.test(word))) {
215
+ break;
216
+ }
217
+ taken.push(word);
218
+ }
219
+ projectPath = taken.join(" ");
220
+ }
221
+ return {
222
+ executable,
223
+ projectPath,
224
+ editor: words.some((word) => word === "-e" || word === "--editor")
225
+ };
226
+ }
227
+ function commandWords(command) {
228
+ const words = [];
229
+ let word = "";
230
+ let quoted = false;
231
+ let started = false;
232
+ for (const char of command) {
233
+ if (char === '"') {
234
+ quoted = !quoted;
235
+ started = true;
236
+ } else if (!quoted && /\s/.test(char)) {
237
+ if (started) {
238
+ words.push(word);
239
+ word = "";
240
+ started = false;
241
+ }
242
+ } else {
243
+ word += char;
244
+ started = true;
245
+ }
246
+ }
247
+ if (started) {
248
+ words.push(word);
249
+ }
250
+ return words;
251
+ }
252
+ async function askWindows() {
253
+ const { stdout } = await run("powershell.exe", [
254
+ "-NoProfile",
255
+ "-NonInteractive",
256
+ "-Command",
257
+ `${POWERSHELL_UTF8}Get-CimInstance Win32_Process | ForEach-Object { $began = 0; if ($_.CreationDate) { $began = ([DateTimeOffset]$_.CreationDate).ToUnixTimeMilliseconds() }; "$($_.ProcessId) $($_.ParentProcessId) $began $($_.CommandLine)" }`
258
+ ], { timeout: ASK_TIMEOUT_MS, windowsHide: true, maxBuffer: 16 * 1024 * 1024 });
259
+ return stdout;
260
+ }
261
+ async function askPosix() {
262
+ const { stdout } = await run("ps", ["-e", "-o", "pid=,ppid=,args="], {
263
+ timeout: ASK_TIMEOUT_MS,
264
+ maxBuffer: 16 * 1024 * 1024
265
+ });
266
+ return stdout;
267
+ }
268
+ var run, POWERSHELL_UTF8 = "[Console]::OutputEncoding = [System.Text.Encoding]::UTF8; ", ASK_TIMEOUT_MS = 15000;
269
+ var init_process_children = __esm(() => {
270
+ run = promisify(execFile);
271
+ });
272
+
50
273
  // src/detection.ts
51
274
  import { existsSync, readdirSync, statSync } from "node:fs";
52
275
  import { homedir } from "node:os";
53
- import { join } from "node:path";
276
+ import { join as join2 } from "node:path";
54
277
  function resolveHomeDirectory() {
55
278
  try {
56
279
  return homedir();
@@ -74,7 +297,7 @@ function scanDirectoryForGodotBinaries(directory, platform) {
74
297
  if (!pattern.test(name)) {
75
298
  continue;
76
299
  }
77
- const fullPath = join(directory, name);
300
+ const fullPath = join2(directory, name);
78
301
  try {
79
302
  const stat = statSync(fullPath);
80
303
  if (stat.isFile()) {
@@ -83,7 +306,7 @@ function scanDirectoryForGodotBinaries(directory, platform) {
83
306
  } catch {}
84
307
  }
85
308
  matches.sort((a, b) => b.mtime - a.mtime);
86
- return matches.map((m) => join(directory, m.name));
309
+ return matches.map((m) => join2(directory, m.name));
87
310
  }
88
311
  function conventionalPaths(platform, home) {
89
312
  const paths = ["godot"];
@@ -133,28 +356,6 @@ function godotCandidates(platform, home) {
133
356
  }
134
357
  var init_detection = () => {};
135
358
 
136
- // src/errors.ts
137
- function errorMessage(error, fallback = "Unknown error") {
138
- if (error instanceof Error) {
139
- return error.message || fallback;
140
- }
141
- if (typeof error === "string") {
142
- return error || fallback;
143
- }
144
- if (typeof error === "number" || typeof error === "boolean") {
145
- return String(error);
146
- }
147
- return fallback;
148
- }
149
- function toError(error) {
150
- return error instanceof Error ? error : new Error(errorMessage(error), { cause: error });
151
- }
152
- var Refusal2;
153
- var init_errors = __esm(() => {
154
- Refusal2 = class Refusal2 extends Error {
155
- };
156
- });
157
-
158
359
  // src/launch.ts
159
360
  function envValue(name, variables = process.env) {
160
361
  const value = variables[name];
@@ -7161,7 +7362,7 @@ var require_public_api = __commonJS(function(exports) {
7161
7362
 
7162
7363
  // src/runner.ts
7163
7364
  import { existsSync as existsSync3 } from "node:fs";
7164
- import { dirname, join as join2 } from "node:path";
7365
+ import { dirname as dirname2, join as join3 } from "node:path";
7165
7366
  import process2 from "node:process";
7166
7367
  function currentRunner() {
7167
7368
  const versions = process2.versions;
@@ -7182,11 +7383,24 @@ function spawnFor(runner, version, execPath = process2.execPath) {
7182
7383
  return { command: alongside(execPath, "npx") ?? "npx", args: ["-y", spec] };
7183
7384
  }
7184
7385
  function alongside(execPath, name) {
7185
- const runner = join2(dirname(execPath), process2.platform === "win32" ? `${name}.cmd` : name);
7386
+ const runner = join3(dirname2(execPath), process2.platform === "win32" ? `${name}.cmd` : name);
7186
7387
  return existsSync3(runner) ? runner : undefined;
7187
7388
  }
7188
7389
  var init_runner = () => {};
7189
7390
 
7391
+ // src/call-signal.ts
7392
+ import { AsyncLocalStorage } from "node:async_hooks";
7393
+ function withCallSignal(signal, answer) {
7394
+ return signal === undefined ? answer() : current.run(signal, answer);
7395
+ }
7396
+ function callSignal() {
7397
+ return current.getStore();
7398
+ }
7399
+ var current;
7400
+ var init_call_signal = __esm(() => {
7401
+ current = new AsyncLocalStorage;
7402
+ });
7403
+
7190
7404
  // src/dictionary.ts
7191
7405
  function emptyRecord() {
7192
7406
  return Object.create(null);
@@ -7311,7 +7525,7 @@ var init_game_log = __esm(() => {
7311
7525
  });
7312
7526
 
7313
7527
  // src/scratch.ts
7314
- import { rmSync as rmSync2 } from "node:fs";
7528
+ import { rmSync as rmSync3 } from "node:fs";
7315
7529
  function discardWith(remove, paths) {
7316
7530
  const left = [];
7317
7531
  for (const path of paths) {
@@ -7328,7 +7542,7 @@ function discardWith(remove, paths) {
7328
7542
  }
7329
7543
  function discard(...paths) {
7330
7544
  return discardWith((path) => {
7331
- rmSync2(path, SWEPT);
7545
+ rmSync3(path, SWEPT);
7332
7546
  }, paths);
7333
7547
  }
7334
7548
  var SWEPT;
@@ -7337,9 +7551,9 @@ var init_scratch = __esm(() => {
7337
7551
  });
7338
7552
 
7339
7553
  // src/update-check.ts
7340
- import { existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync3 } from "node:fs";
7554
+ import { existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync4 } from "node:fs";
7341
7555
  import { homedir as homedir3, tmpdir as tmpdir2 } from "node:os";
7342
- import { join as join5 } from "node:path";
7556
+ import { join as join6 } from "node:path";
7343
7557
  function packageUrl(root) {
7344
7558
  return `${root.replace(/\/+$/, "")}/gdharness/latest`;
7345
7559
  }
@@ -7361,20 +7575,20 @@ function cacheDirectory(environment) {
7361
7575
  };
7362
7576
  const home = set("HOME") ?? homedir3();
7363
7577
  if (process.platform === "win32") {
7364
- return join5(set("LOCALAPPDATA") ?? home, "gdharness");
7578
+ return join6(set("LOCALAPPDATA") ?? home, "gdharness");
7365
7579
  }
7366
7580
  if (process.platform === "darwin") {
7367
- return join5(home, "Library", "Caches", "gdharness");
7581
+ return join6(home, "Library", "Caches", "gdharness");
7368
7582
  }
7369
- return join5(set("XDG_CACHE_HOME") ?? join5(home, ".cache"), "gdharness");
7583
+ return join6(set("XDG_CACHE_HOME") ?? join6(home, ".cache"), "gdharness");
7370
7584
  }
7371
7585
  function cacheFile(environment = process.env) {
7372
7586
  try {
7373
7587
  const directory = cacheDirectory(environment);
7374
- mkdirSync2(directory, { recursive: true, mode: 448 });
7375
- return join5(directory, "update-check.json");
7588
+ mkdirSync3(directory, { recursive: true, mode: 448 });
7589
+ return join6(directory, "update-check.json");
7376
7590
  } catch {
7377
- return join5(tmpdir2(), "gdharness-update-check.json");
7591
+ return join6(tmpdir2(), "gdharness-update-check.json");
7378
7592
  }
7379
7593
  }
7380
7594
  function readCache(path) {
@@ -7382,7 +7596,7 @@ function readCache(path) {
7382
7596
  if (!existsSync5(path)) {
7383
7597
  return null;
7384
7598
  }
7385
- const parsed = JSON.parse(readFileSync2(path, "utf8"));
7599
+ const parsed = JSON.parse(readFileSync3(path, "utf8"));
7386
7600
  if (typeof parsed !== "object" || parsed === null) {
7387
7601
  return null;
7388
7602
  }
@@ -7399,7 +7613,7 @@ function readCache(path) {
7399
7613
  }
7400
7614
  function writeCache(path, entry) {
7401
7615
  try {
7402
- writeFileSync3(path, JSON.stringify(entry), { encoding: "utf8", mode: 384 });
7616
+ writeFileSync4(path, JSON.stringify(entry), { encoding: "utf8", mode: 384 });
7403
7617
  } catch {}
7404
7618
  }
7405
7619
  function parts(version) {
@@ -7530,7 +7744,7 @@ var init_update_check = __esm(() => {
7530
7744
  });
7531
7745
 
7532
7746
  // src/server-version.ts
7533
- import { readFileSync as readFileSync3 } from "node:fs";
7747
+ import { readFileSync as readFileSync4 } from "node:fs";
7534
7748
  function editorIsStale(addonVersion, serverVersion, addonDigest, shippedDigest) {
7535
7749
  if (addonDigest !== undefined && addonDigest !== "" && shippedDigest !== undefined) {
7536
7750
  return addonDigest !== shippedDigest;
@@ -7566,7 +7780,7 @@ var init_server_version = __esm(() => {
7566
7780
  GODOT_DEBUG_MODE_DEFAULT = process.env["GODOT_DEBUG"] === "true" || DEBUG_MODE;
7567
7781
  SERVER_VERSION = (() => {
7568
7782
  try {
7569
- const pkg = JSON.parse(readFileSync3(new URL("../package.json", import.meta.url), "utf8"));
7783
+ const pkg = JSON.parse(readFileSync4(new URL("../package.json", import.meta.url), "utf8"));
7570
7784
  return typeof pkg.version === "string" ? pkg.version : "0.0.0";
7571
7785
  } catch {
7572
7786
  return "0.0.0";
@@ -7575,23 +7789,23 @@ var init_server_version = __esm(() => {
7575
7789
  });
7576
7790
 
7577
7791
  // src/class-cache.ts
7578
- import { existsSync as existsSync6, readdirSync as readdirSync2, readFileSync as readFileSync5, statSync as statSync2 } from "node:fs";
7579
- import { join as join6 } from "node:path";
7792
+ import { existsSync as existsSync6, readdirSync as readdirSync2, readFileSync as readFileSync6, statSync as statSync2 } from "node:fs";
7793
+ import { join as join7 } from "node:path";
7580
7794
  function declaredClasses(projectPath) {
7581
7795
  const declared = new Map;
7582
7796
  const visit = (directory, prefix) => {
7583
- if (existsSync6(join6(directory, ".gdignore"))) {
7797
+ if (existsSync6(join7(directory, ".gdignore"))) {
7584
7798
  return;
7585
7799
  }
7586
7800
  for (const entry of readdirSync2(directory, { withFileTypes: true })) {
7587
7801
  if (entry.name.startsWith(".")) {
7588
7802
  continue;
7589
7803
  }
7590
- const path = join6(directory, entry.name);
7804
+ const path = join7(directory, entry.name);
7591
7805
  if (entry.isDirectory()) {
7592
7806
  visit(path, `${prefix}${entry.name}/`);
7593
7807
  } else if (entry.isFile() && entry.name.endsWith(".gd")) {
7594
- const found = DECLARATION.exec(readFileSync5(path, "utf8"));
7808
+ const found = DECLARATION.exec(readFileSync6(path, "utf8"));
7595
7809
  if (found?.[1]) {
7596
7810
  declared.set(found[1], `res://${prefix}${entry.name}`);
7597
7811
  }
@@ -7602,12 +7816,12 @@ function declaredClasses(projectPath) {
7602
7816
  return declared;
7603
7817
  }
7604
7818
  function cachedClasses(projectPath) {
7605
- const cache = join6(projectPath, ".godot", "global_script_class_cache.cfg");
7819
+ const cache = join7(projectPath, ".godot", "global_script_class_cache.cfg");
7606
7820
  if (!existsSync6(cache)) {
7607
7821
  return null;
7608
7822
  }
7609
7823
  const listed = new Map;
7610
- const text = readFileSync5(cache, "utf8");
7824
+ const text = readFileSync6(cache, "utf8");
7611
7825
  for (const entry of text.matchAll(/"class":\s*&"([^"]+)"[\s\S]*?"path":\s*"([^"]+)"/g)) {
7612
7826
  listed.set(entry[1] ?? "", entry[2] ?? "");
7613
7827
  }
@@ -7692,7 +7906,7 @@ function uncachedClassNote(types) {
7692
7906
  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.";
7693
7907
  }
7694
7908
  function cacheWrittenAt(projectPath) {
7695
- const cache = join6(projectPath, ".godot", "global_script_class_cache.cfg");
7909
+ const cache = join7(projectPath, ".godot", "global_script_class_cache.cfg");
7696
7910
  try {
7697
7911
  return statSync2(cache).mtimeMs;
7698
7912
  } catch {
@@ -7706,14 +7920,14 @@ function declaredSince(projectPath, paths, names, since) {
7706
7920
  return false;
7707
7921
  }
7708
7922
  try {
7709
- return statSync2(join6(projectPath, path.slice("res://".length))).mtimeMs > since;
7923
+ return statSync2(join7(projectPath, path.slice("res://".length))).mtimeMs > since;
7710
7924
  } catch {
7711
7925
  return false;
7712
7926
  }
7713
7927
  });
7714
7928
  }
7715
7929
  function cachedAtMissingPaths(cached, projectPath) {
7716
- return [...cached].filter(([, path]) => path.startsWith("res://") && !existsSync6(join6(projectPath, path.slice("res://".length)))).map(([name]) => name).sort();
7930
+ return [...cached].filter(([, path]) => path.startsWith("res://") && !existsSync6(join7(projectPath, path.slice("res://".length)))).map(([name]) => name).sort();
7717
7931
  }
7718
7932
  function staleAgainst(cached, projectPath) {
7719
7933
  return [...declaredClasses(projectPath)].filter(([name, path]) => cached.get(name) !== path).map(([name]) => name);
@@ -7736,11 +7950,11 @@ var init_class_cache = __esm(() => {
7736
7950
  });
7737
7951
 
7738
7952
  // src/headless.ts
7739
- import { execFile as execFile3 } from "node:child_process";
7740
- import { mkdtempSync as mkdtempSync2, writeFileSync as writeFileSync4 } from "node:fs";
7953
+ import { execFile as execFile4 } from "node:child_process";
7954
+ import { mkdtempSync as mkdtempSync2, writeFileSync as writeFileSync5 } from "node:fs";
7741
7955
  import { tmpdir as tmpdir3 } from "node:os";
7742
- import { join as join7 } from "node:path";
7743
- import { promisify as promisify3 } from "node:util";
7956
+ import { join as join8 } from "node:path";
7957
+ import { promisify as promisify4 } from "node:util";
7744
7958
  function snakeCased2(params) {
7745
7959
  const result = emptyRecord();
7746
7960
  for (const [key, value] of Object.entries(params)) {
@@ -7779,16 +7993,9 @@ function reason2(stdout, stderr) {
7779
7993
  return stdout.trim().split(/\r?\n/).at(-1) ?? "no output at all";
7780
7994
  }
7781
7995
  async function runImport(godotPath, projectPath) {
7782
- const logDir = mkdtempSync2(join7(tmpdir3(), "gdharness-import-"));
7996
+ const logDir = mkdtempSync2(join8(tmpdir3(), "gdharness-import-"));
7783
7997
  try {
7784
- const { stderr } = await run3(godotPath, [
7785
- "--headless",
7786
- "--log-file",
7787
- join7(logDir, "engine.log"),
7788
- "--path",
7789
- projectPath,
7790
- "--import"
7791
- ]);
7998
+ const { stderr } = await run4(godotPath, ["--headless", "--log-file", join8(logDir, "engine.log"), "--path", projectPath, "--import"], { signal: callSignal() });
7792
7999
  return { ok: true, messages: problems2(stderr) };
7793
8000
  } catch (error) {
7794
8001
  if (error instanceof Error && "stdout" in error && "stderr" in error) {
@@ -7809,13 +8016,13 @@ async function runImport(godotPath, projectPath) {
7809
8016
  }
7810
8017
  }
7811
8018
  async function runOperation2(engine, operation, params, projectPath) {
7812
- const paramsDir = mkdtempSync2(join7(tmpdir3(), "gdharness-params-"));
7813
- const paramsFile = join7(paramsDir, `${operation}.json`);
7814
- writeFileSync4(paramsFile, JSON.stringify(snakeCased2(params)), "utf8");
8019
+ const paramsDir = mkdtempSync2(join8(tmpdir3(), "gdharness-params-"));
8020
+ const paramsFile = join8(paramsDir, `${operation}.json`);
8021
+ writeFileSync5(paramsFile, JSON.stringify(snakeCased2(params)), "utf8");
7815
8022
  const args = [
7816
8023
  "--headless",
7817
8024
  "--log-file",
7818
- join7(paramsDir, "engine.log"),
8025
+ join8(paramsDir, "engine.log"),
7819
8026
  "--path",
7820
8027
  projectPath,
7821
8028
  "--script",
@@ -7827,7 +8034,7 @@ async function runOperation2(engine, operation, params, projectPath) {
7827
8034
  let stdout;
7828
8035
  let stderr;
7829
8036
  try {
7830
- ({ stdout, stderr } = await run3(engine.godotPath, args));
8037
+ ({ stdout, stderr } = await run4(engine.godotPath, args, { signal: callSignal() }));
7831
8038
  } catch (error) {
7832
8039
  if (error instanceof Error && "stdout" in error && "stderr" in error) {
7833
8040
  const failed = error;
@@ -7855,11 +8062,12 @@ async function runOperation2(engine, operation, params, projectPath) {
7855
8062
  }
7856
8063
  return { ok: true, payload, messages: problems2(stderr) };
7857
8064
  }
7858
- var run3;
8065
+ var run4;
7859
8066
  var init_headless = __esm(() => {
8067
+ init_call_signal();
7860
8068
  init_game_log();
7861
8069
  init_scratch();
7862
- run3 = promisify3(execFile3);
8070
+ run4 = promisify4(execFile4);
7863
8071
  });
7864
8072
 
7865
8073
  // node_modules/zod/v4/core/util.js
@@ -15027,26 +15235,26 @@ var init_paths = __esm(() => {
15027
15235
  });
15028
15236
 
15029
15237
  // src/resources.ts
15030
- import { readFileSync as readFileSync6 } from "node:fs";
15238
+ import { readFileSync as readFileSync7 } from "node:fs";
15031
15239
  import { extname, resolve as resolve2 } from "node:path";
15032
15240
  function ensureProjectPath(getProjectPath) {
15033
15241
  const projectPath = getProjectPath();
15034
15242
  if (!projectPath) {
15035
- throw new Refusal2("Project path is not set. Set a Godot project path first.");
15243
+ throw new Refusal("Project path is not set. Set a Godot project path first.");
15036
15244
  }
15037
15245
  return resolve2(projectPath);
15038
15246
  }
15039
15247
  function uriPathToProjectPath(inputPath) {
15040
15248
  const normalized = inputPath.replace(/\\/g, "/").trim();
15041
15249
  if (normalized.replace(/\//g, "") === "") {
15042
- throw new Refusal2("Resource path is empty.");
15250
+ throw new Refusal("Resource path is empty.");
15043
15251
  }
15044
15252
  return normalized.replace(/^\/+/, "");
15045
15253
  }
15046
15254
  function resolveProjectFile(projectPath, resourcePath) {
15047
15255
  const contained = resolveWithinProject(projectPath, resourcePath);
15048
15256
  if (!contained.ok) {
15049
- throw new Refusal2(contained.reason);
15257
+ throw new Refusal(contained.reason);
15050
15258
  }
15051
15259
  return contained.absolutePath;
15052
15260
  }
@@ -15058,14 +15266,14 @@ function parseGodotUri(uri) {
15058
15266
  try {
15059
15267
  parsed = new URL(uri);
15060
15268
  } catch {
15061
- throw new Refusal2(`Invalid URI: ${uri}`);
15269
+ throw new Refusal(`Invalid URI: ${uri}`);
15062
15270
  }
15063
15271
  if (parsed.protocol !== "godot:") {
15064
- throw new Refusal2(`Unsupported URI scheme: ${parsed.protocol}`);
15272
+ throw new Refusal(`Unsupported URI scheme: ${parsed.protocol}`);
15065
15273
  }
15066
15274
  const host = parsed.hostname;
15067
15275
  if (host !== "scene" && host !== "script" && host !== "resource") {
15068
- throw new Refusal2(`Unsupported Godot resource type: ${host}`);
15276
+ throw new Refusal(`Unsupported Godot resource type: ${host}`);
15069
15277
  }
15070
15278
  const resourcePath = uriPathToProjectPath(decodeURIComponent(parsed.pathname));
15071
15279
  return { kind: host, resourcePath };
@@ -15073,13 +15281,13 @@ function parseGodotUri(uri) {
15073
15281
  function ensureAllowedExtension(kind, filePath) {
15074
15282
  const extension = extname(filePath).toLowerCase();
15075
15283
  if (kind === "scene" && extension !== ".tscn") {
15076
- throw new Refusal2("Scene resources must use .tscn extension.");
15284
+ throw new Refusal("Scene resources must use .tscn extension.");
15077
15285
  }
15078
15286
  if (kind === "script" && extension !== ".gd") {
15079
- throw new Refusal2("Script resources must use .gd extension.");
15287
+ throw new Refusal("Script resources must use .gd extension.");
15080
15288
  }
15081
15289
  if (kind === "resource" && ![".tres", ".tscn", ".gd"].includes(extension)) {
15082
- throw new Refusal2("Resource URIs support only .tres, .tscn, and .gd files.");
15290
+ throw new Refusal("Resource URIs support only .tres, .tscn, and .gd files.");
15083
15291
  }
15084
15292
  }
15085
15293
  function isValueComplete(value) {
@@ -15193,7 +15401,7 @@ function readResourceText(uri, getProjectPath) {
15193
15401
  const parsedUri = parseGodotUri(uri);
15194
15402
  if (parsedUri.kind === "project-info") {
15195
15403
  const projectFilePath = resolveProjectFile(projectPath, "project.godot");
15196
- const rawProject = readFileSync6(projectFilePath, "utf-8");
15404
+ const rawProject = readFileSync7(projectFilePath, "utf-8");
15197
15405
  const parsedProject = parseProjectGodot(rawProject);
15198
15406
  return {
15199
15407
  mimeType: "application/json",
@@ -15202,7 +15410,7 @@ function readResourceText(uri, getProjectPath) {
15202
15410
  }
15203
15411
  const filePath = resolveProjectFile(projectPath, parsedUri.resourcePath);
15204
15412
  ensureAllowedExtension(parsedUri.kind, filePath);
15205
- const text = readFileSync6(filePath, "utf-8");
15413
+ const text = readFileSync7(filePath, "utf-8");
15206
15414
  return {
15207
15415
  mimeType: parsedUri.kind === "script" ? "text/x-gdscript" : "text/plain",
15208
15416
  text
@@ -15231,9 +15439,9 @@ function setupResourceHandlers(mcp, getProjectPath) {
15231
15439
  };
15232
15440
  } catch (error) {
15233
15441
  if (error instanceof Error) {
15234
- throw new Refusal2(`Failed to read resource '${uri}': ${error.message}`, { cause: error });
15442
+ throw new Refusal(`Failed to read resource '${uri}': ${error.message}`, { cause: error });
15235
15443
  }
15236
- throw new Refusal2(`Failed to read resource '${uri}'.`, { cause: error });
15444
+ throw new Refusal(`Failed to read resource '${uri}'.`, { cause: error });
15237
15445
  }
15238
15446
  });
15239
15447
  }
@@ -15334,14 +15542,14 @@ import { createHash as createHash3 } from "node:crypto";
15334
15542
  import {
15335
15543
  cpSync as cpSync2,
15336
15544
  existsSync as existsSync8,
15337
- mkdirSync as mkdirSync4,
15545
+ mkdirSync as mkdirSync5,
15338
15546
  readdirSync as readdirSync4,
15339
- readFileSync as readFileSync8,
15340
- rmSync as rmSync4,
15547
+ readFileSync as readFileSync9,
15548
+ rmSync as rmSync5,
15341
15549
  statSync as statSync4,
15342
- writeFileSync as writeFileSync6
15550
+ writeFileSync as writeFileSync7
15343
15551
  } from "node:fs";
15344
- import { dirname as dirname4, join as join9 } from "node:path";
15552
+ import { dirname as dirname5, join as join10 } from "node:path";
15345
15553
  import { fileURLToPath as fileURLToPath2 } from "node:url";
15346
15554
  function editorAddonDigest2(from = shippedAddonsDirectory2()) {
15347
15555
  const hash = createHash3("sha256");
@@ -15350,21 +15558,21 @@ function editorAddonDigest2(from = shippedAddonsDirectory2()) {
15350
15558
  if (name === VERSION_MARKER2 || name === DIGEST_MARKER2) {
15351
15559
  continue;
15352
15560
  }
15353
- const path = join9(directory, name);
15561
+ const path = join10(directory, name);
15354
15562
  const inside = `${relative}/${name}`;
15355
15563
  if (statSync4(path).isDirectory()) {
15356
15564
  walk(path, inside);
15357
15565
  } else {
15358
15566
  hash.update(`${inside}
15359
15567
  `);
15360
- hash.update(readFileSync8(path));
15568
+ hash.update(readFileSync9(path));
15361
15569
  hash.update(`
15362
15570
  `);
15363
15571
  }
15364
15572
  }
15365
15573
  };
15366
15574
  for (const name of EDITOR_PLUGINS2) {
15367
- walk(join9(from, name), name);
15575
+ walk(join10(from, name), name);
15368
15576
  }
15369
15577
  return hash.digest("hex");
15370
15578
  }
@@ -15379,14 +15587,14 @@ function shippedEditorDigest() {
15379
15587
  return shippedDigest ?? undefined;
15380
15588
  }
15381
15589
  function shippedAddonsDirectory2() {
15382
- return join9(dirname4(fileURLToPath2(import.meta.url)), "godot", "addons");
15590
+ return join10(dirname5(fileURLToPath2(import.meta.url)), "godot", "addons");
15383
15591
  }
15384
15592
  function installedAddonVersion(projectPath) {
15385
- const marker = join9(projectPath, "addons", ADDONS2[0], VERSION_MARKER2);
15593
+ const marker = join10(projectPath, "addons", ADDONS2[0], VERSION_MARKER2);
15386
15594
  if (!existsSync8(marker)) {
15387
15595
  return null;
15388
15596
  }
15389
- const said = readFileSync8(marker, "utf8").trim();
15597
+ const said = readFileSync9(marker, "utf8").trim();
15390
15598
  return said === "" ? null : said;
15391
15599
  }
15392
15600
  var ADDONS2, EDITOR_PLUGINS2, RUNTIME_AUTOLOAD2, SCRIPT_RUNS_SETTING = "gdharness/runtime/serve_script_runs", BIND_ADDRESS_SETTING = "gdharness/runtime/bind_address", PORT_SETTING = "gdharness/runtime/port", VERSION_MARKER2 = ".gdharness-version", DIGEST_MARKER2 = ".gdharness-digest", shippedDigest;
@@ -31368,212 +31576,11 @@ var init_stdio2 = __esm(() => {
31368
31576
  init_stdio();
31369
31577
  });
31370
31578
 
31371
- // src/process-children.ts
31372
- import { execFile as execFile4, execFileSync, spawnSync as spawnSync2 } from "node:child_process";
31373
- import { basename as basename2 } from "node:path";
31374
- import { promisify as promisify4 } from "node:util";
31375
- function startTimesOf(pids) {
31376
- const began = new Map;
31377
- if (pids.length === 0) {
31378
- return began;
31379
- }
31380
- try {
31381
- if (process.platform === "win32") {
31382
- const filter = pids.map((pid) => `ProcessId=${pid}`).join(" OR ");
31383
- const said = execFileSync("powershell", [
31384
- "-NoProfile",
31385
- "-NonInteractive",
31386
- "-Command",
31387
- `Get-CimInstance Win32_Process -Filter "${filter}" | ForEach-Object { "$($_.ProcessId) $(([DateTimeOffset]$_.CreationDate).ToUnixTimeMilliseconds())" }`
31388
- ], { encoding: "utf8", timeout: ASK_TIMEOUT_MS, windowsHide: true });
31389
- for (const line of said.split(`
31390
- `)) {
31391
- const found = /^\s*(\d+)\s+(\d+)\s*$/.exec(line);
31392
- if (found?.[1] !== undefined && found[2] !== undefined) {
31393
- began.set(Number(found[1]), Number(found[2]));
31394
- }
31395
- }
31396
- return began;
31397
- }
31398
- const said = spawnSync2("ps", ["-p", pids.join(","), "-o", "pid=,lstart="], {
31399
- encoding: "utf8",
31400
- timeout: ASK_TIMEOUT_MS,
31401
- env: { ...process.env, LC_ALL: "C" }
31402
- }).stdout;
31403
- for (const line of said.split(`
31404
- `)) {
31405
- const found = /^\s*(\d+)\s+(.+)$/.exec(line);
31406
- const at = Date.parse(found?.[2]?.trim() ?? "");
31407
- if (found?.[1] !== undefined && !Number.isNaN(at)) {
31408
- began.set(Number(found[1]), at);
31409
- }
31410
- }
31411
- } catch {}
31412
- return began;
31413
- }
31414
- async function processTree() {
31415
- try {
31416
- if (process.platform === "win32") {
31417
- return parseProcessTable(await askWindows(), true);
31418
- }
31419
- return parseProcessTable(await askPosix());
31420
- } catch {
31421
- return;
31422
- }
31423
- }
31424
- function parseProcessTable(printed, started = false) {
31425
- const tree = new Map;
31426
- for (const line of printed.split(/\r?\n/)) {
31427
- const fields = /^\s*(\d+)\s+(\d+)(?:\s+(.*))?$/.exec(line);
31428
- if (fields === null) {
31429
- continue;
31430
- }
31431
- const pid = Number(fields[1]);
31432
- const parent = Number(fields[2]);
31433
- const rest = (fields[3] ?? "").trim();
31434
- if (pid <= 0) {
31435
- continue;
31436
- }
31437
- if (!started) {
31438
- tree.set(pid, { parent, command: rest });
31439
- continue;
31440
- }
31441
- const timed = /^(\d+)(?:\s+(.*))?$/.exec(rest);
31442
- if (timed === null) {
31443
- continue;
31444
- }
31445
- const began = Number(timed[1]);
31446
- tree.set(pid, { parent, command: (timed[2] ?? "").trim(), ...began > 0 ? { startedAt: began } : {} });
31447
- }
31448
- return tree;
31449
- }
31450
- function linked(tree, child, parent) {
31451
- const from = tree.get(child);
31452
- const to = tree.get(parent);
31453
- if (from === undefined || from.parent !== parent) {
31454
- return false;
31455
- }
31456
- if (from.startedAt === undefined || to?.startedAt === undefined) {
31457
- return true;
31458
- }
31459
- return from.startedAt >= to.startedAt;
31460
- }
31461
- function childrenIn(tree, pid) {
31462
- const children = [];
31463
- for (const child of tree.keys()) {
31464
- if (linked(tree, child, pid)) {
31465
- children.push(child);
31466
- }
31467
- }
31468
- return children;
31469
- }
31470
- function descendantsIn(tree, pid) {
31471
- const found = [];
31472
- const queue = [pid];
31473
- const seen = new Set([pid]);
31474
- while (queue.length > 0) {
31475
- const next = queue.shift();
31476
- if (next === undefined) {
31477
- break;
31478
- }
31479
- for (const child of childrenIn(tree, next)) {
31480
- if (!seen.has(child)) {
31481
- seen.add(child);
31482
- found.push(child);
31483
- queue.push(child);
31484
- }
31485
- }
31486
- }
31487
- return found;
31488
- }
31489
- function ancestorsIn(tree, pid, root) {
31490
- const chain = [];
31491
- const seen = new Set([pid]);
31492
- let child = pid;
31493
- let parent = tree.get(pid)?.parent;
31494
- while (parent !== undefined && !seen.has(parent) && linked(tree, child, parent)) {
31495
- chain.push(parent);
31496
- if (parent === root) {
31497
- break;
31498
- }
31499
- seen.add(parent);
31500
- child = parent;
31501
- parent = tree.get(parent)?.parent;
31502
- }
31503
- return chain;
31504
- }
31505
- function readCommandLine(command) {
31506
- const words = commandWords(command);
31507
- const executable = words[0] === undefined ? "" : basename2(words[0].replaceAll("\\", "/"));
31508
- const at = words.indexOf("--path");
31509
- let projectPath = null;
31510
- if (at !== -1 && at + 1 < words.length) {
31511
- const taken = [];
31512
- for (const word of words.slice(at + 1)) {
31513
- if (taken.length > 0 && (word.startsWith("-") || /^[a-z]+:\/\//.test(word))) {
31514
- break;
31515
- }
31516
- taken.push(word);
31517
- }
31518
- projectPath = taken.join(" ");
31519
- }
31520
- return {
31521
- executable,
31522
- projectPath,
31523
- editor: words.some((word) => word === "-e" || word === "--editor")
31524
- };
31525
- }
31526
- function commandWords(command) {
31527
- const words = [];
31528
- let word = "";
31529
- let quoted = false;
31530
- let started = false;
31531
- for (const char of command) {
31532
- if (char === '"') {
31533
- quoted = !quoted;
31534
- started = true;
31535
- } else if (!quoted && /\s/.test(char)) {
31536
- if (started) {
31537
- words.push(word);
31538
- word = "";
31539
- started = false;
31540
- }
31541
- } else {
31542
- word += char;
31543
- started = true;
31544
- }
31545
- }
31546
- if (started) {
31547
- words.push(word);
31548
- }
31549
- return words;
31550
- }
31551
- async function askWindows() {
31552
- const { stdout } = await run4("powershell.exe", [
31553
- "-NoProfile",
31554
- "-NonInteractive",
31555
- "-Command",
31556
- `${POWERSHELL_UTF8}Get-CimInstance Win32_Process | ForEach-Object { $began = 0; if ($_.CreationDate) { $began = ([DateTimeOffset]$_.CreationDate).ToUnixTimeMilliseconds() }; "$($_.ProcessId) $($_.ParentProcessId) $began $($_.CommandLine)" }`
31557
- ], { timeout: ASK_TIMEOUT_MS, windowsHide: true, maxBuffer: 16 * 1024 * 1024 });
31558
- return stdout;
31559
- }
31560
- async function askPosix() {
31561
- const { stdout } = await run4("ps", ["-e", "-o", "pid=,ppid=,args="], {
31562
- timeout: ASK_TIMEOUT_MS,
31563
- maxBuffer: 16 * 1024 * 1024
31564
- });
31565
- return stdout;
31566
- }
31567
- var run4, POWERSHELL_UTF8 = "[Console]::OutputEncoding = [System.Text.Encoding]::UTF8; ", ASK_TIMEOUT_MS = 15000;
31568
- var init_process_children = __esm(() => {
31569
- run4 = promisify4(execFile4);
31570
- });
31571
-
31572
31579
  // src/runtime-client.ts
31573
- import { existsSync as existsSync10, readdirSync as readdirSync6, readFileSync as readFileSync10, statSync as statSync5, unlinkSync } from "node:fs";
31580
+ import { existsSync as existsSync10, readdirSync as readdirSync6, readFileSync as readFileSync11, statSync as statSync5, unlinkSync } from "node:fs";
31574
31581
  import { createConnection } from "node:net";
31575
31582
  import { tmpdir as tmpdir4 } from "node:os";
31576
- import { dirname as dirname7, join as join12, resolve as resolve3 } from "node:path";
31583
+ import { dirname as dirname8, join as join13, resolve as resolve3 } from "node:path";
31577
31584
  import { setTimeout as delay } from "node:timers/promises";
31578
31585
  function runtimeDirectory(variables = process.env) {
31579
31586
  const explicit = envValue("GDHARNESS_RUNTIME_DIR", variables);
@@ -31581,19 +31588,19 @@ function runtimeDirectory(variables = process.env) {
31581
31588
  return explicit;
31582
31589
  }
31583
31590
  const perUser = envValue("XDG_RUNTIME_DIR", variables);
31584
- return join12(perUser ?? tmpdir4(), "gdharness");
31591
+ return join13(perUser ?? tmpdir4(), "gdharness");
31585
31592
  }
31586
31593
  function runtimeDirectories(variables = process.env) {
31587
31594
  const candidates = [runtimeDirectory(variables)];
31588
31595
  const fallbacks = process.platform === "win32" ? [envValue("SystemRoot", variables) ?? envValue("windir", variables) ?? "C:\\Windows"] : ["/tmp", "/var/tmp"];
31589
31596
  for (const base of fallbacks) {
31590
- candidates.push(join12(base, process.platform === "win32" ? "Temp" : "", "gdharness"));
31597
+ candidates.push(join13(base, process.platform === "win32" ? "Temp" : "", "gdharness"));
31591
31598
  }
31592
31599
  return [...new Set(candidates.map((path) => resolve3(path)))];
31593
31600
  }
31594
31601
  function errorReportOf(pid, directories = runtimeDirectories()) {
31595
31602
  for (const directory of directories) {
31596
- const path = join12(directory, `runtime-${pid}.log`);
31603
+ const path = join13(directory, `runtime-${pid}.log`);
31597
31604
  if (existsSync10(path)) {
31598
31605
  return path;
31599
31606
  }
@@ -31611,7 +31618,7 @@ function processAlive(pid) {
31611
31618
  function parseAnnouncement(file, pid) {
31612
31619
  let fields;
31613
31620
  try {
31614
- fields = asParams(JSON.parse(readFileSync10(file, "utf8")));
31621
+ fields = asParams(JSON.parse(readFileSync11(file, "utf8")));
31615
31622
  } catch {
31616
31623
  return { kind: "rubbish" };
31617
31624
  }
@@ -31663,7 +31670,7 @@ function discoverRuntimes(directories = runtimeDirectories()) {
31663
31670
  function wentAway(file, pid) {
31664
31671
  let project = "";
31665
31672
  try {
31666
- const fields = asParams(JSON.parse(readFileSync10(file, "utf8")));
31673
+ const fields = asParams(JSON.parse(readFileSync11(file, "utf8")));
31667
31674
  project = readString(readParams(fields, "project") ?? {}, "path") ?? "";
31668
31675
  } catch {
31669
31676
  return;
@@ -31709,14 +31716,14 @@ function announcedIn(directory) {
31709
31716
  for (const entry of readdirSync6(directory)) {
31710
31717
  const report = ERROR_REPORT_PATTERN.exec(entry);
31711
31718
  if (report) {
31712
- sweepErrorReport(join12(directory, entry), Number.parseInt(report[1] ?? "", 10));
31719
+ sweepErrorReport(join13(directory, entry), Number.parseInt(report[1] ?? "", 10));
31713
31720
  continue;
31714
31721
  }
31715
31722
  const match = ANNOUNCEMENT_PATTERN.exec(entry);
31716
31723
  if (!match) {
31717
31724
  continue;
31718
31725
  }
31719
- const file = join12(directory, entry);
31726
+ const file = join13(directory, entry);
31720
31727
  const pid = Number.parseInt(match[1] ?? "", 10);
31721
31728
  const alive = processAlive(pid);
31722
31729
  entries.push({ file, pid, alive });
@@ -31744,16 +31751,16 @@ function announcedIn(directory) {
31744
31751
  found.push(announced);
31745
31752
  }
31746
31753
  const held = new Set(entries.map((one) => one.file));
31747
- const here = join12(directory, ".");
31754
+ const here = join13(directory, ".");
31748
31755
  for (const file of verdicts.keys()) {
31749
- if (dirname7(file) === here && !held.has(file)) {
31756
+ if (dirname8(file) === here && !held.has(file)) {
31750
31757
  verdicts.delete(file);
31751
31758
  }
31752
31759
  }
31753
31760
  return found;
31754
31761
  }
31755
31762
  async function announcementEnded(pid, directories = runtimeDirectories()) {
31756
- const files = directories.map((directory) => join12(directory, `runtime-${pid}.json`)).filter((file) => existsSync10(file));
31763
+ const files = directories.map((directory) => join13(directory, `runtime-${pid}.json`)).filter((file) => existsSync10(file));
31757
31764
  const first = files[0];
31758
31765
  if (first === undefined) {
31759
31766
  return;
@@ -32002,14 +32009,14 @@ var init_runtime_client = __esm(() => {
32002
32009
  });
32003
32010
 
32004
32011
  // src/boot-note.ts
32005
- import { mkdirSync as mkdirSync6, readFileSync as readFileSync11, writeFileSync as writeFileSync8 } from "node:fs";
32006
- import { dirname as dirname8, join as join13 } from "node:path";
32012
+ import { mkdirSync as mkdirSync7, readFileSync as readFileSync12, writeFileSync as writeFileSync9 } from "node:fs";
32013
+ import { dirname as dirname9, join as join14 } from "node:path";
32007
32014
  function bootNotePath(projectPath) {
32008
- return join13(projectPath, ".godot", "gdharness-boot.json");
32015
+ return join14(projectPath, ".godot", "gdharness-boot.json");
32009
32016
  }
32010
32017
  function readBootNote(projectPath) {
32011
32018
  try {
32012
- const noted = readNumber(asParams(JSON.parse(readFileSync11(bootNotePath(projectPath), "utf8"))), "announcedAfterMs");
32019
+ const noted = readNumber(asParams(JSON.parse(readFileSync12(bootNotePath(projectPath), "utf8"))), "announcedAfterMs");
32013
32020
  return noted !== undefined && Number.isFinite(noted) && noted > 0 ? noted : null;
32014
32021
  } catch {
32015
32022
  return null;
@@ -32018,8 +32025,8 @@ function readBootNote(projectPath) {
32018
32025
  function writeBootNote(projectPath, announcedAfterMs) {
32019
32026
  try {
32020
32027
  const path = bootNotePath(projectPath);
32021
- mkdirSync6(dirname8(path), { recursive: true });
32022
- writeFileSync8(path, JSON.stringify({ announcedAfterMs: Math.round(announcedAfterMs), at: new Date().toISOString() }, null, 2), "utf8");
32028
+ mkdirSync7(dirname9(path), { recursive: true });
32029
+ writeFileSync9(path, JSON.stringify({ announcedAfterMs: Math.round(announcedAfterMs), at: new Date().toISOString() }, null, 2), "utf8");
32023
32030
  } catch {}
32024
32031
  }
32025
32032
  function halfAsLongAgain(lastBootMs) {
@@ -32037,10 +32044,10 @@ var init_boot_note = __esm(() => {
32037
32044
  });
32038
32045
 
32039
32046
  // src/breakpoint-note.ts
32040
- import { mkdirSync as mkdirSync7, readFileSync as readFileSync12, rmSync as rmSync6, writeFileSync as writeFileSync9 } from "node:fs";
32041
- import { dirname as dirname9, isAbsolute as isAbsolute2, join as join14, relative as relative2 } from "node:path";
32047
+ import { mkdirSync as mkdirSync8, readFileSync as readFileSync13, rmSync as rmSync7, writeFileSync as writeFileSync10 } from "node:fs";
32048
+ import { dirname as dirname10, isAbsolute as isAbsolute2, join as join15, relative as relative2 } from "node:path";
32042
32049
  function breakpointNotePath(projectPath) {
32043
- return join14(projectPath, ".godot", "gdharness-breakpoints.json");
32050
+ return join15(projectPath, ".godot", "gdharness-breakpoints.json");
32044
32051
  }
32045
32052
  function writeBreakpointNote(projectPath, held) {
32046
32053
  const path = breakpointNotePath(projectPath);
@@ -32048,7 +32055,7 @@ function writeBreakpointNote(projectPath, held) {
32048
32055
  const inside = held.map((one) => ({ scriptPath: realPathOr(one.scriptPath), lines: one.lines })).filter((one) => one.lines.length > 0 && withinProject(root, one.scriptPath));
32049
32056
  try {
32050
32057
  if (inside.length === 0) {
32051
- rmSync6(path, { force: true });
32058
+ rmSync7(path, { force: true });
32052
32059
  return;
32053
32060
  }
32054
32061
  const note = {
@@ -32057,8 +32064,8 @@ function writeBreakpointNote(projectPath, held) {
32057
32064
  lines: [...one.lines]
32058
32065
  }))
32059
32066
  };
32060
- mkdirSync7(dirname9(path), { recursive: true });
32061
- writeFileSync9(path, `${JSON.stringify(note, null, 2)}
32067
+ mkdirSync8(dirname10(path), { recursive: true });
32068
+ writeFileSync10(path, `${JSON.stringify(note, null, 2)}
32062
32069
  `, "utf8");
32063
32070
  } catch (error) {
32064
32071
  console.error(`[SERVER] Could not record the breakpoints at ${path}: ${errorMessage(error)}`);
@@ -32066,7 +32073,7 @@ function writeBreakpointNote(projectPath, held) {
32066
32073
  }
32067
32074
  function readBreakpointNote(projectPath) {
32068
32075
  try {
32069
- const parsed = JSON.parse(readFileSync12(breakpointNotePath(projectPath), "utf8"));
32076
+ const parsed = JSON.parse(readFileSync13(breakpointNotePath(projectPath), "utf8"));
32070
32077
  if (typeof parsed !== "object" || parsed === null || !Array.isArray(parsed.breakpoints)) {
32071
32078
  return [];
32072
32079
  }
@@ -32081,7 +32088,7 @@ function readBreakpointNote(projectPath) {
32081
32088
  }
32082
32089
  const lines = Array.isArray(listed) ? listed.filter((line) => Number.isInteger(line) && line > 0) : [];
32083
32090
  if (lines.length > 0) {
32084
- held.push({ scriptPath: realPathOr(join14(projectPath, script)), lines });
32091
+ held.push({ scriptPath: realPathOr(join15(projectPath, script)), lines });
32085
32092
  }
32086
32093
  }
32087
32094
  return held;
@@ -32099,14 +32106,14 @@ var init_breakpoint_note = __esm(() => {
32099
32106
  });
32100
32107
 
32101
32108
  // src/bridge-announce.ts
32102
- import { mkdirSync as mkdirSync8, readFileSync as readFileSync13, rmSync as rmSync7, writeFileSync as writeFileSync10 } from "node:fs";
32103
- import { dirname as dirname10, join as join15 } from "node:path";
32104
- function announcementPath(projectPath) {
32105
- return join15(projectPath, ".godot", "gdharness-bridge.json");
32109
+ import { mkdirSync as mkdirSync9, readFileSync as readFileSync14, rmSync as rmSync8, writeFileSync as writeFileSync11 } from "node:fs";
32110
+ import { dirname as dirname11, join as join16 } from "node:path";
32111
+ function announcementPath2(projectPath) {
32112
+ return join16(projectPath, ".godot", "gdharness-bridge.json");
32106
32113
  }
32107
- function readAnnouncement(path) {
32114
+ function readAnnouncement2(path) {
32108
32115
  try {
32109
- const parsed = JSON.parse(readFileSync13(path, "utf8"));
32116
+ const parsed = JSON.parse(readFileSync14(path, "utf8"));
32110
32117
  if (typeof parsed !== "object" || parsed === null) {
32111
32118
  return null;
32112
32119
  }
@@ -32120,12 +32127,12 @@ function readAnnouncement(path) {
32120
32127
  }
32121
32128
  }
32122
32129
  function announcementIsOurs(path) {
32123
- return readAnnouncement(path)?.pid === process.pid;
32130
+ return readAnnouncement2(path)?.pid === process.pid;
32124
32131
  }
32125
32132
  function announceBridge(projectPath, bridge) {
32126
- const path = announcementPath(projectPath);
32133
+ const path = announcementPath2(projectPath);
32127
32134
  try {
32128
- mkdirSync8(dirname10(path), { recursive: true });
32135
+ mkdirSync9(dirname11(path), { recursive: true });
32129
32136
  const announcement = {
32130
32137
  protocol: BRIDGE_ANNOUNCE_PROTOCOL,
32131
32138
  host: bridge.host,
@@ -32134,7 +32141,7 @@ function announceBridge(projectPath, bridge) {
32134
32141
  version: bridge.version,
32135
32142
  startedAt: new Date().toISOString()
32136
32143
  };
32137
- writeFileSync10(path, `${JSON.stringify(announcement, null, 2)}
32144
+ writeFileSync11(path, `${JSON.stringify(announcement, null, 2)}
32138
32145
  `, "utf8");
32139
32146
  return path;
32140
32147
  } catch (error) {
@@ -32147,7 +32154,7 @@ function withdrawBridge(path) {
32147
32154
  return;
32148
32155
  }
32149
32156
  try {
32150
- rmSync7(path, { force: true });
32157
+ rmSync8(path, { force: true });
32151
32158
  } catch (error) {
32152
32159
  console.error(`[SERVER] Could not withdraw the editor bridge announcement: ${errorMessage(error)}`);
32153
32160
  }
@@ -32155,17 +32162,18 @@ function withdrawBridge(path) {
32155
32162
  var BRIDGE_ANNOUNCE_PROTOCOL = 1;
32156
32163
  var init_bridge_announce = __esm(() => {
32157
32164
  init_errors();
32165
+ init_process_children();
32158
32166
  });
32159
32167
 
32160
32168
  // src/class-note.ts
32161
- import { mkdirSync as mkdirSync9, readFileSync as readFileSync14, writeFileSync as writeFileSync11 } from "node:fs";
32162
- import { dirname as dirname11, join as join16 } from "node:path";
32169
+ import { mkdirSync as mkdirSync10, readFileSync as readFileSync15, writeFileSync as writeFileSync12 } from "node:fs";
32170
+ import { dirname as dirname12, join as join17 } from "node:path";
32163
32171
  function classNotePath(projectPath) {
32164
- return join16(projectPath, ".godot", "gdharness-classes.json");
32172
+ return join17(projectPath, ".godot", "gdharness-classes.json");
32165
32173
  }
32166
32174
  function readClassNote(projectPath) {
32167
32175
  try {
32168
- const fields = asParams(JSON.parse(readFileSync14(classNotePath(projectPath), "utf8")));
32176
+ const fields = asParams(JSON.parse(readFileSync15(classNotePath(projectPath), "utf8")));
32169
32177
  const rebuiltWith = readArray(fields, "rebuiltWith");
32170
32178
  if (rebuiltWith === undefined || !rebuiltWith.every((name) => typeof name === "string")) {
32171
32179
  return null;
@@ -32182,24 +32190,24 @@ function readClassNote(projectPath) {
32182
32190
  function writeClassNote(projectPath, note) {
32183
32191
  try {
32184
32192
  const path = classNotePath(projectPath);
32185
- mkdirSync9(dirname11(path), { recursive: true });
32186
- writeFileSync11(path, `${JSON.stringify(note, null, 2)}
32193
+ mkdirSync10(dirname12(path), { recursive: true });
32194
+ writeFileSync12(path, `${JSON.stringify(note, null, 2)}
32187
32195
  `, "utf8");
32188
32196
  } catch {}
32189
32197
  }
32190
32198
  var init_class_note = () => {};
32191
32199
 
32192
32200
  // src/config-pin.ts
32193
- import { existsSync as existsSync11, readFileSync as readFileSync15 } from "node:fs";
32194
- import { join as join17 } from "node:path";
32201
+ import { existsSync as existsSync11, readFileSync as readFileSync16 } from "node:fs";
32202
+ import { join as join18 } from "node:path";
32195
32203
  function versionsNamedInConfig(projectPath) {
32196
- const config = join17(projectPath, ".mcp.json");
32204
+ const config = join18(projectPath, ".mcp.json");
32197
32205
  if (!existsSync11(config)) {
32198
32206
  return [];
32199
32207
  }
32200
32208
  let text;
32201
32209
  try {
32202
- text = readFileSync15(config, "utf8");
32210
+ text = readFileSync16(config, "utf8");
32203
32211
  JSON.parse(text);
32204
32212
  } catch {
32205
32213
  return [];
@@ -32307,7 +32315,7 @@ function portFromEnvOrNull(variable) {
32307
32315
  }
32308
32316
  const parsed = Number.parseInt(raw, 10);
32309
32317
  if (!Number.isInteger(parsed) || parsed < 1 || parsed > 65535 || String(parsed) !== raw) {
32310
- throw new Refusal2(`${variable} is "${raw}", not a port between 1 and 65535.`);
32318
+ throw new Refusal(`${variable} is "${raw}", not a port between 1 and 65535.`);
32311
32319
  }
32312
32320
  return parsed;
32313
32321
  }
@@ -32321,7 +32329,7 @@ async function freePort(preferred, host = "127.0.0.1") {
32321
32329
  }
32322
32330
  const spare = await bindable(0, host);
32323
32331
  if (spare === null) {
32324
- throw new Refusal2(`No port could be bound on ${host}.`);
32332
+ throw new Refusal(`No port could be bound on ${host}.`);
32325
32333
  }
32326
32334
  return spare;
32327
32335
  }
@@ -32471,7 +32479,7 @@ class GodotDAPClient {
32471
32479
  async sendRequest(command, args, timeoutMs = DAP_REQUEST_TIMEOUT_MS) {
32472
32480
  await this.ensureConnected();
32473
32481
  if (!this.socket) {
32474
- throw new Refusal2("DAP socket is not available");
32482
+ throw new Refusal("DAP socket is not available");
32475
32483
  }
32476
32484
  const requestSeq = this.seq++;
32477
32485
  const request = {
@@ -32881,7 +32889,7 @@ async function handleDAPTool(client, toolName, args) {
32881
32889
  }
32882
32890
  case "dap_set_breakpoint": {
32883
32891
  if (typeof safeArgs.scriptPath !== "string" || typeof safeArgs.line !== "number") {
32884
- throw new Refusal2("dap_set_breakpoint requires { scriptPath: string, line: number }");
32892
+ throw new Refusal("dap_set_breakpoint requires { scriptPath: string, line: number }");
32885
32893
  }
32886
32894
  const result = await client.setBreakpoint(safeArgs.scriptPath, safeArgs.line);
32887
32895
  return {
@@ -32890,7 +32898,7 @@ async function handleDAPTool(client, toolName, args) {
32890
32898
  }
32891
32899
  case "dap_remove_breakpoint": {
32892
32900
  if (typeof safeArgs.scriptPath !== "string" || typeof safeArgs.line !== "number") {
32893
- throw new Refusal2("dap_remove_breakpoint requires { scriptPath: string, line: number }");
32901
+ throw new Refusal("dap_remove_breakpoint requires { scriptPath: string, line: number }");
32894
32902
  }
32895
32903
  const result = await client.removeBreakpoint(safeArgs.scriptPath, safeArgs.line);
32896
32904
  return {
@@ -32953,24 +32961,24 @@ var init_dap_client = __esm(() => {
32953
32961
  import {
32954
32962
  closeSync,
32955
32963
  existsSync as existsSync12,
32956
- mkdirSync as mkdirSync10,
32964
+ mkdirSync as mkdirSync11,
32957
32965
  openSync,
32958
- readFileSync as readFileSync16,
32966
+ readFileSync as readFileSync17,
32959
32967
  readSync,
32960
- rmSync as rmSync8,
32968
+ rmSync as rmSync9,
32961
32969
  statSync as statSync6,
32962
- writeFileSync as writeFileSync12
32970
+ writeFileSync as writeFileSync13
32963
32971
  } from "node:fs";
32964
- import { dirname as dirname12, join as join18 } from "node:path";
32972
+ import { dirname as dirname13, join as join19 } from "node:path";
32965
32973
  function editorLogPath(projectPath) {
32966
- return join18(projectPath, ".godot", "gdharness-editor.log");
32974
+ return join19(projectPath, ".godot", "gdharness-editor.log");
32967
32975
  }
32968
32976
  function editorLogNotePath(projectPath) {
32969
- return join18(projectPath, ".godot", "gdharness-editor.json");
32977
+ return join19(projectPath, ".godot", "gdharness-editor.json");
32970
32978
  }
32971
32979
  function readEditorLogNote(projectPath) {
32972
32980
  try {
32973
- const fields = asParams(JSON.parse(readFileSync16(editorLogNotePath(projectPath), "utf8")));
32981
+ const fields = asParams(JSON.parse(readFileSync17(editorLogNotePath(projectPath), "utf8")));
32974
32982
  const pid = readNumber(fields, "pid");
32975
32983
  const startedAt = readString(fields, "startedAt");
32976
32984
  return pid !== undefined && Number.isInteger(pid) && pid > 0 && startedAt !== undefined ? { pid, startedAt } : null;
@@ -32981,16 +32989,16 @@ function readEditorLogNote(projectPath) {
32981
32989
  function clearEditorLog(projectPath) {
32982
32990
  try {
32983
32991
  const path = editorLogPath(projectPath);
32984
- mkdirSync10(dirname12(path), { recursive: true });
32985
- rmSync8(path, { force: true });
32986
- rmSync8(editorLogNotePath(projectPath), { force: true });
32992
+ mkdirSync11(dirname13(path), { recursive: true });
32993
+ rmSync9(path, { force: true });
32994
+ rmSync9(editorLogNotePath(projectPath), { force: true });
32987
32995
  } catch {}
32988
32996
  }
32989
32997
  function writeEditorLogNote(projectPath, pid) {
32990
32998
  try {
32991
32999
  const path = editorLogNotePath(projectPath);
32992
- mkdirSync10(dirname12(path), { recursive: true });
32993
- writeFileSync12(path, JSON.stringify({ pid, startedAt: new Date().toISOString() }, null, 2), "utf8");
33000
+ mkdirSync11(dirname13(path), { recursive: true });
33001
+ writeFileSync13(path, JSON.stringify({ pid, startedAt: new Date().toISOString() }, null, 2), "utf8");
32994
33002
  } catch {}
32995
33003
  }
32996
33004
  function theConsoleWasNotCaptured(why, projectPath) {
@@ -33023,7 +33031,7 @@ function editorConsole(projectPath, editorPid, openedByAServer) {
33023
33031
  function theFirstOf(path) {
33024
33032
  const whole = statSync6(path).size;
33025
33033
  if (whole <= MOST_CONSOLE_BYTES) {
33026
- return { text: readFileSync16(path, "utf8") };
33034
+ return { text: readFileSync17(path, "utf8") };
33027
33035
  }
33028
33036
  const handle = openSync(path, "r");
33029
33037
  try {
@@ -36441,7 +36449,7 @@ var init_godot_bridge = __esm(() => {
36441
36449
  }
36442
36450
  sendMessage(message) {
36443
36451
  if (this.socket?.readyState !== import_websocket.default.OPEN) {
36444
- throw new Refusal2("Godot is not connected");
36452
+ throw new Refusal("Godot is not connected");
36445
36453
  }
36446
36454
  this.socket.send(JSON.stringify(message));
36447
36455
  }
@@ -36991,7 +36999,7 @@ var init_junit = __esm(() => {
36991
36999
  import { realpathSync as realpathSync2 } from "node:fs";
36992
37000
  import { readFile, realpath } from "node:fs/promises";
36993
37001
  import { createConnection as createConnection3 } from "node:net";
36994
- import { dirname as dirname13, resolve as resolve4 } from "node:path";
37002
+ import { dirname as dirname14, resolve as resolve4 } from "node:path";
36995
37003
  import { fileURLToPath as fileURLToPath4, pathToFileURL } from "node:url";
36996
37004
  function diagnosticsKey(uri) {
36997
37005
  let decoded;
@@ -37119,13 +37127,13 @@ class GodotLSPClient {
37119
37127
  if (this.initialized) {
37120
37128
  return;
37121
37129
  }
37122
- const rootPath = this.rootPath ?? dirname13(resolve4(filePath));
37130
+ const rootPath = this.rootPath ?? dirname14(resolve4(filePath));
37123
37131
  await this.initialize(rootPath);
37124
37132
  }
37125
37133
  async sendRequest(method, params) {
37126
37134
  await this.ensureConnected();
37127
37135
  if (!this.socket) {
37128
- throw new Refusal2("Not connected to Godot LSP");
37136
+ throw new Refusal("Not connected to Godot LSP");
37129
37137
  }
37130
37138
  this.requestId += 1;
37131
37139
  const id = this.requestId;
@@ -37164,7 +37172,7 @@ class GodotLSPClient {
37164
37172
  }
37165
37173
  sendNotification(method, params) {
37166
37174
  if (!this.connected || !this.socket) {
37167
- throw new Refusal2("Not connected to Godot LSP");
37175
+ throw new Refusal("Not connected to Godot LSP");
37168
37176
  }
37169
37177
  const payload = {
37170
37178
  jsonrpc: "2.0",
@@ -37446,36 +37454,36 @@ async function resolveLSPPaths(projectPathValue, scriptPathValue) {
37446
37454
  try {
37447
37455
  projectPath = await realpath(requestedProjectPath);
37448
37456
  } catch {
37449
- throw new Refusal2(`Project path does not exist: ${requestedProjectPath}`);
37457
+ throw new Refusal(`Project path does not exist: ${requestedProjectPath}`);
37450
37458
  }
37451
37459
  const contained = resolveWithinProject(projectPath, scriptPathValue);
37452
37460
  if (!contained.ok) {
37453
- throw new Refusal2(contained.reason);
37461
+ throw new Refusal(contained.reason);
37454
37462
  }
37455
37463
  let scriptPath;
37456
37464
  try {
37457
37465
  scriptPath = await realpath(contained.absolutePath);
37458
37466
  } catch {
37459
- throw new Refusal2(`Script file does not exist: ${contained.absolutePath}`);
37467
+ throw new Refusal(`Script file does not exist: ${contained.absolutePath}`);
37460
37468
  }
37461
37469
  if (!isWithinRoot(projectPath, scriptPath)) {
37462
- throw new Refusal2("scriptPath resolves outside the project root boundary.");
37470
+ throw new Refusal("scriptPath resolves outside the project root boundary.");
37463
37471
  }
37464
37472
  return { projectPath, scriptPath };
37465
37473
  }
37466
37474
  async function handleLSPTool(client, toolName, args) {
37467
37475
  try {
37468
37476
  if (!args || typeof args !== "object") {
37469
- throw new Refusal2("Tool arguments must be an object.");
37477
+ throw new Refusal("Tool arguments must be an object.");
37470
37478
  }
37471
37479
  const parsedArgs = args;
37472
37480
  const projectPathValue = parsedArgs["projectPath"];
37473
37481
  const scriptPathValue = parsedArgs["scriptPath"];
37474
37482
  if (typeof projectPathValue !== "string" || projectPathValue.length === 0) {
37475
- throw new Refusal2("Missing required argument: projectPath");
37483
+ throw new Refusal("Missing required argument: projectPath");
37476
37484
  }
37477
37485
  if (typeof scriptPathValue !== "string" || scriptPathValue.length === 0) {
37478
- throw new Refusal2("Missing required argument: scriptPath");
37486
+ throw new Refusal("Missing required argument: scriptPath");
37479
37487
  }
37480
37488
  const { projectPath, scriptPath } = await resolveLSPPaths(projectPathValue, scriptPathValue);
37481
37489
  const content = await readFile(scriptPath, "utf8");
@@ -37489,7 +37497,7 @@ async function handleLSPTool(client, toolName, args) {
37489
37497
  const line = Number(parsedArgs["line"]);
37490
37498
  const character = Number(parsedArgs["character"]);
37491
37499
  if (!Number.isFinite(line) || !Number.isFinite(character)) {
37492
- throw new Refusal2("Arguments line and character must be numbers.");
37500
+ throw new Refusal("Arguments line and character must be numbers.");
37493
37501
  }
37494
37502
  const completions = await client.getCompletions(scriptPath, content, line, character);
37495
37503
  return asToolResponse({ completions });
@@ -37498,7 +37506,7 @@ async function handleLSPTool(client, toolName, args) {
37498
37506
  const line = Number(parsedArgs["line"]);
37499
37507
  const character = Number(parsedArgs["character"]);
37500
37508
  if (!Number.isFinite(line) || !Number.isFinite(character)) {
37501
- throw new Refusal2("Arguments line and character must be numbers.");
37509
+ throw new Refusal("Arguments line and character must be numbers.");
37502
37510
  }
37503
37511
  const hover = await client.getHover(scriptPath, content, line, character);
37504
37512
  return asToolResponse({ hover });
@@ -37564,13 +37572,13 @@ var init_process_time = __esm(() => {
37564
37572
  });
37565
37573
 
37566
37574
  // src/project-scan.ts
37567
- import { existsSync as existsSync14, readdirSync as readdirSync7, readFileSync as readFileSync17 } from "node:fs";
37568
- import { join as join19 } from "node:path";
37575
+ import { existsSync as existsSync14, readdirSync as readdirSync7, readFileSync as readFileSync18 } from "node:fs";
37576
+ import { join as join20 } from "node:path";
37569
37577
  function skipped(name) {
37570
37578
  return name.startsWith(".") || SKIPPED.has(name);
37571
37579
  }
37572
37580
  function steppedOver(directory) {
37573
- return existsSync14(join19(directory, ".gdignore"));
37581
+ return existsSync14(join20(directory, ".gdignore"));
37574
37582
  }
37575
37583
  function projectStructure(projectPath) {
37576
37584
  const structure = { scenes: 0, scripts: 0, assets: 0, other: 0 };
@@ -37583,7 +37591,7 @@ function projectStructure(projectPath) {
37583
37591
  continue;
37584
37592
  }
37585
37593
  if (entry.isDirectory()) {
37586
- visit(join19(directory, entry.name));
37594
+ visit(join20(directory, entry.name));
37587
37595
  } else if (entry.isFile()) {
37588
37596
  const extension = entry.name.split(".").pop()?.toLowerCase() ?? "";
37589
37597
  if (extension === "tscn") {
@@ -37613,14 +37621,14 @@ function scriptsWithoutUid(projectPath) {
37613
37621
  }
37614
37622
  const spelled = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
37615
37623
  if (entry.isDirectory()) {
37616
- visit(join19(directory, entry.name), spelled);
37624
+ visit(join20(directory, entry.name), spelled);
37617
37625
  continue;
37618
37626
  }
37619
37627
  if (!entry.isFile()) {
37620
37628
  continue;
37621
37629
  }
37622
37630
  const extension = entry.name.split(".").pop()?.toLowerCase() ?? "";
37623
- if (CARRIES_A_UID.has(extension) && !existsSync14(join19(directory, `${entry.name}.uid`))) {
37631
+ if (CARRIES_A_UID.has(extension) && !existsSync14(join20(directory, `${entry.name}.uid`))) {
37624
37632
  found.push(spelled);
37625
37633
  }
37626
37634
  }
@@ -37655,7 +37663,7 @@ function searchProject(projectPath, options) {
37655
37663
  if (skipped(entry.name)) {
37656
37664
  continue;
37657
37665
  }
37658
- const entryPath = join19(directory, entry.name);
37666
+ const entryPath = join20(directory, entry.name);
37659
37667
  if (entry.isDirectory()) {
37660
37668
  visit(entryPath);
37661
37669
  continue;
@@ -37666,7 +37674,7 @@ function searchProject(projectPath, options) {
37666
37674
  }
37667
37675
  result.summary.files_searched += 1;
37668
37676
  const matches = [];
37669
- for (const [index, line] of readFileSync17(entryPath, "utf8").split(`
37677
+ for (const [index, line] of readFileSync18(entryPath, "utf8").split(`
37670
37678
  `).entries()) {
37671
37679
  if (full()) {
37672
37680
  break;
@@ -37695,16 +37703,16 @@ var init_project_scan = __esm(() => {
37695
37703
  });
37696
37704
 
37697
37705
  // src/restart-note.ts
37698
- import { mkdirSync as mkdirSync11, readFileSync as readFileSync18, rmSync as rmSync9, writeFileSync as writeFileSync13 } from "node:fs";
37699
- import { dirname as dirname14, join as join20 } from "node:path";
37706
+ import { mkdirSync as mkdirSync12, readFileSync as readFileSync19, rmSync as rmSync10, writeFileSync as writeFileSync14 } from "node:fs";
37707
+ import { dirname as dirname15, join as join21 } from "node:path";
37700
37708
  function restartNotePath(projectPath) {
37701
- return join20(projectPath, ".godot", "gdharness-restart.json");
37709
+ return join21(projectPath, ".godot", "gdharness-restart.json");
37702
37710
  }
37703
37711
  function noteRestartBegun(note) {
37704
37712
  const path = restartNotePath(note.projectPath);
37705
37713
  try {
37706
- mkdirSync11(dirname14(path), { recursive: true });
37707
- writeFileSync13(path, `${JSON.stringify(note, null, 2)}
37714
+ mkdirSync12(dirname15(path), { recursive: true });
37715
+ writeFileSync14(path, `${JSON.stringify(note, null, 2)}
37708
37716
  `, "utf8");
37709
37717
  } catch (error) {
37710
37718
  console.error(`[SERVER] Could not record the restart at ${path}: ${errorMessage(error)}`);
@@ -37712,7 +37720,7 @@ function noteRestartBegun(note) {
37712
37720
  }
37713
37721
  function restartOwed(projectPath) {
37714
37722
  try {
37715
- const parsed = JSON.parse(readFileSync18(restartNotePath(projectPath), "utf8"));
37723
+ const parsed = JSON.parse(readFileSync19(restartNotePath(projectPath), "utf8"));
37716
37724
  if (typeof parsed !== "object" || parsed === null) {
37717
37725
  return null;
37718
37726
  }
@@ -37727,7 +37735,7 @@ function restartOwed(projectPath) {
37727
37735
  }
37728
37736
  function restartSettled(projectPath) {
37729
37737
  try {
37730
- rmSync9(restartNotePath(projectPath), { force: true });
37738
+ rmSync10(restartNotePath(projectPath), { force: true });
37731
37739
  } catch (error) {
37732
37740
  console.error(`[SERVER] Could not clear the restart note: ${errorMessage(error)}`);
37733
37741
  }
@@ -37738,36 +37746,36 @@ var init_restart_note = __esm(() => {
37738
37746
 
37739
37747
  // src/run-record.ts
37740
37748
  import { execFileSync as execFileSync2 } from "node:child_process";
37741
- import { mkdirSync as mkdirSync12, openSync as openSync2, readdirSync as readdirSync8, readFileSync as readFileSync19, rmSync as rmSync10, statSync as statSync7, writeFileSync as writeFileSync14 } from "node:fs";
37742
- import { basename as basename3, join as join21 } from "node:path";
37749
+ import { mkdirSync as mkdirSync13, openSync as openSync2, readdirSync as readdirSync8, readFileSync as readFileSync20, rmSync as rmSync11, statSync as statSync7, writeFileSync as writeFileSync15 } from "node:fs";
37750
+ import { basename as basename3, join as join22 } from "node:path";
37743
37751
  function runsDirectory() {
37744
- return join21(runtimeDirectory(), "runs");
37752
+ return join22(runtimeDirectory(), "runs");
37745
37753
  }
37746
37754
  function recordPath() {
37747
- return join21(runsDirectory(), "run.json");
37755
+ return join22(runsDirectory(), "run.json");
37748
37756
  }
37749
37757
  function recordPaths() {
37750
- return runtimeDirectories().map((directory) => join21(directory, "runs", "run.json"));
37758
+ return runtimeDirectories().map((directory) => join22(directory, "runs", "run.json"));
37751
37759
  }
37752
37760
  function openTranscript(startedAt) {
37753
- mkdirSync12(runsDirectory(), { recursive: true });
37754
- const path = join21(runsDirectory(), `run-${startedAt}.log`);
37761
+ mkdirSync13(runsDirectory(), { recursive: true });
37762
+ const path = join22(runsDirectory(), `run-${startedAt}.log`);
37755
37763
  return { path, fd: openSync2(path, "a") };
37756
37764
  }
37757
37765
  function writeRunRecord(record) {
37758
- mkdirSync12(runsDirectory(), { recursive: true });
37759
- writeFileSync14(recordPath(), JSON.stringify(record, null, 2), "utf8");
37766
+ mkdirSync13(runsDirectory(), { recursive: true });
37767
+ writeFileSync15(recordPath(), JSON.stringify(record, null, 2), "utf8");
37760
37768
  }
37761
37769
  function editorNotePath() {
37762
- return join21(runsDirectory(), "editor-run.json");
37770
+ return join22(runsDirectory(), "editor-run.json");
37763
37771
  }
37764
37772
  function writeEditorRunNote(note) {
37765
- mkdirSync12(runsDirectory(), { recursive: true });
37766
- writeFileSync14(editorNotePath(), JSON.stringify(note, null, 2), "utf8");
37773
+ mkdirSync13(runsDirectory(), { recursive: true });
37774
+ writeFileSync15(editorNotePath(), JSON.stringify(note, null, 2), "utf8");
37767
37775
  }
37768
37776
  function readEditorRunNote() {
37769
37777
  for (const directory of runtimeDirectories()) {
37770
- const note = editorNoteAt(join21(directory, "runs", "editor-run.json"));
37778
+ const note = editorNoteAt(join22(directory, "runs", "editor-run.json"));
37771
37779
  if (note !== null) {
37772
37780
  return note;
37773
37781
  }
@@ -37777,7 +37785,7 @@ function readEditorRunNote() {
37777
37785
  function editorNoteAt(path) {
37778
37786
  let parsed;
37779
37787
  try {
37780
- parsed = JSON.parse(readFileSync19(path, "utf8"));
37788
+ parsed = JSON.parse(readFileSync20(path, "utf8"));
37781
37789
  } catch {
37782
37790
  return null;
37783
37791
  }
@@ -37800,7 +37808,7 @@ function recordRunEnded(pid, exitCode) {
37800
37808
  return;
37801
37809
  }
37802
37810
  try {
37803
- writeFileSync14(path, JSON.stringify({ ...record, exitCode }, null, 2), "utf8");
37811
+ writeFileSync15(path, JSON.stringify({ ...record, exitCode }, null, 2), "utf8");
37804
37812
  } catch {}
37805
37813
  }
37806
37814
  function readRunRecord() {
@@ -37815,7 +37823,7 @@ function readRunRecord() {
37815
37823
  function recordAt(path) {
37816
37824
  let parsed;
37817
37825
  try {
37818
- parsed = JSON.parse(readFileSync19(path, "utf8"));
37826
+ parsed = JSON.parse(readFileSync20(path, "utf8"));
37819
37827
  } catch {
37820
37828
  return null;
37821
37829
  }
@@ -37860,7 +37868,7 @@ function runningAs(pid) {
37860
37868
  const began = startedAt(pid, null);
37861
37869
  const when = began === null ? {} : { startedAt: began };
37862
37870
  if (process.platform === "linux") {
37863
- const raw = readFileSync19(`/proc/${pid}/cmdline`, "utf8").replaceAll("\x00", " ").trim();
37871
+ const raw = readFileSync20(`/proc/${pid}/cmdline`, "utf8").replaceAll("\x00", " ").trim();
37864
37872
  return raw === "" ? null : { kind: "commandLine", text: raw, ...when };
37865
37873
  }
37866
37874
  const args = execFileSync2("ps", ["-p", String(pid), "-o", "args="], {
@@ -37987,7 +37995,7 @@ function clearRunRecord(ours = () => true) {
37987
37995
  continue;
37988
37996
  }
37989
37997
  try {
37990
- rmSync10(path);
37998
+ rmSync11(path);
37991
37999
  } catch {}
37992
38000
  }
37993
38001
  }
@@ -38011,12 +38019,12 @@ function sweepTranscripts(keepMs = 24 * 60 * 60 * 1000, now = Date.now()) {
38011
38019
  if (!entry.startsWith("run-") || !entry.endsWith(".log")) {
38012
38020
  continue;
38013
38021
  }
38014
- const path = join21(directory, entry);
38022
+ const path = join22(directory, entry);
38015
38023
  if (path === keep || ageMs(path, now) < keepMs) {
38016
38024
  continue;
38017
38025
  }
38018
38026
  try {
38019
- rmSync10(path);
38027
+ rmSync11(path);
38020
38028
  } catch {}
38021
38029
  }
38022
38030
  }
@@ -38037,17 +38045,17 @@ import {
38037
38045
  copyFileSync,
38038
38046
  existsSync as existsSync15,
38039
38047
  fstatSync,
38040
- mkdirSync as mkdirSync13,
38048
+ mkdirSync as mkdirSync14,
38041
38049
  mkdtempSync as mkdtempSync3,
38042
38050
  openSync as openSync3,
38043
38051
  readdirSync as readdirSync9,
38044
- readFileSync as readFileSync20,
38052
+ readFileSync as readFileSync21,
38045
38053
  readSync as readSync2,
38046
38054
  rmdirSync,
38047
38055
  statSync as statSync8
38048
38056
  } from "node:fs";
38049
38057
  import { tmpdir as tmpdir5 } from "node:os";
38050
- import { basename as basename4, dirname as dirname15, extname as extname2, isAbsolute as isAbsolute3, join as join22, normalize as normalize3, relative as relative3 } from "node:path";
38058
+ import { basename as basename4, dirname as dirname16, extname as extname2, isAbsolute as isAbsolute3, join as join23, normalize as normalize3, relative as relative3 } from "node:path";
38051
38059
  import { setTimeout as delay3 } from "node:timers/promises";
38052
38060
  import { fileURLToPath as fileURLToPath5 } from "node:url";
38053
38061
  import { promisify as promisify7 } from "node:util";
@@ -38061,11 +38069,11 @@ function captureDestinationRefusal(outputPath, projects) {
38061
38069
  if (extname2(outputPath).toLowerCase() !== ".png") {
38062
38070
  return `outputPath ${outputPath} does not end in .png, which is what a capture is.`;
38063
38071
  }
38064
- const directory = realPathOr(dirname15(outputPath));
38072
+ const directory = realPathOr(dirname16(outputPath));
38065
38073
  if (!existsSync15(directory)) {
38066
- return `outputPath's directory ${dirname15(outputPath)} does not exist; a capture creates no directories.`;
38074
+ return `outputPath's directory ${dirname16(outputPath)} does not exist; a capture creates no directories.`;
38067
38075
  }
38068
- const where = join22(directory, basename4(outputPath));
38076
+ const where = join23(directory, basename4(outputPath));
38069
38077
  const inside = projects.find((project) => project !== "" && isWithinRoot(realPathOr(project), where));
38070
38078
  if (inside !== undefined) {
38071
38079
  return `outputPath ${outputPath} is inside the project at ${inside}; a capture is saved outside it, so it never becomes part of the game.`;
@@ -38313,7 +38321,7 @@ function aboveTheRunner(entry) {
38313
38321
  }
38314
38322
  function elsewhereIn(projectPath, asked) {
38315
38323
  const wanted = asked.replace(/^res:\/\//, "").replace(/\/+$/, "");
38316
- if (wanted === "" || existsSync15(join22(projectPath, wanted))) {
38324
+ if (wanted === "" || existsSync15(join23(projectPath, wanted))) {
38317
38325
  return "";
38318
38326
  }
38319
38327
  const root = (wanted.split("/")[0] ?? "").toLowerCase();
@@ -38327,7 +38335,7 @@ function elsewhereIn(projectPath, asked) {
38327
38335
  return near.length === 0 ? "" : ` The project does have ${there}.`;
38328
38336
  }
38329
38337
  function hasMainScene(projectFile) {
38330
- const scene = parseProjectGodot(readFileSync20(projectFile, "utf8"))["application"]?.["run/main_scene"];
38338
+ const scene = parseProjectGodot(readFileSync21(projectFile, "utf8"))["application"]?.["run/main_scene"];
38331
38339
  return typeof scene === "string" && scene !== "";
38332
38340
  }
38333
38341
  function runtimeAddonAnnouncesEditors(projectPath) {
@@ -38335,13 +38343,13 @@ function runtimeAddonAnnouncesEditors(projectPath) {
38335
38343
  return false;
38336
38344
  }
38337
38345
  try {
38338
- return readFileSync20(join22(projectPath, RUNTIME_AUTOLOAD2.path), "utf8").includes('"editor_pid"');
38346
+ return readFileSync21(join23(projectPath, RUNTIME_AUTOLOAD2.path), "utf8").includes('"editor_pid"');
38339
38347
  } catch {
38340
38348
  return false;
38341
38349
  }
38342
38350
  }
38343
38351
  function editorPlaysHeadless(projectFile) {
38344
- const runArgs = parseProjectGodot(readFileSync20(projectFile, "utf8"))["editor"]?.["run/main_run_args"];
38352
+ const runArgs = parseProjectGodot(readFileSync21(projectFile, "utf8"))["editor"]?.["run/main_run_args"];
38345
38353
  return typeof runArgs === "string" && /(?:^|\s)--headless(?:\s|$)/.test(runArgs);
38346
38354
  }
38347
38355
  function camelCased(params) {
@@ -38360,7 +38368,7 @@ function needsItsOwnProcess(asked) {
38360
38368
  class GodotServer {
38361
38369
  mcp;
38362
38370
  locator = new GodotLocator2;
38363
- operationsScript = join22(__dirname2, "godot", "operations", "godot_operations.gd");
38371
+ operationsScript = join23(__dirname2, "godot", "operations", "godot_operations.gd");
38364
38372
  godotBridge;
38365
38373
  tools = buildToolDefinitions();
38366
38374
  activeProcess = null;
@@ -38504,7 +38512,7 @@ class GodotServer {
38504
38512
  if (this.ownProject === null) {
38505
38513
  return null;
38506
38514
  }
38507
- const announced = readAnnouncement(announcementPath(this.ownProject));
38515
+ const announced = readAnnouncement2(announcementPath2(this.ownProject));
38508
38516
  if (announced === null || announced.pid === process.pid) {
38509
38517
  return null;
38510
38518
  }
@@ -38591,7 +38599,7 @@ class GodotServer {
38591
38599
  }
38592
38600
  setupToolHandlers() {
38593
38601
  this.mcp.server.setRequestHandler(ListToolsRequestSchema, () => ({ tools: this.tools }));
38594
- this.mcp.server.setRequestHandler(CallToolRequestSchema, async (request) => {
38602
+ this.mcp.server.setRequestHandler(CallToolRequestSchema, async (request, extra) => {
38595
38603
  this.logDebug(`Handling tool request: ${request.params.name}`);
38596
38604
  const spec = toolSpec(request.params.name);
38597
38605
  if (!spec) {
@@ -38606,7 +38614,7 @@ class GodotServer {
38606
38614
  this.lastProjectPath = args["projectPath"];
38607
38615
  }
38608
38616
  this.updates.refresh();
38609
- const answer = await this.answered(spec.name, checked.op ?? "", args);
38617
+ const answer = await withCallSignal(extra.signal, () => this.answered(spec.name, checked.op ?? "", args));
38610
38618
  return this.withFeedbackNotice(this.withUpdateNotice(answer));
38611
38619
  });
38612
38620
  }
@@ -38614,7 +38622,7 @@ class GodotServer {
38614
38622
  try {
38615
38623
  return await this.dispatch(tool, op, args);
38616
38624
  } catch (error) {
38617
- if (error instanceof McpError || error instanceof Refusal2) {
38625
+ if (error instanceof McpError || error instanceof Refusal) {
38618
38626
  throw error;
38619
38627
  }
38620
38628
  const where = op === "" ? tool : `${tool} op=${op}`;
@@ -39025,7 +39033,7 @@ class GodotServer {
39025
39033
  if (path === undefined) {
39026
39034
  return { ok: false, response: this.createErrorResponse("projectPath is required.") };
39027
39035
  }
39028
- const file = join22(path, "project.godot");
39036
+ const file = join23(path, "project.godot");
39029
39037
  if (!existsSync15(file)) {
39030
39038
  return {
39031
39039
  ok: false,
@@ -39294,7 +39302,7 @@ class GodotServer {
39294
39302
  if (!engine.ok) {
39295
39303
  return engine.response;
39296
39304
  }
39297
- const application = parseProjectGodot(readFileSync20(project.value.file, "utf8"))["application"] ?? {};
39305
+ const application = parseProjectGodot(readFileSync21(project.value.file, "utf8"))["application"] ?? {};
39298
39306
  const name = application["config/name"];
39299
39307
  const mainScene = application["run/main_scene"];
39300
39308
  const info = {
@@ -39355,18 +39363,18 @@ class GodotServer {
39355
39363
  if (!engine.ok) {
39356
39364
  return engine.response;
39357
39365
  }
39358
- const folder = dirname15(output.absolutePath);
39366
+ const folder = dirname16(output.absolutePath);
39359
39367
  try {
39360
- mkdirSync13(folder, { recursive: true });
39368
+ mkdirSync14(folder, { recursive: true });
39361
39369
  } catch (error) {
39362
- return this.createErrorResponse(`Cannot create ${dirname15(output.relativePath)} to export into: ${errorMessage(error)}`);
39370
+ return this.createErrorResponse(`Cannot create ${dirname16(output.relativePath)} to export into: ${errorMessage(error)}`);
39363
39371
  }
39364
39372
  const debug = readBoolean(args, "debug") ?? false;
39365
- const exportLogs = mkdtempSync3(join22(tmpdir5(), "gdharness-export-"));
39373
+ const exportLogs = mkdtempSync3(join23(tmpdir5(), "gdharness-export-"));
39366
39374
  const exportArgs = [
39367
39375
  "--headless",
39368
39376
  "--log-file",
39369
- join22(exportLogs, "engine.log"),
39377
+ join23(exportLogs, "engine.log"),
39370
39378
  "--path",
39371
39379
  project.value.path,
39372
39380
  debug ? "--export-debug" : "--export-release",
@@ -39377,7 +39385,10 @@ class GodotServer {
39377
39385
  const log = new GameLog;
39378
39386
  let exitCode = 0;
39379
39387
  try {
39380
- const { stdout, stderr } = await run7(engine.value, exportArgs, { timeout: 300000 });
39388
+ const { stdout, stderr } = await run7(engine.value, exportArgs, {
39389
+ timeout: 300000,
39390
+ signal: callSignal()
39391
+ });
39381
39392
  log.append("stdout", stdout);
39382
39393
  log.append("stderr", stderr);
39383
39394
  } catch (error) {
@@ -39424,7 +39435,7 @@ class GodotServer {
39424
39435
  return contained.response;
39425
39436
  }
39426
39437
  const runner = "addons/gdUnit4/bin/GdUnitCmdTool.gd";
39427
- if (!existsSync15(join22(project.value.path, runner))) {
39438
+ if (!existsSync15(join23(project.value.path, runner))) {
39428
39439
  return this.createErrorResponse(`gdUnit4 is not installed in this project: no ${runner}.`, [
39429
39440
  "Install gdUnit4 under addons/gdUnit4, from https://github.com/godot-gdunit-labs/gdUnit4"
39430
39441
  ]);
@@ -39459,7 +39470,7 @@ class GodotServer {
39459
39470
  ];
39460
39471
  const timeoutMs = readPositiveNumber(args, "timeoutMs") ?? 600000;
39461
39472
  this.logDebug(`Running tests: ${engine.value} ${cmdArgs.join(" ")}`);
39462
- const userData = mkdtempSync3(join22(tmpdir5(), "gdharness-tests-"));
39473
+ const userData = mkdtempSync3(join23(tmpdir5(), "gdharness-tests-"));
39463
39474
  const run = this.spawnGame(engine.value, cmdArgs, userDataIn(userData));
39464
39475
  const hung = await new Promise((resolve) => {
39465
39476
  const timer = setTimeout(() => {
@@ -39475,21 +39486,21 @@ class GodotServer {
39475
39486
  resolve(false);
39476
39487
  });
39477
39488
  });
39478
- const reportsDir = join22(project.value.path, ".godot", ...ours.split("/"));
39489
+ const reportsDir = join23(project.value.path, ".godot", ...ours.split("/"));
39479
39490
  let report = null;
39480
39491
  let reportProblem = null;
39481
39492
  try {
39482
39493
  const written = existsSync15(reportsDir) ? readdirSync9(reportsDir).filter((name) => name.startsWith("report_")).sort((a, b) => Number(a.slice("report_".length)) - Number(b.slice("report_".length))) : [];
39483
39494
  const newest = written.at(-1);
39484
39495
  if (newest !== undefined) {
39485
- report = parseJUnit(readFileSync20(join22(reportsDir, newest, "results.xml"), "utf8"));
39496
+ report = parseJUnit(readFileSync21(join23(reportsDir, newest, "results.xml"), "utf8"));
39486
39497
  }
39487
39498
  } catch (error) {
39488
39499
  reportProblem = errorMessage(error);
39489
39500
  } finally {
39490
39501
  discard(reportsDir, userData);
39491
39502
  try {
39492
- rmdirSync(dirname15(reportsDir));
39503
+ rmdirSync(dirname16(reportsDir));
39493
39504
  } catch {}
39494
39505
  }
39495
39506
  const engineEntries = run.log.select({ severity: "warning", sinceLastCall: false, limit: 200 }).entries.map(aboveTheRunner);
@@ -39625,7 +39636,7 @@ class GodotServer {
39625
39636
  return null;
39626
39637
  }
39627
39638
  try {
39628
- return readFileSync20(contained.absolutePath, "utf8");
39639
+ return readFileSync21(contained.absolutePath, "utf8");
39629
39640
  } catch {
39630
39641
  return null;
39631
39642
  }
@@ -39679,7 +39690,7 @@ class GodotServer {
39679
39690
  return null;
39680
39691
  }
39681
39692
  try {
39682
- return readFileSync20(contained.absolutePath, "utf8");
39693
+ return readFileSync21(contained.absolutePath, "utf8");
39683
39694
  } catch {
39684
39695
  return null;
39685
39696
  }
@@ -39908,7 +39919,7 @@ class GodotServer {
39908
39919
  return null;
39909
39920
  }
39910
39921
  try {
39911
- return settingKeys(readFileSync20(join22(projectPath, "project.godot"), "utf8"));
39922
+ return settingKeys(readFileSync21(join23(projectPath, "project.godot"), "utf8"));
39912
39923
  } catch {
39913
39924
  return null;
39914
39925
  }
@@ -40159,7 +40170,7 @@ class GodotServer {
40159
40170
  }
40160
40171
  async runtimeUp(projectPath, before, budget, withArgs) {
40161
40172
  const budgetMs = budget.ms;
40162
- if (!existsSync15(join22(projectPath, RUNTIME_AUTOLOAD2.path))) {
40173
+ if (!existsSync15(join23(projectPath, RUNTIME_AUTOLOAD2.path))) {
40163
40174
  return runtimeVerdict(null, { addon: false, budgetMs, heldAt: null, running: false, withArgs });
40164
40175
  }
40165
40176
  let asking = null;
@@ -40393,6 +40404,23 @@ class GodotServer {
40393
40404
  }
40394
40405
  spawnGame(godotPath, cmdArgs, env) {
40395
40406
  const child = spawn(godotPath, cmdArgs, { stdio: ["ignore", "pipe", "pipe"], ...env ? { env } : {} });
40407
+ const cancelled = callSignal();
40408
+ if (cancelled !== undefined) {
40409
+ const end = () => {
40410
+ if (child.exitCode === null && child.signalCode === null) {
40411
+ this.logDebug(`The call waiting on pid ${child.pid ?? "unknown"} was cancelled; ending it.`);
40412
+ child.kill();
40413
+ }
40414
+ };
40415
+ if (cancelled.aborted) {
40416
+ end();
40417
+ } else {
40418
+ cancelled.addEventListener("abort", end, { once: true });
40419
+ child.once("exit", () => {
40420
+ cancelled.removeEventListener("abort", end);
40421
+ });
40422
+ }
40423
+ }
40396
40424
  const log = new GameLog;
40397
40425
  const started = {
40398
40426
  process: child,
@@ -41325,7 +41353,7 @@ class GodotServer {
41325
41353
  async ownRunOnItsWay(projectPath, budgetMs) {
41326
41354
  const run = this.currentRun();
41327
41355
  const project = projectPath ?? run?.projectPath ?? null;
41328
- if (run === null || project === null || run.announcedPid !== undefined || run.projectPath !== null && !isSameDirectory(run.projectPath, project) || !existsSync15(join22(project, RUNTIME_AUTOLOAD2.path)) || !await this.runStillGoing(run)) {
41356
+ if (run === null || project === null || run.announcedPid !== undefined || run.projectPath !== null && !isSameDirectory(run.projectPath, project) || !existsSync15(join23(project, RUNTIME_AUTOLOAD2.path)) || !await this.runStillGoing(run)) {
41329
41357
  return null;
41330
41358
  }
41331
41359
  const already = runtimesAnnounced().running;
@@ -41404,8 +41432,8 @@ class GodotServer {
41404
41432
  return this.createErrorResponse(refusal);
41405
41433
  }
41406
41434
  }
41407
- const screenshotDir = expectsScreenshot ? mkdtempSync3(join22(tmpdir5(), "gdharness-runtime-screenshot-")) : null;
41408
- const screenshotPath = screenshotDir ? join22(screenshotDir, "capture.png") : null;
41435
+ const screenshotDir = expectsScreenshot ? mkdtempSync3(join23(tmpdir5(), "gdharness-runtime-screenshot-")) : null;
41436
+ const screenshotPath = screenshotDir ? join23(screenshotDir, "capture.png") : null;
41409
41437
  try {
41410
41438
  const reply = await runtimeRequest(choice.endpoint, command, screenshotPath ? { ...params, output_path: screenshotPath } : params, timeoutMs);
41411
41439
  if (!reply.ok) {
@@ -41436,7 +41464,7 @@ class GodotServer {
41436
41464
  type: "text",
41437
41465
  text: `Screenshot captured: ${dimensions}${chosenAmongSeveral ? ` from pid ${own}, the game this server holds` : ""}${saveTo === null ? "" : `. Saved to ${saveTo}`}`
41438
41466
  },
41439
- { type: "image", data: readFileSync20(screenshotPath).toString("base64"), mimeType: "image/png" }
41467
+ { type: "image", data: readFileSync21(screenshotPath).toString("base64"), mimeType: "image/png" }
41440
41468
  ]
41441
41469
  };
41442
41470
  } finally {
@@ -41515,6 +41543,7 @@ var init_server3 = __esm(() => {
41515
41543
  init_boot_note();
41516
41544
  init_breakpoint_note();
41517
41545
  init_bridge_announce();
41546
+ init_call_signal();
41518
41547
  init_class_cache();
41519
41548
  init_class_note();
41520
41549
  init_config_pin();
@@ -41544,7 +41573,7 @@ var init_server3 = __esm(() => {
41544
41573
  init_tool_definitions();
41545
41574
  init_update_check();
41546
41575
  run7 = promisify7(execFile7);
41547
- __dirname2 = dirname15(fileURLToPath5(import.meta.url));
41576
+ __dirname2 = dirname16(fileURLToPath5(import.meta.url));
41548
41577
  CONSOLE_WRAPPER = /_console(\.exe)?$/i;
41549
41578
  LONGEST_BOOT_NOTED_MS = 5 * 60000;
41550
41579
  VALUE_NAMES = {
@@ -41585,20 +41614,61 @@ var init_server3 = __esm(() => {
41585
41614
 
41586
41615
  // src/cli.ts
41587
41616
  import { existsSync as existsSync16 } from "fs";
41588
- import { join as join23, resolve as resolve5 } from "path";
41617
+ import { join as join24, resolve as resolve5 } from "path";
41618
+
41619
+ // src/bridge-announce.ts
41620
+ init_errors();
41621
+ init_process_children();
41622
+ import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
41623
+ import { dirname, join } from "node:path";
41624
+ function announcementPath(projectPath) {
41625
+ return join(projectPath, ".godot", "gdharness-bridge.json");
41626
+ }
41627
+ function readAnnouncement(path) {
41628
+ try {
41629
+ const parsed = JSON.parse(readFileSync(path, "utf8"));
41630
+ if (typeof parsed !== "object" || parsed === null) {
41631
+ return null;
41632
+ }
41633
+ const said = parsed;
41634
+ if (typeof said.port !== "number" || typeof said.pid !== "number") {
41635
+ return null;
41636
+ }
41637
+ return said;
41638
+ } catch {
41639
+ return null;
41640
+ }
41641
+ }
41642
+ function serverBehind(announcement, startedAt) {
41643
+ if (announcement === null || typeof announcement.version !== "string" || startedAt === undefined) {
41644
+ return null;
41645
+ }
41646
+ const announcedAt = Date.parse(announcement.startedAt);
41647
+ if (Number.isNaN(announcedAt) || startedAt > announcedAt + 1000) {
41648
+ return null;
41649
+ }
41650
+ return { version: announcement.version, pid: announcement.pid };
41651
+ }
41652
+ function serverServing(projectPath) {
41653
+ const announcement = readAnnouncement(announcementPath(projectPath));
41654
+ if (announcement === null) {
41655
+ return null;
41656
+ }
41657
+ return serverBehind(announcement, startTimesOf([announcement.pid]).get(announcement.pid));
41658
+ }
41589
41659
 
41590
41660
  // src/errors.ts
41591
- class Refusal extends Error {
41661
+ class Refusal2 extends Error {
41592
41662
  }
41593
41663
 
41594
41664
  // src/godot-path.ts
41595
41665
  init_detection();
41596
41666
  init_errors();
41597
- import { execFile } from "node:child_process";
41667
+ import { execFile as execFile2 } from "node:child_process";
41598
41668
  import { existsSync as existsSync2 } from "node:fs";
41599
41669
  import { normalize } from "node:path";
41600
- import { promisify } from "node:util";
41601
- var run = promisify(execFile);
41670
+ import { promisify as promisify2 } from "node:util";
41671
+ var run2 = promisify2(execFile2);
41602
41672
 
41603
41673
  class GodotLocator {
41604
41674
  answered = new Map;
@@ -41655,7 +41725,7 @@ class GodotLocator {
41655
41725
  reason = "does not exist";
41656
41726
  } else {
41657
41727
  try {
41658
- await run(path, ["--version"]);
41728
+ await run2(path, ["--version"]);
41659
41729
  reason = null;
41660
41730
  } catch (error) {
41661
41731
  reason = `does not answer --version: ${errorMessage(error)}`;
@@ -41667,9 +41737,9 @@ class GodotLocator {
41667
41737
  }
41668
41738
 
41669
41739
  // src/harnesses.ts
41670
- import { existsSync as existsSync4, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
41740
+ import { existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync2, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "node:fs";
41671
41741
  import { homedir as homedir2 } from "node:os";
41672
- import { dirname as dirname2, join as join3 } from "node:path";
41742
+ import { dirname as dirname3, join as join4 } from "node:path";
41673
41743
  import process3 from "node:process";
41674
41744
 
41675
41745
  // node_modules/yaml/dist/index.js
@@ -41842,7 +41912,7 @@ var HARNESSES = [
41842
41912
  shape: "plain",
41843
41913
  home: ".claude",
41844
41914
  marker: ".claude",
41845
- skills: { dir: join3(".claude", "skills"), shared: false }
41915
+ skills: { dir: join4(".claude", "skills"), shared: false }
41846
41916
  },
41847
41917
  {
41848
41918
  id: "copilot-cli",
@@ -41852,7 +41922,7 @@ var HARNESSES = [
41852
41922
  container: "mcpServers",
41853
41923
  shape: "plain",
41854
41924
  home: ".copilot",
41855
- skills: { dir: join3(".github", "skills"), shared: true },
41925
+ skills: { dir: join4(".github", "skills"), shared: true },
41856
41926
  manual: "Trust the folder when Copilot CLI first asks, or it reads no config here."
41857
41927
  },
41858
41928
  {
@@ -41878,22 +41948,22 @@ var HARNESSES = [
41878
41948
  id: "cursor",
41879
41949
  name: "Cursor",
41880
41950
  scope: "project",
41881
- file: join3(".cursor", "mcp.json"),
41951
+ file: join4(".cursor", "mcp.json"),
41882
41952
  container: "mcpServers",
41883
41953
  shape: "plain",
41884
41954
  home: ".cursor",
41885
- skills: { dir: join3(".cursor", "skills"), shared: true },
41955
+ skills: { dir: join4(".cursor", "skills"), shared: true },
41886
41956
  manual: "Cursor asks before each tool call by default, and the server can be toggled off."
41887
41957
  },
41888
41958
  {
41889
41959
  id: "vscode",
41890
41960
  name: "VS Code",
41891
41961
  scope: "project",
41892
- file: join3(".vscode", "mcp.json"),
41962
+ file: join4(".vscode", "mcp.json"),
41893
41963
  container: "servers",
41894
41964
  shape: "typed",
41895
41965
  home: ".vscode",
41896
- skills: { dir: join3(".github", "skills"), shared: true },
41966
+ skills: { dir: join4(".github", "skills"), shared: true },
41897
41967
  manual: "VS Code asks you to trust a server the first time it starts one."
41898
41968
  },
41899
41969
  {
@@ -41903,45 +41973,45 @@ var HARNESSES = [
41903
41973
  file: "opencode.json",
41904
41974
  container: "mcp",
41905
41975
  shape: "opencode",
41906
- home: join3(".config", "opencode"),
41976
+ home: join4(".config", "opencode"),
41907
41977
  marker: ".opencode",
41908
- skills: { dir: join3(".opencode", "skills"), shared: true }
41978
+ skills: { dir: join4(".opencode", "skills"), shared: true }
41909
41979
  },
41910
41980
  {
41911
41981
  id: "junie",
41912
41982
  name: "Junie",
41913
41983
  scope: "project",
41914
- file: join3(".junie", "mcp", "mcp.json"),
41984
+ file: join4(".junie", "mcp", "mcp.json"),
41915
41985
  container: "mcpServers",
41916
41986
  shape: "plain",
41917
41987
  home: ".junie",
41918
- skills: { dir: join3(".junie", "skills"), shared: true }
41988
+ skills: { dir: join4(".junie", "skills"), shared: true }
41919
41989
  },
41920
41990
  {
41921
41991
  id: "kiro",
41922
41992
  name: "Kiro",
41923
41993
  scope: "project",
41924
- file: join3(".kiro", "settings", "mcp.json"),
41994
+ file: join4(".kiro", "settings", "mcp.json"),
41925
41995
  container: "mcpServers",
41926
41996
  shape: "plain",
41927
41997
  home: ".kiro",
41928
- skills: { dir: join3(".kiro", "skills"), shared: false }
41998
+ skills: { dir: join4(".kiro", "skills"), shared: false }
41929
41999
  },
41930
42000
  {
41931
42001
  id: "gemini",
41932
42002
  name: "Gemini CLI",
41933
42003
  scope: "project",
41934
- file: join3(".gemini", "settings.json"),
42004
+ file: join4(".gemini", "settings.json"),
41935
42005
  container: "mcpServers",
41936
42006
  shape: "plain",
41937
42007
  home: ".gemini",
41938
- skills: { dir: join3(".gemini", "skills"), shared: true }
42008
+ skills: { dir: join4(".gemini", "skills"), shared: true }
41939
42009
  },
41940
42010
  {
41941
42011
  id: "roo",
41942
42012
  name: "Roo Code",
41943
42013
  scope: "project",
41944
- file: join3(".roo", "mcp.json"),
42014
+ file: join4(".roo", "mcp.json"),
41945
42015
  container: "mcpServers",
41946
42016
  shape: "plain"
41947
42017
  },
@@ -41949,7 +42019,7 @@ var HARNESSES = [
41949
42019
  id: "kilo",
41950
42020
  name: "Kilo Code",
41951
42021
  scope: "project",
41952
- file: join3(".kilocode", "mcp.json"),
42022
+ file: join4(".kilocode", "mcp.json"),
41953
42023
  container: "mcpServers",
41954
42024
  shape: "plain"
41955
42025
  },
@@ -41957,16 +42027,16 @@ var HARNESSES = [
41957
42027
  id: "amazon-q",
41958
42028
  name: "Amazon Q Developer CLI",
41959
42029
  scope: "project",
41960
- file: join3(".amazonq", "mcp.json"),
42030
+ file: join4(".amazonq", "mcp.json"),
41961
42031
  container: "mcpServers",
41962
42032
  shape: "plain",
41963
- home: join3(".aws", "amazonq")
42033
+ home: join4(".aws", "amazonq")
41964
42034
  },
41965
42035
  {
41966
42036
  id: "zed",
41967
42037
  name: "Zed",
41968
42038
  scope: "project",
41969
- file: join3(".zed", "settings.json"),
42039
+ file: join4(".zed", "settings.json"),
41970
42040
  container: "context_servers",
41971
42041
  shape: "plain"
41972
42042
  },
@@ -41974,16 +42044,16 @@ var HARNESSES = [
41974
42044
  id: "amp",
41975
42045
  name: "Amp",
41976
42046
  scope: "project",
41977
- file: join3(".amp", "settings.json"),
42047
+ file: join4(".amp", "settings.json"),
41978
42048
  container: "amp.mcpServers",
41979
42049
  shape: "plain",
41980
- home: join3(".config", "amp")
42050
+ home: join4(".config", "amp")
41981
42051
  },
41982
42052
  {
41983
42053
  id: "warp",
41984
42054
  name: "Warp",
41985
42055
  scope: "project",
41986
- file: join3(".warp", ".mcp.json"),
42056
+ file: join4(".warp", ".mcp.json"),
41987
42057
  container: "mcpServers",
41988
42058
  shape: "plain",
41989
42059
  home: ".warp",
@@ -41993,7 +42063,7 @@ var HARNESSES = [
41993
42063
  id: "trae",
41994
42064
  name: "Trae",
41995
42065
  scope: "project",
41996
- file: join3(".trae", "mcp.json"),
42066
+ file: join4(".trae", "mcp.json"),
41997
42067
  container: "mcpServers",
41998
42068
  shape: "plain",
41999
42069
  home: ".trae"
@@ -42002,7 +42072,7 @@ var HARNESSES = [
42002
42072
  id: "factory",
42003
42073
  name: "Factory Droid",
42004
42074
  scope: "project",
42005
- file: join3(".factory", "mcp.json"),
42075
+ file: join4(".factory", "mcp.json"),
42006
42076
  container: "mcpServers",
42007
42077
  shape: "typed",
42008
42078
  home: ".factory"
@@ -42011,7 +42081,7 @@ var HARNESSES = [
42011
42081
  id: "tabnine",
42012
42082
  name: "Tabnine",
42013
42083
  scope: "project",
42014
- file: join3(".tabnine", "mcp_servers.json"),
42084
+ file: join4(".tabnine", "mcp_servers.json"),
42015
42085
  container: "mcpServers",
42016
42086
  shape: "plain",
42017
42087
  home: ".tabnine"
@@ -42028,7 +42098,7 @@ var HARNESSES = [
42028
42098
  id: "pi",
42029
42099
  name: "pi",
42030
42100
  scope: "project",
42031
- file: join3(".pi", "mcp.json"),
42101
+ file: join4(".pi", "mcp.json"),
42032
42102
  container: "mcpServers",
42033
42103
  shape: "plain",
42034
42104
  home: ".pi"
@@ -42037,7 +42107,7 @@ var HARNESSES = [
42037
42107
  id: "mistral-vibe",
42038
42108
  name: "Mistral Vibe",
42039
42109
  scope: "project",
42040
- file: join3(".vibe", "config.toml"),
42110
+ file: join4(".vibe", "config.toml"),
42041
42111
  container: "mcp_servers",
42042
42112
  shape: "plain",
42043
42113
  toml: {
@@ -42093,7 +42163,7 @@ var HARNESSES = [
42093
42163
  id: "codex",
42094
42164
  name: "Codex CLI",
42095
42165
  scope: "home",
42096
- file: join3(".codex", "config.toml"),
42166
+ file: join4(".codex", "config.toml"),
42097
42167
  container: "mcp_servers",
42098
42168
  shape: "plain",
42099
42169
  manual: "Codex reads its config at startup, so restart it.",
@@ -42112,20 +42182,20 @@ var HARNESSES = [
42112
42182
  id: "cline",
42113
42183
  name: "Cline",
42114
42184
  scope: "home",
42115
- file: join3(".cline", "data", "settings", "cline_mcp_settings.json"),
42185
+ file: join4(".cline", "data", "settings", "cline_mcp_settings.json"),
42116
42186
  container: "mcpServers",
42117
42187
  shape: "plain",
42118
42188
  marker: ".cline",
42119
- skills: { dir: join3(".cline", "skills"), shared: false }
42189
+ skills: { dir: join4(".cline", "skills"), shared: false }
42120
42190
  },
42121
42191
  {
42122
42192
  id: "goose",
42123
42193
  name: "Goose",
42124
42194
  scope: "home",
42125
42195
  file: {
42126
- win32: { appData: join3("Block", "goose", "config", "config.yaml") },
42127
- darwin: join3(".config", "goose", "config.yaml"),
42128
- linux: join3(".config", "goose", "config.yaml")
42196
+ win32: { appData: join4("Block", "goose", "config", "config.yaml") },
42197
+ darwin: join4(".config", "goose", "config.yaml"),
42198
+ linux: join4(".config", "goose", "config.yaml")
42129
42199
  },
42130
42200
  container: "extensions",
42131
42201
  shape: "plain",
@@ -42146,19 +42216,19 @@ var HARNESSES = [
42146
42216
  id: "windsurf",
42147
42217
  name: "Windsurf",
42148
42218
  scope: "home",
42149
- file: join3(".codeium", "windsurf", "mcp_config.json"),
42219
+ file: join4(".codeium", "windsurf", "mcp_config.json"),
42150
42220
  container: "mcpServers",
42151
42221
  shape: "plain",
42152
- skills: { dir: join3(".windsurf", "skills"), shared: true }
42222
+ skills: { dir: join4(".windsurf", "skills"), shared: true }
42153
42223
  },
42154
42224
  {
42155
42225
  id: "hermes",
42156
42226
  name: "Hermes",
42157
42227
  scope: "home",
42158
- file: join3(".hermes", "config.yaml"),
42228
+ file: join4(".hermes", "config.yaml"),
42159
42229
  container: "mcp_servers",
42160
42230
  shape: "plain",
42161
- skills: { dir: join3(".hermes", "skills"), shared: true },
42231
+ skills: { dir: join4(".hermes", "skills"), shared: true },
42162
42232
  yaml: {
42163
42233
  path: ["mcp_servers", SERVER_KEY],
42164
42234
  entry: (launch) => ({
@@ -42173,7 +42243,7 @@ var HARNESSES = [
42173
42243
  id: "openclaw",
42174
42244
  name: "OpenClaw",
42175
42245
  scope: "home",
42176
- file: join3(".openclaw", "openclaw.json"),
42246
+ file: join4(".openclaw", "openclaw.json"),
42177
42247
  container: "mcpServers",
42178
42248
  shape: "plain"
42179
42249
  },
@@ -42182,9 +42252,9 @@ var HARNESSES = [
42182
42252
  name: "Claude Desktop",
42183
42253
  scope: "home",
42184
42254
  file: {
42185
- win32: { appData: join3("Claude", "claude_desktop_config.json") },
42186
- darwin: join3("Library", "Application Support", "Claude", "claude_desktop_config.json"),
42187
- linux: join3(".config", "Claude", "claude_desktop_config.json")
42255
+ win32: { appData: join4("Claude", "claude_desktop_config.json") },
42256
+ darwin: join4("Library", "Application Support", "Claude", "claude_desktop_config.json"),
42257
+ linux: join4(".config", "Claude", "claude_desktop_config.json")
42188
42258
  },
42189
42259
  container: "mcpServers",
42190
42260
  shape: "plain"
@@ -42193,7 +42263,7 @@ var HARNESSES = [
42193
42263
  id: "zeroclaw",
42194
42264
  name: "ZeroClaw",
42195
42265
  scope: "home",
42196
- file: join3(".zeroclaw", "config.toml"),
42266
+ file: join4(".zeroclaw", "config.toml"),
42197
42267
  container: "mcp.servers",
42198
42268
  shape: "plain",
42199
42269
  toml: {
@@ -42214,7 +42284,7 @@ var HARNESSES = [
42214
42284
  id: "deepcode",
42215
42285
  name: "Deep Code",
42216
42286
  scope: "home",
42217
- file: join3(".deepcode", "settings.json"),
42287
+ file: join4(".deepcode", "settings.json"),
42218
42288
  container: "mcpServers",
42219
42289
  shape: "plain"
42220
42290
  },
@@ -42222,7 +42292,7 @@ var HARNESSES = [
42222
42292
  id: "nanobot",
42223
42293
  name: "nanobot",
42224
42294
  scope: "home",
42225
- file: join3(".nanobot", "config.json"),
42295
+ file: join4(".nanobot", "config.json"),
42226
42296
  container: "mcpServers",
42227
42297
  shape: "plain",
42228
42298
  snippet: (launch) => jsonSnippet(launch, "mcpServers", "plain")
@@ -42231,17 +42301,17 @@ var HARNESSES = [
42231
42301
  id: "autohand",
42232
42302
  name: "Autohand",
42233
42303
  scope: "home",
42234
- file: join3(".autohand", "config.json"),
42304
+ file: join4(".autohand", "config.json"),
42235
42305
  container: "mcpServers",
42236
42306
  shape: "plain",
42237
42307
  snippet: (launch) => jsonSnippet(launch, "mcpServers", "plain")
42238
42308
  }
42239
42309
  ];
42240
42310
  function appDataRoot() {
42241
- return process3.env["APPDATA"] ?? join3(homedir2(), "AppData", "Roaming");
42311
+ return process3.env["APPDATA"] ?? join4(homedir2(), "AppData", "Roaming");
42242
42312
  }
42243
42313
  function homePath(path) {
42244
- return typeof path === "string" ? join3(homedir2(), path) : join3(appDataRoot(), path.appData);
42314
+ return typeof path === "string" ? join4(homedir2(), path) : join4(appDataRoot(), path.appData);
42245
42315
  }
42246
42316
  function fileFor(harness, platform = process3.platform) {
42247
42317
  if (typeof harness.file === "string") {
@@ -42252,7 +42322,7 @@ function fileFor(harness, platform = process3.platform) {
42252
42322
  function configPath(harness, projectPath) {
42253
42323
  const file = fileFor(harness);
42254
42324
  if (harness.scope === "project") {
42255
- return join3(projectPath, file);
42325
+ return join4(projectPath, file);
42256
42326
  }
42257
42327
  return homePath(file);
42258
42328
  }
@@ -42311,11 +42381,11 @@ function present(harness, projectPath) {
42311
42381
  return true;
42312
42382
  }
42313
42383
  const root = harness.scope === "project" ? projectPath : homedir2();
42314
- const own = dirname2(path);
42384
+ const own = dirname3(path);
42315
42385
  if (own !== root && existsSync4(own)) {
42316
42386
  return true;
42317
42387
  }
42318
- return harness.marker !== undefined && existsSync4(join3(root, harness.marker));
42388
+ return harness.marker !== undefined && existsSync4(join4(root, harness.marker));
42319
42389
  }
42320
42390
  function candidates(projectPath) {
42321
42391
  const found = [];
@@ -42328,7 +42398,7 @@ function candidates(projectPath) {
42328
42398
  }
42329
42399
  if (present(harness, projectPath)) {
42330
42400
  found.push({ harness, reason: "configured" });
42331
- } else if (harness.home !== undefined && existsSync4(join3(homedir2(), harness.home))) {
42401
+ } else if (harness.home !== undefined && existsSync4(join4(homedir2(), harness.home))) {
42332
42402
  found.push({ harness, reason: "installed" });
42333
42403
  }
42334
42404
  }
@@ -42353,7 +42423,7 @@ function registered(harness, projectPath) {
42353
42423
  if (harness.snippet !== undefined || !existsSync4(path)) {
42354
42424
  return false;
42355
42425
  }
42356
- const text = readFileSync(path, "utf8");
42426
+ const text = readFileSync2(path, "utf8");
42357
42427
  if (harness.toml !== undefined) {
42358
42428
  return text.includes(harness.toml.identity ?? harness.toml.header);
42359
42429
  }
@@ -42383,7 +42453,7 @@ function recordedEnginePath(projectPath) {
42383
42453
  }
42384
42454
  let parsed;
42385
42455
  try {
42386
- parsed = JSON.parse(readFileSync(path, "utf8"));
42456
+ parsed = JSON.parse(readFileSync2(path, "utf8"));
42387
42457
  } catch {
42388
42458
  continue;
42389
42459
  }
@@ -42406,7 +42476,7 @@ function disconnect(harness, projectPath) {
42406
42476
  if (harness.snippet !== undefined) {
42407
42477
  return { harness, path, action: "manual" };
42408
42478
  }
42409
- const text = readFileSync(path, "utf8");
42479
+ const text = readFileSync2(path, "utf8");
42410
42480
  if (harness.toml !== undefined) {
42411
42481
  return taken(harness, path, text, removeToml(text, harness.toml));
42412
42482
  }
@@ -42415,7 +42485,7 @@ function disconnect(harness, projectPath) {
42415
42485
  }
42416
42486
  let existing;
42417
42487
  try {
42418
- existing = JSON.parse(readFileSync(path, "utf8"));
42488
+ existing = JSON.parse(readFileSync2(path, "utf8"));
42419
42489
  } catch {
42420
42490
  return { harness, path, action: "absent" };
42421
42491
  }
@@ -42435,25 +42505,32 @@ function disconnect(harness, projectPath) {
42435
42505
  root[harness.container] = servers;
42436
42506
  }
42437
42507
  if (Object.keys(root).length === 0) {
42438
- rmSync(path, { force: true });
42508
+ rmSync2(path, { force: true });
42439
42509
  return { harness, path, action: "deleted" };
42440
42510
  }
42441
- writeFileSync(path, `${JSON.stringify(root, null, 2)}
42511
+ writeFileSync2(path, `${JSON.stringify(root, null, 2)}
42442
42512
  `, "utf8");
42443
42513
  return { harness, path, action: "removed" };
42444
42514
  }
42445
- function harnessNote(repinned, now) {
42515
+ function harnessNote(repinned, now, running = null) {
42446
42516
  const alsoByHand = repinned.byHand === 0 ? "" : `
42447
42517
  ${repinned.byHand === 1 ? "One harness above was" : `${repinned.byHand} harnesses above were`} handed a command to run instead; run it too.`;
42518
+ const serving = running === null ? ` No gdharness server is announcing itself for this project, so which one your harness
42519
+ ` + ` is running cannot be read from here.
42520
+ ` : ` The server serving this project is gdharness ${running.server.version} (pid ${running.server.pid})${running.isThisVersion ? ", which is already this version." : "."}
42521
+ `;
42522
+ if (running?.isThisVersion === true && repinned.byHand === 0 && (repinned.moved.length > 0 || repinned.written > 0)) {
42523
+ return ` 2. Nothing for the harness: the server serving this project is gdharness ${running.server.version}
42524
+ (pid ${running.server.pid}), which is already this version.`;
42525
+ }
42448
42526
  if (repinned.moved.length > 0) {
42449
- return ` 2. Your harness is still running the server it spawned from ${repinned.moved.join(", ")}.
42450
- ` + ` Its config names ${now} now.
42451
- ` + ` Reconnect the MCP server, or restart the harness.${alsoByHand}`;
42527
+ return ` 2. Your harness config held ${repinned.moved.join(", ")} before this upgrade.
42528
+ ` + ` It names ${now} now.
42529
+ ` + serving + ` Reconnect the MCP server, or restart the harness.${alsoByHand}`;
42452
42530
  }
42453
42531
  if (repinned.written > 0) {
42454
- return ` 2. Your harness config already named this version, but the server it is running is
42455
- ` + ` whichever one it spawned when the session started, which this cannot see from here.
42456
- ` + ` Reconnect the MCP server, or restart the harness.${alsoByHand}`;
42532
+ return ` 2. Your harness config already named this version.
42533
+ ` + serving + ` Reconnect the MCP server, or restart the harness.${alsoByHand}`;
42457
42534
  }
42458
42535
  if (repinned.byHand > 0) {
42459
42536
  return ` 2. None of the harness configs found here can be written for you, so nothing was
@@ -42480,7 +42557,7 @@ function connect(harness, projectPath, launch) {
42480
42557
  if (harness.snippet !== undefined) {
42481
42558
  return { harness, path, action: "snippet", snippet: harness.snippet(launch) };
42482
42559
  }
42483
- const held = existsSync4(path) ? readFileSync(path, "utf8") : "";
42560
+ const held = existsSync4(path) ? readFileSync2(path, "utf8") : "";
42484
42561
  if (harness.toml !== undefined) {
42485
42562
  const already = held.includes(harness.toml.identity ?? harness.toml.header);
42486
42563
  return put(harness, path, writeToml(held, harness.toml, launch), already);
@@ -42494,7 +42571,7 @@ function connect(harness, projectPath, launch) {
42494
42571
  try {
42495
42572
  existing = JSON.parse(held);
42496
42573
  } catch (cause) {
42497
- throw new Refusal2(`${path} is not valid JSON, so gdharness was not added to it.`, { cause });
42574
+ throw new Refusal(`${path} is not valid JSON, so gdharness was not added to it.`, { cause });
42498
42575
  }
42499
42576
  }
42500
42577
  const container = typeof existing === "object" && existing !== null && !Array.isArray(existing) ? existing[harness.container] : undefined;
@@ -42506,8 +42583,8 @@ function connect(harness, projectPath, launch) {
42506
42583
  return was === undefined || was === now ? written : { ...written, wasLaunchedBy: was };
42507
42584
  }
42508
42585
  function put(harness, path, contents, already) {
42509
- mkdirSync(dirname2(path), { recursive: true });
42510
- writeFileSync(path, contents, "utf8");
42586
+ mkdirSync2(dirname3(path), { recursive: true });
42587
+ writeFileSync2(path, contents, "utf8");
42511
42588
  return { harness, path, action: already ? "replaced" : "written" };
42512
42589
  }
42513
42590
  function taken(harness, path, held, stripped) {
@@ -42515,22 +42592,23 @@ function taken(harness, path, held, stripped) {
42515
42592
  return { harness, path, action: "absent" };
42516
42593
  }
42517
42594
  if (stripped.trim() === "" || stripped.trim() === "{}") {
42518
- rmSync(path, { force: true });
42595
+ rmSync2(path, { force: true });
42519
42596
  return { harness, path, action: "deleted" };
42520
42597
  }
42521
- writeFileSync(path, stripped, "utf8");
42598
+ writeFileSync2(path, stripped, "utf8");
42522
42599
  return { harness, path, action: "removed" };
42523
42600
  }
42524
42601
 
42525
42602
  // src/headless.ts
42603
+ init_call_signal();
42526
42604
  init_game_log();
42527
42605
  init_scratch();
42528
- import { execFile as execFile2 } from "node:child_process";
42529
- import { mkdtempSync, writeFileSync as writeFileSync2 } from "node:fs";
42606
+ import { execFile as execFile3 } from "node:child_process";
42607
+ import { mkdtempSync, writeFileSync as writeFileSync3 } from "node:fs";
42530
42608
  import { tmpdir } from "node:os";
42531
- import { join as join4 } from "node:path";
42532
- import { promisify as promisify2 } from "node:util";
42533
- var run2 = promisify2(execFile2);
42609
+ import { join as join5 } from "node:path";
42610
+ import { promisify as promisify3 } from "node:util";
42611
+ var run3 = promisify3(execFile3);
42534
42612
  function snakeCased(params) {
42535
42613
  const result = emptyRecord();
42536
42614
  for (const [key, value] of Object.entries(params)) {
@@ -42569,13 +42647,13 @@ function reason(stdout, stderr) {
42569
42647
  return stdout.trim().split(/\r?\n/).at(-1) ?? "no output at all";
42570
42648
  }
42571
42649
  async function runOperation(engine, operation, params, projectPath) {
42572
- const paramsDir = mkdtempSync(join4(tmpdir(), "gdharness-params-"));
42573
- const paramsFile = join4(paramsDir, `${operation}.json`);
42574
- writeFileSync2(paramsFile, JSON.stringify(snakeCased(params)), "utf8");
42650
+ const paramsDir = mkdtempSync(join5(tmpdir(), "gdharness-params-"));
42651
+ const paramsFile = join5(paramsDir, `${operation}.json`);
42652
+ writeFileSync3(paramsFile, JSON.stringify(snakeCased(params)), "utf8");
42575
42653
  const args = [
42576
42654
  "--headless",
42577
42655
  "--log-file",
42578
- join4(paramsDir, "engine.log"),
42656
+ join5(paramsDir, "engine.log"),
42579
42657
  "--path",
42580
42658
  projectPath,
42581
42659
  "--script",
@@ -42587,7 +42665,7 @@ async function runOperation(engine, operation, params, projectPath) {
42587
42665
  let stdout;
42588
42666
  let stderr;
42589
42667
  try {
42590
- ({ stdout, stderr } = await run2(engine.godotPath, args));
42668
+ ({ stdout, stderr } = await run3(engine.godotPath, args, { signal: callSignal() }));
42591
42669
  } catch (error) {
42592
42670
  if (error instanceof Error && "stdout" in error && "stderr" in error) {
42593
42671
  const failed = error;
@@ -42745,12 +42823,12 @@ class Ask {
42745
42823
 
42746
42824
  // src/server-version.ts
42747
42825
  init_update_check();
42748
- import { readFileSync as readFileSync4 } from "node:fs";
42826
+ import { readFileSync as readFileSync5 } from "node:fs";
42749
42827
  var DEBUG_MODE2 = process.env["DEBUG"] === "true";
42750
42828
  var GODOT_DEBUG_MODE_DEFAULT2 = process.env["GODOT_DEBUG"] === "true" || DEBUG_MODE2;
42751
42829
  var SERVER_VERSION2 = (() => {
42752
42830
  try {
42753
- const pkg = JSON.parse(readFileSync4(new URL("../package.json", import.meta.url), "utf8"));
42831
+ const pkg = JSON.parse(readFileSync5(new URL("../package.json", import.meta.url), "utf8"));
42754
42832
  return typeof pkg.version === "string" ? pkg.version : "0.0.0";
42755
42833
  } catch {
42756
42834
  return "0.0.0";
@@ -42762,19 +42840,19 @@ init_class_cache();
42762
42840
  init_headless();
42763
42841
  init_resources();
42764
42842
  init_server_version();
42765
- import { spawnSync } from "node:child_process";
42843
+ import { spawnSync as spawnSync2 } from "node:child_process";
42766
42844
  import { createHash as createHash2 } from "node:crypto";
42767
42845
  import {
42768
42846
  cpSync,
42769
42847
  existsSync as existsSync7,
42770
- mkdirSync as mkdirSync3,
42848
+ mkdirSync as mkdirSync4,
42771
42849
  readdirSync as readdirSync3,
42772
- readFileSync as readFileSync7,
42773
- rmSync as rmSync3,
42850
+ readFileSync as readFileSync8,
42851
+ rmSync as rmSync4,
42774
42852
  statSync as statSync3,
42775
- writeFileSync as writeFileSync5
42853
+ writeFileSync as writeFileSync6
42776
42854
  } from "node:fs";
42777
- import { dirname as dirname3, join as join8 } from "node:path";
42855
+ import { dirname as dirname4, join as join9 } from "node:path";
42778
42856
  import { fileURLToPath } from "node:url";
42779
42857
  var ADDONS = ["gdharness_editor", "gdharness_runtime", "auto_reload"];
42780
42858
  var EDITOR_PLUGINS = ["gdharness_editor", "auto_reload"];
@@ -42791,64 +42869,64 @@ function editorAddonDigest(from = shippedAddonsDirectory()) {
42791
42869
  if (name === VERSION_MARKER || name === DIGEST_MARKER) {
42792
42870
  continue;
42793
42871
  }
42794
- const path = join8(directory, name);
42872
+ const path = join9(directory, name);
42795
42873
  const inside = `${relative}/${name}`;
42796
42874
  if (statSync3(path).isDirectory()) {
42797
42875
  walk(path, inside);
42798
42876
  } else {
42799
42877
  hash.update(`${inside}
42800
42878
  `);
42801
- hash.update(readFileSync7(path));
42879
+ hash.update(readFileSync8(path));
42802
42880
  hash.update(`
42803
42881
  `);
42804
42882
  }
42805
42883
  }
42806
42884
  };
42807
42885
  for (const name of EDITOR_PLUGINS) {
42808
- walk(join8(from, name), name);
42886
+ walk(join9(from, name), name);
42809
42887
  }
42810
42888
  return hash.digest("hex");
42811
42889
  }
42812
42890
  function installedEditorDigest(projectPath) {
42813
- const marker = join8(projectPath, "addons", ADDONS[0], DIGEST_MARKER);
42891
+ const marker = join9(projectPath, "addons", ADDONS[0], DIGEST_MARKER);
42814
42892
  if (!existsSync7(marker)) {
42815
42893
  return null;
42816
42894
  }
42817
- const said = readFileSync7(marker, "utf8").trim();
42895
+ const said = readFileSync8(marker, "utf8").trim();
42818
42896
  return said === "" ? null : said;
42819
42897
  }
42820
42898
  function shippedAddonsDirectory() {
42821
- return join8(dirname3(fileURLToPath(import.meta.url)), "godot", "addons");
42899
+ return join9(dirname4(fileURLToPath(import.meta.url)), "godot", "addons");
42822
42900
  }
42823
42901
  function shippedOperationsScript() {
42824
- return join8(dirname3(fileURLToPath(import.meta.url)), "godot", "operations", "godot_operations.gd");
42902
+ return join9(dirname4(fileURLToPath(import.meta.url)), "godot", "operations", "godot_operations.gd");
42825
42903
  }
42826
42904
  function installAddons(projectPath, from = shippedAddonsDirectory()) {
42827
42905
  const installed = [];
42828
42906
  for (const name of ADDONS) {
42829
- const source = join8(from, name);
42830
- if (!existsSync7(join8(source, name === "gdharness_runtime" ? "runtime_autoload.gd" : "plugin.cfg"))) {
42907
+ const source = join9(from, name);
42908
+ if (!existsSync7(join9(source, name === "gdharness_runtime" ? "runtime_autoload.gd" : "plugin.cfg"))) {
42831
42909
  throw new Error(`The package holds no ${name} addon at ${source}.`);
42832
42910
  }
42833
- const target = join8(projectPath, "addons", name);
42911
+ const target = join9(projectPath, "addons", name);
42834
42912
  const replaced = existsSync7(target);
42835
- rmSync3(target, { recursive: true, force: true });
42836
- mkdirSync3(dirname3(target), { recursive: true });
42913
+ rmSync4(target, { recursive: true, force: true });
42914
+ mkdirSync4(dirname4(target), { recursive: true });
42837
42915
  cpSync(source, target, { recursive: true });
42838
- writeFileSync5(join8(target, VERSION_MARKER), `${SERVER_VERSION}
42916
+ writeFileSync6(join9(target, VERSION_MARKER), `${SERVER_VERSION}
42839
42917
  `);
42840
42918
  installed.push({ name, path: target, replaced });
42841
42919
  }
42842
- writeFileSync5(join8(projectPath, "addons", ADDONS[0], DIGEST_MARKER), `${editorAddonDigest(from)}
42920
+ writeFileSync6(join9(projectPath, "addons", ADDONS[0], DIGEST_MARKER), `${editorAddonDigest(from)}
42843
42921
  `);
42844
42922
  return installed;
42845
42923
  }
42846
42924
  function removeAddons(projectPath) {
42847
42925
  const removed = [];
42848
42926
  for (const name of ADDONS) {
42849
- const target = join8(projectPath, "addons", name);
42927
+ const target = join9(projectPath, "addons", name);
42850
42928
  if (existsSync7(target)) {
42851
- rmSync3(target, { recursive: true, force: true });
42929
+ rmSync4(target, { recursive: true, force: true });
42852
42930
  removed.push(target);
42853
42931
  }
42854
42932
  }
@@ -42882,7 +42960,7 @@ function loaderAmong(projectPath, named) {
42882
42960
  }
42883
42961
  let source = null;
42884
42962
  try {
42885
- source = readFileSync7(join8(projectPath, path), "utf8");
42963
+ source = readFileSync8(join9(projectPath, path), "utf8");
42886
42964
  } catch {}
42887
42965
  if (source?.includes(RUNTIME_AUTOLOAD.path)) {
42888
42966
  return { name: entry, path, exists: true };
@@ -42905,7 +42983,7 @@ function trackedByGit(projectPath, wanted) {
42905
42983
  if (wanted.length === 0) {
42906
42984
  return new Set;
42907
42985
  }
42908
- const asked = spawnSync("git", ["ls-files", "-z", "--", ...wanted], {
42986
+ const asked = spawnSync2("git", ["ls-files", "-z", "--", ...wanted], {
42909
42987
  cwd: projectPath,
42910
42988
  encoding: "utf8"
42911
42989
  });
@@ -42924,10 +43002,10 @@ function enabledPlugins(settings) {
42924
43002
  function inspectProject(projectPath) {
42925
43003
  const problems = [];
42926
43004
  const addons = ADDONS.map((name) => {
42927
- const directory = join8(projectPath, "addons", name);
42928
- const marker = join8(directory, VERSION_MARKER);
43005
+ const directory = join9(projectPath, "addons", name);
43006
+ const marker = join9(directory, VERSION_MARKER);
42929
43007
  const installed = existsSync7(directory);
42930
- const version = existsSync7(marker) ? readFileSync7(marker, "utf8").trim() : null;
43008
+ const version = existsSync7(marker) ? readFileSync8(marker, "utf8").trim() : null;
42931
43009
  const current = version === SERVER_VERSION;
42932
43010
  if (!installed) {
42933
43011
  problems.push(`addons/${name} is not installed; run gdharness setup`);
@@ -42936,7 +43014,7 @@ function inspectProject(projectPath) {
42936
43014
  }
42937
43015
  return { name, installed, version, current };
42938
43016
  });
42939
- const settings = parseProjectGodot(readFileSync7(join8(projectPath, "project.godot"), "utf8"));
43017
+ const settings = parseProjectGodot(readFileSync8(join9(projectPath, "project.godot"), "utf8"));
42940
43018
  const pluginsEnabled = enabledPlugins(settings);
42941
43019
  for (const name of EDITOR_PLUGINS) {
42942
43020
  if (!pluginsEnabled.includes(name)) {
@@ -42952,7 +43030,7 @@ function inspectProject(projectPath) {
42952
43030
  const tracked = trackedByGit(projectPath, [...named.values()]);
42953
43031
  const addonMissing = (path) => addons.some((addon) => !addon.installed && path.startsWith(`addons/${addon.name}/`));
42954
43032
  for (const [name, path] of named) {
42955
- if (!existsSync7(join8(projectPath, path))) {
43033
+ if (!existsSync7(join9(projectPath, path))) {
42956
43034
  if (!addonMissing(path)) {
42957
43035
  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`);
42958
43036
  }
@@ -42985,8 +43063,8 @@ function inspectProject(projectPath) {
42985
43063
  // src/skill.ts
42986
43064
  init_setup();
42987
43065
  init_tool_definitions();
42988
- import { existsSync as existsSync9, mkdirSync as mkdirSync5, readdirSync as readdirSync5, rmSync as rmSync5, writeFileSync as writeFileSync7 } from "node:fs";
42989
- import { basename, dirname as dirname5, join as join10 } from "node:path";
43066
+ import { existsSync as existsSync9, mkdirSync as mkdirSync6, readdirSync as readdirSync5, rmSync as rmSync6, writeFileSync as writeFileSync8 } from "node:fs";
43067
+ import { basename as basename2, dirname as dirname6, join as join11 } from "node:path";
42990
43068
 
42991
43069
  // src/tool-reference.ts
42992
43070
  init_headless_operations();
@@ -43082,7 +43160,7 @@ function renderToolsMarkdown() {
43082
43160
 
43083
43161
  // src/skill.ts
43084
43162
  var SKILL_NAME = "gdharness";
43085
- var SHARED_SKILLS = join10(".agents", "skills");
43163
+ var SHARED_SKILLS = join11(".agents", "skills");
43086
43164
  function skillMarkdown(version) {
43087
43165
  return `---
43088
43166
  name: ${SKILL_NAME}
@@ -43255,27 +43333,27 @@ whatever the engine said on stderr comes back under \`engine_messages\`.
43255
43333
  function skillFiles(version) {
43256
43334
  return new Map([
43257
43335
  ["SKILL.md", skillMarkdown(version)],
43258
- [join10("references", "tools.md"), renderToolsMarkdown()]
43336
+ [join11("references", "tools.md"), renderToolsMarkdown()]
43259
43337
  ]);
43260
43338
  }
43261
43339
  function skillDirectories(harnesses, projectPath, exists) {
43262
- const directories = [join10(projectPath, SHARED_SKILLS, SKILL_NAME)];
43340
+ const directories = [join11(projectPath, SHARED_SKILLS, SKILL_NAME)];
43263
43341
  for (const harness of harnesses) {
43264
43342
  if (harness.skills === undefined) {
43265
43343
  continue;
43266
43344
  }
43267
- const own = join10(projectPath, harness.skills.dir);
43268
- if ((!harness.skills.shared || exists(own)) && !directories.includes(join10(own, SKILL_NAME))) {
43269
- directories.push(join10(own, SKILL_NAME));
43345
+ const own = join11(projectPath, harness.skills.dir);
43346
+ if ((!harness.skills.shared || exists(own)) && !directories.includes(join11(own, SKILL_NAME))) {
43347
+ directories.push(join11(own, SKILL_NAME));
43270
43348
  }
43271
43349
  }
43272
43350
  return directories;
43273
43351
  }
43274
43352
  function everySkillDirectory(projectPath, harnesses) {
43275
- const directories = new Set([join10(projectPath, SHARED_SKILLS, SKILL_NAME)]);
43353
+ const directories = new Set([join11(projectPath, SHARED_SKILLS, SKILL_NAME)]);
43276
43354
  for (const harness of harnesses) {
43277
43355
  if (harness.skills !== undefined) {
43278
- directories.add(join10(projectPath, harness.skills.dir, SKILL_NAME));
43356
+ directories.add(join11(projectPath, harness.skills.dir, SKILL_NAME));
43279
43357
  }
43280
43358
  }
43281
43359
  return [...directories];
@@ -43284,51 +43362,51 @@ function removeSkill(directories, projectPath) {
43284
43362
  const removed = [];
43285
43363
  for (const directory of directories) {
43286
43364
  if (existsSync9(directory)) {
43287
- rmSync5(directory, { recursive: true, force: true });
43288
- pruneEmpty(dirname5(directory), projectPath);
43365
+ rmSync6(directory, { recursive: true, force: true });
43366
+ pruneEmpty(dirname6(directory), projectPath);
43289
43367
  removed.push(directory);
43290
43368
  }
43291
43369
  }
43292
43370
  return removed;
43293
43371
  }
43294
43372
  function pruneEmpty(directory, projectPath) {
43295
- const shared = join10(projectPath, ".agents");
43373
+ const shared = join11(projectPath, ".agents");
43296
43374
  let path = directory;
43297
- while (path !== projectPath && (basename(path) === "skills" || path === shared)) {
43375
+ while (path !== projectPath && (basename2(path) === "skills" || path === shared)) {
43298
43376
  if (readdirSync5(path).length > 0) {
43299
43377
  return;
43300
43378
  }
43301
- rmSync5(path, { recursive: true, force: true });
43302
- path = dirname5(path);
43379
+ rmSync6(path, { recursive: true, force: true });
43380
+ path = dirname6(path);
43303
43381
  }
43304
43382
  }
43305
43383
  function writeSkill(directories, version) {
43306
43384
  const files = skillFiles(version);
43307
43385
  return directories.map((directory) => {
43308
43386
  const replaced = existsSync9(directory);
43309
- rmSync5(directory, { recursive: true, force: true });
43387
+ rmSync6(directory, { recursive: true, force: true });
43310
43388
  for (const [name, contents] of files) {
43311
- const path = join10(directory, name);
43312
- mkdirSync5(dirname5(path), { recursive: true });
43313
- writeFileSync7(path, contents, "utf8");
43389
+ const path = join11(directory, name);
43390
+ mkdirSync6(dirname6(path), { recursive: true });
43391
+ writeFileSync8(path, contents, "utf8");
43314
43392
  }
43315
43393
  return { path: directory, replaced };
43316
43394
  });
43317
43395
  }
43318
43396
 
43319
43397
  // src/version.ts
43320
- import { readFileSync as readFileSync9 } from "node:fs";
43321
- import { dirname as dirname6, join as join11 } from "node:path";
43398
+ import { readFileSync as readFileSync10 } from "node:fs";
43399
+ import { dirname as dirname7, join as join12 } from "node:path";
43322
43400
  import { fileURLToPath as fileURLToPath3 } from "node:url";
43323
43401
  function getLocalVersion() {
43324
- const currentDirectory = dirname6(fileURLToPath3(import.meta.url));
43402
+ const currentDirectory = dirname7(fileURLToPath3(import.meta.url));
43325
43403
  const packageCandidates = [
43326
- join11(currentDirectory, "..", "package.json"),
43327
- join11(currentDirectory, "..", "..", "package.json")
43404
+ join12(currentDirectory, "..", "package.json"),
43405
+ join12(currentDirectory, "..", "..", "package.json")
43328
43406
  ];
43329
43407
  for (const packagePath of packageCandidates) {
43330
43408
  try {
43331
- const value = JSON.parse(readFileSync9(packagePath, "utf-8"));
43409
+ const value = JSON.parse(readFileSync10(packagePath, "utf-8"));
43332
43410
  if (typeof value === "object" && value !== null && "name" in value && value.name === "gdharness" && "version" in value && typeof value.version === "string") {
43333
43411
  return value.version;
43334
43412
  }
@@ -43341,12 +43419,12 @@ function getLocalVersion() {
43341
43419
  var args = process.argv.slice(2);
43342
43420
  var command = args[0];
43343
43421
 
43344
- class UsageError extends Refusal {
43422
+ class UsageError extends Refusal2 {
43345
43423
  }
43346
43424
  function projectArgument(at) {
43347
43425
  const named = args.slice(at).find((value) => !value.startsWith("--"));
43348
43426
  const projectPath = resolve5(named ?? ".");
43349
- if (!existsSync16(join23(projectPath, "project.godot"))) {
43427
+ if (!existsSync16(join24(projectPath, "project.godot"))) {
43350
43428
  throw new UsageError(`Not a Godot project: ${projectPath} holds no project.godot.`);
43351
43429
  }
43352
43430
  return projectPath;
@@ -43365,7 +43443,7 @@ async function engine(projectPath) {
43365
43443
  }
43366
43444
  function said2(outcome, what) {
43367
43445
  if (!outcome.ok) {
43368
- throw new Refusal(`${what}: ${outcome.message}`);
43446
+ throw new Refusal2(`${what}: ${outcome.message}`);
43369
43447
  }
43370
43448
  for (const entry of outcome.messages) {
43371
43449
  console.error(` engine ${entry.severity}: ${entry.text}`);
@@ -43385,7 +43463,7 @@ async function registerRuntime(godot, projectPath) {
43385
43463
  return;
43386
43464
  }
43387
43465
  console.log(`${RUNTIME_AUTOLOAD.name} autoload left at ${named}, which this project registers itself`);
43388
- if (!existsSync16(join23(projectPath, (named ?? "").replace(/^res:\/\//, "")))) {
43466
+ if (!existsSync16(join24(projectPath, (named ?? "").replace(/^res:\/\//, "")))) {
43389
43467
  console.log(` that file is not in this project, so it boots with a missing script: restore it, or point the entry at res://${RUNTIME_AUTOLOAD.path}`);
43390
43468
  }
43391
43469
  }
@@ -43620,7 +43698,8 @@ On ${version}. What is left for you:`);
43620
43698
  ` + ` so it gives an open editor nothing to restart for. editor_status says addonIsStale
43621
43699
  ` + " if the editor is older than that." : ` 1. The open editor is still running the addons it loaded at startup. Restart it with the
43622
43700
  ` + " editor_launch restart tool, which closes and reopens the window.");
43623
- console.log(harnessNote({ moved, written, byHand }, [launch.command, ...launch.args].join(" ")));
43701
+ const serving = serverServing(projectPath);
43702
+ console.log(harnessNote({ moved, written, byHand }, [launch.command, ...launch.args].join(" "), serving === null ? null : { server: serving, isThisVersion: serving.version === version }));
43624
43703
  }
43625
43704
  function doctorReport(projectPath) {
43626
43705
  const report = inspectProject(projectPath);
@@ -43780,7 +43859,7 @@ async function main() {
43780
43859
  }
43781
43860
  }
43782
43861
  await main().catch((error) => {
43783
- if (error instanceof Refusal) {
43862
+ if (error instanceof Refusal2) {
43784
43863
  console.error("gdharness:", error.message);
43785
43864
  process.exit(error instanceof UsageError ? 2 : 1);
43786
43865
  }