gdharness 1.0.16 → 1.0.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/cli.js +642 -591
- package/build/index.js +2 -1
- 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 =
|
|
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) =>
|
|
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
|
|
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,7 +7383,7 @@ 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 =
|
|
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 = () => {};
|
|
@@ -7324,7 +7525,7 @@ var init_game_log = __esm(() => {
|
|
|
7324
7525
|
});
|
|
7325
7526
|
|
|
7326
7527
|
// src/scratch.ts
|
|
7327
|
-
import { rmSync as
|
|
7528
|
+
import { rmSync as rmSync3 } from "node:fs";
|
|
7328
7529
|
function discardWith(remove, paths) {
|
|
7329
7530
|
const left = [];
|
|
7330
7531
|
for (const path of paths) {
|
|
@@ -7341,7 +7542,7 @@ function discardWith(remove, paths) {
|
|
|
7341
7542
|
}
|
|
7342
7543
|
function discard(...paths) {
|
|
7343
7544
|
return discardWith((path) => {
|
|
7344
|
-
|
|
7545
|
+
rmSync3(path, SWEPT);
|
|
7345
7546
|
}, paths);
|
|
7346
7547
|
}
|
|
7347
7548
|
var SWEPT;
|
|
@@ -7350,9 +7551,9 @@ var init_scratch = __esm(() => {
|
|
|
7350
7551
|
});
|
|
7351
7552
|
|
|
7352
7553
|
// src/update-check.ts
|
|
7353
|
-
import { existsSync as existsSync5, mkdirSync as
|
|
7554
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync4 } from "node:fs";
|
|
7354
7555
|
import { homedir as homedir3, tmpdir as tmpdir2 } from "node:os";
|
|
7355
|
-
import { join as
|
|
7556
|
+
import { join as join6 } from "node:path";
|
|
7356
7557
|
function packageUrl(root) {
|
|
7357
7558
|
return `${root.replace(/\/+$/, "")}/gdharness/latest`;
|
|
7358
7559
|
}
|
|
@@ -7374,20 +7575,20 @@ function cacheDirectory(environment) {
|
|
|
7374
7575
|
};
|
|
7375
7576
|
const home = set("HOME") ?? homedir3();
|
|
7376
7577
|
if (process.platform === "win32") {
|
|
7377
|
-
return
|
|
7578
|
+
return join6(set("LOCALAPPDATA") ?? home, "gdharness");
|
|
7378
7579
|
}
|
|
7379
7580
|
if (process.platform === "darwin") {
|
|
7380
|
-
return
|
|
7581
|
+
return join6(home, "Library", "Caches", "gdharness");
|
|
7381
7582
|
}
|
|
7382
|
-
return
|
|
7583
|
+
return join6(set("XDG_CACHE_HOME") ?? join6(home, ".cache"), "gdharness");
|
|
7383
7584
|
}
|
|
7384
7585
|
function cacheFile(environment = process.env) {
|
|
7385
7586
|
try {
|
|
7386
7587
|
const directory = cacheDirectory(environment);
|
|
7387
|
-
|
|
7388
|
-
return
|
|
7588
|
+
mkdirSync3(directory, { recursive: true, mode: 448 });
|
|
7589
|
+
return join6(directory, "update-check.json");
|
|
7389
7590
|
} catch {
|
|
7390
|
-
return
|
|
7591
|
+
return join6(tmpdir2(), "gdharness-update-check.json");
|
|
7391
7592
|
}
|
|
7392
7593
|
}
|
|
7393
7594
|
function readCache(path) {
|
|
@@ -7395,7 +7596,7 @@ function readCache(path) {
|
|
|
7395
7596
|
if (!existsSync5(path)) {
|
|
7396
7597
|
return null;
|
|
7397
7598
|
}
|
|
7398
|
-
const parsed = JSON.parse(
|
|
7599
|
+
const parsed = JSON.parse(readFileSync3(path, "utf8"));
|
|
7399
7600
|
if (typeof parsed !== "object" || parsed === null) {
|
|
7400
7601
|
return null;
|
|
7401
7602
|
}
|
|
@@ -7412,7 +7613,7 @@ function readCache(path) {
|
|
|
7412
7613
|
}
|
|
7413
7614
|
function writeCache(path, entry) {
|
|
7414
7615
|
try {
|
|
7415
|
-
|
|
7616
|
+
writeFileSync4(path, JSON.stringify(entry), { encoding: "utf8", mode: 384 });
|
|
7416
7617
|
} catch {}
|
|
7417
7618
|
}
|
|
7418
7619
|
function parts(version) {
|
|
@@ -7543,7 +7744,7 @@ var init_update_check = __esm(() => {
|
|
|
7543
7744
|
});
|
|
7544
7745
|
|
|
7545
7746
|
// src/server-version.ts
|
|
7546
|
-
import { readFileSync as
|
|
7747
|
+
import { readFileSync as readFileSync4 } from "node:fs";
|
|
7547
7748
|
function editorIsStale(addonVersion, serverVersion, addonDigest, shippedDigest) {
|
|
7548
7749
|
if (addonDigest !== undefined && addonDigest !== "" && shippedDigest !== undefined) {
|
|
7549
7750
|
return addonDigest !== shippedDigest;
|
|
@@ -7579,7 +7780,7 @@ var init_server_version = __esm(() => {
|
|
|
7579
7780
|
GODOT_DEBUG_MODE_DEFAULT = process.env["GODOT_DEBUG"] === "true" || DEBUG_MODE;
|
|
7580
7781
|
SERVER_VERSION = (() => {
|
|
7581
7782
|
try {
|
|
7582
|
-
const pkg = JSON.parse(
|
|
7783
|
+
const pkg = JSON.parse(readFileSync4(new URL("../package.json", import.meta.url), "utf8"));
|
|
7583
7784
|
return typeof pkg.version === "string" ? pkg.version : "0.0.0";
|
|
7584
7785
|
} catch {
|
|
7585
7786
|
return "0.0.0";
|
|
@@ -7588,23 +7789,23 @@ var init_server_version = __esm(() => {
|
|
|
7588
7789
|
});
|
|
7589
7790
|
|
|
7590
7791
|
// src/class-cache.ts
|
|
7591
|
-
import { existsSync as existsSync6, readdirSync as readdirSync2, readFileSync as
|
|
7592
|
-
import { join as
|
|
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";
|
|
7593
7794
|
function declaredClasses(projectPath) {
|
|
7594
7795
|
const declared = new Map;
|
|
7595
7796
|
const visit = (directory, prefix) => {
|
|
7596
|
-
if (existsSync6(
|
|
7797
|
+
if (existsSync6(join7(directory, ".gdignore"))) {
|
|
7597
7798
|
return;
|
|
7598
7799
|
}
|
|
7599
7800
|
for (const entry of readdirSync2(directory, { withFileTypes: true })) {
|
|
7600
7801
|
if (entry.name.startsWith(".")) {
|
|
7601
7802
|
continue;
|
|
7602
7803
|
}
|
|
7603
|
-
const path =
|
|
7804
|
+
const path = join7(directory, entry.name);
|
|
7604
7805
|
if (entry.isDirectory()) {
|
|
7605
7806
|
visit(path, `${prefix}${entry.name}/`);
|
|
7606
7807
|
} else if (entry.isFile() && entry.name.endsWith(".gd")) {
|
|
7607
|
-
const found = DECLARATION.exec(
|
|
7808
|
+
const found = DECLARATION.exec(readFileSync6(path, "utf8"));
|
|
7608
7809
|
if (found?.[1]) {
|
|
7609
7810
|
declared.set(found[1], `res://${prefix}${entry.name}`);
|
|
7610
7811
|
}
|
|
@@ -7615,12 +7816,12 @@ function declaredClasses(projectPath) {
|
|
|
7615
7816
|
return declared;
|
|
7616
7817
|
}
|
|
7617
7818
|
function cachedClasses(projectPath) {
|
|
7618
|
-
const cache =
|
|
7819
|
+
const cache = join7(projectPath, ".godot", "global_script_class_cache.cfg");
|
|
7619
7820
|
if (!existsSync6(cache)) {
|
|
7620
7821
|
return null;
|
|
7621
7822
|
}
|
|
7622
7823
|
const listed = new Map;
|
|
7623
|
-
const text =
|
|
7824
|
+
const text = readFileSync6(cache, "utf8");
|
|
7624
7825
|
for (const entry of text.matchAll(/"class":\s*&"([^"]+)"[\s\S]*?"path":\s*"([^"]+)"/g)) {
|
|
7625
7826
|
listed.set(entry[1] ?? "", entry[2] ?? "");
|
|
7626
7827
|
}
|
|
@@ -7705,7 +7906,7 @@ function uncachedClassNote(types) {
|
|
|
7705
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.";
|
|
7706
7907
|
}
|
|
7707
7908
|
function cacheWrittenAt(projectPath) {
|
|
7708
|
-
const cache =
|
|
7909
|
+
const cache = join7(projectPath, ".godot", "global_script_class_cache.cfg");
|
|
7709
7910
|
try {
|
|
7710
7911
|
return statSync2(cache).mtimeMs;
|
|
7711
7912
|
} catch {
|
|
@@ -7719,14 +7920,14 @@ function declaredSince(projectPath, paths, names, since) {
|
|
|
7719
7920
|
return false;
|
|
7720
7921
|
}
|
|
7721
7922
|
try {
|
|
7722
|
-
return statSync2(
|
|
7923
|
+
return statSync2(join7(projectPath, path.slice("res://".length))).mtimeMs > since;
|
|
7723
7924
|
} catch {
|
|
7724
7925
|
return false;
|
|
7725
7926
|
}
|
|
7726
7927
|
});
|
|
7727
7928
|
}
|
|
7728
7929
|
function cachedAtMissingPaths(cached, projectPath) {
|
|
7729
|
-
return [...cached].filter(([, path]) => path.startsWith("res://") && !existsSync6(
|
|
7930
|
+
return [...cached].filter(([, path]) => path.startsWith("res://") && !existsSync6(join7(projectPath, path.slice("res://".length)))).map(([name]) => name).sort();
|
|
7730
7931
|
}
|
|
7731
7932
|
function staleAgainst(cached, projectPath) {
|
|
7732
7933
|
return [...declaredClasses(projectPath)].filter(([name, path]) => cached.get(name) !== path).map(([name]) => name);
|
|
@@ -7749,11 +7950,11 @@ var init_class_cache = __esm(() => {
|
|
|
7749
7950
|
});
|
|
7750
7951
|
|
|
7751
7952
|
// src/headless.ts
|
|
7752
|
-
import { execFile as
|
|
7753
|
-
import { mkdtempSync as mkdtempSync2, writeFileSync as
|
|
7953
|
+
import { execFile as execFile4 } from "node:child_process";
|
|
7954
|
+
import { mkdtempSync as mkdtempSync2, writeFileSync as writeFileSync5 } from "node:fs";
|
|
7754
7955
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
7755
|
-
import { join as
|
|
7756
|
-
import { promisify as
|
|
7956
|
+
import { join as join8 } from "node:path";
|
|
7957
|
+
import { promisify as promisify4 } from "node:util";
|
|
7757
7958
|
function snakeCased2(params) {
|
|
7758
7959
|
const result = emptyRecord();
|
|
7759
7960
|
for (const [key, value] of Object.entries(params)) {
|
|
@@ -7792,9 +7993,9 @@ function reason2(stdout, stderr) {
|
|
|
7792
7993
|
return stdout.trim().split(/\r?\n/).at(-1) ?? "no output at all";
|
|
7793
7994
|
}
|
|
7794
7995
|
async function runImport(godotPath, projectPath) {
|
|
7795
|
-
const logDir = mkdtempSync2(
|
|
7996
|
+
const logDir = mkdtempSync2(join8(tmpdir3(), "gdharness-import-"));
|
|
7796
7997
|
try {
|
|
7797
|
-
const { stderr } = await
|
|
7998
|
+
const { stderr } = await run4(godotPath, ["--headless", "--log-file", join8(logDir, "engine.log"), "--path", projectPath, "--import"], { signal: callSignal() });
|
|
7798
7999
|
return { ok: true, messages: problems2(stderr) };
|
|
7799
8000
|
} catch (error) {
|
|
7800
8001
|
if (error instanceof Error && "stdout" in error && "stderr" in error) {
|
|
@@ -7815,13 +8016,13 @@ async function runImport(godotPath, projectPath) {
|
|
|
7815
8016
|
}
|
|
7816
8017
|
}
|
|
7817
8018
|
async function runOperation2(engine, operation, params, projectPath) {
|
|
7818
|
-
const paramsDir = mkdtempSync2(
|
|
7819
|
-
const paramsFile =
|
|
7820
|
-
|
|
8019
|
+
const paramsDir = mkdtempSync2(join8(tmpdir3(), "gdharness-params-"));
|
|
8020
|
+
const paramsFile = join8(paramsDir, `${operation}.json`);
|
|
8021
|
+
writeFileSync5(paramsFile, JSON.stringify(snakeCased2(params)), "utf8");
|
|
7821
8022
|
const args = [
|
|
7822
8023
|
"--headless",
|
|
7823
8024
|
"--log-file",
|
|
7824
|
-
|
|
8025
|
+
join8(paramsDir, "engine.log"),
|
|
7825
8026
|
"--path",
|
|
7826
8027
|
projectPath,
|
|
7827
8028
|
"--script",
|
|
@@ -7833,7 +8034,7 @@ async function runOperation2(engine, operation, params, projectPath) {
|
|
|
7833
8034
|
let stdout;
|
|
7834
8035
|
let stderr;
|
|
7835
8036
|
try {
|
|
7836
|
-
({ stdout, stderr } = await
|
|
8037
|
+
({ stdout, stderr } = await run4(engine.godotPath, args, { signal: callSignal() }));
|
|
7837
8038
|
} catch (error) {
|
|
7838
8039
|
if (error instanceof Error && "stdout" in error && "stderr" in error) {
|
|
7839
8040
|
const failed = error;
|
|
@@ -7861,12 +8062,12 @@ async function runOperation2(engine, operation, params, projectPath) {
|
|
|
7861
8062
|
}
|
|
7862
8063
|
return { ok: true, payload, messages: problems2(stderr) };
|
|
7863
8064
|
}
|
|
7864
|
-
var
|
|
8065
|
+
var run4;
|
|
7865
8066
|
var init_headless = __esm(() => {
|
|
7866
8067
|
init_call_signal();
|
|
7867
8068
|
init_game_log();
|
|
7868
8069
|
init_scratch();
|
|
7869
|
-
|
|
8070
|
+
run4 = promisify4(execFile4);
|
|
7870
8071
|
});
|
|
7871
8072
|
|
|
7872
8073
|
// node_modules/zod/v4/core/util.js
|
|
@@ -15034,26 +15235,26 @@ var init_paths = __esm(() => {
|
|
|
15034
15235
|
});
|
|
15035
15236
|
|
|
15036
15237
|
// src/resources.ts
|
|
15037
|
-
import { readFileSync as
|
|
15238
|
+
import { readFileSync as readFileSync7 } from "node:fs";
|
|
15038
15239
|
import { extname, resolve as resolve2 } from "node:path";
|
|
15039
15240
|
function ensureProjectPath(getProjectPath) {
|
|
15040
15241
|
const projectPath = getProjectPath();
|
|
15041
15242
|
if (!projectPath) {
|
|
15042
|
-
throw new
|
|
15243
|
+
throw new Refusal("Project path is not set. Set a Godot project path first.");
|
|
15043
15244
|
}
|
|
15044
15245
|
return resolve2(projectPath);
|
|
15045
15246
|
}
|
|
15046
15247
|
function uriPathToProjectPath(inputPath) {
|
|
15047
15248
|
const normalized = inputPath.replace(/\\/g, "/").trim();
|
|
15048
15249
|
if (normalized.replace(/\//g, "") === "") {
|
|
15049
|
-
throw new
|
|
15250
|
+
throw new Refusal("Resource path is empty.");
|
|
15050
15251
|
}
|
|
15051
15252
|
return normalized.replace(/^\/+/, "");
|
|
15052
15253
|
}
|
|
15053
15254
|
function resolveProjectFile(projectPath, resourcePath) {
|
|
15054
15255
|
const contained = resolveWithinProject(projectPath, resourcePath);
|
|
15055
15256
|
if (!contained.ok) {
|
|
15056
|
-
throw new
|
|
15257
|
+
throw new Refusal(contained.reason);
|
|
15057
15258
|
}
|
|
15058
15259
|
return contained.absolutePath;
|
|
15059
15260
|
}
|
|
@@ -15065,14 +15266,14 @@ function parseGodotUri(uri) {
|
|
|
15065
15266
|
try {
|
|
15066
15267
|
parsed = new URL(uri);
|
|
15067
15268
|
} catch {
|
|
15068
|
-
throw new
|
|
15269
|
+
throw new Refusal(`Invalid URI: ${uri}`);
|
|
15069
15270
|
}
|
|
15070
15271
|
if (parsed.protocol !== "godot:") {
|
|
15071
|
-
throw new
|
|
15272
|
+
throw new Refusal(`Unsupported URI scheme: ${parsed.protocol}`);
|
|
15072
15273
|
}
|
|
15073
15274
|
const host = parsed.hostname;
|
|
15074
15275
|
if (host !== "scene" && host !== "script" && host !== "resource") {
|
|
15075
|
-
throw new
|
|
15276
|
+
throw new Refusal(`Unsupported Godot resource type: ${host}`);
|
|
15076
15277
|
}
|
|
15077
15278
|
const resourcePath = uriPathToProjectPath(decodeURIComponent(parsed.pathname));
|
|
15078
15279
|
return { kind: host, resourcePath };
|
|
@@ -15080,13 +15281,13 @@ function parseGodotUri(uri) {
|
|
|
15080
15281
|
function ensureAllowedExtension(kind, filePath) {
|
|
15081
15282
|
const extension = extname(filePath).toLowerCase();
|
|
15082
15283
|
if (kind === "scene" && extension !== ".tscn") {
|
|
15083
|
-
throw new
|
|
15284
|
+
throw new Refusal("Scene resources must use .tscn extension.");
|
|
15084
15285
|
}
|
|
15085
15286
|
if (kind === "script" && extension !== ".gd") {
|
|
15086
|
-
throw new
|
|
15287
|
+
throw new Refusal("Script resources must use .gd extension.");
|
|
15087
15288
|
}
|
|
15088
15289
|
if (kind === "resource" && ![".tres", ".tscn", ".gd"].includes(extension)) {
|
|
15089
|
-
throw new
|
|
15290
|
+
throw new Refusal("Resource URIs support only .tres, .tscn, and .gd files.");
|
|
15090
15291
|
}
|
|
15091
15292
|
}
|
|
15092
15293
|
function isValueComplete(value) {
|
|
@@ -15200,7 +15401,7 @@ function readResourceText(uri, getProjectPath) {
|
|
|
15200
15401
|
const parsedUri = parseGodotUri(uri);
|
|
15201
15402
|
if (parsedUri.kind === "project-info") {
|
|
15202
15403
|
const projectFilePath = resolveProjectFile(projectPath, "project.godot");
|
|
15203
|
-
const rawProject =
|
|
15404
|
+
const rawProject = readFileSync7(projectFilePath, "utf-8");
|
|
15204
15405
|
const parsedProject = parseProjectGodot(rawProject);
|
|
15205
15406
|
return {
|
|
15206
15407
|
mimeType: "application/json",
|
|
@@ -15209,7 +15410,7 @@ function readResourceText(uri, getProjectPath) {
|
|
|
15209
15410
|
}
|
|
15210
15411
|
const filePath = resolveProjectFile(projectPath, parsedUri.resourcePath);
|
|
15211
15412
|
ensureAllowedExtension(parsedUri.kind, filePath);
|
|
15212
|
-
const text =
|
|
15413
|
+
const text = readFileSync7(filePath, "utf-8");
|
|
15213
15414
|
return {
|
|
15214
15415
|
mimeType: parsedUri.kind === "script" ? "text/x-gdscript" : "text/plain",
|
|
15215
15416
|
text
|
|
@@ -15238,9 +15439,9 @@ function setupResourceHandlers(mcp, getProjectPath) {
|
|
|
15238
15439
|
};
|
|
15239
15440
|
} catch (error) {
|
|
15240
15441
|
if (error instanceof Error) {
|
|
15241
|
-
throw new
|
|
15442
|
+
throw new Refusal(`Failed to read resource '${uri}': ${error.message}`, { cause: error });
|
|
15242
15443
|
}
|
|
15243
|
-
throw new
|
|
15444
|
+
throw new Refusal(`Failed to read resource '${uri}'.`, { cause: error });
|
|
15244
15445
|
}
|
|
15245
15446
|
});
|
|
15246
15447
|
}
|
|
@@ -15341,14 +15542,14 @@ import { createHash as createHash3 } from "node:crypto";
|
|
|
15341
15542
|
import {
|
|
15342
15543
|
cpSync as cpSync2,
|
|
15343
15544
|
existsSync as existsSync8,
|
|
15344
|
-
mkdirSync as
|
|
15545
|
+
mkdirSync as mkdirSync5,
|
|
15345
15546
|
readdirSync as readdirSync4,
|
|
15346
|
-
readFileSync as
|
|
15347
|
-
rmSync as
|
|
15547
|
+
readFileSync as readFileSync9,
|
|
15548
|
+
rmSync as rmSync5,
|
|
15348
15549
|
statSync as statSync4,
|
|
15349
|
-
writeFileSync as
|
|
15550
|
+
writeFileSync as writeFileSync7
|
|
15350
15551
|
} from "node:fs";
|
|
15351
|
-
import { dirname as
|
|
15552
|
+
import { dirname as dirname5, join as join10 } from "node:path";
|
|
15352
15553
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
15353
15554
|
function editorAddonDigest2(from = shippedAddonsDirectory2()) {
|
|
15354
15555
|
const hash = createHash3("sha256");
|
|
@@ -15357,21 +15558,21 @@ function editorAddonDigest2(from = shippedAddonsDirectory2()) {
|
|
|
15357
15558
|
if (name === VERSION_MARKER2 || name === DIGEST_MARKER2) {
|
|
15358
15559
|
continue;
|
|
15359
15560
|
}
|
|
15360
|
-
const path =
|
|
15561
|
+
const path = join10(directory, name);
|
|
15361
15562
|
const inside = `${relative}/${name}`;
|
|
15362
15563
|
if (statSync4(path).isDirectory()) {
|
|
15363
15564
|
walk(path, inside);
|
|
15364
15565
|
} else {
|
|
15365
15566
|
hash.update(`${inside}
|
|
15366
15567
|
`);
|
|
15367
|
-
hash.update(
|
|
15568
|
+
hash.update(readFileSync9(path));
|
|
15368
15569
|
hash.update(`
|
|
15369
15570
|
`);
|
|
15370
15571
|
}
|
|
15371
15572
|
}
|
|
15372
15573
|
};
|
|
15373
15574
|
for (const name of EDITOR_PLUGINS2) {
|
|
15374
|
-
walk(
|
|
15575
|
+
walk(join10(from, name), name);
|
|
15375
15576
|
}
|
|
15376
15577
|
return hash.digest("hex");
|
|
15377
15578
|
}
|
|
@@ -15386,14 +15587,14 @@ function shippedEditorDigest() {
|
|
|
15386
15587
|
return shippedDigest ?? undefined;
|
|
15387
15588
|
}
|
|
15388
15589
|
function shippedAddonsDirectory2() {
|
|
15389
|
-
return
|
|
15590
|
+
return join10(dirname5(fileURLToPath2(import.meta.url)), "godot", "addons");
|
|
15390
15591
|
}
|
|
15391
15592
|
function installedAddonVersion(projectPath) {
|
|
15392
|
-
const marker =
|
|
15593
|
+
const marker = join10(projectPath, "addons", ADDONS2[0], VERSION_MARKER2);
|
|
15393
15594
|
if (!existsSync8(marker)) {
|
|
15394
15595
|
return null;
|
|
15395
15596
|
}
|
|
15396
|
-
const said =
|
|
15597
|
+
const said = readFileSync9(marker, "utf8").trim();
|
|
15397
15598
|
return said === "" ? null : said;
|
|
15398
15599
|
}
|
|
15399
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;
|
|
@@ -31375,212 +31576,11 @@ var init_stdio2 = __esm(() => {
|
|
|
31375
31576
|
init_stdio();
|
|
31376
31577
|
});
|
|
31377
31578
|
|
|
31378
|
-
// src/process-children.ts
|
|
31379
|
-
import { execFile as execFile4, execFileSync, spawnSync as spawnSync2 } from "node:child_process";
|
|
31380
|
-
import { basename as basename2 } from "node:path";
|
|
31381
|
-
import { promisify as promisify4 } from "node:util";
|
|
31382
|
-
function startTimesOf(pids) {
|
|
31383
|
-
const began = new Map;
|
|
31384
|
-
if (pids.length === 0) {
|
|
31385
|
-
return began;
|
|
31386
|
-
}
|
|
31387
|
-
try {
|
|
31388
|
-
if (process.platform === "win32") {
|
|
31389
|
-
const filter = pids.map((pid) => `ProcessId=${pid}`).join(" OR ");
|
|
31390
|
-
const said = execFileSync("powershell", [
|
|
31391
|
-
"-NoProfile",
|
|
31392
|
-
"-NonInteractive",
|
|
31393
|
-
"-Command",
|
|
31394
|
-
`Get-CimInstance Win32_Process -Filter "${filter}" | ForEach-Object { "$($_.ProcessId) $(([DateTimeOffset]$_.CreationDate).ToUnixTimeMilliseconds())" }`
|
|
31395
|
-
], { encoding: "utf8", timeout: ASK_TIMEOUT_MS, windowsHide: true });
|
|
31396
|
-
for (const line of said.split(`
|
|
31397
|
-
`)) {
|
|
31398
|
-
const found = /^\s*(\d+)\s+(\d+)\s*$/.exec(line);
|
|
31399
|
-
if (found?.[1] !== undefined && found[2] !== undefined) {
|
|
31400
|
-
began.set(Number(found[1]), Number(found[2]));
|
|
31401
|
-
}
|
|
31402
|
-
}
|
|
31403
|
-
return began;
|
|
31404
|
-
}
|
|
31405
|
-
const said = spawnSync2("ps", ["-p", pids.join(","), "-o", "pid=,lstart="], {
|
|
31406
|
-
encoding: "utf8",
|
|
31407
|
-
timeout: ASK_TIMEOUT_MS,
|
|
31408
|
-
env: { ...process.env, LC_ALL: "C" }
|
|
31409
|
-
}).stdout;
|
|
31410
|
-
for (const line of said.split(`
|
|
31411
|
-
`)) {
|
|
31412
|
-
const found = /^\s*(\d+)\s+(.+)$/.exec(line);
|
|
31413
|
-
const at = Date.parse(found?.[2]?.trim() ?? "");
|
|
31414
|
-
if (found?.[1] !== undefined && !Number.isNaN(at)) {
|
|
31415
|
-
began.set(Number(found[1]), at);
|
|
31416
|
-
}
|
|
31417
|
-
}
|
|
31418
|
-
} catch {}
|
|
31419
|
-
return began;
|
|
31420
|
-
}
|
|
31421
|
-
async function processTree() {
|
|
31422
|
-
try {
|
|
31423
|
-
if (process.platform === "win32") {
|
|
31424
|
-
return parseProcessTable(await askWindows(), true);
|
|
31425
|
-
}
|
|
31426
|
-
return parseProcessTable(await askPosix());
|
|
31427
|
-
} catch {
|
|
31428
|
-
return;
|
|
31429
|
-
}
|
|
31430
|
-
}
|
|
31431
|
-
function parseProcessTable(printed, started = false) {
|
|
31432
|
-
const tree = new Map;
|
|
31433
|
-
for (const line of printed.split(/\r?\n/)) {
|
|
31434
|
-
const fields = /^\s*(\d+)\s+(\d+)(?:\s+(.*))?$/.exec(line);
|
|
31435
|
-
if (fields === null) {
|
|
31436
|
-
continue;
|
|
31437
|
-
}
|
|
31438
|
-
const pid = Number(fields[1]);
|
|
31439
|
-
const parent = Number(fields[2]);
|
|
31440
|
-
const rest = (fields[3] ?? "").trim();
|
|
31441
|
-
if (pid <= 0) {
|
|
31442
|
-
continue;
|
|
31443
|
-
}
|
|
31444
|
-
if (!started) {
|
|
31445
|
-
tree.set(pid, { parent, command: rest });
|
|
31446
|
-
continue;
|
|
31447
|
-
}
|
|
31448
|
-
const timed = /^(\d+)(?:\s+(.*))?$/.exec(rest);
|
|
31449
|
-
if (timed === null) {
|
|
31450
|
-
continue;
|
|
31451
|
-
}
|
|
31452
|
-
const began = Number(timed[1]);
|
|
31453
|
-
tree.set(pid, { parent, command: (timed[2] ?? "").trim(), ...began > 0 ? { startedAt: began } : {} });
|
|
31454
|
-
}
|
|
31455
|
-
return tree;
|
|
31456
|
-
}
|
|
31457
|
-
function linked(tree, child, parent) {
|
|
31458
|
-
const from = tree.get(child);
|
|
31459
|
-
const to = tree.get(parent);
|
|
31460
|
-
if (from === undefined || from.parent !== parent) {
|
|
31461
|
-
return false;
|
|
31462
|
-
}
|
|
31463
|
-
if (from.startedAt === undefined || to?.startedAt === undefined) {
|
|
31464
|
-
return true;
|
|
31465
|
-
}
|
|
31466
|
-
return from.startedAt >= to.startedAt;
|
|
31467
|
-
}
|
|
31468
|
-
function childrenIn(tree, pid) {
|
|
31469
|
-
const children = [];
|
|
31470
|
-
for (const child of tree.keys()) {
|
|
31471
|
-
if (linked(tree, child, pid)) {
|
|
31472
|
-
children.push(child);
|
|
31473
|
-
}
|
|
31474
|
-
}
|
|
31475
|
-
return children;
|
|
31476
|
-
}
|
|
31477
|
-
function descendantsIn(tree, pid) {
|
|
31478
|
-
const found = [];
|
|
31479
|
-
const queue = [pid];
|
|
31480
|
-
const seen = new Set([pid]);
|
|
31481
|
-
while (queue.length > 0) {
|
|
31482
|
-
const next = queue.shift();
|
|
31483
|
-
if (next === undefined) {
|
|
31484
|
-
break;
|
|
31485
|
-
}
|
|
31486
|
-
for (const child of childrenIn(tree, next)) {
|
|
31487
|
-
if (!seen.has(child)) {
|
|
31488
|
-
seen.add(child);
|
|
31489
|
-
found.push(child);
|
|
31490
|
-
queue.push(child);
|
|
31491
|
-
}
|
|
31492
|
-
}
|
|
31493
|
-
}
|
|
31494
|
-
return found;
|
|
31495
|
-
}
|
|
31496
|
-
function ancestorsIn(tree, pid, root) {
|
|
31497
|
-
const chain = [];
|
|
31498
|
-
const seen = new Set([pid]);
|
|
31499
|
-
let child = pid;
|
|
31500
|
-
let parent = tree.get(pid)?.parent;
|
|
31501
|
-
while (parent !== undefined && !seen.has(parent) && linked(tree, child, parent)) {
|
|
31502
|
-
chain.push(parent);
|
|
31503
|
-
if (parent === root) {
|
|
31504
|
-
break;
|
|
31505
|
-
}
|
|
31506
|
-
seen.add(parent);
|
|
31507
|
-
child = parent;
|
|
31508
|
-
parent = tree.get(parent)?.parent;
|
|
31509
|
-
}
|
|
31510
|
-
return chain;
|
|
31511
|
-
}
|
|
31512
|
-
function readCommandLine(command) {
|
|
31513
|
-
const words = commandWords(command);
|
|
31514
|
-
const executable = words[0] === undefined ? "" : basename2(words[0].replaceAll("\\", "/"));
|
|
31515
|
-
const at = words.indexOf("--path");
|
|
31516
|
-
let projectPath = null;
|
|
31517
|
-
if (at !== -1 && at + 1 < words.length) {
|
|
31518
|
-
const taken = [];
|
|
31519
|
-
for (const word of words.slice(at + 1)) {
|
|
31520
|
-
if (taken.length > 0 && (word.startsWith("-") || /^[a-z]+:\/\//.test(word))) {
|
|
31521
|
-
break;
|
|
31522
|
-
}
|
|
31523
|
-
taken.push(word);
|
|
31524
|
-
}
|
|
31525
|
-
projectPath = taken.join(" ");
|
|
31526
|
-
}
|
|
31527
|
-
return {
|
|
31528
|
-
executable,
|
|
31529
|
-
projectPath,
|
|
31530
|
-
editor: words.some((word) => word === "-e" || word === "--editor")
|
|
31531
|
-
};
|
|
31532
|
-
}
|
|
31533
|
-
function commandWords(command) {
|
|
31534
|
-
const words = [];
|
|
31535
|
-
let word = "";
|
|
31536
|
-
let quoted = false;
|
|
31537
|
-
let started = false;
|
|
31538
|
-
for (const char of command) {
|
|
31539
|
-
if (char === '"') {
|
|
31540
|
-
quoted = !quoted;
|
|
31541
|
-
started = true;
|
|
31542
|
-
} else if (!quoted && /\s/.test(char)) {
|
|
31543
|
-
if (started) {
|
|
31544
|
-
words.push(word);
|
|
31545
|
-
word = "";
|
|
31546
|
-
started = false;
|
|
31547
|
-
}
|
|
31548
|
-
} else {
|
|
31549
|
-
word += char;
|
|
31550
|
-
started = true;
|
|
31551
|
-
}
|
|
31552
|
-
}
|
|
31553
|
-
if (started) {
|
|
31554
|
-
words.push(word);
|
|
31555
|
-
}
|
|
31556
|
-
return words;
|
|
31557
|
-
}
|
|
31558
|
-
async function askWindows() {
|
|
31559
|
-
const { stdout } = await run4("powershell.exe", [
|
|
31560
|
-
"-NoProfile",
|
|
31561
|
-
"-NonInteractive",
|
|
31562
|
-
"-Command",
|
|
31563
|
-
`${POWERSHELL_UTF8}Get-CimInstance Win32_Process | ForEach-Object { $began = 0; if ($_.CreationDate) { $began = ([DateTimeOffset]$_.CreationDate).ToUnixTimeMilliseconds() }; "$($_.ProcessId) $($_.ParentProcessId) $began $($_.CommandLine)" }`
|
|
31564
|
-
], { timeout: ASK_TIMEOUT_MS, windowsHide: true, maxBuffer: 16 * 1024 * 1024 });
|
|
31565
|
-
return stdout;
|
|
31566
|
-
}
|
|
31567
|
-
async function askPosix() {
|
|
31568
|
-
const { stdout } = await run4("ps", ["-e", "-o", "pid=,ppid=,args="], {
|
|
31569
|
-
timeout: ASK_TIMEOUT_MS,
|
|
31570
|
-
maxBuffer: 16 * 1024 * 1024
|
|
31571
|
-
});
|
|
31572
|
-
return stdout;
|
|
31573
|
-
}
|
|
31574
|
-
var run4, POWERSHELL_UTF8 = "[Console]::OutputEncoding = [System.Text.Encoding]::UTF8; ", ASK_TIMEOUT_MS = 15000;
|
|
31575
|
-
var init_process_children = __esm(() => {
|
|
31576
|
-
run4 = promisify4(execFile4);
|
|
31577
|
-
});
|
|
31578
|
-
|
|
31579
31579
|
// src/runtime-client.ts
|
|
31580
|
-
import { existsSync as existsSync10, readdirSync as readdirSync6, readFileSync as
|
|
31580
|
+
import { existsSync as existsSync10, readdirSync as readdirSync6, readFileSync as readFileSync11, statSync as statSync5, unlinkSync } from "node:fs";
|
|
31581
31581
|
import { createConnection } from "node:net";
|
|
31582
31582
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
31583
|
-
import { dirname as
|
|
31583
|
+
import { dirname as dirname8, join as join13, resolve as resolve3 } from "node:path";
|
|
31584
31584
|
import { setTimeout as delay } from "node:timers/promises";
|
|
31585
31585
|
function runtimeDirectory(variables = process.env) {
|
|
31586
31586
|
const explicit = envValue("GDHARNESS_RUNTIME_DIR", variables);
|
|
@@ -31588,19 +31588,19 @@ function runtimeDirectory(variables = process.env) {
|
|
|
31588
31588
|
return explicit;
|
|
31589
31589
|
}
|
|
31590
31590
|
const perUser = envValue("XDG_RUNTIME_DIR", variables);
|
|
31591
|
-
return
|
|
31591
|
+
return join13(perUser ?? tmpdir4(), "gdharness");
|
|
31592
31592
|
}
|
|
31593
31593
|
function runtimeDirectories(variables = process.env) {
|
|
31594
31594
|
const candidates = [runtimeDirectory(variables)];
|
|
31595
31595
|
const fallbacks = process.platform === "win32" ? [envValue("SystemRoot", variables) ?? envValue("windir", variables) ?? "C:\\Windows"] : ["/tmp", "/var/tmp"];
|
|
31596
31596
|
for (const base of fallbacks) {
|
|
31597
|
-
candidates.push(
|
|
31597
|
+
candidates.push(join13(base, process.platform === "win32" ? "Temp" : "", "gdharness"));
|
|
31598
31598
|
}
|
|
31599
31599
|
return [...new Set(candidates.map((path) => resolve3(path)))];
|
|
31600
31600
|
}
|
|
31601
31601
|
function errorReportOf(pid, directories = runtimeDirectories()) {
|
|
31602
31602
|
for (const directory of directories) {
|
|
31603
|
-
const path =
|
|
31603
|
+
const path = join13(directory, `runtime-${pid}.log`);
|
|
31604
31604
|
if (existsSync10(path)) {
|
|
31605
31605
|
return path;
|
|
31606
31606
|
}
|
|
@@ -31618,7 +31618,7 @@ function processAlive(pid) {
|
|
|
31618
31618
|
function parseAnnouncement(file, pid) {
|
|
31619
31619
|
let fields;
|
|
31620
31620
|
try {
|
|
31621
|
-
fields = asParams(JSON.parse(
|
|
31621
|
+
fields = asParams(JSON.parse(readFileSync11(file, "utf8")));
|
|
31622
31622
|
} catch {
|
|
31623
31623
|
return { kind: "rubbish" };
|
|
31624
31624
|
}
|
|
@@ -31670,7 +31670,7 @@ function discoverRuntimes(directories = runtimeDirectories()) {
|
|
|
31670
31670
|
function wentAway(file, pid) {
|
|
31671
31671
|
let project = "";
|
|
31672
31672
|
try {
|
|
31673
|
-
const fields = asParams(JSON.parse(
|
|
31673
|
+
const fields = asParams(JSON.parse(readFileSync11(file, "utf8")));
|
|
31674
31674
|
project = readString(readParams(fields, "project") ?? {}, "path") ?? "";
|
|
31675
31675
|
} catch {
|
|
31676
31676
|
return;
|
|
@@ -31716,14 +31716,14 @@ function announcedIn(directory) {
|
|
|
31716
31716
|
for (const entry of readdirSync6(directory)) {
|
|
31717
31717
|
const report = ERROR_REPORT_PATTERN.exec(entry);
|
|
31718
31718
|
if (report) {
|
|
31719
|
-
sweepErrorReport(
|
|
31719
|
+
sweepErrorReport(join13(directory, entry), Number.parseInt(report[1] ?? "", 10));
|
|
31720
31720
|
continue;
|
|
31721
31721
|
}
|
|
31722
31722
|
const match = ANNOUNCEMENT_PATTERN.exec(entry);
|
|
31723
31723
|
if (!match) {
|
|
31724
31724
|
continue;
|
|
31725
31725
|
}
|
|
31726
|
-
const file =
|
|
31726
|
+
const file = join13(directory, entry);
|
|
31727
31727
|
const pid = Number.parseInt(match[1] ?? "", 10);
|
|
31728
31728
|
const alive = processAlive(pid);
|
|
31729
31729
|
entries.push({ file, pid, alive });
|
|
@@ -31751,16 +31751,16 @@ function announcedIn(directory) {
|
|
|
31751
31751
|
found.push(announced);
|
|
31752
31752
|
}
|
|
31753
31753
|
const held = new Set(entries.map((one) => one.file));
|
|
31754
|
-
const here =
|
|
31754
|
+
const here = join13(directory, ".");
|
|
31755
31755
|
for (const file of verdicts.keys()) {
|
|
31756
|
-
if (
|
|
31756
|
+
if (dirname8(file) === here && !held.has(file)) {
|
|
31757
31757
|
verdicts.delete(file);
|
|
31758
31758
|
}
|
|
31759
31759
|
}
|
|
31760
31760
|
return found;
|
|
31761
31761
|
}
|
|
31762
31762
|
async function announcementEnded(pid, directories = runtimeDirectories()) {
|
|
31763
|
-
const files = directories.map((directory) =>
|
|
31763
|
+
const files = directories.map((directory) => join13(directory, `runtime-${pid}.json`)).filter((file) => existsSync10(file));
|
|
31764
31764
|
const first = files[0];
|
|
31765
31765
|
if (first === undefined) {
|
|
31766
31766
|
return;
|
|
@@ -32009,14 +32009,14 @@ var init_runtime_client = __esm(() => {
|
|
|
32009
32009
|
});
|
|
32010
32010
|
|
|
32011
32011
|
// src/boot-note.ts
|
|
32012
|
-
import { mkdirSync as
|
|
32013
|
-
import { dirname as
|
|
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";
|
|
32014
32014
|
function bootNotePath(projectPath) {
|
|
32015
|
-
return
|
|
32015
|
+
return join14(projectPath, ".godot", "gdharness-boot.json");
|
|
32016
32016
|
}
|
|
32017
32017
|
function readBootNote(projectPath) {
|
|
32018
32018
|
try {
|
|
32019
|
-
const noted = readNumber(asParams(JSON.parse(
|
|
32019
|
+
const noted = readNumber(asParams(JSON.parse(readFileSync12(bootNotePath(projectPath), "utf8"))), "announcedAfterMs");
|
|
32020
32020
|
return noted !== undefined && Number.isFinite(noted) && noted > 0 ? noted : null;
|
|
32021
32021
|
} catch {
|
|
32022
32022
|
return null;
|
|
@@ -32025,8 +32025,8 @@ function readBootNote(projectPath) {
|
|
|
32025
32025
|
function writeBootNote(projectPath, announcedAfterMs) {
|
|
32026
32026
|
try {
|
|
32027
32027
|
const path = bootNotePath(projectPath);
|
|
32028
|
-
|
|
32029
|
-
|
|
32028
|
+
mkdirSync7(dirname9(path), { recursive: true });
|
|
32029
|
+
writeFileSync9(path, JSON.stringify({ announcedAfterMs: Math.round(announcedAfterMs), at: new Date().toISOString() }, null, 2), "utf8");
|
|
32030
32030
|
} catch {}
|
|
32031
32031
|
}
|
|
32032
32032
|
function halfAsLongAgain(lastBootMs) {
|
|
@@ -32044,10 +32044,10 @@ var init_boot_note = __esm(() => {
|
|
|
32044
32044
|
});
|
|
32045
32045
|
|
|
32046
32046
|
// src/breakpoint-note.ts
|
|
32047
|
-
import { mkdirSync as
|
|
32048
|
-
import { dirname as
|
|
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";
|
|
32049
32049
|
function breakpointNotePath(projectPath) {
|
|
32050
|
-
return
|
|
32050
|
+
return join15(projectPath, ".godot", "gdharness-breakpoints.json");
|
|
32051
32051
|
}
|
|
32052
32052
|
function writeBreakpointNote(projectPath, held) {
|
|
32053
32053
|
const path = breakpointNotePath(projectPath);
|
|
@@ -32055,7 +32055,7 @@ function writeBreakpointNote(projectPath, held) {
|
|
|
32055
32055
|
const inside = held.map((one) => ({ scriptPath: realPathOr(one.scriptPath), lines: one.lines })).filter((one) => one.lines.length > 0 && withinProject(root, one.scriptPath));
|
|
32056
32056
|
try {
|
|
32057
32057
|
if (inside.length === 0) {
|
|
32058
|
-
|
|
32058
|
+
rmSync7(path, { force: true });
|
|
32059
32059
|
return;
|
|
32060
32060
|
}
|
|
32061
32061
|
const note = {
|
|
@@ -32064,8 +32064,8 @@ function writeBreakpointNote(projectPath, held) {
|
|
|
32064
32064
|
lines: [...one.lines]
|
|
32065
32065
|
}))
|
|
32066
32066
|
};
|
|
32067
|
-
|
|
32068
|
-
|
|
32067
|
+
mkdirSync8(dirname10(path), { recursive: true });
|
|
32068
|
+
writeFileSync10(path, `${JSON.stringify(note, null, 2)}
|
|
32069
32069
|
`, "utf8");
|
|
32070
32070
|
} catch (error) {
|
|
32071
32071
|
console.error(`[SERVER] Could not record the breakpoints at ${path}: ${errorMessage(error)}`);
|
|
@@ -32073,7 +32073,7 @@ function writeBreakpointNote(projectPath, held) {
|
|
|
32073
32073
|
}
|
|
32074
32074
|
function readBreakpointNote(projectPath) {
|
|
32075
32075
|
try {
|
|
32076
|
-
const parsed = JSON.parse(
|
|
32076
|
+
const parsed = JSON.parse(readFileSync13(breakpointNotePath(projectPath), "utf8"));
|
|
32077
32077
|
if (typeof parsed !== "object" || parsed === null || !Array.isArray(parsed.breakpoints)) {
|
|
32078
32078
|
return [];
|
|
32079
32079
|
}
|
|
@@ -32088,7 +32088,7 @@ function readBreakpointNote(projectPath) {
|
|
|
32088
32088
|
}
|
|
32089
32089
|
const lines = Array.isArray(listed) ? listed.filter((line) => Number.isInteger(line) && line > 0) : [];
|
|
32090
32090
|
if (lines.length > 0) {
|
|
32091
|
-
held.push({ scriptPath: realPathOr(
|
|
32091
|
+
held.push({ scriptPath: realPathOr(join15(projectPath, script)), lines });
|
|
32092
32092
|
}
|
|
32093
32093
|
}
|
|
32094
32094
|
return held;
|
|
@@ -32106,14 +32106,14 @@ var init_breakpoint_note = __esm(() => {
|
|
|
32106
32106
|
});
|
|
32107
32107
|
|
|
32108
32108
|
// src/bridge-announce.ts
|
|
32109
|
-
import { mkdirSync as
|
|
32110
|
-
import { dirname as
|
|
32111
|
-
function
|
|
32112
|
-
return
|
|
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");
|
|
32113
32113
|
}
|
|
32114
|
-
function
|
|
32114
|
+
function readAnnouncement2(path) {
|
|
32115
32115
|
try {
|
|
32116
|
-
const parsed = JSON.parse(
|
|
32116
|
+
const parsed = JSON.parse(readFileSync14(path, "utf8"));
|
|
32117
32117
|
if (typeof parsed !== "object" || parsed === null) {
|
|
32118
32118
|
return null;
|
|
32119
32119
|
}
|
|
@@ -32127,12 +32127,12 @@ function readAnnouncement(path) {
|
|
|
32127
32127
|
}
|
|
32128
32128
|
}
|
|
32129
32129
|
function announcementIsOurs(path) {
|
|
32130
|
-
return
|
|
32130
|
+
return readAnnouncement2(path)?.pid === process.pid;
|
|
32131
32131
|
}
|
|
32132
32132
|
function announceBridge(projectPath, bridge) {
|
|
32133
|
-
const path =
|
|
32133
|
+
const path = announcementPath2(projectPath);
|
|
32134
32134
|
try {
|
|
32135
|
-
|
|
32135
|
+
mkdirSync9(dirname11(path), { recursive: true });
|
|
32136
32136
|
const announcement = {
|
|
32137
32137
|
protocol: BRIDGE_ANNOUNCE_PROTOCOL,
|
|
32138
32138
|
host: bridge.host,
|
|
@@ -32141,7 +32141,7 @@ function announceBridge(projectPath, bridge) {
|
|
|
32141
32141
|
version: bridge.version,
|
|
32142
32142
|
startedAt: new Date().toISOString()
|
|
32143
32143
|
};
|
|
32144
|
-
|
|
32144
|
+
writeFileSync11(path, `${JSON.stringify(announcement, null, 2)}
|
|
32145
32145
|
`, "utf8");
|
|
32146
32146
|
return path;
|
|
32147
32147
|
} catch (error) {
|
|
@@ -32154,7 +32154,7 @@ function withdrawBridge(path) {
|
|
|
32154
32154
|
return;
|
|
32155
32155
|
}
|
|
32156
32156
|
try {
|
|
32157
|
-
|
|
32157
|
+
rmSync8(path, { force: true });
|
|
32158
32158
|
} catch (error) {
|
|
32159
32159
|
console.error(`[SERVER] Could not withdraw the editor bridge announcement: ${errorMessage(error)}`);
|
|
32160
32160
|
}
|
|
@@ -32162,17 +32162,18 @@ function withdrawBridge(path) {
|
|
|
32162
32162
|
var BRIDGE_ANNOUNCE_PROTOCOL = 1;
|
|
32163
32163
|
var init_bridge_announce = __esm(() => {
|
|
32164
32164
|
init_errors();
|
|
32165
|
+
init_process_children();
|
|
32165
32166
|
});
|
|
32166
32167
|
|
|
32167
32168
|
// src/class-note.ts
|
|
32168
|
-
import { mkdirSync as
|
|
32169
|
-
import { dirname as
|
|
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";
|
|
32170
32171
|
function classNotePath(projectPath) {
|
|
32171
|
-
return
|
|
32172
|
+
return join17(projectPath, ".godot", "gdharness-classes.json");
|
|
32172
32173
|
}
|
|
32173
32174
|
function readClassNote(projectPath) {
|
|
32174
32175
|
try {
|
|
32175
|
-
const fields = asParams(JSON.parse(
|
|
32176
|
+
const fields = asParams(JSON.parse(readFileSync15(classNotePath(projectPath), "utf8")));
|
|
32176
32177
|
const rebuiltWith = readArray(fields, "rebuiltWith");
|
|
32177
32178
|
if (rebuiltWith === undefined || !rebuiltWith.every((name) => typeof name === "string")) {
|
|
32178
32179
|
return null;
|
|
@@ -32189,24 +32190,24 @@ function readClassNote(projectPath) {
|
|
|
32189
32190
|
function writeClassNote(projectPath, note) {
|
|
32190
32191
|
try {
|
|
32191
32192
|
const path = classNotePath(projectPath);
|
|
32192
|
-
|
|
32193
|
-
|
|
32193
|
+
mkdirSync10(dirname12(path), { recursive: true });
|
|
32194
|
+
writeFileSync12(path, `${JSON.stringify(note, null, 2)}
|
|
32194
32195
|
`, "utf8");
|
|
32195
32196
|
} catch {}
|
|
32196
32197
|
}
|
|
32197
32198
|
var init_class_note = () => {};
|
|
32198
32199
|
|
|
32199
32200
|
// src/config-pin.ts
|
|
32200
|
-
import { existsSync as existsSync11, readFileSync as
|
|
32201
|
-
import { join as
|
|
32201
|
+
import { existsSync as existsSync11, readFileSync as readFileSync16 } from "node:fs";
|
|
32202
|
+
import { join as join18 } from "node:path";
|
|
32202
32203
|
function versionsNamedInConfig(projectPath) {
|
|
32203
|
-
const config =
|
|
32204
|
+
const config = join18(projectPath, ".mcp.json");
|
|
32204
32205
|
if (!existsSync11(config)) {
|
|
32205
32206
|
return [];
|
|
32206
32207
|
}
|
|
32207
32208
|
let text;
|
|
32208
32209
|
try {
|
|
32209
|
-
text =
|
|
32210
|
+
text = readFileSync16(config, "utf8");
|
|
32210
32211
|
JSON.parse(text);
|
|
32211
32212
|
} catch {
|
|
32212
32213
|
return [];
|
|
@@ -32314,7 +32315,7 @@ function portFromEnvOrNull(variable) {
|
|
|
32314
32315
|
}
|
|
32315
32316
|
const parsed = Number.parseInt(raw, 10);
|
|
32316
32317
|
if (!Number.isInteger(parsed) || parsed < 1 || parsed > 65535 || String(parsed) !== raw) {
|
|
32317
|
-
throw new
|
|
32318
|
+
throw new Refusal(`${variable} is "${raw}", not a port between 1 and 65535.`);
|
|
32318
32319
|
}
|
|
32319
32320
|
return parsed;
|
|
32320
32321
|
}
|
|
@@ -32328,7 +32329,7 @@ async function freePort(preferred, host = "127.0.0.1") {
|
|
|
32328
32329
|
}
|
|
32329
32330
|
const spare = await bindable(0, host);
|
|
32330
32331
|
if (spare === null) {
|
|
32331
|
-
throw new
|
|
32332
|
+
throw new Refusal(`No port could be bound on ${host}.`);
|
|
32332
32333
|
}
|
|
32333
32334
|
return spare;
|
|
32334
32335
|
}
|
|
@@ -32478,7 +32479,7 @@ class GodotDAPClient {
|
|
|
32478
32479
|
async sendRequest(command, args, timeoutMs = DAP_REQUEST_TIMEOUT_MS) {
|
|
32479
32480
|
await this.ensureConnected();
|
|
32480
32481
|
if (!this.socket) {
|
|
32481
|
-
throw new
|
|
32482
|
+
throw new Refusal("DAP socket is not available");
|
|
32482
32483
|
}
|
|
32483
32484
|
const requestSeq = this.seq++;
|
|
32484
32485
|
const request = {
|
|
@@ -32888,7 +32889,7 @@ async function handleDAPTool(client, toolName, args) {
|
|
|
32888
32889
|
}
|
|
32889
32890
|
case "dap_set_breakpoint": {
|
|
32890
32891
|
if (typeof safeArgs.scriptPath !== "string" || typeof safeArgs.line !== "number") {
|
|
32891
|
-
throw new
|
|
32892
|
+
throw new Refusal("dap_set_breakpoint requires { scriptPath: string, line: number }");
|
|
32892
32893
|
}
|
|
32893
32894
|
const result = await client.setBreakpoint(safeArgs.scriptPath, safeArgs.line);
|
|
32894
32895
|
return {
|
|
@@ -32897,7 +32898,7 @@ async function handleDAPTool(client, toolName, args) {
|
|
|
32897
32898
|
}
|
|
32898
32899
|
case "dap_remove_breakpoint": {
|
|
32899
32900
|
if (typeof safeArgs.scriptPath !== "string" || typeof safeArgs.line !== "number") {
|
|
32900
|
-
throw new
|
|
32901
|
+
throw new Refusal("dap_remove_breakpoint requires { scriptPath: string, line: number }");
|
|
32901
32902
|
}
|
|
32902
32903
|
const result = await client.removeBreakpoint(safeArgs.scriptPath, safeArgs.line);
|
|
32903
32904
|
return {
|
|
@@ -32960,24 +32961,24 @@ var init_dap_client = __esm(() => {
|
|
|
32960
32961
|
import {
|
|
32961
32962
|
closeSync,
|
|
32962
32963
|
existsSync as existsSync12,
|
|
32963
|
-
mkdirSync as
|
|
32964
|
+
mkdirSync as mkdirSync11,
|
|
32964
32965
|
openSync,
|
|
32965
|
-
readFileSync as
|
|
32966
|
+
readFileSync as readFileSync17,
|
|
32966
32967
|
readSync,
|
|
32967
|
-
rmSync as
|
|
32968
|
+
rmSync as rmSync9,
|
|
32968
32969
|
statSync as statSync6,
|
|
32969
|
-
writeFileSync as
|
|
32970
|
+
writeFileSync as writeFileSync13
|
|
32970
32971
|
} from "node:fs";
|
|
32971
|
-
import { dirname as
|
|
32972
|
+
import { dirname as dirname13, join as join19 } from "node:path";
|
|
32972
32973
|
function editorLogPath(projectPath) {
|
|
32973
|
-
return
|
|
32974
|
+
return join19(projectPath, ".godot", "gdharness-editor.log");
|
|
32974
32975
|
}
|
|
32975
32976
|
function editorLogNotePath(projectPath) {
|
|
32976
|
-
return
|
|
32977
|
+
return join19(projectPath, ".godot", "gdharness-editor.json");
|
|
32977
32978
|
}
|
|
32978
32979
|
function readEditorLogNote(projectPath) {
|
|
32979
32980
|
try {
|
|
32980
|
-
const fields = asParams(JSON.parse(
|
|
32981
|
+
const fields = asParams(JSON.parse(readFileSync17(editorLogNotePath(projectPath), "utf8")));
|
|
32981
32982
|
const pid = readNumber(fields, "pid");
|
|
32982
32983
|
const startedAt = readString(fields, "startedAt");
|
|
32983
32984
|
return pid !== undefined && Number.isInteger(pid) && pid > 0 && startedAt !== undefined ? { pid, startedAt } : null;
|
|
@@ -32988,16 +32989,16 @@ function readEditorLogNote(projectPath) {
|
|
|
32988
32989
|
function clearEditorLog(projectPath) {
|
|
32989
32990
|
try {
|
|
32990
32991
|
const path = editorLogPath(projectPath);
|
|
32991
|
-
|
|
32992
|
-
|
|
32993
|
-
|
|
32992
|
+
mkdirSync11(dirname13(path), { recursive: true });
|
|
32993
|
+
rmSync9(path, { force: true });
|
|
32994
|
+
rmSync9(editorLogNotePath(projectPath), { force: true });
|
|
32994
32995
|
} catch {}
|
|
32995
32996
|
}
|
|
32996
32997
|
function writeEditorLogNote(projectPath, pid) {
|
|
32997
32998
|
try {
|
|
32998
32999
|
const path = editorLogNotePath(projectPath);
|
|
32999
|
-
|
|
33000
|
-
|
|
33000
|
+
mkdirSync11(dirname13(path), { recursive: true });
|
|
33001
|
+
writeFileSync13(path, JSON.stringify({ pid, startedAt: new Date().toISOString() }, null, 2), "utf8");
|
|
33001
33002
|
} catch {}
|
|
33002
33003
|
}
|
|
33003
33004
|
function theConsoleWasNotCaptured(why, projectPath) {
|
|
@@ -33030,7 +33031,7 @@ function editorConsole(projectPath, editorPid, openedByAServer) {
|
|
|
33030
33031
|
function theFirstOf(path) {
|
|
33031
33032
|
const whole = statSync6(path).size;
|
|
33032
33033
|
if (whole <= MOST_CONSOLE_BYTES) {
|
|
33033
|
-
return { text:
|
|
33034
|
+
return { text: readFileSync17(path, "utf8") };
|
|
33034
33035
|
}
|
|
33035
33036
|
const handle = openSync(path, "r");
|
|
33036
33037
|
try {
|
|
@@ -36448,7 +36449,7 @@ var init_godot_bridge = __esm(() => {
|
|
|
36448
36449
|
}
|
|
36449
36450
|
sendMessage(message) {
|
|
36450
36451
|
if (this.socket?.readyState !== import_websocket.default.OPEN) {
|
|
36451
|
-
throw new
|
|
36452
|
+
throw new Refusal("Godot is not connected");
|
|
36452
36453
|
}
|
|
36453
36454
|
this.socket.send(JSON.stringify(message));
|
|
36454
36455
|
}
|
|
@@ -36998,7 +36999,7 @@ var init_junit = __esm(() => {
|
|
|
36998
36999
|
import { realpathSync as realpathSync2 } from "node:fs";
|
|
36999
37000
|
import { readFile, realpath } from "node:fs/promises";
|
|
37000
37001
|
import { createConnection as createConnection3 } from "node:net";
|
|
37001
|
-
import { dirname as
|
|
37002
|
+
import { dirname as dirname14, resolve as resolve4 } from "node:path";
|
|
37002
37003
|
import { fileURLToPath as fileURLToPath4, pathToFileURL } from "node:url";
|
|
37003
37004
|
function diagnosticsKey(uri) {
|
|
37004
37005
|
let decoded;
|
|
@@ -37126,13 +37127,13 @@ class GodotLSPClient {
|
|
|
37126
37127
|
if (this.initialized) {
|
|
37127
37128
|
return;
|
|
37128
37129
|
}
|
|
37129
|
-
const rootPath = this.rootPath ??
|
|
37130
|
+
const rootPath = this.rootPath ?? dirname14(resolve4(filePath));
|
|
37130
37131
|
await this.initialize(rootPath);
|
|
37131
37132
|
}
|
|
37132
37133
|
async sendRequest(method, params) {
|
|
37133
37134
|
await this.ensureConnected();
|
|
37134
37135
|
if (!this.socket) {
|
|
37135
|
-
throw new
|
|
37136
|
+
throw new Refusal("Not connected to Godot LSP");
|
|
37136
37137
|
}
|
|
37137
37138
|
this.requestId += 1;
|
|
37138
37139
|
const id = this.requestId;
|
|
@@ -37171,7 +37172,7 @@ class GodotLSPClient {
|
|
|
37171
37172
|
}
|
|
37172
37173
|
sendNotification(method, params) {
|
|
37173
37174
|
if (!this.connected || !this.socket) {
|
|
37174
|
-
throw new
|
|
37175
|
+
throw new Refusal("Not connected to Godot LSP");
|
|
37175
37176
|
}
|
|
37176
37177
|
const payload = {
|
|
37177
37178
|
jsonrpc: "2.0",
|
|
@@ -37453,36 +37454,36 @@ async function resolveLSPPaths(projectPathValue, scriptPathValue) {
|
|
|
37453
37454
|
try {
|
|
37454
37455
|
projectPath = await realpath(requestedProjectPath);
|
|
37455
37456
|
} catch {
|
|
37456
|
-
throw new
|
|
37457
|
+
throw new Refusal(`Project path does not exist: ${requestedProjectPath}`);
|
|
37457
37458
|
}
|
|
37458
37459
|
const contained = resolveWithinProject(projectPath, scriptPathValue);
|
|
37459
37460
|
if (!contained.ok) {
|
|
37460
|
-
throw new
|
|
37461
|
+
throw new Refusal(contained.reason);
|
|
37461
37462
|
}
|
|
37462
37463
|
let scriptPath;
|
|
37463
37464
|
try {
|
|
37464
37465
|
scriptPath = await realpath(contained.absolutePath);
|
|
37465
37466
|
} catch {
|
|
37466
|
-
throw new
|
|
37467
|
+
throw new Refusal(`Script file does not exist: ${contained.absolutePath}`);
|
|
37467
37468
|
}
|
|
37468
37469
|
if (!isWithinRoot(projectPath, scriptPath)) {
|
|
37469
|
-
throw new
|
|
37470
|
+
throw new Refusal("scriptPath resolves outside the project root boundary.");
|
|
37470
37471
|
}
|
|
37471
37472
|
return { projectPath, scriptPath };
|
|
37472
37473
|
}
|
|
37473
37474
|
async function handleLSPTool(client, toolName, args) {
|
|
37474
37475
|
try {
|
|
37475
37476
|
if (!args || typeof args !== "object") {
|
|
37476
|
-
throw new
|
|
37477
|
+
throw new Refusal("Tool arguments must be an object.");
|
|
37477
37478
|
}
|
|
37478
37479
|
const parsedArgs = args;
|
|
37479
37480
|
const projectPathValue = parsedArgs["projectPath"];
|
|
37480
37481
|
const scriptPathValue = parsedArgs["scriptPath"];
|
|
37481
37482
|
if (typeof projectPathValue !== "string" || projectPathValue.length === 0) {
|
|
37482
|
-
throw new
|
|
37483
|
+
throw new Refusal("Missing required argument: projectPath");
|
|
37483
37484
|
}
|
|
37484
37485
|
if (typeof scriptPathValue !== "string" || scriptPathValue.length === 0) {
|
|
37485
|
-
throw new
|
|
37486
|
+
throw new Refusal("Missing required argument: scriptPath");
|
|
37486
37487
|
}
|
|
37487
37488
|
const { projectPath, scriptPath } = await resolveLSPPaths(projectPathValue, scriptPathValue);
|
|
37488
37489
|
const content = await readFile(scriptPath, "utf8");
|
|
@@ -37496,7 +37497,7 @@ async function handleLSPTool(client, toolName, args) {
|
|
|
37496
37497
|
const line = Number(parsedArgs["line"]);
|
|
37497
37498
|
const character = Number(parsedArgs["character"]);
|
|
37498
37499
|
if (!Number.isFinite(line) || !Number.isFinite(character)) {
|
|
37499
|
-
throw new
|
|
37500
|
+
throw new Refusal("Arguments line and character must be numbers.");
|
|
37500
37501
|
}
|
|
37501
37502
|
const completions = await client.getCompletions(scriptPath, content, line, character);
|
|
37502
37503
|
return asToolResponse({ completions });
|
|
@@ -37505,7 +37506,7 @@ async function handleLSPTool(client, toolName, args) {
|
|
|
37505
37506
|
const line = Number(parsedArgs["line"]);
|
|
37506
37507
|
const character = Number(parsedArgs["character"]);
|
|
37507
37508
|
if (!Number.isFinite(line) || !Number.isFinite(character)) {
|
|
37508
|
-
throw new
|
|
37509
|
+
throw new Refusal("Arguments line and character must be numbers.");
|
|
37509
37510
|
}
|
|
37510
37511
|
const hover = await client.getHover(scriptPath, content, line, character);
|
|
37511
37512
|
return asToolResponse({ hover });
|
|
@@ -37571,13 +37572,13 @@ var init_process_time = __esm(() => {
|
|
|
37571
37572
|
});
|
|
37572
37573
|
|
|
37573
37574
|
// src/project-scan.ts
|
|
37574
|
-
import { existsSync as existsSync14, readdirSync as readdirSync7, readFileSync as
|
|
37575
|
-
import { join as
|
|
37575
|
+
import { existsSync as existsSync14, readdirSync as readdirSync7, readFileSync as readFileSync18 } from "node:fs";
|
|
37576
|
+
import { join as join20 } from "node:path";
|
|
37576
37577
|
function skipped(name) {
|
|
37577
37578
|
return name.startsWith(".") || SKIPPED.has(name);
|
|
37578
37579
|
}
|
|
37579
37580
|
function steppedOver(directory) {
|
|
37580
|
-
return existsSync14(
|
|
37581
|
+
return existsSync14(join20(directory, ".gdignore"));
|
|
37581
37582
|
}
|
|
37582
37583
|
function projectStructure(projectPath) {
|
|
37583
37584
|
const structure = { scenes: 0, scripts: 0, assets: 0, other: 0 };
|
|
@@ -37590,7 +37591,7 @@ function projectStructure(projectPath) {
|
|
|
37590
37591
|
continue;
|
|
37591
37592
|
}
|
|
37592
37593
|
if (entry.isDirectory()) {
|
|
37593
|
-
visit(
|
|
37594
|
+
visit(join20(directory, entry.name));
|
|
37594
37595
|
} else if (entry.isFile()) {
|
|
37595
37596
|
const extension = entry.name.split(".").pop()?.toLowerCase() ?? "";
|
|
37596
37597
|
if (extension === "tscn") {
|
|
@@ -37620,14 +37621,14 @@ function scriptsWithoutUid(projectPath) {
|
|
|
37620
37621
|
}
|
|
37621
37622
|
const spelled = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
|
|
37622
37623
|
if (entry.isDirectory()) {
|
|
37623
|
-
visit(
|
|
37624
|
+
visit(join20(directory, entry.name), spelled);
|
|
37624
37625
|
continue;
|
|
37625
37626
|
}
|
|
37626
37627
|
if (!entry.isFile()) {
|
|
37627
37628
|
continue;
|
|
37628
37629
|
}
|
|
37629
37630
|
const extension = entry.name.split(".").pop()?.toLowerCase() ?? "";
|
|
37630
|
-
if (CARRIES_A_UID.has(extension) && !existsSync14(
|
|
37631
|
+
if (CARRIES_A_UID.has(extension) && !existsSync14(join20(directory, `${entry.name}.uid`))) {
|
|
37631
37632
|
found.push(spelled);
|
|
37632
37633
|
}
|
|
37633
37634
|
}
|
|
@@ -37662,7 +37663,7 @@ function searchProject(projectPath, options) {
|
|
|
37662
37663
|
if (skipped(entry.name)) {
|
|
37663
37664
|
continue;
|
|
37664
37665
|
}
|
|
37665
|
-
const entryPath =
|
|
37666
|
+
const entryPath = join20(directory, entry.name);
|
|
37666
37667
|
if (entry.isDirectory()) {
|
|
37667
37668
|
visit(entryPath);
|
|
37668
37669
|
continue;
|
|
@@ -37673,7 +37674,7 @@ function searchProject(projectPath, options) {
|
|
|
37673
37674
|
}
|
|
37674
37675
|
result.summary.files_searched += 1;
|
|
37675
37676
|
const matches = [];
|
|
37676
|
-
for (const [index, line] of
|
|
37677
|
+
for (const [index, line] of readFileSync18(entryPath, "utf8").split(`
|
|
37677
37678
|
`).entries()) {
|
|
37678
37679
|
if (full()) {
|
|
37679
37680
|
break;
|
|
@@ -37702,16 +37703,16 @@ var init_project_scan = __esm(() => {
|
|
|
37702
37703
|
});
|
|
37703
37704
|
|
|
37704
37705
|
// src/restart-note.ts
|
|
37705
|
-
import { mkdirSync as
|
|
37706
|
-
import { dirname as
|
|
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";
|
|
37707
37708
|
function restartNotePath(projectPath) {
|
|
37708
|
-
return
|
|
37709
|
+
return join21(projectPath, ".godot", "gdharness-restart.json");
|
|
37709
37710
|
}
|
|
37710
37711
|
function noteRestartBegun(note) {
|
|
37711
37712
|
const path = restartNotePath(note.projectPath);
|
|
37712
37713
|
try {
|
|
37713
|
-
|
|
37714
|
-
|
|
37714
|
+
mkdirSync12(dirname15(path), { recursive: true });
|
|
37715
|
+
writeFileSync14(path, `${JSON.stringify(note, null, 2)}
|
|
37715
37716
|
`, "utf8");
|
|
37716
37717
|
} catch (error) {
|
|
37717
37718
|
console.error(`[SERVER] Could not record the restart at ${path}: ${errorMessage(error)}`);
|
|
@@ -37719,7 +37720,7 @@ function noteRestartBegun(note) {
|
|
|
37719
37720
|
}
|
|
37720
37721
|
function restartOwed(projectPath) {
|
|
37721
37722
|
try {
|
|
37722
|
-
const parsed = JSON.parse(
|
|
37723
|
+
const parsed = JSON.parse(readFileSync19(restartNotePath(projectPath), "utf8"));
|
|
37723
37724
|
if (typeof parsed !== "object" || parsed === null) {
|
|
37724
37725
|
return null;
|
|
37725
37726
|
}
|
|
@@ -37734,7 +37735,7 @@ function restartOwed(projectPath) {
|
|
|
37734
37735
|
}
|
|
37735
37736
|
function restartSettled(projectPath) {
|
|
37736
37737
|
try {
|
|
37737
|
-
|
|
37738
|
+
rmSync10(restartNotePath(projectPath), { force: true });
|
|
37738
37739
|
} catch (error) {
|
|
37739
37740
|
console.error(`[SERVER] Could not clear the restart note: ${errorMessage(error)}`);
|
|
37740
37741
|
}
|
|
@@ -37745,36 +37746,36 @@ var init_restart_note = __esm(() => {
|
|
|
37745
37746
|
|
|
37746
37747
|
// src/run-record.ts
|
|
37747
37748
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
37748
|
-
import { mkdirSync as
|
|
37749
|
-
import { basename as basename3, join as
|
|
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";
|
|
37750
37751
|
function runsDirectory() {
|
|
37751
|
-
return
|
|
37752
|
+
return join22(runtimeDirectory(), "runs");
|
|
37752
37753
|
}
|
|
37753
37754
|
function recordPath() {
|
|
37754
|
-
return
|
|
37755
|
+
return join22(runsDirectory(), "run.json");
|
|
37755
37756
|
}
|
|
37756
37757
|
function recordPaths() {
|
|
37757
|
-
return runtimeDirectories().map((directory) =>
|
|
37758
|
+
return runtimeDirectories().map((directory) => join22(directory, "runs", "run.json"));
|
|
37758
37759
|
}
|
|
37759
37760
|
function openTranscript(startedAt) {
|
|
37760
|
-
|
|
37761
|
-
const path =
|
|
37761
|
+
mkdirSync13(runsDirectory(), { recursive: true });
|
|
37762
|
+
const path = join22(runsDirectory(), `run-${startedAt}.log`);
|
|
37762
37763
|
return { path, fd: openSync2(path, "a") };
|
|
37763
37764
|
}
|
|
37764
37765
|
function writeRunRecord(record) {
|
|
37765
|
-
|
|
37766
|
-
|
|
37766
|
+
mkdirSync13(runsDirectory(), { recursive: true });
|
|
37767
|
+
writeFileSync15(recordPath(), JSON.stringify(record, null, 2), "utf8");
|
|
37767
37768
|
}
|
|
37768
37769
|
function editorNotePath() {
|
|
37769
|
-
return
|
|
37770
|
+
return join22(runsDirectory(), "editor-run.json");
|
|
37770
37771
|
}
|
|
37771
37772
|
function writeEditorRunNote(note) {
|
|
37772
|
-
|
|
37773
|
-
|
|
37773
|
+
mkdirSync13(runsDirectory(), { recursive: true });
|
|
37774
|
+
writeFileSync15(editorNotePath(), JSON.stringify(note, null, 2), "utf8");
|
|
37774
37775
|
}
|
|
37775
37776
|
function readEditorRunNote() {
|
|
37776
37777
|
for (const directory of runtimeDirectories()) {
|
|
37777
|
-
const note = editorNoteAt(
|
|
37778
|
+
const note = editorNoteAt(join22(directory, "runs", "editor-run.json"));
|
|
37778
37779
|
if (note !== null) {
|
|
37779
37780
|
return note;
|
|
37780
37781
|
}
|
|
@@ -37784,7 +37785,7 @@ function readEditorRunNote() {
|
|
|
37784
37785
|
function editorNoteAt(path) {
|
|
37785
37786
|
let parsed;
|
|
37786
37787
|
try {
|
|
37787
|
-
parsed = JSON.parse(
|
|
37788
|
+
parsed = JSON.parse(readFileSync20(path, "utf8"));
|
|
37788
37789
|
} catch {
|
|
37789
37790
|
return null;
|
|
37790
37791
|
}
|
|
@@ -37807,7 +37808,7 @@ function recordRunEnded(pid, exitCode) {
|
|
|
37807
37808
|
return;
|
|
37808
37809
|
}
|
|
37809
37810
|
try {
|
|
37810
|
-
|
|
37811
|
+
writeFileSync15(path, JSON.stringify({ ...record, exitCode }, null, 2), "utf8");
|
|
37811
37812
|
} catch {}
|
|
37812
37813
|
}
|
|
37813
37814
|
function readRunRecord() {
|
|
@@ -37822,7 +37823,7 @@ function readRunRecord() {
|
|
|
37822
37823
|
function recordAt(path) {
|
|
37823
37824
|
let parsed;
|
|
37824
37825
|
try {
|
|
37825
|
-
parsed = JSON.parse(
|
|
37826
|
+
parsed = JSON.parse(readFileSync20(path, "utf8"));
|
|
37826
37827
|
} catch {
|
|
37827
37828
|
return null;
|
|
37828
37829
|
}
|
|
@@ -37867,7 +37868,7 @@ function runningAs(pid) {
|
|
|
37867
37868
|
const began = startedAt(pid, null);
|
|
37868
37869
|
const when = began === null ? {} : { startedAt: began };
|
|
37869
37870
|
if (process.platform === "linux") {
|
|
37870
|
-
const raw =
|
|
37871
|
+
const raw = readFileSync20(`/proc/${pid}/cmdline`, "utf8").replaceAll("\x00", " ").trim();
|
|
37871
37872
|
return raw === "" ? null : { kind: "commandLine", text: raw, ...when };
|
|
37872
37873
|
}
|
|
37873
37874
|
const args = execFileSync2("ps", ["-p", String(pid), "-o", "args="], {
|
|
@@ -37994,7 +37995,7 @@ function clearRunRecord(ours = () => true) {
|
|
|
37994
37995
|
continue;
|
|
37995
37996
|
}
|
|
37996
37997
|
try {
|
|
37997
|
-
|
|
37998
|
+
rmSync11(path);
|
|
37998
37999
|
} catch {}
|
|
37999
38000
|
}
|
|
38000
38001
|
}
|
|
@@ -38018,12 +38019,12 @@ function sweepTranscripts(keepMs = 24 * 60 * 60 * 1000, now = Date.now()) {
|
|
|
38018
38019
|
if (!entry.startsWith("run-") || !entry.endsWith(".log")) {
|
|
38019
38020
|
continue;
|
|
38020
38021
|
}
|
|
38021
|
-
const path =
|
|
38022
|
+
const path = join22(directory, entry);
|
|
38022
38023
|
if (path === keep || ageMs(path, now) < keepMs) {
|
|
38023
38024
|
continue;
|
|
38024
38025
|
}
|
|
38025
38026
|
try {
|
|
38026
|
-
|
|
38027
|
+
rmSync11(path);
|
|
38027
38028
|
} catch {}
|
|
38028
38029
|
}
|
|
38029
38030
|
}
|
|
@@ -38044,17 +38045,17 @@ import {
|
|
|
38044
38045
|
copyFileSync,
|
|
38045
38046
|
existsSync as existsSync15,
|
|
38046
38047
|
fstatSync,
|
|
38047
|
-
mkdirSync as
|
|
38048
|
+
mkdirSync as mkdirSync14,
|
|
38048
38049
|
mkdtempSync as mkdtempSync3,
|
|
38049
38050
|
openSync as openSync3,
|
|
38050
38051
|
readdirSync as readdirSync9,
|
|
38051
|
-
readFileSync as
|
|
38052
|
+
readFileSync as readFileSync21,
|
|
38052
38053
|
readSync as readSync2,
|
|
38053
38054
|
rmdirSync,
|
|
38054
38055
|
statSync as statSync8
|
|
38055
38056
|
} from "node:fs";
|
|
38056
38057
|
import { tmpdir as tmpdir5 } from "node:os";
|
|
38057
|
-
import { basename as basename4, dirname as
|
|
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";
|
|
38058
38059
|
import { setTimeout as delay3 } from "node:timers/promises";
|
|
38059
38060
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
38060
38061
|
import { promisify as promisify7 } from "node:util";
|
|
@@ -38068,11 +38069,11 @@ function captureDestinationRefusal(outputPath, projects) {
|
|
|
38068
38069
|
if (extname2(outputPath).toLowerCase() !== ".png") {
|
|
38069
38070
|
return `outputPath ${outputPath} does not end in .png, which is what a capture is.`;
|
|
38070
38071
|
}
|
|
38071
|
-
const directory = realPathOr(
|
|
38072
|
+
const directory = realPathOr(dirname16(outputPath));
|
|
38072
38073
|
if (!existsSync15(directory)) {
|
|
38073
|
-
return `outputPath's directory ${
|
|
38074
|
+
return `outputPath's directory ${dirname16(outputPath)} does not exist; a capture creates no directories.`;
|
|
38074
38075
|
}
|
|
38075
|
-
const where =
|
|
38076
|
+
const where = join23(directory, basename4(outputPath));
|
|
38076
38077
|
const inside = projects.find((project) => project !== "" && isWithinRoot(realPathOr(project), where));
|
|
38077
38078
|
if (inside !== undefined) {
|
|
38078
38079
|
return `outputPath ${outputPath} is inside the project at ${inside}; a capture is saved outside it, so it never becomes part of the game.`;
|
|
@@ -38320,7 +38321,7 @@ function aboveTheRunner(entry) {
|
|
|
38320
38321
|
}
|
|
38321
38322
|
function elsewhereIn(projectPath, asked) {
|
|
38322
38323
|
const wanted = asked.replace(/^res:\/\//, "").replace(/\/+$/, "");
|
|
38323
|
-
if (wanted === "" || existsSync15(
|
|
38324
|
+
if (wanted === "" || existsSync15(join23(projectPath, wanted))) {
|
|
38324
38325
|
return "";
|
|
38325
38326
|
}
|
|
38326
38327
|
const root = (wanted.split("/")[0] ?? "").toLowerCase();
|
|
@@ -38334,7 +38335,7 @@ function elsewhereIn(projectPath, asked) {
|
|
|
38334
38335
|
return near.length === 0 ? "" : ` The project does have ${there}.`;
|
|
38335
38336
|
}
|
|
38336
38337
|
function hasMainScene(projectFile) {
|
|
38337
|
-
const scene = parseProjectGodot(
|
|
38338
|
+
const scene = parseProjectGodot(readFileSync21(projectFile, "utf8"))["application"]?.["run/main_scene"];
|
|
38338
38339
|
return typeof scene === "string" && scene !== "";
|
|
38339
38340
|
}
|
|
38340
38341
|
function runtimeAddonAnnouncesEditors(projectPath) {
|
|
@@ -38342,13 +38343,13 @@ function runtimeAddonAnnouncesEditors(projectPath) {
|
|
|
38342
38343
|
return false;
|
|
38343
38344
|
}
|
|
38344
38345
|
try {
|
|
38345
|
-
return
|
|
38346
|
+
return readFileSync21(join23(projectPath, RUNTIME_AUTOLOAD2.path), "utf8").includes('"editor_pid"');
|
|
38346
38347
|
} catch {
|
|
38347
38348
|
return false;
|
|
38348
38349
|
}
|
|
38349
38350
|
}
|
|
38350
38351
|
function editorPlaysHeadless(projectFile) {
|
|
38351
|
-
const runArgs = parseProjectGodot(
|
|
38352
|
+
const runArgs = parseProjectGodot(readFileSync21(projectFile, "utf8"))["editor"]?.["run/main_run_args"];
|
|
38352
38353
|
return typeof runArgs === "string" && /(?:^|\s)--headless(?:\s|$)/.test(runArgs);
|
|
38353
38354
|
}
|
|
38354
38355
|
function camelCased(params) {
|
|
@@ -38367,7 +38368,7 @@ function needsItsOwnProcess(asked) {
|
|
|
38367
38368
|
class GodotServer {
|
|
38368
38369
|
mcp;
|
|
38369
38370
|
locator = new GodotLocator2;
|
|
38370
|
-
operationsScript =
|
|
38371
|
+
operationsScript = join23(__dirname2, "godot", "operations", "godot_operations.gd");
|
|
38371
38372
|
godotBridge;
|
|
38372
38373
|
tools = buildToolDefinitions();
|
|
38373
38374
|
activeProcess = null;
|
|
@@ -38511,7 +38512,7 @@ class GodotServer {
|
|
|
38511
38512
|
if (this.ownProject === null) {
|
|
38512
38513
|
return null;
|
|
38513
38514
|
}
|
|
38514
|
-
const announced =
|
|
38515
|
+
const announced = readAnnouncement2(announcementPath2(this.ownProject));
|
|
38515
38516
|
if (announced === null || announced.pid === process.pid) {
|
|
38516
38517
|
return null;
|
|
38517
38518
|
}
|
|
@@ -38621,7 +38622,7 @@ class GodotServer {
|
|
|
38621
38622
|
try {
|
|
38622
38623
|
return await this.dispatch(tool, op, args);
|
|
38623
38624
|
} catch (error) {
|
|
38624
|
-
if (error instanceof McpError || error instanceof
|
|
38625
|
+
if (error instanceof McpError || error instanceof Refusal) {
|
|
38625
38626
|
throw error;
|
|
38626
38627
|
}
|
|
38627
38628
|
const where = op === "" ? tool : `${tool} op=${op}`;
|
|
@@ -39032,7 +39033,7 @@ class GodotServer {
|
|
|
39032
39033
|
if (path === undefined) {
|
|
39033
39034
|
return { ok: false, response: this.createErrorResponse("projectPath is required.") };
|
|
39034
39035
|
}
|
|
39035
|
-
const file =
|
|
39036
|
+
const file = join23(path, "project.godot");
|
|
39036
39037
|
if (!existsSync15(file)) {
|
|
39037
39038
|
return {
|
|
39038
39039
|
ok: false,
|
|
@@ -39301,7 +39302,7 @@ class GodotServer {
|
|
|
39301
39302
|
if (!engine.ok) {
|
|
39302
39303
|
return engine.response;
|
|
39303
39304
|
}
|
|
39304
|
-
const application = parseProjectGodot(
|
|
39305
|
+
const application = parseProjectGodot(readFileSync21(project.value.file, "utf8"))["application"] ?? {};
|
|
39305
39306
|
const name = application["config/name"];
|
|
39306
39307
|
const mainScene = application["run/main_scene"];
|
|
39307
39308
|
const info = {
|
|
@@ -39362,18 +39363,18 @@ class GodotServer {
|
|
|
39362
39363
|
if (!engine.ok) {
|
|
39363
39364
|
return engine.response;
|
|
39364
39365
|
}
|
|
39365
|
-
const folder =
|
|
39366
|
+
const folder = dirname16(output.absolutePath);
|
|
39366
39367
|
try {
|
|
39367
|
-
|
|
39368
|
+
mkdirSync14(folder, { recursive: true });
|
|
39368
39369
|
} catch (error) {
|
|
39369
|
-
return this.createErrorResponse(`Cannot create ${
|
|
39370
|
+
return this.createErrorResponse(`Cannot create ${dirname16(output.relativePath)} to export into: ${errorMessage(error)}`);
|
|
39370
39371
|
}
|
|
39371
39372
|
const debug = readBoolean(args, "debug") ?? false;
|
|
39372
|
-
const exportLogs = mkdtempSync3(
|
|
39373
|
+
const exportLogs = mkdtempSync3(join23(tmpdir5(), "gdharness-export-"));
|
|
39373
39374
|
const exportArgs = [
|
|
39374
39375
|
"--headless",
|
|
39375
39376
|
"--log-file",
|
|
39376
|
-
|
|
39377
|
+
join23(exportLogs, "engine.log"),
|
|
39377
39378
|
"--path",
|
|
39378
39379
|
project.value.path,
|
|
39379
39380
|
debug ? "--export-debug" : "--export-release",
|
|
@@ -39434,7 +39435,7 @@ class GodotServer {
|
|
|
39434
39435
|
return contained.response;
|
|
39435
39436
|
}
|
|
39436
39437
|
const runner = "addons/gdUnit4/bin/GdUnitCmdTool.gd";
|
|
39437
|
-
if (!existsSync15(
|
|
39438
|
+
if (!existsSync15(join23(project.value.path, runner))) {
|
|
39438
39439
|
return this.createErrorResponse(`gdUnit4 is not installed in this project: no ${runner}.`, [
|
|
39439
39440
|
"Install gdUnit4 under addons/gdUnit4, from https://github.com/godot-gdunit-labs/gdUnit4"
|
|
39440
39441
|
]);
|
|
@@ -39469,7 +39470,7 @@ class GodotServer {
|
|
|
39469
39470
|
];
|
|
39470
39471
|
const timeoutMs = readPositiveNumber(args, "timeoutMs") ?? 600000;
|
|
39471
39472
|
this.logDebug(`Running tests: ${engine.value} ${cmdArgs.join(" ")}`);
|
|
39472
|
-
const userData = mkdtempSync3(
|
|
39473
|
+
const userData = mkdtempSync3(join23(tmpdir5(), "gdharness-tests-"));
|
|
39473
39474
|
const run = this.spawnGame(engine.value, cmdArgs, userDataIn(userData));
|
|
39474
39475
|
const hung = await new Promise((resolve) => {
|
|
39475
39476
|
const timer = setTimeout(() => {
|
|
@@ -39485,21 +39486,21 @@ class GodotServer {
|
|
|
39485
39486
|
resolve(false);
|
|
39486
39487
|
});
|
|
39487
39488
|
});
|
|
39488
|
-
const reportsDir =
|
|
39489
|
+
const reportsDir = join23(project.value.path, ".godot", ...ours.split("/"));
|
|
39489
39490
|
let report = null;
|
|
39490
39491
|
let reportProblem = null;
|
|
39491
39492
|
try {
|
|
39492
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))) : [];
|
|
39493
39494
|
const newest = written.at(-1);
|
|
39494
39495
|
if (newest !== undefined) {
|
|
39495
|
-
report = parseJUnit(
|
|
39496
|
+
report = parseJUnit(readFileSync21(join23(reportsDir, newest, "results.xml"), "utf8"));
|
|
39496
39497
|
}
|
|
39497
39498
|
} catch (error) {
|
|
39498
39499
|
reportProblem = errorMessage(error);
|
|
39499
39500
|
} finally {
|
|
39500
39501
|
discard(reportsDir, userData);
|
|
39501
39502
|
try {
|
|
39502
|
-
rmdirSync(
|
|
39503
|
+
rmdirSync(dirname16(reportsDir));
|
|
39503
39504
|
} catch {}
|
|
39504
39505
|
}
|
|
39505
39506
|
const engineEntries = run.log.select({ severity: "warning", sinceLastCall: false, limit: 200 }).entries.map(aboveTheRunner);
|
|
@@ -39635,7 +39636,7 @@ class GodotServer {
|
|
|
39635
39636
|
return null;
|
|
39636
39637
|
}
|
|
39637
39638
|
try {
|
|
39638
|
-
return
|
|
39639
|
+
return readFileSync21(contained.absolutePath, "utf8");
|
|
39639
39640
|
} catch {
|
|
39640
39641
|
return null;
|
|
39641
39642
|
}
|
|
@@ -39689,7 +39690,7 @@ class GodotServer {
|
|
|
39689
39690
|
return null;
|
|
39690
39691
|
}
|
|
39691
39692
|
try {
|
|
39692
|
-
return
|
|
39693
|
+
return readFileSync21(contained.absolutePath, "utf8");
|
|
39693
39694
|
} catch {
|
|
39694
39695
|
return null;
|
|
39695
39696
|
}
|
|
@@ -39918,7 +39919,7 @@ class GodotServer {
|
|
|
39918
39919
|
return null;
|
|
39919
39920
|
}
|
|
39920
39921
|
try {
|
|
39921
|
-
return settingKeys(
|
|
39922
|
+
return settingKeys(readFileSync21(join23(projectPath, "project.godot"), "utf8"));
|
|
39922
39923
|
} catch {
|
|
39923
39924
|
return null;
|
|
39924
39925
|
}
|
|
@@ -40169,7 +40170,7 @@ class GodotServer {
|
|
|
40169
40170
|
}
|
|
40170
40171
|
async runtimeUp(projectPath, before, budget, withArgs) {
|
|
40171
40172
|
const budgetMs = budget.ms;
|
|
40172
|
-
if (!existsSync15(
|
|
40173
|
+
if (!existsSync15(join23(projectPath, RUNTIME_AUTOLOAD2.path))) {
|
|
40173
40174
|
return runtimeVerdict(null, { addon: false, budgetMs, heldAt: null, running: false, withArgs });
|
|
40174
40175
|
}
|
|
40175
40176
|
let asking = null;
|
|
@@ -40826,7 +40827,8 @@ class GodotServer {
|
|
|
40826
40827
|
}
|
|
40827
40828
|
const budgetMs = readPositiveNumber(args, "timeoutMs") ?? WAIT_FOR_RUN_MS;
|
|
40828
40829
|
const until = Date.now() + budgetMs;
|
|
40829
|
-
|
|
40830
|
+
const cancelled = callSignal();
|
|
40831
|
+
while (cancelled?.aborted !== true && await this.runStillGoing(run) && Date.now() < until) {
|
|
40830
40832
|
this.drainEditorOutput(run);
|
|
40831
40833
|
this.drainTranscript(run);
|
|
40832
40834
|
await delay3(RUN_POLL_MS);
|
|
@@ -41352,7 +41354,7 @@ class GodotServer {
|
|
|
41352
41354
|
async ownRunOnItsWay(projectPath, budgetMs) {
|
|
41353
41355
|
const run = this.currentRun();
|
|
41354
41356
|
const project = projectPath ?? run?.projectPath ?? null;
|
|
41355
|
-
if (run === null || project === null || run.announcedPid !== undefined || run.projectPath !== null && !isSameDirectory(run.projectPath, project) || !existsSync15(
|
|
41357
|
+
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)) {
|
|
41356
41358
|
return null;
|
|
41357
41359
|
}
|
|
41358
41360
|
const already = runtimesAnnounced().running;
|
|
@@ -41431,8 +41433,8 @@ class GodotServer {
|
|
|
41431
41433
|
return this.createErrorResponse(refusal);
|
|
41432
41434
|
}
|
|
41433
41435
|
}
|
|
41434
|
-
const screenshotDir = expectsScreenshot ? mkdtempSync3(
|
|
41435
|
-
const screenshotPath = screenshotDir ?
|
|
41436
|
+
const screenshotDir = expectsScreenshot ? mkdtempSync3(join23(tmpdir5(), "gdharness-runtime-screenshot-")) : null;
|
|
41437
|
+
const screenshotPath = screenshotDir ? join23(screenshotDir, "capture.png") : null;
|
|
41436
41438
|
try {
|
|
41437
41439
|
const reply = await runtimeRequest(choice.endpoint, command, screenshotPath ? { ...params, output_path: screenshotPath } : params, timeoutMs);
|
|
41438
41440
|
if (!reply.ok) {
|
|
@@ -41463,7 +41465,7 @@ class GodotServer {
|
|
|
41463
41465
|
type: "text",
|
|
41464
41466
|
text: `Screenshot captured: ${dimensions}${chosenAmongSeveral ? ` from pid ${own}, the game this server holds` : ""}${saveTo === null ? "" : `. Saved to ${saveTo}`}`
|
|
41465
41467
|
},
|
|
41466
|
-
{ type: "image", data:
|
|
41468
|
+
{ type: "image", data: readFileSync21(screenshotPath).toString("base64"), mimeType: "image/png" }
|
|
41467
41469
|
]
|
|
41468
41470
|
};
|
|
41469
41471
|
} finally {
|
|
@@ -41572,7 +41574,7 @@ var init_server3 = __esm(() => {
|
|
|
41572
41574
|
init_tool_definitions();
|
|
41573
41575
|
init_update_check();
|
|
41574
41576
|
run7 = promisify7(execFile7);
|
|
41575
|
-
__dirname2 =
|
|
41577
|
+
__dirname2 = dirname16(fileURLToPath5(import.meta.url));
|
|
41576
41578
|
CONSOLE_WRAPPER = /_console(\.exe)?$/i;
|
|
41577
41579
|
LONGEST_BOOT_NOTED_MS = 5 * 60000;
|
|
41578
41580
|
VALUE_NAMES = {
|
|
@@ -41613,20 +41615,61 @@ var init_server3 = __esm(() => {
|
|
|
41613
41615
|
|
|
41614
41616
|
// src/cli.ts
|
|
41615
41617
|
import { existsSync as existsSync16 } from "fs";
|
|
41616
|
-
import { join as
|
|
41618
|
+
import { join as join24, resolve as resolve5 } from "path";
|
|
41619
|
+
|
|
41620
|
+
// src/bridge-announce.ts
|
|
41621
|
+
init_errors();
|
|
41622
|
+
init_process_children();
|
|
41623
|
+
import { mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
41624
|
+
import { dirname, join } from "node:path";
|
|
41625
|
+
function announcementPath(projectPath) {
|
|
41626
|
+
return join(projectPath, ".godot", "gdharness-bridge.json");
|
|
41627
|
+
}
|
|
41628
|
+
function readAnnouncement(path) {
|
|
41629
|
+
try {
|
|
41630
|
+
const parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
41631
|
+
if (typeof parsed !== "object" || parsed === null) {
|
|
41632
|
+
return null;
|
|
41633
|
+
}
|
|
41634
|
+
const said = parsed;
|
|
41635
|
+
if (typeof said.port !== "number" || typeof said.pid !== "number") {
|
|
41636
|
+
return null;
|
|
41637
|
+
}
|
|
41638
|
+
return said;
|
|
41639
|
+
} catch {
|
|
41640
|
+
return null;
|
|
41641
|
+
}
|
|
41642
|
+
}
|
|
41643
|
+
function serverBehind(announcement, startedAt) {
|
|
41644
|
+
if (announcement === null || typeof announcement.version !== "string" || startedAt === undefined) {
|
|
41645
|
+
return null;
|
|
41646
|
+
}
|
|
41647
|
+
const announcedAt = Date.parse(announcement.startedAt);
|
|
41648
|
+
if (Number.isNaN(announcedAt) || startedAt > announcedAt + 1000) {
|
|
41649
|
+
return null;
|
|
41650
|
+
}
|
|
41651
|
+
return { version: announcement.version, pid: announcement.pid };
|
|
41652
|
+
}
|
|
41653
|
+
function serverServing(projectPath) {
|
|
41654
|
+
const announcement = readAnnouncement(announcementPath(projectPath));
|
|
41655
|
+
if (announcement === null) {
|
|
41656
|
+
return null;
|
|
41657
|
+
}
|
|
41658
|
+
return serverBehind(announcement, startTimesOf([announcement.pid]).get(announcement.pid));
|
|
41659
|
+
}
|
|
41617
41660
|
|
|
41618
41661
|
// src/errors.ts
|
|
41619
|
-
class
|
|
41662
|
+
class Refusal2 extends Error {
|
|
41620
41663
|
}
|
|
41621
41664
|
|
|
41622
41665
|
// src/godot-path.ts
|
|
41623
41666
|
init_detection();
|
|
41624
41667
|
init_errors();
|
|
41625
|
-
import { execFile } from "node:child_process";
|
|
41668
|
+
import { execFile as execFile2 } from "node:child_process";
|
|
41626
41669
|
import { existsSync as existsSync2 } from "node:fs";
|
|
41627
41670
|
import { normalize } from "node:path";
|
|
41628
|
-
import { promisify } from "node:util";
|
|
41629
|
-
var
|
|
41671
|
+
import { promisify as promisify2 } from "node:util";
|
|
41672
|
+
var run2 = promisify2(execFile2);
|
|
41630
41673
|
|
|
41631
41674
|
class GodotLocator {
|
|
41632
41675
|
answered = new Map;
|
|
@@ -41683,7 +41726,7 @@ class GodotLocator {
|
|
|
41683
41726
|
reason = "does not exist";
|
|
41684
41727
|
} else {
|
|
41685
41728
|
try {
|
|
41686
|
-
await
|
|
41729
|
+
await run2(path, ["--version"]);
|
|
41687
41730
|
reason = null;
|
|
41688
41731
|
} catch (error) {
|
|
41689
41732
|
reason = `does not answer --version: ${errorMessage(error)}`;
|
|
@@ -41695,9 +41738,9 @@ class GodotLocator {
|
|
|
41695
41738
|
}
|
|
41696
41739
|
|
|
41697
41740
|
// src/harnesses.ts
|
|
41698
|
-
import { existsSync as existsSync4, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
41741
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync2, rmSync as rmSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
41699
41742
|
import { homedir as homedir2 } from "node:os";
|
|
41700
|
-
import { dirname as
|
|
41743
|
+
import { dirname as dirname3, join as join4 } from "node:path";
|
|
41701
41744
|
import process3 from "node:process";
|
|
41702
41745
|
|
|
41703
41746
|
// node_modules/yaml/dist/index.js
|
|
@@ -41870,7 +41913,7 @@ var HARNESSES = [
|
|
|
41870
41913
|
shape: "plain",
|
|
41871
41914
|
home: ".claude",
|
|
41872
41915
|
marker: ".claude",
|
|
41873
|
-
skills: { dir:
|
|
41916
|
+
skills: { dir: join4(".claude", "skills"), shared: false }
|
|
41874
41917
|
},
|
|
41875
41918
|
{
|
|
41876
41919
|
id: "copilot-cli",
|
|
@@ -41880,7 +41923,7 @@ var HARNESSES = [
|
|
|
41880
41923
|
container: "mcpServers",
|
|
41881
41924
|
shape: "plain",
|
|
41882
41925
|
home: ".copilot",
|
|
41883
|
-
skills: { dir:
|
|
41926
|
+
skills: { dir: join4(".github", "skills"), shared: true },
|
|
41884
41927
|
manual: "Trust the folder when Copilot CLI first asks, or it reads no config here."
|
|
41885
41928
|
},
|
|
41886
41929
|
{
|
|
@@ -41906,22 +41949,22 @@ var HARNESSES = [
|
|
|
41906
41949
|
id: "cursor",
|
|
41907
41950
|
name: "Cursor",
|
|
41908
41951
|
scope: "project",
|
|
41909
|
-
file:
|
|
41952
|
+
file: join4(".cursor", "mcp.json"),
|
|
41910
41953
|
container: "mcpServers",
|
|
41911
41954
|
shape: "plain",
|
|
41912
41955
|
home: ".cursor",
|
|
41913
|
-
skills: { dir:
|
|
41956
|
+
skills: { dir: join4(".cursor", "skills"), shared: true },
|
|
41914
41957
|
manual: "Cursor asks before each tool call by default, and the server can be toggled off."
|
|
41915
41958
|
},
|
|
41916
41959
|
{
|
|
41917
41960
|
id: "vscode",
|
|
41918
41961
|
name: "VS Code",
|
|
41919
41962
|
scope: "project",
|
|
41920
|
-
file:
|
|
41963
|
+
file: join4(".vscode", "mcp.json"),
|
|
41921
41964
|
container: "servers",
|
|
41922
41965
|
shape: "typed",
|
|
41923
41966
|
home: ".vscode",
|
|
41924
|
-
skills: { dir:
|
|
41967
|
+
skills: { dir: join4(".github", "skills"), shared: true },
|
|
41925
41968
|
manual: "VS Code asks you to trust a server the first time it starts one."
|
|
41926
41969
|
},
|
|
41927
41970
|
{
|
|
@@ -41931,45 +41974,45 @@ var HARNESSES = [
|
|
|
41931
41974
|
file: "opencode.json",
|
|
41932
41975
|
container: "mcp",
|
|
41933
41976
|
shape: "opencode",
|
|
41934
|
-
home:
|
|
41977
|
+
home: join4(".config", "opencode"),
|
|
41935
41978
|
marker: ".opencode",
|
|
41936
|
-
skills: { dir:
|
|
41979
|
+
skills: { dir: join4(".opencode", "skills"), shared: true }
|
|
41937
41980
|
},
|
|
41938
41981
|
{
|
|
41939
41982
|
id: "junie",
|
|
41940
41983
|
name: "Junie",
|
|
41941
41984
|
scope: "project",
|
|
41942
|
-
file:
|
|
41985
|
+
file: join4(".junie", "mcp", "mcp.json"),
|
|
41943
41986
|
container: "mcpServers",
|
|
41944
41987
|
shape: "plain",
|
|
41945
41988
|
home: ".junie",
|
|
41946
|
-
skills: { dir:
|
|
41989
|
+
skills: { dir: join4(".junie", "skills"), shared: true }
|
|
41947
41990
|
},
|
|
41948
41991
|
{
|
|
41949
41992
|
id: "kiro",
|
|
41950
41993
|
name: "Kiro",
|
|
41951
41994
|
scope: "project",
|
|
41952
|
-
file:
|
|
41995
|
+
file: join4(".kiro", "settings", "mcp.json"),
|
|
41953
41996
|
container: "mcpServers",
|
|
41954
41997
|
shape: "plain",
|
|
41955
41998
|
home: ".kiro",
|
|
41956
|
-
skills: { dir:
|
|
41999
|
+
skills: { dir: join4(".kiro", "skills"), shared: false }
|
|
41957
42000
|
},
|
|
41958
42001
|
{
|
|
41959
42002
|
id: "gemini",
|
|
41960
42003
|
name: "Gemini CLI",
|
|
41961
42004
|
scope: "project",
|
|
41962
|
-
file:
|
|
42005
|
+
file: join4(".gemini", "settings.json"),
|
|
41963
42006
|
container: "mcpServers",
|
|
41964
42007
|
shape: "plain",
|
|
41965
42008
|
home: ".gemini",
|
|
41966
|
-
skills: { dir:
|
|
42009
|
+
skills: { dir: join4(".gemini", "skills"), shared: true }
|
|
41967
42010
|
},
|
|
41968
42011
|
{
|
|
41969
42012
|
id: "roo",
|
|
41970
42013
|
name: "Roo Code",
|
|
41971
42014
|
scope: "project",
|
|
41972
|
-
file:
|
|
42015
|
+
file: join4(".roo", "mcp.json"),
|
|
41973
42016
|
container: "mcpServers",
|
|
41974
42017
|
shape: "plain"
|
|
41975
42018
|
},
|
|
@@ -41977,7 +42020,7 @@ var HARNESSES = [
|
|
|
41977
42020
|
id: "kilo",
|
|
41978
42021
|
name: "Kilo Code",
|
|
41979
42022
|
scope: "project",
|
|
41980
|
-
file:
|
|
42023
|
+
file: join4(".kilocode", "mcp.json"),
|
|
41981
42024
|
container: "mcpServers",
|
|
41982
42025
|
shape: "plain"
|
|
41983
42026
|
},
|
|
@@ -41985,16 +42028,16 @@ var HARNESSES = [
|
|
|
41985
42028
|
id: "amazon-q",
|
|
41986
42029
|
name: "Amazon Q Developer CLI",
|
|
41987
42030
|
scope: "project",
|
|
41988
|
-
file:
|
|
42031
|
+
file: join4(".amazonq", "mcp.json"),
|
|
41989
42032
|
container: "mcpServers",
|
|
41990
42033
|
shape: "plain",
|
|
41991
|
-
home:
|
|
42034
|
+
home: join4(".aws", "amazonq")
|
|
41992
42035
|
},
|
|
41993
42036
|
{
|
|
41994
42037
|
id: "zed",
|
|
41995
42038
|
name: "Zed",
|
|
41996
42039
|
scope: "project",
|
|
41997
|
-
file:
|
|
42040
|
+
file: join4(".zed", "settings.json"),
|
|
41998
42041
|
container: "context_servers",
|
|
41999
42042
|
shape: "plain"
|
|
42000
42043
|
},
|
|
@@ -42002,16 +42045,16 @@ var HARNESSES = [
|
|
|
42002
42045
|
id: "amp",
|
|
42003
42046
|
name: "Amp",
|
|
42004
42047
|
scope: "project",
|
|
42005
|
-
file:
|
|
42048
|
+
file: join4(".amp", "settings.json"),
|
|
42006
42049
|
container: "amp.mcpServers",
|
|
42007
42050
|
shape: "plain",
|
|
42008
|
-
home:
|
|
42051
|
+
home: join4(".config", "amp")
|
|
42009
42052
|
},
|
|
42010
42053
|
{
|
|
42011
42054
|
id: "warp",
|
|
42012
42055
|
name: "Warp",
|
|
42013
42056
|
scope: "project",
|
|
42014
|
-
file:
|
|
42057
|
+
file: join4(".warp", ".mcp.json"),
|
|
42015
42058
|
container: "mcpServers",
|
|
42016
42059
|
shape: "plain",
|
|
42017
42060
|
home: ".warp",
|
|
@@ -42021,7 +42064,7 @@ var HARNESSES = [
|
|
|
42021
42064
|
id: "trae",
|
|
42022
42065
|
name: "Trae",
|
|
42023
42066
|
scope: "project",
|
|
42024
|
-
file:
|
|
42067
|
+
file: join4(".trae", "mcp.json"),
|
|
42025
42068
|
container: "mcpServers",
|
|
42026
42069
|
shape: "plain",
|
|
42027
42070
|
home: ".trae"
|
|
@@ -42030,7 +42073,7 @@ var HARNESSES = [
|
|
|
42030
42073
|
id: "factory",
|
|
42031
42074
|
name: "Factory Droid",
|
|
42032
42075
|
scope: "project",
|
|
42033
|
-
file:
|
|
42076
|
+
file: join4(".factory", "mcp.json"),
|
|
42034
42077
|
container: "mcpServers",
|
|
42035
42078
|
shape: "typed",
|
|
42036
42079
|
home: ".factory"
|
|
@@ -42039,7 +42082,7 @@ var HARNESSES = [
|
|
|
42039
42082
|
id: "tabnine",
|
|
42040
42083
|
name: "Tabnine",
|
|
42041
42084
|
scope: "project",
|
|
42042
|
-
file:
|
|
42085
|
+
file: join4(".tabnine", "mcp_servers.json"),
|
|
42043
42086
|
container: "mcpServers",
|
|
42044
42087
|
shape: "plain",
|
|
42045
42088
|
home: ".tabnine"
|
|
@@ -42056,7 +42099,7 @@ var HARNESSES = [
|
|
|
42056
42099
|
id: "pi",
|
|
42057
42100
|
name: "pi",
|
|
42058
42101
|
scope: "project",
|
|
42059
|
-
file:
|
|
42102
|
+
file: join4(".pi", "mcp.json"),
|
|
42060
42103
|
container: "mcpServers",
|
|
42061
42104
|
shape: "plain",
|
|
42062
42105
|
home: ".pi"
|
|
@@ -42065,7 +42108,7 @@ var HARNESSES = [
|
|
|
42065
42108
|
id: "mistral-vibe",
|
|
42066
42109
|
name: "Mistral Vibe",
|
|
42067
42110
|
scope: "project",
|
|
42068
|
-
file:
|
|
42111
|
+
file: join4(".vibe", "config.toml"),
|
|
42069
42112
|
container: "mcp_servers",
|
|
42070
42113
|
shape: "plain",
|
|
42071
42114
|
toml: {
|
|
@@ -42121,7 +42164,7 @@ var HARNESSES = [
|
|
|
42121
42164
|
id: "codex",
|
|
42122
42165
|
name: "Codex CLI",
|
|
42123
42166
|
scope: "home",
|
|
42124
|
-
file:
|
|
42167
|
+
file: join4(".codex", "config.toml"),
|
|
42125
42168
|
container: "mcp_servers",
|
|
42126
42169
|
shape: "plain",
|
|
42127
42170
|
manual: "Codex reads its config at startup, so restart it.",
|
|
@@ -42140,20 +42183,20 @@ var HARNESSES = [
|
|
|
42140
42183
|
id: "cline",
|
|
42141
42184
|
name: "Cline",
|
|
42142
42185
|
scope: "home",
|
|
42143
|
-
file:
|
|
42186
|
+
file: join4(".cline", "data", "settings", "cline_mcp_settings.json"),
|
|
42144
42187
|
container: "mcpServers",
|
|
42145
42188
|
shape: "plain",
|
|
42146
42189
|
marker: ".cline",
|
|
42147
|
-
skills: { dir:
|
|
42190
|
+
skills: { dir: join4(".cline", "skills"), shared: false }
|
|
42148
42191
|
},
|
|
42149
42192
|
{
|
|
42150
42193
|
id: "goose",
|
|
42151
42194
|
name: "Goose",
|
|
42152
42195
|
scope: "home",
|
|
42153
42196
|
file: {
|
|
42154
|
-
win32: { appData:
|
|
42155
|
-
darwin:
|
|
42156
|
-
linux:
|
|
42197
|
+
win32: { appData: join4("Block", "goose", "config", "config.yaml") },
|
|
42198
|
+
darwin: join4(".config", "goose", "config.yaml"),
|
|
42199
|
+
linux: join4(".config", "goose", "config.yaml")
|
|
42157
42200
|
},
|
|
42158
42201
|
container: "extensions",
|
|
42159
42202
|
shape: "plain",
|
|
@@ -42174,19 +42217,19 @@ var HARNESSES = [
|
|
|
42174
42217
|
id: "windsurf",
|
|
42175
42218
|
name: "Windsurf",
|
|
42176
42219
|
scope: "home",
|
|
42177
|
-
file:
|
|
42220
|
+
file: join4(".codeium", "windsurf", "mcp_config.json"),
|
|
42178
42221
|
container: "mcpServers",
|
|
42179
42222
|
shape: "plain",
|
|
42180
|
-
skills: { dir:
|
|
42223
|
+
skills: { dir: join4(".windsurf", "skills"), shared: true }
|
|
42181
42224
|
},
|
|
42182
42225
|
{
|
|
42183
42226
|
id: "hermes",
|
|
42184
42227
|
name: "Hermes",
|
|
42185
42228
|
scope: "home",
|
|
42186
|
-
file:
|
|
42229
|
+
file: join4(".hermes", "config.yaml"),
|
|
42187
42230
|
container: "mcp_servers",
|
|
42188
42231
|
shape: "plain",
|
|
42189
|
-
skills: { dir:
|
|
42232
|
+
skills: { dir: join4(".hermes", "skills"), shared: true },
|
|
42190
42233
|
yaml: {
|
|
42191
42234
|
path: ["mcp_servers", SERVER_KEY],
|
|
42192
42235
|
entry: (launch) => ({
|
|
@@ -42201,7 +42244,7 @@ var HARNESSES = [
|
|
|
42201
42244
|
id: "openclaw",
|
|
42202
42245
|
name: "OpenClaw",
|
|
42203
42246
|
scope: "home",
|
|
42204
|
-
file:
|
|
42247
|
+
file: join4(".openclaw", "openclaw.json"),
|
|
42205
42248
|
container: "mcpServers",
|
|
42206
42249
|
shape: "plain"
|
|
42207
42250
|
},
|
|
@@ -42210,9 +42253,9 @@ var HARNESSES = [
|
|
|
42210
42253
|
name: "Claude Desktop",
|
|
42211
42254
|
scope: "home",
|
|
42212
42255
|
file: {
|
|
42213
|
-
win32: { appData:
|
|
42214
|
-
darwin:
|
|
42215
|
-
linux:
|
|
42256
|
+
win32: { appData: join4("Claude", "claude_desktop_config.json") },
|
|
42257
|
+
darwin: join4("Library", "Application Support", "Claude", "claude_desktop_config.json"),
|
|
42258
|
+
linux: join4(".config", "Claude", "claude_desktop_config.json")
|
|
42216
42259
|
},
|
|
42217
42260
|
container: "mcpServers",
|
|
42218
42261
|
shape: "plain"
|
|
@@ -42221,7 +42264,7 @@ var HARNESSES = [
|
|
|
42221
42264
|
id: "zeroclaw",
|
|
42222
42265
|
name: "ZeroClaw",
|
|
42223
42266
|
scope: "home",
|
|
42224
|
-
file:
|
|
42267
|
+
file: join4(".zeroclaw", "config.toml"),
|
|
42225
42268
|
container: "mcp.servers",
|
|
42226
42269
|
shape: "plain",
|
|
42227
42270
|
toml: {
|
|
@@ -42242,7 +42285,7 @@ var HARNESSES = [
|
|
|
42242
42285
|
id: "deepcode",
|
|
42243
42286
|
name: "Deep Code",
|
|
42244
42287
|
scope: "home",
|
|
42245
|
-
file:
|
|
42288
|
+
file: join4(".deepcode", "settings.json"),
|
|
42246
42289
|
container: "mcpServers",
|
|
42247
42290
|
shape: "plain"
|
|
42248
42291
|
},
|
|
@@ -42250,7 +42293,7 @@ var HARNESSES = [
|
|
|
42250
42293
|
id: "nanobot",
|
|
42251
42294
|
name: "nanobot",
|
|
42252
42295
|
scope: "home",
|
|
42253
|
-
file:
|
|
42296
|
+
file: join4(".nanobot", "config.json"),
|
|
42254
42297
|
container: "mcpServers",
|
|
42255
42298
|
shape: "plain",
|
|
42256
42299
|
snippet: (launch) => jsonSnippet(launch, "mcpServers", "plain")
|
|
@@ -42259,17 +42302,17 @@ var HARNESSES = [
|
|
|
42259
42302
|
id: "autohand",
|
|
42260
42303
|
name: "Autohand",
|
|
42261
42304
|
scope: "home",
|
|
42262
|
-
file:
|
|
42305
|
+
file: join4(".autohand", "config.json"),
|
|
42263
42306
|
container: "mcpServers",
|
|
42264
42307
|
shape: "plain",
|
|
42265
42308
|
snippet: (launch) => jsonSnippet(launch, "mcpServers", "plain")
|
|
42266
42309
|
}
|
|
42267
42310
|
];
|
|
42268
42311
|
function appDataRoot() {
|
|
42269
|
-
return process3.env["APPDATA"] ??
|
|
42312
|
+
return process3.env["APPDATA"] ?? join4(homedir2(), "AppData", "Roaming");
|
|
42270
42313
|
}
|
|
42271
42314
|
function homePath(path) {
|
|
42272
|
-
return typeof path === "string" ?
|
|
42315
|
+
return typeof path === "string" ? join4(homedir2(), path) : join4(appDataRoot(), path.appData);
|
|
42273
42316
|
}
|
|
42274
42317
|
function fileFor(harness, platform = process3.platform) {
|
|
42275
42318
|
if (typeof harness.file === "string") {
|
|
@@ -42280,7 +42323,7 @@ function fileFor(harness, platform = process3.platform) {
|
|
|
42280
42323
|
function configPath(harness, projectPath) {
|
|
42281
42324
|
const file = fileFor(harness);
|
|
42282
42325
|
if (harness.scope === "project") {
|
|
42283
|
-
return
|
|
42326
|
+
return join4(projectPath, file);
|
|
42284
42327
|
}
|
|
42285
42328
|
return homePath(file);
|
|
42286
42329
|
}
|
|
@@ -42339,11 +42382,11 @@ function present(harness, projectPath) {
|
|
|
42339
42382
|
return true;
|
|
42340
42383
|
}
|
|
42341
42384
|
const root = harness.scope === "project" ? projectPath : homedir2();
|
|
42342
|
-
const own =
|
|
42385
|
+
const own = dirname3(path);
|
|
42343
42386
|
if (own !== root && existsSync4(own)) {
|
|
42344
42387
|
return true;
|
|
42345
42388
|
}
|
|
42346
|
-
return harness.marker !== undefined && existsSync4(
|
|
42389
|
+
return harness.marker !== undefined && existsSync4(join4(root, harness.marker));
|
|
42347
42390
|
}
|
|
42348
42391
|
function candidates(projectPath) {
|
|
42349
42392
|
const found = [];
|
|
@@ -42356,7 +42399,7 @@ function candidates(projectPath) {
|
|
|
42356
42399
|
}
|
|
42357
42400
|
if (present(harness, projectPath)) {
|
|
42358
42401
|
found.push({ harness, reason: "configured" });
|
|
42359
|
-
} else if (harness.home !== undefined && existsSync4(
|
|
42402
|
+
} else if (harness.home !== undefined && existsSync4(join4(homedir2(), harness.home))) {
|
|
42360
42403
|
found.push({ harness, reason: "installed" });
|
|
42361
42404
|
}
|
|
42362
42405
|
}
|
|
@@ -42381,7 +42424,7 @@ function registered(harness, projectPath) {
|
|
|
42381
42424
|
if (harness.snippet !== undefined || !existsSync4(path)) {
|
|
42382
42425
|
return false;
|
|
42383
42426
|
}
|
|
42384
|
-
const text =
|
|
42427
|
+
const text = readFileSync2(path, "utf8");
|
|
42385
42428
|
if (harness.toml !== undefined) {
|
|
42386
42429
|
return text.includes(harness.toml.identity ?? harness.toml.header);
|
|
42387
42430
|
}
|
|
@@ -42411,7 +42454,7 @@ function recordedEnginePath(projectPath) {
|
|
|
42411
42454
|
}
|
|
42412
42455
|
let parsed;
|
|
42413
42456
|
try {
|
|
42414
|
-
parsed = JSON.parse(
|
|
42457
|
+
parsed = JSON.parse(readFileSync2(path, "utf8"));
|
|
42415
42458
|
} catch {
|
|
42416
42459
|
continue;
|
|
42417
42460
|
}
|
|
@@ -42434,7 +42477,7 @@ function disconnect(harness, projectPath) {
|
|
|
42434
42477
|
if (harness.snippet !== undefined) {
|
|
42435
42478
|
return { harness, path, action: "manual" };
|
|
42436
42479
|
}
|
|
42437
|
-
const text =
|
|
42480
|
+
const text = readFileSync2(path, "utf8");
|
|
42438
42481
|
if (harness.toml !== undefined) {
|
|
42439
42482
|
return taken(harness, path, text, removeToml(text, harness.toml));
|
|
42440
42483
|
}
|
|
@@ -42443,7 +42486,7 @@ function disconnect(harness, projectPath) {
|
|
|
42443
42486
|
}
|
|
42444
42487
|
let existing;
|
|
42445
42488
|
try {
|
|
42446
|
-
existing = JSON.parse(
|
|
42489
|
+
existing = JSON.parse(readFileSync2(path, "utf8"));
|
|
42447
42490
|
} catch {
|
|
42448
42491
|
return { harness, path, action: "absent" };
|
|
42449
42492
|
}
|
|
@@ -42463,25 +42506,32 @@ function disconnect(harness, projectPath) {
|
|
|
42463
42506
|
root[harness.container] = servers;
|
|
42464
42507
|
}
|
|
42465
42508
|
if (Object.keys(root).length === 0) {
|
|
42466
|
-
|
|
42509
|
+
rmSync2(path, { force: true });
|
|
42467
42510
|
return { harness, path, action: "deleted" };
|
|
42468
42511
|
}
|
|
42469
|
-
|
|
42512
|
+
writeFileSync2(path, `${JSON.stringify(root, null, 2)}
|
|
42470
42513
|
`, "utf8");
|
|
42471
42514
|
return { harness, path, action: "removed" };
|
|
42472
42515
|
}
|
|
42473
|
-
function harnessNote(repinned, now) {
|
|
42516
|
+
function harnessNote(repinned, now, running = null) {
|
|
42474
42517
|
const alsoByHand = repinned.byHand === 0 ? "" : `
|
|
42475
42518
|
${repinned.byHand === 1 ? "One harness above was" : `${repinned.byHand} harnesses above were`} handed a command to run instead; run it too.`;
|
|
42519
|
+
const serving = running === null ? ` No gdharness server is announcing itself for this project, so which one your harness
|
|
42520
|
+
` + ` is running cannot be read from here.
|
|
42521
|
+
` : ` The server serving this project is gdharness ${running.server.version} (pid ${running.server.pid})${running.isThisVersion ? ", which is already this version." : "."}
|
|
42522
|
+
`;
|
|
42523
|
+
if (running?.isThisVersion === true && repinned.byHand === 0 && (repinned.moved.length > 0 || repinned.written > 0)) {
|
|
42524
|
+
return ` 2. Nothing for the harness: the server serving this project is gdharness ${running.server.version}
|
|
42525
|
+
(pid ${running.server.pid}), which is already this version.`;
|
|
42526
|
+
}
|
|
42476
42527
|
if (repinned.moved.length > 0) {
|
|
42477
|
-
return ` 2. Your harness
|
|
42478
|
-
` + `
|
|
42479
|
-
` + ` Reconnect the MCP server, or restart the harness.${alsoByHand}`;
|
|
42528
|
+
return ` 2. Your harness config held ${repinned.moved.join(", ")} before this upgrade.
|
|
42529
|
+
` + ` It names ${now} now.
|
|
42530
|
+
` + serving + ` Reconnect the MCP server, or restart the harness.${alsoByHand}`;
|
|
42480
42531
|
}
|
|
42481
42532
|
if (repinned.written > 0) {
|
|
42482
|
-
return ` 2. Your harness config already named this version
|
|
42483
|
-
` + `
|
|
42484
|
-
` + ` Reconnect the MCP server, or restart the harness.${alsoByHand}`;
|
|
42533
|
+
return ` 2. Your harness config already named this version.
|
|
42534
|
+
` + serving + ` Reconnect the MCP server, or restart the harness.${alsoByHand}`;
|
|
42485
42535
|
}
|
|
42486
42536
|
if (repinned.byHand > 0) {
|
|
42487
42537
|
return ` 2. None of the harness configs found here can be written for you, so nothing was
|
|
@@ -42508,7 +42558,7 @@ function connect(harness, projectPath, launch) {
|
|
|
42508
42558
|
if (harness.snippet !== undefined) {
|
|
42509
42559
|
return { harness, path, action: "snippet", snippet: harness.snippet(launch) };
|
|
42510
42560
|
}
|
|
42511
|
-
const held = existsSync4(path) ?
|
|
42561
|
+
const held = existsSync4(path) ? readFileSync2(path, "utf8") : "";
|
|
42512
42562
|
if (harness.toml !== undefined) {
|
|
42513
42563
|
const already = held.includes(harness.toml.identity ?? harness.toml.header);
|
|
42514
42564
|
return put(harness, path, writeToml(held, harness.toml, launch), already);
|
|
@@ -42522,7 +42572,7 @@ function connect(harness, projectPath, launch) {
|
|
|
42522
42572
|
try {
|
|
42523
42573
|
existing = JSON.parse(held);
|
|
42524
42574
|
} catch (cause) {
|
|
42525
|
-
throw new
|
|
42575
|
+
throw new Refusal(`${path} is not valid JSON, so gdharness was not added to it.`, { cause });
|
|
42526
42576
|
}
|
|
42527
42577
|
}
|
|
42528
42578
|
const container = typeof existing === "object" && existing !== null && !Array.isArray(existing) ? existing[harness.container] : undefined;
|
|
@@ -42534,8 +42584,8 @@ function connect(harness, projectPath, launch) {
|
|
|
42534
42584
|
return was === undefined || was === now ? written : { ...written, wasLaunchedBy: was };
|
|
42535
42585
|
}
|
|
42536
42586
|
function put(harness, path, contents, already) {
|
|
42537
|
-
|
|
42538
|
-
|
|
42587
|
+
mkdirSync2(dirname3(path), { recursive: true });
|
|
42588
|
+
writeFileSync2(path, contents, "utf8");
|
|
42539
42589
|
return { harness, path, action: already ? "replaced" : "written" };
|
|
42540
42590
|
}
|
|
42541
42591
|
function taken(harness, path, held, stripped) {
|
|
@@ -42543,10 +42593,10 @@ function taken(harness, path, held, stripped) {
|
|
|
42543
42593
|
return { harness, path, action: "absent" };
|
|
42544
42594
|
}
|
|
42545
42595
|
if (stripped.trim() === "" || stripped.trim() === "{}") {
|
|
42546
|
-
|
|
42596
|
+
rmSync2(path, { force: true });
|
|
42547
42597
|
return { harness, path, action: "deleted" };
|
|
42548
42598
|
}
|
|
42549
|
-
|
|
42599
|
+
writeFileSync2(path, stripped, "utf8");
|
|
42550
42600
|
return { harness, path, action: "removed" };
|
|
42551
42601
|
}
|
|
42552
42602
|
|
|
@@ -42554,12 +42604,12 @@ function taken(harness, path, held, stripped) {
|
|
|
42554
42604
|
init_call_signal();
|
|
42555
42605
|
init_game_log();
|
|
42556
42606
|
init_scratch();
|
|
42557
|
-
import { execFile as
|
|
42558
|
-
import { mkdtempSync, writeFileSync as
|
|
42607
|
+
import { execFile as execFile3 } from "node:child_process";
|
|
42608
|
+
import { mkdtempSync, writeFileSync as writeFileSync3 } from "node:fs";
|
|
42559
42609
|
import { tmpdir } from "node:os";
|
|
42560
|
-
import { join as
|
|
42561
|
-
import { promisify as
|
|
42562
|
-
var
|
|
42610
|
+
import { join as join5 } from "node:path";
|
|
42611
|
+
import { promisify as promisify3 } from "node:util";
|
|
42612
|
+
var run3 = promisify3(execFile3);
|
|
42563
42613
|
function snakeCased(params) {
|
|
42564
42614
|
const result = emptyRecord();
|
|
42565
42615
|
for (const [key, value] of Object.entries(params)) {
|
|
@@ -42598,13 +42648,13 @@ function reason(stdout, stderr) {
|
|
|
42598
42648
|
return stdout.trim().split(/\r?\n/).at(-1) ?? "no output at all";
|
|
42599
42649
|
}
|
|
42600
42650
|
async function runOperation(engine, operation, params, projectPath) {
|
|
42601
|
-
const paramsDir = mkdtempSync(
|
|
42602
|
-
const paramsFile =
|
|
42603
|
-
|
|
42651
|
+
const paramsDir = mkdtempSync(join5(tmpdir(), "gdharness-params-"));
|
|
42652
|
+
const paramsFile = join5(paramsDir, `${operation}.json`);
|
|
42653
|
+
writeFileSync3(paramsFile, JSON.stringify(snakeCased(params)), "utf8");
|
|
42604
42654
|
const args = [
|
|
42605
42655
|
"--headless",
|
|
42606
42656
|
"--log-file",
|
|
42607
|
-
|
|
42657
|
+
join5(paramsDir, "engine.log"),
|
|
42608
42658
|
"--path",
|
|
42609
42659
|
projectPath,
|
|
42610
42660
|
"--script",
|
|
@@ -42616,7 +42666,7 @@ async function runOperation(engine, operation, params, projectPath) {
|
|
|
42616
42666
|
let stdout;
|
|
42617
42667
|
let stderr;
|
|
42618
42668
|
try {
|
|
42619
|
-
({ stdout, stderr } = await
|
|
42669
|
+
({ stdout, stderr } = await run3(engine.godotPath, args, { signal: callSignal() }));
|
|
42620
42670
|
} catch (error) {
|
|
42621
42671
|
if (error instanceof Error && "stdout" in error && "stderr" in error) {
|
|
42622
42672
|
const failed = error;
|
|
@@ -42774,12 +42824,12 @@ class Ask {
|
|
|
42774
42824
|
|
|
42775
42825
|
// src/server-version.ts
|
|
42776
42826
|
init_update_check();
|
|
42777
|
-
import { readFileSync as
|
|
42827
|
+
import { readFileSync as readFileSync5 } from "node:fs";
|
|
42778
42828
|
var DEBUG_MODE2 = process.env["DEBUG"] === "true";
|
|
42779
42829
|
var GODOT_DEBUG_MODE_DEFAULT2 = process.env["GODOT_DEBUG"] === "true" || DEBUG_MODE2;
|
|
42780
42830
|
var SERVER_VERSION2 = (() => {
|
|
42781
42831
|
try {
|
|
42782
|
-
const pkg = JSON.parse(
|
|
42832
|
+
const pkg = JSON.parse(readFileSync5(new URL("../package.json", import.meta.url), "utf8"));
|
|
42783
42833
|
return typeof pkg.version === "string" ? pkg.version : "0.0.0";
|
|
42784
42834
|
} catch {
|
|
42785
42835
|
return "0.0.0";
|
|
@@ -42791,19 +42841,19 @@ init_class_cache();
|
|
|
42791
42841
|
init_headless();
|
|
42792
42842
|
init_resources();
|
|
42793
42843
|
init_server_version();
|
|
42794
|
-
import { spawnSync } from "node:child_process";
|
|
42844
|
+
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
42795
42845
|
import { createHash as createHash2 } from "node:crypto";
|
|
42796
42846
|
import {
|
|
42797
42847
|
cpSync,
|
|
42798
42848
|
existsSync as existsSync7,
|
|
42799
|
-
mkdirSync as
|
|
42849
|
+
mkdirSync as mkdirSync4,
|
|
42800
42850
|
readdirSync as readdirSync3,
|
|
42801
|
-
readFileSync as
|
|
42802
|
-
rmSync as
|
|
42851
|
+
readFileSync as readFileSync8,
|
|
42852
|
+
rmSync as rmSync4,
|
|
42803
42853
|
statSync as statSync3,
|
|
42804
|
-
writeFileSync as
|
|
42854
|
+
writeFileSync as writeFileSync6
|
|
42805
42855
|
} from "node:fs";
|
|
42806
|
-
import { dirname as
|
|
42856
|
+
import { dirname as dirname4, join as join9 } from "node:path";
|
|
42807
42857
|
import { fileURLToPath } from "node:url";
|
|
42808
42858
|
var ADDONS = ["gdharness_editor", "gdharness_runtime", "auto_reload"];
|
|
42809
42859
|
var EDITOR_PLUGINS = ["gdharness_editor", "auto_reload"];
|
|
@@ -42820,64 +42870,64 @@ function editorAddonDigest(from = shippedAddonsDirectory()) {
|
|
|
42820
42870
|
if (name === VERSION_MARKER || name === DIGEST_MARKER) {
|
|
42821
42871
|
continue;
|
|
42822
42872
|
}
|
|
42823
|
-
const path =
|
|
42873
|
+
const path = join9(directory, name);
|
|
42824
42874
|
const inside = `${relative}/${name}`;
|
|
42825
42875
|
if (statSync3(path).isDirectory()) {
|
|
42826
42876
|
walk(path, inside);
|
|
42827
42877
|
} else {
|
|
42828
42878
|
hash.update(`${inside}
|
|
42829
42879
|
`);
|
|
42830
|
-
hash.update(
|
|
42880
|
+
hash.update(readFileSync8(path));
|
|
42831
42881
|
hash.update(`
|
|
42832
42882
|
`);
|
|
42833
42883
|
}
|
|
42834
42884
|
}
|
|
42835
42885
|
};
|
|
42836
42886
|
for (const name of EDITOR_PLUGINS) {
|
|
42837
|
-
walk(
|
|
42887
|
+
walk(join9(from, name), name);
|
|
42838
42888
|
}
|
|
42839
42889
|
return hash.digest("hex");
|
|
42840
42890
|
}
|
|
42841
42891
|
function installedEditorDigest(projectPath) {
|
|
42842
|
-
const marker =
|
|
42892
|
+
const marker = join9(projectPath, "addons", ADDONS[0], DIGEST_MARKER);
|
|
42843
42893
|
if (!existsSync7(marker)) {
|
|
42844
42894
|
return null;
|
|
42845
42895
|
}
|
|
42846
|
-
const said =
|
|
42896
|
+
const said = readFileSync8(marker, "utf8").trim();
|
|
42847
42897
|
return said === "" ? null : said;
|
|
42848
42898
|
}
|
|
42849
42899
|
function shippedAddonsDirectory() {
|
|
42850
|
-
return
|
|
42900
|
+
return join9(dirname4(fileURLToPath(import.meta.url)), "godot", "addons");
|
|
42851
42901
|
}
|
|
42852
42902
|
function shippedOperationsScript() {
|
|
42853
|
-
return
|
|
42903
|
+
return join9(dirname4(fileURLToPath(import.meta.url)), "godot", "operations", "godot_operations.gd");
|
|
42854
42904
|
}
|
|
42855
42905
|
function installAddons(projectPath, from = shippedAddonsDirectory()) {
|
|
42856
42906
|
const installed = [];
|
|
42857
42907
|
for (const name of ADDONS) {
|
|
42858
|
-
const source =
|
|
42859
|
-
if (!existsSync7(
|
|
42908
|
+
const source = join9(from, name);
|
|
42909
|
+
if (!existsSync7(join9(source, name === "gdharness_runtime" ? "runtime_autoload.gd" : "plugin.cfg"))) {
|
|
42860
42910
|
throw new Error(`The package holds no ${name} addon at ${source}.`);
|
|
42861
42911
|
}
|
|
42862
|
-
const target =
|
|
42912
|
+
const target = join9(projectPath, "addons", name);
|
|
42863
42913
|
const replaced = existsSync7(target);
|
|
42864
|
-
|
|
42865
|
-
|
|
42914
|
+
rmSync4(target, { recursive: true, force: true });
|
|
42915
|
+
mkdirSync4(dirname4(target), { recursive: true });
|
|
42866
42916
|
cpSync(source, target, { recursive: true });
|
|
42867
|
-
|
|
42917
|
+
writeFileSync6(join9(target, VERSION_MARKER), `${SERVER_VERSION}
|
|
42868
42918
|
`);
|
|
42869
42919
|
installed.push({ name, path: target, replaced });
|
|
42870
42920
|
}
|
|
42871
|
-
|
|
42921
|
+
writeFileSync6(join9(projectPath, "addons", ADDONS[0], DIGEST_MARKER), `${editorAddonDigest(from)}
|
|
42872
42922
|
`);
|
|
42873
42923
|
return installed;
|
|
42874
42924
|
}
|
|
42875
42925
|
function removeAddons(projectPath) {
|
|
42876
42926
|
const removed = [];
|
|
42877
42927
|
for (const name of ADDONS) {
|
|
42878
|
-
const target =
|
|
42928
|
+
const target = join9(projectPath, "addons", name);
|
|
42879
42929
|
if (existsSync7(target)) {
|
|
42880
|
-
|
|
42930
|
+
rmSync4(target, { recursive: true, force: true });
|
|
42881
42931
|
removed.push(target);
|
|
42882
42932
|
}
|
|
42883
42933
|
}
|
|
@@ -42911,7 +42961,7 @@ function loaderAmong(projectPath, named) {
|
|
|
42911
42961
|
}
|
|
42912
42962
|
let source = null;
|
|
42913
42963
|
try {
|
|
42914
|
-
source =
|
|
42964
|
+
source = readFileSync8(join9(projectPath, path), "utf8");
|
|
42915
42965
|
} catch {}
|
|
42916
42966
|
if (source?.includes(RUNTIME_AUTOLOAD.path)) {
|
|
42917
42967
|
return { name: entry, path, exists: true };
|
|
@@ -42934,7 +42984,7 @@ function trackedByGit(projectPath, wanted) {
|
|
|
42934
42984
|
if (wanted.length === 0) {
|
|
42935
42985
|
return new Set;
|
|
42936
42986
|
}
|
|
42937
|
-
const asked =
|
|
42987
|
+
const asked = spawnSync2("git", ["ls-files", "-z", "--", ...wanted], {
|
|
42938
42988
|
cwd: projectPath,
|
|
42939
42989
|
encoding: "utf8"
|
|
42940
42990
|
});
|
|
@@ -42953,10 +43003,10 @@ function enabledPlugins(settings) {
|
|
|
42953
43003
|
function inspectProject(projectPath) {
|
|
42954
43004
|
const problems = [];
|
|
42955
43005
|
const addons = ADDONS.map((name) => {
|
|
42956
|
-
const directory =
|
|
42957
|
-
const marker =
|
|
43006
|
+
const directory = join9(projectPath, "addons", name);
|
|
43007
|
+
const marker = join9(directory, VERSION_MARKER);
|
|
42958
43008
|
const installed = existsSync7(directory);
|
|
42959
|
-
const version = existsSync7(marker) ?
|
|
43009
|
+
const version = existsSync7(marker) ? readFileSync8(marker, "utf8").trim() : null;
|
|
42960
43010
|
const current = version === SERVER_VERSION;
|
|
42961
43011
|
if (!installed) {
|
|
42962
43012
|
problems.push(`addons/${name} is not installed; run gdharness setup`);
|
|
@@ -42965,7 +43015,7 @@ function inspectProject(projectPath) {
|
|
|
42965
43015
|
}
|
|
42966
43016
|
return { name, installed, version, current };
|
|
42967
43017
|
});
|
|
42968
|
-
const settings = parseProjectGodot(
|
|
43018
|
+
const settings = parseProjectGodot(readFileSync8(join9(projectPath, "project.godot"), "utf8"));
|
|
42969
43019
|
const pluginsEnabled = enabledPlugins(settings);
|
|
42970
43020
|
for (const name of EDITOR_PLUGINS) {
|
|
42971
43021
|
if (!pluginsEnabled.includes(name)) {
|
|
@@ -42981,7 +43031,7 @@ function inspectProject(projectPath) {
|
|
|
42981
43031
|
const tracked = trackedByGit(projectPath, [...named.values()]);
|
|
42982
43032
|
const addonMissing = (path) => addons.some((addon) => !addon.installed && path.startsWith(`addons/${addon.name}/`));
|
|
42983
43033
|
for (const [name, path] of named) {
|
|
42984
|
-
if (!existsSync7(
|
|
43034
|
+
if (!existsSync7(join9(projectPath, path))) {
|
|
42985
43035
|
if (!addonMissing(path)) {
|
|
42986
43036
|
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`);
|
|
42987
43037
|
}
|
|
@@ -43014,8 +43064,8 @@ function inspectProject(projectPath) {
|
|
|
43014
43064
|
// src/skill.ts
|
|
43015
43065
|
init_setup();
|
|
43016
43066
|
init_tool_definitions();
|
|
43017
|
-
import { existsSync as existsSync9, mkdirSync as
|
|
43018
|
-
import { basename, dirname as
|
|
43067
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync6, readdirSync as readdirSync5, rmSync as rmSync6, writeFileSync as writeFileSync8 } from "node:fs";
|
|
43068
|
+
import { basename as basename2, dirname as dirname6, join as join11 } from "node:path";
|
|
43019
43069
|
|
|
43020
43070
|
// src/tool-reference.ts
|
|
43021
43071
|
init_headless_operations();
|
|
@@ -43111,7 +43161,7 @@ function renderToolsMarkdown() {
|
|
|
43111
43161
|
|
|
43112
43162
|
// src/skill.ts
|
|
43113
43163
|
var SKILL_NAME = "gdharness";
|
|
43114
|
-
var SHARED_SKILLS =
|
|
43164
|
+
var SHARED_SKILLS = join11(".agents", "skills");
|
|
43115
43165
|
function skillMarkdown(version) {
|
|
43116
43166
|
return `---
|
|
43117
43167
|
name: ${SKILL_NAME}
|
|
@@ -43284,27 +43334,27 @@ whatever the engine said on stderr comes back under \`engine_messages\`.
|
|
|
43284
43334
|
function skillFiles(version) {
|
|
43285
43335
|
return new Map([
|
|
43286
43336
|
["SKILL.md", skillMarkdown(version)],
|
|
43287
|
-
[
|
|
43337
|
+
[join11("references", "tools.md"), renderToolsMarkdown()]
|
|
43288
43338
|
]);
|
|
43289
43339
|
}
|
|
43290
43340
|
function skillDirectories(harnesses, projectPath, exists) {
|
|
43291
|
-
const directories = [
|
|
43341
|
+
const directories = [join11(projectPath, SHARED_SKILLS, SKILL_NAME)];
|
|
43292
43342
|
for (const harness of harnesses) {
|
|
43293
43343
|
if (harness.skills === undefined) {
|
|
43294
43344
|
continue;
|
|
43295
43345
|
}
|
|
43296
|
-
const own =
|
|
43297
|
-
if ((!harness.skills.shared || exists(own)) && !directories.includes(
|
|
43298
|
-
directories.push(
|
|
43346
|
+
const own = join11(projectPath, harness.skills.dir);
|
|
43347
|
+
if ((!harness.skills.shared || exists(own)) && !directories.includes(join11(own, SKILL_NAME))) {
|
|
43348
|
+
directories.push(join11(own, SKILL_NAME));
|
|
43299
43349
|
}
|
|
43300
43350
|
}
|
|
43301
43351
|
return directories;
|
|
43302
43352
|
}
|
|
43303
43353
|
function everySkillDirectory(projectPath, harnesses) {
|
|
43304
|
-
const directories = new Set([
|
|
43354
|
+
const directories = new Set([join11(projectPath, SHARED_SKILLS, SKILL_NAME)]);
|
|
43305
43355
|
for (const harness of harnesses) {
|
|
43306
43356
|
if (harness.skills !== undefined) {
|
|
43307
|
-
directories.add(
|
|
43357
|
+
directories.add(join11(projectPath, harness.skills.dir, SKILL_NAME));
|
|
43308
43358
|
}
|
|
43309
43359
|
}
|
|
43310
43360
|
return [...directories];
|
|
@@ -43313,51 +43363,51 @@ function removeSkill(directories, projectPath) {
|
|
|
43313
43363
|
const removed = [];
|
|
43314
43364
|
for (const directory of directories) {
|
|
43315
43365
|
if (existsSync9(directory)) {
|
|
43316
|
-
|
|
43317
|
-
pruneEmpty(
|
|
43366
|
+
rmSync6(directory, { recursive: true, force: true });
|
|
43367
|
+
pruneEmpty(dirname6(directory), projectPath);
|
|
43318
43368
|
removed.push(directory);
|
|
43319
43369
|
}
|
|
43320
43370
|
}
|
|
43321
43371
|
return removed;
|
|
43322
43372
|
}
|
|
43323
43373
|
function pruneEmpty(directory, projectPath) {
|
|
43324
|
-
const shared =
|
|
43374
|
+
const shared = join11(projectPath, ".agents");
|
|
43325
43375
|
let path = directory;
|
|
43326
|
-
while (path !== projectPath && (
|
|
43376
|
+
while (path !== projectPath && (basename2(path) === "skills" || path === shared)) {
|
|
43327
43377
|
if (readdirSync5(path).length > 0) {
|
|
43328
43378
|
return;
|
|
43329
43379
|
}
|
|
43330
|
-
|
|
43331
|
-
path =
|
|
43380
|
+
rmSync6(path, { recursive: true, force: true });
|
|
43381
|
+
path = dirname6(path);
|
|
43332
43382
|
}
|
|
43333
43383
|
}
|
|
43334
43384
|
function writeSkill(directories, version) {
|
|
43335
43385
|
const files = skillFiles(version);
|
|
43336
43386
|
return directories.map((directory) => {
|
|
43337
43387
|
const replaced = existsSync9(directory);
|
|
43338
|
-
|
|
43388
|
+
rmSync6(directory, { recursive: true, force: true });
|
|
43339
43389
|
for (const [name, contents] of files) {
|
|
43340
|
-
const path =
|
|
43341
|
-
|
|
43342
|
-
|
|
43390
|
+
const path = join11(directory, name);
|
|
43391
|
+
mkdirSync6(dirname6(path), { recursive: true });
|
|
43392
|
+
writeFileSync8(path, contents, "utf8");
|
|
43343
43393
|
}
|
|
43344
43394
|
return { path: directory, replaced };
|
|
43345
43395
|
});
|
|
43346
43396
|
}
|
|
43347
43397
|
|
|
43348
43398
|
// src/version.ts
|
|
43349
|
-
import { readFileSync as
|
|
43350
|
-
import { dirname as
|
|
43399
|
+
import { readFileSync as readFileSync10 } from "node:fs";
|
|
43400
|
+
import { dirname as dirname7, join as join12 } from "node:path";
|
|
43351
43401
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
43352
43402
|
function getLocalVersion() {
|
|
43353
|
-
const currentDirectory =
|
|
43403
|
+
const currentDirectory = dirname7(fileURLToPath3(import.meta.url));
|
|
43354
43404
|
const packageCandidates = [
|
|
43355
|
-
|
|
43356
|
-
|
|
43405
|
+
join12(currentDirectory, "..", "package.json"),
|
|
43406
|
+
join12(currentDirectory, "..", "..", "package.json")
|
|
43357
43407
|
];
|
|
43358
43408
|
for (const packagePath of packageCandidates) {
|
|
43359
43409
|
try {
|
|
43360
|
-
const value = JSON.parse(
|
|
43410
|
+
const value = JSON.parse(readFileSync10(packagePath, "utf-8"));
|
|
43361
43411
|
if (typeof value === "object" && value !== null && "name" in value && value.name === "gdharness" && "version" in value && typeof value.version === "string") {
|
|
43362
43412
|
return value.version;
|
|
43363
43413
|
}
|
|
@@ -43370,12 +43420,12 @@ function getLocalVersion() {
|
|
|
43370
43420
|
var args = process.argv.slice(2);
|
|
43371
43421
|
var command = args[0];
|
|
43372
43422
|
|
|
43373
|
-
class UsageError extends
|
|
43423
|
+
class UsageError extends Refusal2 {
|
|
43374
43424
|
}
|
|
43375
43425
|
function projectArgument(at) {
|
|
43376
43426
|
const named = args.slice(at).find((value) => !value.startsWith("--"));
|
|
43377
43427
|
const projectPath = resolve5(named ?? ".");
|
|
43378
|
-
if (!existsSync16(
|
|
43428
|
+
if (!existsSync16(join24(projectPath, "project.godot"))) {
|
|
43379
43429
|
throw new UsageError(`Not a Godot project: ${projectPath} holds no project.godot.`);
|
|
43380
43430
|
}
|
|
43381
43431
|
return projectPath;
|
|
@@ -43394,7 +43444,7 @@ async function engine(projectPath) {
|
|
|
43394
43444
|
}
|
|
43395
43445
|
function said2(outcome, what) {
|
|
43396
43446
|
if (!outcome.ok) {
|
|
43397
|
-
throw new
|
|
43447
|
+
throw new Refusal2(`${what}: ${outcome.message}`);
|
|
43398
43448
|
}
|
|
43399
43449
|
for (const entry of outcome.messages) {
|
|
43400
43450
|
console.error(` engine ${entry.severity}: ${entry.text}`);
|
|
@@ -43414,7 +43464,7 @@ async function registerRuntime(godot, projectPath) {
|
|
|
43414
43464
|
return;
|
|
43415
43465
|
}
|
|
43416
43466
|
console.log(`${RUNTIME_AUTOLOAD.name} autoload left at ${named}, which this project registers itself`);
|
|
43417
|
-
if (!existsSync16(
|
|
43467
|
+
if (!existsSync16(join24(projectPath, (named ?? "").replace(/^res:\/\//, "")))) {
|
|
43418
43468
|
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}`);
|
|
43419
43469
|
}
|
|
43420
43470
|
}
|
|
@@ -43649,7 +43699,8 @@ On ${version}. What is left for you:`);
|
|
|
43649
43699
|
` + ` so it gives an open editor nothing to restart for. editor_status says addonIsStale
|
|
43650
43700
|
` + " if the editor is older than that." : ` 1. The open editor is still running the addons it loaded at startup. Restart it with the
|
|
43651
43701
|
` + " editor_launch restart tool, which closes and reopens the window.");
|
|
43652
|
-
|
|
43702
|
+
const serving = serverServing(projectPath);
|
|
43703
|
+
console.log(harnessNote({ moved, written, byHand }, [launch.command, ...launch.args].join(" "), serving === null ? null : { server: serving, isThisVersion: serving.version === version }));
|
|
43653
43704
|
}
|
|
43654
43705
|
function doctorReport(projectPath) {
|
|
43655
43706
|
const report = inspectProject(projectPath);
|
|
@@ -43809,7 +43860,7 @@ async function main() {
|
|
|
43809
43860
|
}
|
|
43810
43861
|
}
|
|
43811
43862
|
await main().catch((error) => {
|
|
43812
|
-
if (error instanceof
|
|
43863
|
+
if (error instanceof Refusal2) {
|
|
43813
43864
|
console.error("gdharness:", error.message);
|
|
43814
43865
|
process.exit(error instanceof UsageError ? 2 : 1);
|
|
43815
43866
|
}
|