gdharness 1.0.7 → 1.0.9
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 +128 -83
- package/build/index.js +139 -96
- package/package.json +1 -1
package/build/cli.js
CHANGED
|
@@ -31425,7 +31425,7 @@ async function askWindows() {
|
|
|
31425
31425
|
"-NoProfile",
|
|
31426
31426
|
"-NonInteractive",
|
|
31427
31427
|
"-Command",
|
|
31428
|
-
|
|
31428
|
+
`${POWERSHELL_UTF8}Get-CimInstance Win32_Process | ForEach-Object { $began = 0; if ($_.CreationDate) { $began = ([DateTimeOffset]$_.CreationDate).ToUnixTimeMilliseconds() }; "$($_.ProcessId) $($_.ParentProcessId) $began $($_.CommandLine)" }`
|
|
31429
31429
|
], { timeout: ASK_TIMEOUT_MS, windowsHide: true, maxBuffer: 16 * 1024 * 1024 });
|
|
31430
31430
|
return stdout;
|
|
31431
31431
|
}
|
|
@@ -31436,7 +31436,7 @@ async function askPosix() {
|
|
|
31436
31436
|
});
|
|
31437
31437
|
return stdout;
|
|
31438
31438
|
}
|
|
31439
|
-
var run4, ASK_TIMEOUT_MS = 15000;
|
|
31439
|
+
var run4, POWERSHELL_UTF8 = "[Console]::OutputEncoding = [System.Text.Encoding]::UTF8; ", ASK_TIMEOUT_MS = 15000;
|
|
31440
31440
|
var init_process_children = __esm(() => {
|
|
31441
31441
|
run4 = promisify4(execFile4);
|
|
31442
31442
|
});
|
|
@@ -31993,17 +31993,49 @@ var init_bridge_announce = __esm(() => {
|
|
|
31993
31993
|
init_errors();
|
|
31994
31994
|
});
|
|
31995
31995
|
|
|
31996
|
+
// src/class-note.ts
|
|
31997
|
+
import { mkdirSync as mkdirSync9, readFileSync as readFileSync14, writeFileSync as writeFileSync11 } from "node:fs";
|
|
31998
|
+
import { dirname as dirname11, join as join16 } from "node:path";
|
|
31999
|
+
function classNotePath(projectPath) {
|
|
32000
|
+
return join16(projectPath, ".godot", "gdharness-classes.json");
|
|
32001
|
+
}
|
|
32002
|
+
function readClassNote(projectPath) {
|
|
32003
|
+
try {
|
|
32004
|
+
const fields = asParams(JSON.parse(readFileSync14(classNotePath(projectPath), "utf8")));
|
|
32005
|
+
const rebuiltWith = readArray(fields, "rebuiltWith");
|
|
32006
|
+
if (rebuiltWith === undefined || !rebuiltWith.every((name) => typeof name === "string")) {
|
|
32007
|
+
return null;
|
|
32008
|
+
}
|
|
32009
|
+
const pid = readNumber(fields, "shortListEditorPid");
|
|
32010
|
+
return {
|
|
32011
|
+
rebuiltWith: rebuiltWith.map(String),
|
|
32012
|
+
...Object.hasOwn(fields, "shortListEditorPid") ? { shortListEditorPid: pid ?? null } : {}
|
|
32013
|
+
};
|
|
32014
|
+
} catch {
|
|
32015
|
+
return null;
|
|
32016
|
+
}
|
|
32017
|
+
}
|
|
32018
|
+
function writeClassNote(projectPath, note) {
|
|
32019
|
+
try {
|
|
32020
|
+
const path = classNotePath(projectPath);
|
|
32021
|
+
mkdirSync9(dirname11(path), { recursive: true });
|
|
32022
|
+
writeFileSync11(path, `${JSON.stringify(note, null, 2)}
|
|
32023
|
+
`, "utf8");
|
|
32024
|
+
} catch {}
|
|
32025
|
+
}
|
|
32026
|
+
var init_class_note = () => {};
|
|
32027
|
+
|
|
31996
32028
|
// src/config-pin.ts
|
|
31997
|
-
import { existsSync as existsSync11, readFileSync as
|
|
31998
|
-
import { join as
|
|
32029
|
+
import { existsSync as existsSync11, readFileSync as readFileSync15 } from "node:fs";
|
|
32030
|
+
import { join as join17 } from "node:path";
|
|
31999
32031
|
function versionsNamedInConfig(projectPath) {
|
|
32000
|
-
const config =
|
|
32032
|
+
const config = join17(projectPath, ".mcp.json");
|
|
32001
32033
|
if (!existsSync11(config)) {
|
|
32002
32034
|
return [];
|
|
32003
32035
|
}
|
|
32004
32036
|
let text;
|
|
32005
32037
|
try {
|
|
32006
|
-
text =
|
|
32038
|
+
text = readFileSync15(config, "utf8");
|
|
32007
32039
|
JSON.parse(text);
|
|
32008
32040
|
} catch {
|
|
32009
32041
|
return [];
|
|
@@ -36564,7 +36596,7 @@ var init_junit = __esm(() => {
|
|
|
36564
36596
|
import { realpathSync as realpathSync2 } from "node:fs";
|
|
36565
36597
|
import { readFile, realpath } from "node:fs/promises";
|
|
36566
36598
|
import { createConnection as createConnection3 } from "node:net";
|
|
36567
|
-
import { dirname as
|
|
36599
|
+
import { dirname as dirname12, resolve as resolve4 } from "node:path";
|
|
36568
36600
|
import { fileURLToPath as fileURLToPath3, pathToFileURL } from "node:url";
|
|
36569
36601
|
function diagnosticsKey(uri) {
|
|
36570
36602
|
let decoded;
|
|
@@ -36692,7 +36724,7 @@ class GodotLSPClient {
|
|
|
36692
36724
|
if (this.initialized) {
|
|
36693
36725
|
return;
|
|
36694
36726
|
}
|
|
36695
|
-
const rootPath = this.rootPath ??
|
|
36727
|
+
const rootPath = this.rootPath ?? dirname12(resolve4(filePath));
|
|
36696
36728
|
await this.initialize(rootPath);
|
|
36697
36729
|
}
|
|
36698
36730
|
async sendRequest(method, params) {
|
|
@@ -37137,13 +37169,13 @@ var init_process_time = __esm(() => {
|
|
|
37137
37169
|
});
|
|
37138
37170
|
|
|
37139
37171
|
// src/project-scan.ts
|
|
37140
|
-
import { existsSync as existsSync13, readdirSync as readdirSync5, readFileSync as
|
|
37141
|
-
import { join as
|
|
37172
|
+
import { existsSync as existsSync13, readdirSync as readdirSync5, readFileSync as readFileSync16 } from "node:fs";
|
|
37173
|
+
import { join as join18 } from "node:path";
|
|
37142
37174
|
function skipped(name) {
|
|
37143
37175
|
return name.startsWith(".") || SKIPPED.has(name);
|
|
37144
37176
|
}
|
|
37145
37177
|
function steppedOver(directory) {
|
|
37146
|
-
return existsSync13(
|
|
37178
|
+
return existsSync13(join18(directory, ".gdignore"));
|
|
37147
37179
|
}
|
|
37148
37180
|
function projectStructure(projectPath) {
|
|
37149
37181
|
const structure = { scenes: 0, scripts: 0, assets: 0, other: 0 };
|
|
@@ -37156,7 +37188,7 @@ function projectStructure(projectPath) {
|
|
|
37156
37188
|
continue;
|
|
37157
37189
|
}
|
|
37158
37190
|
if (entry.isDirectory()) {
|
|
37159
|
-
visit(
|
|
37191
|
+
visit(join18(directory, entry.name));
|
|
37160
37192
|
} else if (entry.isFile()) {
|
|
37161
37193
|
const extension = entry.name.split(".").pop()?.toLowerCase() ?? "";
|
|
37162
37194
|
if (extension === "tscn") {
|
|
@@ -37186,14 +37218,14 @@ function scriptsWithoutUid(projectPath) {
|
|
|
37186
37218
|
}
|
|
37187
37219
|
const spelled = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
|
|
37188
37220
|
if (entry.isDirectory()) {
|
|
37189
|
-
visit(
|
|
37221
|
+
visit(join18(directory, entry.name), spelled);
|
|
37190
37222
|
continue;
|
|
37191
37223
|
}
|
|
37192
37224
|
if (!entry.isFile()) {
|
|
37193
37225
|
continue;
|
|
37194
37226
|
}
|
|
37195
37227
|
const extension = entry.name.split(".").pop()?.toLowerCase() ?? "";
|
|
37196
|
-
if (CARRIES_A_UID.has(extension) && !existsSync13(
|
|
37228
|
+
if (CARRIES_A_UID.has(extension) && !existsSync13(join18(directory, `${entry.name}.uid`))) {
|
|
37197
37229
|
found.push(spelled);
|
|
37198
37230
|
}
|
|
37199
37231
|
}
|
|
@@ -37228,7 +37260,7 @@ function searchProject(projectPath, options) {
|
|
|
37228
37260
|
if (skipped(entry.name)) {
|
|
37229
37261
|
continue;
|
|
37230
37262
|
}
|
|
37231
|
-
const entryPath =
|
|
37263
|
+
const entryPath = join18(directory, entry.name);
|
|
37232
37264
|
if (entry.isDirectory()) {
|
|
37233
37265
|
visit(entryPath);
|
|
37234
37266
|
continue;
|
|
@@ -37239,7 +37271,7 @@ function searchProject(projectPath, options) {
|
|
|
37239
37271
|
}
|
|
37240
37272
|
result.summary.files_searched += 1;
|
|
37241
37273
|
const matches = [];
|
|
37242
|
-
for (const [index, line] of
|
|
37274
|
+
for (const [index, line] of readFileSync16(entryPath, "utf8").split(`
|
|
37243
37275
|
`).entries()) {
|
|
37244
37276
|
if (full()) {
|
|
37245
37277
|
break;
|
|
@@ -37268,16 +37300,16 @@ var init_project_scan = __esm(() => {
|
|
|
37268
37300
|
});
|
|
37269
37301
|
|
|
37270
37302
|
// src/restart-note.ts
|
|
37271
|
-
import { mkdirSync as
|
|
37272
|
-
import { dirname as
|
|
37303
|
+
import { mkdirSync as mkdirSync10, readFileSync as readFileSync17, rmSync as rmSync8, writeFileSync as writeFileSync12 } from "node:fs";
|
|
37304
|
+
import { dirname as dirname13, join as join19 } from "node:path";
|
|
37273
37305
|
function restartNotePath(projectPath) {
|
|
37274
|
-
return
|
|
37306
|
+
return join19(projectPath, ".godot", "gdharness-restart.json");
|
|
37275
37307
|
}
|
|
37276
37308
|
function noteRestartBegun(note) {
|
|
37277
37309
|
const path = restartNotePath(note.projectPath);
|
|
37278
37310
|
try {
|
|
37279
|
-
|
|
37280
|
-
|
|
37311
|
+
mkdirSync10(dirname13(path), { recursive: true });
|
|
37312
|
+
writeFileSync12(path, `${JSON.stringify(note, null, 2)}
|
|
37281
37313
|
`, "utf8");
|
|
37282
37314
|
} catch (error) {
|
|
37283
37315
|
console.error(`[SERVER] Could not record the restart at ${path}: ${errorMessage(error)}`);
|
|
@@ -37285,7 +37317,7 @@ function noteRestartBegun(note) {
|
|
|
37285
37317
|
}
|
|
37286
37318
|
function restartOwed(projectPath) {
|
|
37287
37319
|
try {
|
|
37288
|
-
const parsed = JSON.parse(
|
|
37320
|
+
const parsed = JSON.parse(readFileSync17(restartNotePath(projectPath), "utf8"));
|
|
37289
37321
|
if (typeof parsed !== "object" || parsed === null) {
|
|
37290
37322
|
return null;
|
|
37291
37323
|
}
|
|
@@ -37311,36 +37343,36 @@ var init_restart_note = __esm(() => {
|
|
|
37311
37343
|
|
|
37312
37344
|
// src/run-record.ts
|
|
37313
37345
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
37314
|
-
import { mkdirSync as
|
|
37315
|
-
import { basename as basename3, join as
|
|
37346
|
+
import { mkdirSync as mkdirSync11, openSync, readdirSync as readdirSync6, readFileSync as readFileSync18, rmSync as rmSync9, statSync as statSync4, writeFileSync as writeFileSync13 } from "node:fs";
|
|
37347
|
+
import { basename as basename3, join as join20 } from "node:path";
|
|
37316
37348
|
function runsDirectory() {
|
|
37317
|
-
return
|
|
37349
|
+
return join20(runtimeDirectory(), "runs");
|
|
37318
37350
|
}
|
|
37319
37351
|
function recordPath() {
|
|
37320
|
-
return
|
|
37352
|
+
return join20(runsDirectory(), "run.json");
|
|
37321
37353
|
}
|
|
37322
37354
|
function recordPaths() {
|
|
37323
|
-
return runtimeDirectories().map((directory) =>
|
|
37355
|
+
return runtimeDirectories().map((directory) => join20(directory, "runs", "run.json"));
|
|
37324
37356
|
}
|
|
37325
37357
|
function openTranscript(startedAt) {
|
|
37326
|
-
|
|
37327
|
-
const path =
|
|
37358
|
+
mkdirSync11(runsDirectory(), { recursive: true });
|
|
37359
|
+
const path = join20(runsDirectory(), `run-${startedAt}.log`);
|
|
37328
37360
|
return { path, fd: openSync(path, "a") };
|
|
37329
37361
|
}
|
|
37330
37362
|
function writeRunRecord(record) {
|
|
37331
|
-
|
|
37332
|
-
|
|
37363
|
+
mkdirSync11(runsDirectory(), { recursive: true });
|
|
37364
|
+
writeFileSync13(recordPath(), JSON.stringify(record, null, 2), "utf8");
|
|
37333
37365
|
}
|
|
37334
37366
|
function editorNotePath() {
|
|
37335
|
-
return
|
|
37367
|
+
return join20(runsDirectory(), "editor-run.json");
|
|
37336
37368
|
}
|
|
37337
37369
|
function writeEditorRunNote(note) {
|
|
37338
|
-
|
|
37339
|
-
|
|
37370
|
+
mkdirSync11(runsDirectory(), { recursive: true });
|
|
37371
|
+
writeFileSync13(editorNotePath(), JSON.stringify(note, null, 2), "utf8");
|
|
37340
37372
|
}
|
|
37341
37373
|
function readEditorRunNote() {
|
|
37342
37374
|
for (const directory of runtimeDirectories()) {
|
|
37343
|
-
const note = editorNoteAt(
|
|
37375
|
+
const note = editorNoteAt(join20(directory, "runs", "editor-run.json"));
|
|
37344
37376
|
if (note !== null) {
|
|
37345
37377
|
return note;
|
|
37346
37378
|
}
|
|
@@ -37350,7 +37382,7 @@ function readEditorRunNote() {
|
|
|
37350
37382
|
function editorNoteAt(path) {
|
|
37351
37383
|
let parsed;
|
|
37352
37384
|
try {
|
|
37353
|
-
parsed = JSON.parse(
|
|
37385
|
+
parsed = JSON.parse(readFileSync18(path, "utf8"));
|
|
37354
37386
|
} catch {
|
|
37355
37387
|
return null;
|
|
37356
37388
|
}
|
|
@@ -37373,7 +37405,7 @@ function recordRunEnded(pid, exitCode) {
|
|
|
37373
37405
|
return;
|
|
37374
37406
|
}
|
|
37375
37407
|
try {
|
|
37376
|
-
|
|
37408
|
+
writeFileSync13(path, JSON.stringify({ ...record, exitCode }, null, 2), "utf8");
|
|
37377
37409
|
} catch {}
|
|
37378
37410
|
}
|
|
37379
37411
|
function readRunRecord() {
|
|
@@ -37388,7 +37420,7 @@ function readRunRecord() {
|
|
|
37388
37420
|
function recordAt(path) {
|
|
37389
37421
|
let parsed;
|
|
37390
37422
|
try {
|
|
37391
|
-
parsed = JSON.parse(
|
|
37423
|
+
parsed = JSON.parse(readFileSync18(path, "utf8"));
|
|
37392
37424
|
} catch {
|
|
37393
37425
|
return null;
|
|
37394
37426
|
}
|
|
@@ -37433,7 +37465,7 @@ function runningAs(pid) {
|
|
|
37433
37465
|
const began = startedAt(pid, null);
|
|
37434
37466
|
const when = began === null ? {} : { startedAt: began };
|
|
37435
37467
|
if (process.platform === "linux") {
|
|
37436
|
-
const raw =
|
|
37468
|
+
const raw = readFileSync18(`/proc/${pid}/cmdline`, "utf8").replaceAll("\x00", " ").trim();
|
|
37437
37469
|
return raw === "" ? null : { kind: "commandLine", text: raw, ...when };
|
|
37438
37470
|
}
|
|
37439
37471
|
const args = execFileSync2("ps", ["-p", String(pid), "-o", "args="], {
|
|
@@ -37486,7 +37518,7 @@ function windowsCommandLine(pid) {
|
|
|
37486
37518
|
"-NoProfile",
|
|
37487
37519
|
"-NonInteractive",
|
|
37488
37520
|
"-Command",
|
|
37489
|
-
`$p = Get-CimInstance Win32_Process -Filter "ProcessId=${pid}"; if ($p) { $p.CreationDate.ToUniversalTime().ToString("o"); $p.CommandLine }`
|
|
37521
|
+
`${POWERSHELL_UTF8}$p = Get-CimInstance Win32_Process -Filter "ProcessId=${pid}"; if ($p) { $p.CreationDate.ToUniversalTime().ToString("o"); $p.CommandLine }`
|
|
37490
37522
|
], { encoding: "utf8", timeout: 15000, windowsHide: true }).trim();
|
|
37491
37523
|
return answer === "" ? null : answer;
|
|
37492
37524
|
} catch {
|
|
@@ -37584,7 +37616,7 @@ function sweepTranscripts(keepMs = 24 * 60 * 60 * 1000, now = Date.now()) {
|
|
|
37584
37616
|
if (!entry.startsWith("run-") || !entry.endsWith(".log")) {
|
|
37585
37617
|
continue;
|
|
37586
37618
|
}
|
|
37587
|
-
const path =
|
|
37619
|
+
const path = join20(directory, entry);
|
|
37588
37620
|
if (path === keep || ageMs(path, now) < keepMs) {
|
|
37589
37621
|
continue;
|
|
37590
37622
|
}
|
|
@@ -37595,6 +37627,7 @@ function sweepTranscripts(keepMs = 24 * 60 * 60 * 1000, now = Date.now()) {
|
|
|
37595
37627
|
}
|
|
37596
37628
|
var AN_EDITOR, SAME_RUN_WINDOW_MS = 1e4;
|
|
37597
37629
|
var init_run_record = __esm(() => {
|
|
37630
|
+
init_process_children();
|
|
37598
37631
|
init_runtime_client();
|
|
37599
37632
|
AN_EDITOR = /(?:^|\s)(?:-e|--editor)(?:\s|$)/;
|
|
37600
37633
|
});
|
|
@@ -37607,16 +37640,17 @@ import {
|
|
|
37607
37640
|
closeSync,
|
|
37608
37641
|
existsSync as existsSync14,
|
|
37609
37642
|
fstatSync,
|
|
37610
|
-
mkdirSync as
|
|
37643
|
+
mkdirSync as mkdirSync12,
|
|
37611
37644
|
mkdtempSync as mkdtempSync3,
|
|
37612
37645
|
openSync as openSync2,
|
|
37613
37646
|
readdirSync as readdirSync7,
|
|
37614
|
-
readFileSync as
|
|
37647
|
+
readFileSync as readFileSync19,
|
|
37615
37648
|
readSync,
|
|
37649
|
+
rmdirSync,
|
|
37616
37650
|
statSync as statSync5
|
|
37617
37651
|
} from "node:fs";
|
|
37618
37652
|
import { tmpdir as tmpdir5 } from "node:os";
|
|
37619
|
-
import { basename as basename4, dirname as
|
|
37653
|
+
import { basename as basename4, dirname as dirname14, join as join21, normalize as normalize3, relative as relative3 } from "node:path";
|
|
37620
37654
|
import { setTimeout as delay3 } from "node:timers/promises";
|
|
37621
37655
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
37622
37656
|
import { promisify as promisify7 } from "node:util";
|
|
@@ -37858,7 +37892,7 @@ function aboveTheRunner(entry) {
|
|
|
37858
37892
|
}
|
|
37859
37893
|
function elsewhereIn(projectPath, asked) {
|
|
37860
37894
|
const wanted = asked.replace(/^res:\/\//, "").replace(/\/+$/, "");
|
|
37861
|
-
if (wanted === "" || existsSync14(
|
|
37895
|
+
if (wanted === "" || existsSync14(join21(projectPath, wanted))) {
|
|
37862
37896
|
return "";
|
|
37863
37897
|
}
|
|
37864
37898
|
const root = (wanted.split("/")[0] ?? "").toLowerCase();
|
|
@@ -37872,7 +37906,7 @@ function elsewhereIn(projectPath, asked) {
|
|
|
37872
37906
|
return near.length === 0 ? "" : ` The project does have ${there}.`;
|
|
37873
37907
|
}
|
|
37874
37908
|
function hasMainScene(projectFile) {
|
|
37875
|
-
const scene = parseProjectGodot(
|
|
37909
|
+
const scene = parseProjectGodot(readFileSync19(projectFile, "utf8"))["application"]?.["run/main_scene"];
|
|
37876
37910
|
return typeof scene === "string" && scene !== "";
|
|
37877
37911
|
}
|
|
37878
37912
|
function runtimeAddonAnnouncesEditors(projectPath) {
|
|
@@ -37880,13 +37914,13 @@ function runtimeAddonAnnouncesEditors(projectPath) {
|
|
|
37880
37914
|
return false;
|
|
37881
37915
|
}
|
|
37882
37916
|
try {
|
|
37883
|
-
return
|
|
37917
|
+
return readFileSync19(join21(projectPath, RUNTIME_AUTOLOAD2.path), "utf8").includes('"editor_pid"');
|
|
37884
37918
|
} catch {
|
|
37885
37919
|
return false;
|
|
37886
37920
|
}
|
|
37887
37921
|
}
|
|
37888
37922
|
function editorPlaysHeadless(projectFile) {
|
|
37889
|
-
const runArgs = parseProjectGodot(
|
|
37923
|
+
const runArgs = parseProjectGodot(readFileSync19(projectFile, "utf8"))["editor"]?.["run/main_run_args"];
|
|
37890
37924
|
return typeof runArgs === "string" && /(?:^|\s)--headless(?:\s|$)/.test(runArgs);
|
|
37891
37925
|
}
|
|
37892
37926
|
function camelCased(params) {
|
|
@@ -37902,7 +37936,7 @@ function camelCased(params) {
|
|
|
37902
37936
|
class GodotServer {
|
|
37903
37937
|
mcp;
|
|
37904
37938
|
locator = new GodotLocator2;
|
|
37905
|
-
operationsScript =
|
|
37939
|
+
operationsScript = join21(__dirname2, "godot", "operations", "godot_operations.gd");
|
|
37906
37940
|
godotBridge;
|
|
37907
37941
|
tools = buildToolDefinitions();
|
|
37908
37942
|
activeProcess = null;
|
|
@@ -37920,8 +37954,6 @@ class GodotServer {
|
|
|
37920
37954
|
successorWatch = null;
|
|
37921
37955
|
lastProjectPath = null;
|
|
37922
37956
|
shutdownInitiated = false;
|
|
37923
|
-
shortListEditors = new Map;
|
|
37924
|
-
cacheLastRebuilt = new Map;
|
|
37925
37957
|
launchedEditor = null;
|
|
37926
37958
|
launchedFrom = null;
|
|
37927
37959
|
ownProject;
|
|
@@ -38569,7 +38601,7 @@ class GodotServer {
|
|
|
38569
38601
|
if (path === undefined) {
|
|
38570
38602
|
return { ok: false, response: this.createErrorResponse("projectPath is required.") };
|
|
38571
38603
|
}
|
|
38572
|
-
const file =
|
|
38604
|
+
const file = join21(path, "project.godot");
|
|
38573
38605
|
if (!existsSync14(file)) {
|
|
38574
38606
|
return {
|
|
38575
38607
|
ok: false,
|
|
@@ -38793,7 +38825,7 @@ class GodotServer {
|
|
|
38793
38825
|
if (!engine.ok) {
|
|
38794
38826
|
return engine.response;
|
|
38795
38827
|
}
|
|
38796
|
-
const application = parseProjectGodot(
|
|
38828
|
+
const application = parseProjectGodot(readFileSync19(project.value.file, "utf8"))["application"] ?? {};
|
|
38797
38829
|
const name = application["config/name"];
|
|
38798
38830
|
const mainScene = application["run/main_scene"];
|
|
38799
38831
|
const info = {
|
|
@@ -38854,18 +38886,18 @@ class GodotServer {
|
|
|
38854
38886
|
if (!engine.ok) {
|
|
38855
38887
|
return engine.response;
|
|
38856
38888
|
}
|
|
38857
|
-
const folder =
|
|
38889
|
+
const folder = dirname14(output.absolutePath);
|
|
38858
38890
|
try {
|
|
38859
|
-
|
|
38891
|
+
mkdirSync12(folder, { recursive: true });
|
|
38860
38892
|
} catch (error) {
|
|
38861
|
-
return this.createErrorResponse(`Cannot create ${
|
|
38893
|
+
return this.createErrorResponse(`Cannot create ${dirname14(output.relativePath)} to export into: ${errorMessage(error)}`);
|
|
38862
38894
|
}
|
|
38863
38895
|
const debug = readBoolean(args, "debug") ?? false;
|
|
38864
|
-
const exportLogs = mkdtempSync3(
|
|
38896
|
+
const exportLogs = mkdtempSync3(join21(tmpdir5(), "gdharness-export-"));
|
|
38865
38897
|
const exportArgs = [
|
|
38866
38898
|
"--headless",
|
|
38867
38899
|
"--log-file",
|
|
38868
|
-
|
|
38900
|
+
join21(exportLogs, "engine.log"),
|
|
38869
38901
|
"--path",
|
|
38870
38902
|
project.value.path,
|
|
38871
38903
|
debug ? "--export-debug" : "--export-release",
|
|
@@ -38923,7 +38955,7 @@ class GodotServer {
|
|
|
38923
38955
|
return contained.response;
|
|
38924
38956
|
}
|
|
38925
38957
|
const runner = "addons/gdUnit4/bin/GdUnitCmdTool.gd";
|
|
38926
|
-
if (!existsSync14(
|
|
38958
|
+
if (!existsSync14(join21(project.value.path, runner))) {
|
|
38927
38959
|
return this.createErrorResponse(`gdUnit4 is not installed in this project: no ${runner}.`, [
|
|
38928
38960
|
"Install gdUnit4 under addons/gdUnit4, from https://github.com/godot-gdunit-labs/gdUnit4"
|
|
38929
38961
|
]);
|
|
@@ -38958,7 +38990,7 @@ class GodotServer {
|
|
|
38958
38990
|
];
|
|
38959
38991
|
const timeoutMs = readPositiveNumber(args, "timeoutMs") ?? 600000;
|
|
38960
38992
|
this.logDebug(`Running tests: ${engine.value} ${cmdArgs.join(" ")}`);
|
|
38961
|
-
const userData = mkdtempSync3(
|
|
38993
|
+
const userData = mkdtempSync3(join21(tmpdir5(), "gdharness-tests-"));
|
|
38962
38994
|
const run = this.spawnGame(engine.value, cmdArgs, userDataIn(userData));
|
|
38963
38995
|
const hung = await new Promise((resolve) => {
|
|
38964
38996
|
const timer = setTimeout(() => {
|
|
@@ -38974,19 +39006,22 @@ class GodotServer {
|
|
|
38974
39006
|
resolve(false);
|
|
38975
39007
|
});
|
|
38976
39008
|
});
|
|
38977
|
-
const reportsDir =
|
|
39009
|
+
const reportsDir = join21(project.value.path, ".godot", ...ours.split("/"));
|
|
38978
39010
|
let report = null;
|
|
38979
39011
|
let reportProblem = null;
|
|
38980
39012
|
try {
|
|
38981
39013
|
const written = existsSync14(reportsDir) ? readdirSync7(reportsDir).filter((name) => name.startsWith("report_")).sort((a, b) => Number(a.slice("report_".length)) - Number(b.slice("report_".length))) : [];
|
|
38982
39014
|
const newest = written.at(-1);
|
|
38983
39015
|
if (newest !== undefined) {
|
|
38984
|
-
report = parseJUnit(
|
|
39016
|
+
report = parseJUnit(readFileSync19(join21(reportsDir, newest, "results.xml"), "utf8"));
|
|
38985
39017
|
}
|
|
38986
39018
|
} catch (error) {
|
|
38987
39019
|
reportProblem = errorMessage(error);
|
|
38988
39020
|
} finally {
|
|
38989
39021
|
discard(reportsDir, userData);
|
|
39022
|
+
try {
|
|
39023
|
+
rmdirSync(dirname14(reportsDir));
|
|
39024
|
+
} catch {}
|
|
38990
39025
|
}
|
|
38991
39026
|
const engineEntries = run.log.select({ severity: "warning", sinceLastCall: false, limit: 200 }).entries.map(aboveTheRunner);
|
|
38992
39027
|
const verdicts = {
|
|
@@ -39121,7 +39156,7 @@ class GodotServer {
|
|
|
39121
39156
|
return null;
|
|
39122
39157
|
}
|
|
39123
39158
|
try {
|
|
39124
|
-
return
|
|
39159
|
+
return readFileSync19(contained.absolutePath, "utf8");
|
|
39125
39160
|
} catch {
|
|
39126
39161
|
return null;
|
|
39127
39162
|
}
|
|
@@ -39159,8 +39194,15 @@ class GodotServer {
|
|
|
39159
39194
|
};
|
|
39160
39195
|
}
|
|
39161
39196
|
editorWritesAShortList(projectPath) {
|
|
39162
|
-
const
|
|
39163
|
-
return
|
|
39197
|
+
const recorded = readClassNote(projectPath)?.shortListEditorPid;
|
|
39198
|
+
return recorded !== undefined && recorded === (this.godotBridge.getStatus().editorPid ?? null);
|
|
39199
|
+
}
|
|
39200
|
+
noteAShortListEditor(projectPath) {
|
|
39201
|
+
const note = readClassNote(projectPath);
|
|
39202
|
+
writeClassNote(projectPath, {
|
|
39203
|
+
rebuiltWith: note?.rebuiltWith ?? [],
|
|
39204
|
+
shortListEditorPid: this.godotBridge.getStatus().editorPid ?? null
|
|
39205
|
+
});
|
|
39164
39206
|
}
|
|
39165
39207
|
sourceOfClass(projectPath, resourcePath) {
|
|
39166
39208
|
const contained = resolveWithinProject(projectPath, resourcePath);
|
|
@@ -39168,7 +39210,7 @@ class GodotServer {
|
|
|
39168
39210
|
return null;
|
|
39169
39211
|
}
|
|
39170
39212
|
try {
|
|
39171
|
-
return
|
|
39213
|
+
return readFileSync19(contained.absolutePath, "utf8");
|
|
39172
39214
|
} catch {
|
|
39173
39215
|
return null;
|
|
39174
39216
|
}
|
|
@@ -39397,7 +39439,7 @@ class GodotServer {
|
|
|
39397
39439
|
return null;
|
|
39398
39440
|
}
|
|
39399
39441
|
try {
|
|
39400
|
-
return settingKeys(
|
|
39442
|
+
return settingKeys(readFileSync19(join21(projectPath, "project.godot"), "utf8"));
|
|
39401
39443
|
} catch {
|
|
39402
39444
|
return null;
|
|
39403
39445
|
}
|
|
@@ -39639,7 +39681,7 @@ class GodotServer {
|
|
|
39639
39681
|
}
|
|
39640
39682
|
async runtimeUp(projectPath, before, budget, withArgs) {
|
|
39641
39683
|
const budgetMs = budget.ms;
|
|
39642
|
-
if (!existsSync14(
|
|
39684
|
+
if (!existsSync14(join21(projectPath, RUNTIME_AUTOLOAD2.path))) {
|
|
39643
39685
|
return runtimeVerdict(null, { addon: false, budgetMs, heldAt: null, running: false, withArgs });
|
|
39644
39686
|
}
|
|
39645
39687
|
let asking = null;
|
|
@@ -40512,7 +40554,7 @@ class GodotServer {
|
|
|
40512
40554
|
const after = projectPath === "" || busy ? null : cachedClasses(projectPath);
|
|
40513
40555
|
const lost = before === null || after === null ? [] : [...before.keys()].filter((name) => !after.has(name));
|
|
40514
40556
|
if (lost.length > 0) {
|
|
40515
|
-
this.
|
|
40557
|
+
this.noteAShortListEditor(projectPath);
|
|
40516
40558
|
}
|
|
40517
40559
|
const atMissingPaths = after === null ? [] : cachedAtMissingPaths(after, projectPath);
|
|
40518
40560
|
const restored = lost.length > 0 || atMissingPaths.length > 0 ? await this.rebuildCacheAfterLoss(projectPath, lost) : [];
|
|
@@ -40578,9 +40620,8 @@ class GodotServer {
|
|
|
40578
40620
|
return now === null ? [] : lost.filter((name) => now.has(name));
|
|
40579
40621
|
}
|
|
40580
40622
|
async rebuildClassCache(projectPath) {
|
|
40581
|
-
const key = resolve5(projectPath);
|
|
40582
40623
|
const before = cachedClasses(projectPath);
|
|
40583
|
-
const
|
|
40624
|
+
const noted = readClassNote(projectPath);
|
|
40584
40625
|
const rebuilt = await this.operation("refresh_class_cache", {}, projectPath);
|
|
40585
40626
|
if (!rebuilt.ok) {
|
|
40586
40627
|
return rebuilt;
|
|
@@ -40589,12 +40630,15 @@ class GodotServer {
|
|
|
40589
40630
|
if (after === null) {
|
|
40590
40631
|
return rebuilt;
|
|
40591
40632
|
}
|
|
40592
|
-
|
|
40593
|
-
const
|
|
40594
|
-
|
|
40633
|
+
const lost = noted === null || before === null ? [] : noted.rebuiltWith.filter((name) => !before.has(name) && after.has(name)).sort();
|
|
40634
|
+
const seenLosing = lost.length > 0 && this.godotBridge.isConnected();
|
|
40635
|
+
writeClassNote(projectPath, {
|
|
40636
|
+
rebuiltWith: [...after.keys()].sort(),
|
|
40637
|
+
...seenLosing ? { shortListEditorPid: this.godotBridge.getStatus().editorPid ?? null } : noted?.shortListEditorPid === undefined ? {} : { shortListEditorPid: noted.shortListEditorPid }
|
|
40638
|
+
});
|
|
40639
|
+
if (!seenLosing) {
|
|
40595
40640
|
return rebuilt;
|
|
40596
40641
|
}
|
|
40597
|
-
this.shortListEditors.set(key, this.godotBridge.getStatus().editorPid);
|
|
40598
40642
|
const them = lost.length === 1 ? "it" : "them";
|
|
40599
40643
|
return {
|
|
40600
40644
|
...rebuilt,
|
|
@@ -40751,8 +40795,8 @@ class GodotServer {
|
|
|
40751
40795
|
]);
|
|
40752
40796
|
}
|
|
40753
40797
|
const expectsScreenshot = command === "capture_screenshot" || command === "capture_viewport";
|
|
40754
|
-
const screenshotDir = expectsScreenshot ? mkdtempSync3(
|
|
40755
|
-
const screenshotPath = screenshotDir ?
|
|
40798
|
+
const screenshotDir = expectsScreenshot ? mkdtempSync3(join21(tmpdir5(), "gdharness-runtime-screenshot-")) : null;
|
|
40799
|
+
const screenshotPath = screenshotDir ? join21(screenshotDir, "capture.png") : null;
|
|
40756
40800
|
try {
|
|
40757
40801
|
const reply = await runtimeRequest(choice.endpoint, command, screenshotPath ? { ...params, output_path: screenshotPath } : params, timeoutMs);
|
|
40758
40802
|
if (!reply.ok) {
|
|
@@ -40776,7 +40820,7 @@ class GodotServer {
|
|
|
40776
40820
|
type: "text",
|
|
40777
40821
|
text: `Screenshot captured: ${dimensions}${chosenAmongSeveral ? ` from pid ${own}, the game this server holds` : ""}`
|
|
40778
40822
|
},
|
|
40779
|
-
{ type: "image", data:
|
|
40823
|
+
{ type: "image", data: readFileSync19(screenshotPath).toString("base64"), mimeType: "image/png" }
|
|
40780
40824
|
]
|
|
40781
40825
|
};
|
|
40782
40826
|
} finally {
|
|
@@ -40852,6 +40896,7 @@ var init_server3 = __esm(() => {
|
|
|
40852
40896
|
init_breakpoint_note();
|
|
40853
40897
|
init_bridge_announce();
|
|
40854
40898
|
init_class_cache();
|
|
40899
|
+
init_class_note();
|
|
40855
40900
|
init_config_pin();
|
|
40856
40901
|
init_dap_client();
|
|
40857
40902
|
init_errors();
|
|
@@ -40878,7 +40923,7 @@ var init_server3 = __esm(() => {
|
|
|
40878
40923
|
init_tool_definitions();
|
|
40879
40924
|
init_update_check();
|
|
40880
40925
|
run7 = promisify7(execFile7);
|
|
40881
|
-
__dirname2 =
|
|
40926
|
+
__dirname2 = dirname14(fileURLToPath4(import.meta.url));
|
|
40882
40927
|
CONSOLE_WRAPPER = /_console(\.exe)?$/i;
|
|
40883
40928
|
LONGEST_BOOT_NOTED_MS = 5 * 60000;
|
|
40884
40929
|
VALUE_NAMES = {
|
|
@@ -40919,7 +40964,7 @@ var init_server3 = __esm(() => {
|
|
|
40919
40964
|
|
|
40920
40965
|
// src/cli.ts
|
|
40921
40966
|
import { existsSync as existsSync15 } from "fs";
|
|
40922
|
-
import { join as
|
|
40967
|
+
import { join as join22, resolve as resolve5 } from "path";
|
|
40923
40968
|
|
|
40924
40969
|
// src/errors.ts
|
|
40925
40970
|
class Refusal extends Error {
|
|
@@ -42623,8 +42668,8 @@ class UsageError extends Refusal {
|
|
|
42623
42668
|
}
|
|
42624
42669
|
function projectArgument(at) {
|
|
42625
42670
|
const named = args.slice(at).find((value) => !value.startsWith("--"));
|
|
42626
|
-
const projectPath =
|
|
42627
|
-
if (!existsSync15(
|
|
42671
|
+
const projectPath = resolve5(named ?? ".");
|
|
42672
|
+
if (!existsSync15(join22(projectPath, "project.godot"))) {
|
|
42628
42673
|
throw new UsageError(`Not a Godot project: ${projectPath} holds no project.godot.`);
|
|
42629
42674
|
}
|
|
42630
42675
|
return projectPath;
|
|
@@ -42663,7 +42708,7 @@ async function registerRuntime(godot, projectPath) {
|
|
|
42663
42708
|
return;
|
|
42664
42709
|
}
|
|
42665
42710
|
console.log(`${RUNTIME_AUTOLOAD.name} autoload left at ${named}, which this project registers itself`);
|
|
42666
|
-
if (!existsSync15(
|
|
42711
|
+
if (!existsSync15(join22(projectPath, (named ?? "").replace(/^res:\/\//, "")))) {
|
|
42667
42712
|
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}`);
|
|
42668
42713
|
}
|
|
42669
42714
|
}
|
package/build/index.js
CHANGED
|
@@ -10175,16 +10175,17 @@ import {
|
|
|
10175
10175
|
closeSync,
|
|
10176
10176
|
existsSync as existsSync9,
|
|
10177
10177
|
fstatSync,
|
|
10178
|
-
mkdirSync as
|
|
10178
|
+
mkdirSync as mkdirSync9,
|
|
10179
10179
|
mkdtempSync as mkdtempSync2,
|
|
10180
10180
|
openSync as openSync2,
|
|
10181
10181
|
readdirSync as readdirSync6,
|
|
10182
|
-
readFileSync as
|
|
10182
|
+
readFileSync as readFileSync15,
|
|
10183
10183
|
readSync,
|
|
10184
|
+
rmdirSync,
|
|
10184
10185
|
statSync as statSync5
|
|
10185
10186
|
} from "node:fs";
|
|
10186
10187
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
10187
|
-
import { basename as basename3, dirname as
|
|
10188
|
+
import { basename as basename3, dirname as dirname9, join as join15, normalize as normalize2, relative as relative3 } from "node:path";
|
|
10188
10189
|
import { setTimeout as delay3 } from "node:timers/promises";
|
|
10189
10190
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
10190
10191
|
import { promisify as promisify5 } from "node:util";
|
|
@@ -24817,6 +24818,7 @@ import { execFile, execFileSync, spawnSync } from "node:child_process";
|
|
|
24817
24818
|
import { basename } from "node:path";
|
|
24818
24819
|
import { promisify } from "node:util";
|
|
24819
24820
|
var run = promisify(execFile);
|
|
24821
|
+
var POWERSHELL_UTF8 = "[Console]::OutputEncoding = [System.Text.Encoding]::UTF8; ";
|
|
24820
24822
|
function startTimesOf(pids) {
|
|
24821
24823
|
const began = new Map;
|
|
24822
24824
|
if (pids.length === 0) {
|
|
@@ -24999,7 +25001,7 @@ async function askWindows() {
|
|
|
24999
25001
|
"-NoProfile",
|
|
25000
25002
|
"-NonInteractive",
|
|
25001
25003
|
"-Command",
|
|
25002
|
-
|
|
25004
|
+
`${POWERSHELL_UTF8}Get-CimInstance Win32_Process | ForEach-Object { $began = 0; if ($_.CreationDate) { $began = ([DateTimeOffset]$_.CreationDate).ToUnixTimeMilliseconds() }; "$($_.ProcessId) $($_.ParentProcessId) $began $($_.CommandLine)" }`
|
|
25003
25005
|
], { timeout: ASK_TIMEOUT_MS, windowsHide: true, maxBuffer: 16 * 1024 * 1024 });
|
|
25004
25006
|
return stdout;
|
|
25005
25007
|
}
|
|
@@ -25814,18 +25816,49 @@ function unseenByEditor(projectPath, editorHolds) {
|
|
|
25814
25816
|
return [...declaredClasses(projectPath)].filter(([name]) => !held.has(name)).map(([className, path]) => ({ className, path })).sort((one, other) => one.className < other.className ? -1 : one.className > other.className ? 1 : 0);
|
|
25815
25817
|
}
|
|
25816
25818
|
|
|
25819
|
+
// src/class-note.ts
|
|
25820
|
+
import { mkdirSync as mkdirSync5, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "node:fs";
|
|
25821
|
+
import { dirname as dirname5, join as join7 } from "node:path";
|
|
25822
|
+
function classNotePath(projectPath) {
|
|
25823
|
+
return join7(projectPath, ".godot", "gdharness-classes.json");
|
|
25824
|
+
}
|
|
25825
|
+
function readClassNote(projectPath) {
|
|
25826
|
+
try {
|
|
25827
|
+
const fields = asParams(JSON.parse(readFileSync8(classNotePath(projectPath), "utf8")));
|
|
25828
|
+
const rebuiltWith = readArray(fields, "rebuiltWith");
|
|
25829
|
+
if (rebuiltWith === undefined || !rebuiltWith.every((name) => typeof name === "string")) {
|
|
25830
|
+
return null;
|
|
25831
|
+
}
|
|
25832
|
+
const pid = readNumber(fields, "shortListEditorPid");
|
|
25833
|
+
return {
|
|
25834
|
+
rebuiltWith: rebuiltWith.map(String),
|
|
25835
|
+
...Object.hasOwn(fields, "shortListEditorPid") ? { shortListEditorPid: pid ?? null } : {}
|
|
25836
|
+
};
|
|
25837
|
+
} catch {
|
|
25838
|
+
return null;
|
|
25839
|
+
}
|
|
25840
|
+
}
|
|
25841
|
+
function writeClassNote(projectPath, note) {
|
|
25842
|
+
try {
|
|
25843
|
+
const path = classNotePath(projectPath);
|
|
25844
|
+
mkdirSync5(dirname5(path), { recursive: true });
|
|
25845
|
+
writeFileSync5(path, `${JSON.stringify(note, null, 2)}
|
|
25846
|
+
`, "utf8");
|
|
25847
|
+
} catch {}
|
|
25848
|
+
}
|
|
25849
|
+
|
|
25817
25850
|
// src/config-pin.ts
|
|
25818
|
-
import { existsSync as existsSync4, readFileSync as
|
|
25819
|
-
import { join as
|
|
25851
|
+
import { existsSync as existsSync4, readFileSync as readFileSync9 } from "node:fs";
|
|
25852
|
+
import { join as join8 } from "node:path";
|
|
25820
25853
|
var PINNED = /\bgdharness@(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)\b/g;
|
|
25821
25854
|
function versionsNamedInConfig(projectPath) {
|
|
25822
|
-
const config =
|
|
25855
|
+
const config = join8(projectPath, ".mcp.json");
|
|
25823
25856
|
if (!existsSync4(config)) {
|
|
25824
25857
|
return [];
|
|
25825
25858
|
}
|
|
25826
25859
|
let text;
|
|
25827
25860
|
try {
|
|
25828
|
-
text =
|
|
25861
|
+
text = readFileSync9(config, "utf8");
|
|
25829
25862
|
JSON.parse(text);
|
|
25830
25863
|
} catch {
|
|
25831
25864
|
return [];
|
|
@@ -27192,7 +27225,7 @@ import { promisify as promisify2 } from "node:util";
|
|
|
27192
27225
|
// src/detection.ts
|
|
27193
27226
|
import { existsSync as existsSync5, readdirSync as readdirSync3, statSync as statSync3 } from "node:fs";
|
|
27194
27227
|
import { homedir as homedir2 } from "node:os";
|
|
27195
|
-
import { join as
|
|
27228
|
+
import { join as join9 } from "node:path";
|
|
27196
27229
|
function resolveHomeDirectory() {
|
|
27197
27230
|
try {
|
|
27198
27231
|
return homedir2();
|
|
@@ -27216,7 +27249,7 @@ function scanDirectoryForGodotBinaries(directory, platform) {
|
|
|
27216
27249
|
if (!pattern.test(name)) {
|
|
27217
27250
|
continue;
|
|
27218
27251
|
}
|
|
27219
|
-
const fullPath =
|
|
27252
|
+
const fullPath = join9(directory, name);
|
|
27220
27253
|
try {
|
|
27221
27254
|
const stat = statSync3(fullPath);
|
|
27222
27255
|
if (stat.isFile()) {
|
|
@@ -27225,7 +27258,7 @@ function scanDirectoryForGodotBinaries(directory, platform) {
|
|
|
27225
27258
|
} catch {}
|
|
27226
27259
|
}
|
|
27227
27260
|
matches.sort((a, b) => b.mtime - a.mtime);
|
|
27228
|
-
return matches.map((m) =>
|
|
27261
|
+
return matches.map((m) => join9(directory, m.name));
|
|
27229
27262
|
}
|
|
27230
27263
|
function conventionalPaths(platform, home) {
|
|
27231
27264
|
const paths = ["godot"];
|
|
@@ -27345,9 +27378,9 @@ class GodotLocator {
|
|
|
27345
27378
|
|
|
27346
27379
|
// src/headless.ts
|
|
27347
27380
|
import { execFile as execFile3 } from "node:child_process";
|
|
27348
|
-
import { mkdtempSync, writeFileSync as
|
|
27381
|
+
import { mkdtempSync, writeFileSync as writeFileSync6 } from "node:fs";
|
|
27349
27382
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
27350
|
-
import { join as
|
|
27383
|
+
import { join as join10 } from "node:path";
|
|
27351
27384
|
import { promisify as promisify3 } from "node:util";
|
|
27352
27385
|
|
|
27353
27386
|
// src/scratch.ts
|
|
@@ -27413,12 +27446,12 @@ function reason(stdout, stderr) {
|
|
|
27413
27446
|
return stdout.trim().split(/\r?\n/).at(-1) ?? "no output at all";
|
|
27414
27447
|
}
|
|
27415
27448
|
async function runImport(godotPath, projectPath) {
|
|
27416
|
-
const logDir = mkdtempSync(
|
|
27449
|
+
const logDir = mkdtempSync(join10(tmpdir3(), "gdharness-import-"));
|
|
27417
27450
|
try {
|
|
27418
27451
|
const { stderr } = await run3(godotPath, [
|
|
27419
27452
|
"--headless",
|
|
27420
27453
|
"--log-file",
|
|
27421
|
-
|
|
27454
|
+
join10(logDir, "engine.log"),
|
|
27422
27455
|
"--path",
|
|
27423
27456
|
projectPath,
|
|
27424
27457
|
"--import"
|
|
@@ -27443,13 +27476,13 @@ async function runImport(godotPath, projectPath) {
|
|
|
27443
27476
|
}
|
|
27444
27477
|
}
|
|
27445
27478
|
async function runOperation(engine, operation, params, projectPath) {
|
|
27446
|
-
const paramsDir = mkdtempSync(
|
|
27447
|
-
const paramsFile =
|
|
27448
|
-
|
|
27479
|
+
const paramsDir = mkdtempSync(join10(tmpdir3(), "gdharness-params-"));
|
|
27480
|
+
const paramsFile = join10(paramsDir, `${operation}.json`);
|
|
27481
|
+
writeFileSync6(paramsFile, JSON.stringify(snakeCased(params)), "utf8");
|
|
27449
27482
|
const args = [
|
|
27450
27483
|
"--headless",
|
|
27451
27484
|
"--log-file",
|
|
27452
|
-
|
|
27485
|
+
join10(paramsDir, "engine.log"),
|
|
27453
27486
|
"--path",
|
|
27454
27487
|
projectPath,
|
|
27455
27488
|
"--script",
|
|
@@ -27878,7 +27911,7 @@ function orphansPrinted(printed) {
|
|
|
27878
27911
|
import { realpathSync as realpathSync2 } from "node:fs";
|
|
27879
27912
|
import { readFile, realpath } from "node:fs/promises";
|
|
27880
27913
|
import { createConnection as createConnection3 } from "node:net";
|
|
27881
|
-
import { dirname as
|
|
27914
|
+
import { dirname as dirname6, resolve as resolve3 } from "node:path";
|
|
27882
27915
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
27883
27916
|
var DEFAULT_LSP_PORT = 6005;
|
|
27884
27917
|
var DIAGNOSTICS_TIMEOUT_MS = 5000;
|
|
@@ -28008,7 +28041,7 @@ class GodotLSPClient {
|
|
|
28008
28041
|
if (this.initialized) {
|
|
28009
28042
|
return;
|
|
28010
28043
|
}
|
|
28011
|
-
const rootPath = this.rootPath ??
|
|
28044
|
+
const rootPath = this.rootPath ?? dirname6(resolve3(filePath));
|
|
28012
28045
|
await this.initialize(rootPath);
|
|
28013
28046
|
}
|
|
28014
28047
|
async sendRequest(method, params) {
|
|
@@ -28444,14 +28477,14 @@ function secondsFromClock(printed) {
|
|
|
28444
28477
|
}
|
|
28445
28478
|
|
|
28446
28479
|
// src/project-scan.ts
|
|
28447
|
-
import { existsSync as existsSync7, readdirSync as readdirSync4, readFileSync as
|
|
28448
|
-
import { join as
|
|
28480
|
+
import { existsSync as existsSync7, readdirSync as readdirSync4, readFileSync as readFileSync10 } from "node:fs";
|
|
28481
|
+
import { join as join11 } from "node:path";
|
|
28449
28482
|
var SKIPPED = new Set(["node_modules"]);
|
|
28450
28483
|
function skipped(name) {
|
|
28451
28484
|
return name.startsWith(".") || SKIPPED.has(name);
|
|
28452
28485
|
}
|
|
28453
28486
|
function steppedOver(directory) {
|
|
28454
|
-
return existsSync7(
|
|
28487
|
+
return existsSync7(join11(directory, ".gdignore"));
|
|
28455
28488
|
}
|
|
28456
28489
|
var ASSET_EXTENSIONS = new Set(["png", "jpg", "jpeg", "webp", "svg", "ttf", "otf", "wav", "mp3", "ogg"]);
|
|
28457
28490
|
function projectStructure(projectPath) {
|
|
@@ -28465,7 +28498,7 @@ function projectStructure(projectPath) {
|
|
|
28465
28498
|
continue;
|
|
28466
28499
|
}
|
|
28467
28500
|
if (entry.isDirectory()) {
|
|
28468
|
-
visit(
|
|
28501
|
+
visit(join11(directory, entry.name));
|
|
28469
28502
|
} else if (entry.isFile()) {
|
|
28470
28503
|
const extension = entry.name.split(".").pop()?.toLowerCase() ?? "";
|
|
28471
28504
|
if (extension === "tscn") {
|
|
@@ -28496,14 +28529,14 @@ function scriptsWithoutUid(projectPath) {
|
|
|
28496
28529
|
}
|
|
28497
28530
|
const spelled = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
|
|
28498
28531
|
if (entry.isDirectory()) {
|
|
28499
|
-
visit(
|
|
28532
|
+
visit(join11(directory, entry.name), spelled);
|
|
28500
28533
|
continue;
|
|
28501
28534
|
}
|
|
28502
28535
|
if (!entry.isFile()) {
|
|
28503
28536
|
continue;
|
|
28504
28537
|
}
|
|
28505
28538
|
const extension = entry.name.split(".").pop()?.toLowerCase() ?? "";
|
|
28506
|
-
if (CARRIES_A_UID.has(extension) && !existsSync7(
|
|
28539
|
+
if (CARRIES_A_UID.has(extension) && !existsSync7(join11(directory, `${entry.name}.uid`))) {
|
|
28507
28540
|
found.push(spelled);
|
|
28508
28541
|
}
|
|
28509
28542
|
}
|
|
@@ -28538,7 +28571,7 @@ function searchProject(projectPath, options) {
|
|
|
28538
28571
|
if (skipped(entry.name)) {
|
|
28539
28572
|
continue;
|
|
28540
28573
|
}
|
|
28541
|
-
const entryPath =
|
|
28574
|
+
const entryPath = join11(directory, entry.name);
|
|
28542
28575
|
if (entry.isDirectory()) {
|
|
28543
28576
|
visit(entryPath);
|
|
28544
28577
|
continue;
|
|
@@ -28549,7 +28582,7 @@ function searchProject(projectPath, options) {
|
|
|
28549
28582
|
}
|
|
28550
28583
|
result.summary.files_searched += 1;
|
|
28551
28584
|
const matches = [];
|
|
28552
|
-
for (const [index, line] of
|
|
28585
|
+
for (const [index, line] of readFileSync10(entryPath, "utf8").split(`
|
|
28553
28586
|
`).entries()) {
|
|
28554
28587
|
if (full()) {
|
|
28555
28588
|
break;
|
|
@@ -28572,7 +28605,7 @@ function searchProject(projectPath, options) {
|
|
|
28572
28605
|
}
|
|
28573
28606
|
|
|
28574
28607
|
// src/resources.ts
|
|
28575
|
-
import { readFileSync as
|
|
28608
|
+
import { readFileSync as readFileSync11 } from "node:fs";
|
|
28576
28609
|
import { extname, resolve as resolve4 } from "node:path";
|
|
28577
28610
|
var STATIC_RESOURCES = [
|
|
28578
28611
|
{
|
|
@@ -28767,7 +28800,7 @@ function readResourceText(uri, getProjectPath) {
|
|
|
28767
28800
|
const parsedUri = parseGodotUri(uri);
|
|
28768
28801
|
if (parsedUri.kind === "project-info") {
|
|
28769
28802
|
const projectFilePath = resolveProjectFile(projectPath, "project.godot");
|
|
28770
|
-
const rawProject =
|
|
28803
|
+
const rawProject = readFileSync11(projectFilePath, "utf-8");
|
|
28771
28804
|
const parsedProject = parseProjectGodot(rawProject);
|
|
28772
28805
|
return {
|
|
28773
28806
|
mimeType: "application/json",
|
|
@@ -28776,7 +28809,7 @@ function readResourceText(uri, getProjectPath) {
|
|
|
28776
28809
|
}
|
|
28777
28810
|
const filePath = resolveProjectFile(projectPath, parsedUri.resourcePath);
|
|
28778
28811
|
ensureAllowedExtension(parsedUri.kind, filePath);
|
|
28779
|
-
const text =
|
|
28812
|
+
const text = readFileSync11(filePath, "utf-8");
|
|
28780
28813
|
return {
|
|
28781
28814
|
mimeType: parsedUri.kind === "script" ? "text/x-gdscript" : "text/plain",
|
|
28782
28815
|
text
|
|
@@ -28813,16 +28846,16 @@ function setupResourceHandlers(mcp, getProjectPath) {
|
|
|
28813
28846
|
}
|
|
28814
28847
|
|
|
28815
28848
|
// src/restart-note.ts
|
|
28816
|
-
import { mkdirSync as
|
|
28817
|
-
import { dirname as
|
|
28849
|
+
import { mkdirSync as mkdirSync6, readFileSync as readFileSync12, rmSync as rmSync4, writeFileSync as writeFileSync7 } from "node:fs";
|
|
28850
|
+
import { dirname as dirname7, join as join12 } from "node:path";
|
|
28818
28851
|
function restartNotePath(projectPath) {
|
|
28819
|
-
return
|
|
28852
|
+
return join12(projectPath, ".godot", "gdharness-restart.json");
|
|
28820
28853
|
}
|
|
28821
28854
|
function noteRestartBegun(note) {
|
|
28822
28855
|
const path = restartNotePath(note.projectPath);
|
|
28823
28856
|
try {
|
|
28824
|
-
|
|
28825
|
-
|
|
28857
|
+
mkdirSync6(dirname7(path), { recursive: true });
|
|
28858
|
+
writeFileSync7(path, `${JSON.stringify(note, null, 2)}
|
|
28826
28859
|
`, "utf8");
|
|
28827
28860
|
} catch (error) {
|
|
28828
28861
|
console.error(`[SERVER] Could not record the restart at ${path}: ${errorMessage(error)}`);
|
|
@@ -28830,7 +28863,7 @@ function noteRestartBegun(note) {
|
|
|
28830
28863
|
}
|
|
28831
28864
|
function restartOwed(projectPath) {
|
|
28832
28865
|
try {
|
|
28833
|
-
const parsed = JSON.parse(
|
|
28866
|
+
const parsed = JSON.parse(readFileSync12(restartNotePath(projectPath), "utf8"));
|
|
28834
28867
|
if (typeof parsed !== "object" || parsed === null) {
|
|
28835
28868
|
return null;
|
|
28836
28869
|
}
|
|
@@ -28853,36 +28886,36 @@ function restartSettled(projectPath) {
|
|
|
28853
28886
|
|
|
28854
28887
|
// src/run-record.ts
|
|
28855
28888
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
28856
|
-
import { mkdirSync as
|
|
28857
|
-
import { basename as basename2, join as
|
|
28889
|
+
import { mkdirSync as mkdirSync7, openSync, readdirSync as readdirSync5, readFileSync as readFileSync13, rmSync as rmSync5, statSync as statSync4, writeFileSync as writeFileSync8 } from "node:fs";
|
|
28890
|
+
import { basename as basename2, join as join13 } from "node:path";
|
|
28858
28891
|
function runsDirectory() {
|
|
28859
|
-
return
|
|
28892
|
+
return join13(runtimeDirectory(), "runs");
|
|
28860
28893
|
}
|
|
28861
28894
|
function recordPath() {
|
|
28862
|
-
return
|
|
28895
|
+
return join13(runsDirectory(), "run.json");
|
|
28863
28896
|
}
|
|
28864
28897
|
function recordPaths() {
|
|
28865
|
-
return runtimeDirectories().map((directory) =>
|
|
28898
|
+
return runtimeDirectories().map((directory) => join13(directory, "runs", "run.json"));
|
|
28866
28899
|
}
|
|
28867
28900
|
function openTranscript(startedAt) {
|
|
28868
|
-
|
|
28869
|
-
const path =
|
|
28901
|
+
mkdirSync7(runsDirectory(), { recursive: true });
|
|
28902
|
+
const path = join13(runsDirectory(), `run-${startedAt}.log`);
|
|
28870
28903
|
return { path, fd: openSync(path, "a") };
|
|
28871
28904
|
}
|
|
28872
28905
|
function writeRunRecord(record) {
|
|
28873
|
-
|
|
28874
|
-
|
|
28906
|
+
mkdirSync7(runsDirectory(), { recursive: true });
|
|
28907
|
+
writeFileSync8(recordPath(), JSON.stringify(record, null, 2), "utf8");
|
|
28875
28908
|
}
|
|
28876
28909
|
function editorNotePath() {
|
|
28877
|
-
return
|
|
28910
|
+
return join13(runsDirectory(), "editor-run.json");
|
|
28878
28911
|
}
|
|
28879
28912
|
function writeEditorRunNote(note) {
|
|
28880
|
-
|
|
28881
|
-
|
|
28913
|
+
mkdirSync7(runsDirectory(), { recursive: true });
|
|
28914
|
+
writeFileSync8(editorNotePath(), JSON.stringify(note, null, 2), "utf8");
|
|
28882
28915
|
}
|
|
28883
28916
|
function readEditorRunNote() {
|
|
28884
28917
|
for (const directory of runtimeDirectories()) {
|
|
28885
|
-
const note = editorNoteAt(
|
|
28918
|
+
const note = editorNoteAt(join13(directory, "runs", "editor-run.json"));
|
|
28886
28919
|
if (note !== null) {
|
|
28887
28920
|
return note;
|
|
28888
28921
|
}
|
|
@@ -28892,7 +28925,7 @@ function readEditorRunNote() {
|
|
|
28892
28925
|
function editorNoteAt(path) {
|
|
28893
28926
|
let parsed;
|
|
28894
28927
|
try {
|
|
28895
|
-
parsed = JSON.parse(
|
|
28928
|
+
parsed = JSON.parse(readFileSync13(path, "utf8"));
|
|
28896
28929
|
} catch {
|
|
28897
28930
|
return null;
|
|
28898
28931
|
}
|
|
@@ -28915,7 +28948,7 @@ function recordRunEnded(pid, exitCode) {
|
|
|
28915
28948
|
return;
|
|
28916
28949
|
}
|
|
28917
28950
|
try {
|
|
28918
|
-
|
|
28951
|
+
writeFileSync8(path, JSON.stringify({ ...record, exitCode }, null, 2), "utf8");
|
|
28919
28952
|
} catch {}
|
|
28920
28953
|
}
|
|
28921
28954
|
function readRunRecord() {
|
|
@@ -28930,7 +28963,7 @@ function readRunRecord() {
|
|
|
28930
28963
|
function recordAt(path) {
|
|
28931
28964
|
let parsed;
|
|
28932
28965
|
try {
|
|
28933
|
-
parsed = JSON.parse(
|
|
28966
|
+
parsed = JSON.parse(readFileSync13(path, "utf8"));
|
|
28934
28967
|
} catch {
|
|
28935
28968
|
return null;
|
|
28936
28969
|
}
|
|
@@ -28975,7 +29008,7 @@ function runningAs(pid) {
|
|
|
28975
29008
|
const began = startedAt(pid, null);
|
|
28976
29009
|
const when = began === null ? {} : { startedAt: began };
|
|
28977
29010
|
if (process.platform === "linux") {
|
|
28978
|
-
const raw =
|
|
29011
|
+
const raw = readFileSync13(`/proc/${pid}/cmdline`, "utf8").replaceAll("\x00", " ").trim();
|
|
28979
29012
|
return raw === "" ? null : { kind: "commandLine", text: raw, ...when };
|
|
28980
29013
|
}
|
|
28981
29014
|
const args = execFileSync2("ps", ["-p", String(pid), "-o", "args="], {
|
|
@@ -29028,7 +29061,7 @@ function windowsCommandLine(pid) {
|
|
|
29028
29061
|
"-NoProfile",
|
|
29029
29062
|
"-NonInteractive",
|
|
29030
29063
|
"-Command",
|
|
29031
|
-
`$p = Get-CimInstance Win32_Process -Filter "ProcessId=${pid}"; if ($p) { $p.CreationDate.ToUniversalTime().ToString("o"); $p.CommandLine }`
|
|
29064
|
+
`${POWERSHELL_UTF8}$p = Get-CimInstance Win32_Process -Filter "ProcessId=${pid}"; if ($p) { $p.CreationDate.ToUniversalTime().ToString("o"); $p.CommandLine }`
|
|
29032
29065
|
], { encoding: "utf8", timeout: 15000, windowsHide: true }).trim();
|
|
29033
29066
|
return answer === "" ? null : answer;
|
|
29034
29067
|
} catch {
|
|
@@ -29128,7 +29161,7 @@ function sweepTranscripts(keepMs = 24 * 60 * 60 * 1000, now = Date.now()) {
|
|
|
29128
29161
|
if (!entry.startsWith("run-") || !entry.endsWith(".log")) {
|
|
29129
29162
|
continue;
|
|
29130
29163
|
}
|
|
29131
|
-
const path =
|
|
29164
|
+
const path = join13(directory, entry);
|
|
29132
29165
|
if (path === keep || ageMs(path, now) < keepMs) {
|
|
29133
29166
|
continue;
|
|
29134
29167
|
}
|
|
@@ -29139,8 +29172,8 @@ function sweepTranscripts(keepMs = 24 * 60 * 60 * 1000, now = Date.now()) {
|
|
|
29139
29172
|
}
|
|
29140
29173
|
|
|
29141
29174
|
// src/setup.ts
|
|
29142
|
-
import { cpSync, existsSync as existsSync8, mkdirSync as
|
|
29143
|
-
import { dirname as
|
|
29175
|
+
import { cpSync, existsSync as existsSync8, mkdirSync as mkdirSync8, readFileSync as readFileSync14, rmSync as rmSync6, writeFileSync as writeFileSync9 } from "node:fs";
|
|
29176
|
+
import { dirname as dirname8, join as join14 } from "node:path";
|
|
29144
29177
|
var ADDONS = ["gdharness_editor", "gdharness_runtime", "auto_reload"];
|
|
29145
29178
|
var RUNTIME_AUTOLOAD = {
|
|
29146
29179
|
name: "GdharnessRuntime",
|
|
@@ -29148,11 +29181,11 @@ var RUNTIME_AUTOLOAD = {
|
|
|
29148
29181
|
};
|
|
29149
29182
|
var VERSION_MARKER = ".gdharness-version";
|
|
29150
29183
|
function installedAddonVersion(projectPath) {
|
|
29151
|
-
const marker =
|
|
29184
|
+
const marker = join14(projectPath, "addons", ADDONS[0], VERSION_MARKER);
|
|
29152
29185
|
if (!existsSync8(marker)) {
|
|
29153
29186
|
return null;
|
|
29154
29187
|
}
|
|
29155
|
-
const said =
|
|
29188
|
+
const said = readFileSync14(marker, "utf8").trim();
|
|
29156
29189
|
return said === "" ? null : said;
|
|
29157
29190
|
}
|
|
29158
29191
|
|
|
@@ -30309,7 +30342,7 @@ function buildToolDefinitions() {
|
|
|
30309
30342
|
var UPDATE_NOTICE_EVERY = 500;
|
|
30310
30343
|
var FEEDBACK_NOTICE_EVERY = 250;
|
|
30311
30344
|
var run5 = promisify5(execFile5);
|
|
30312
|
-
var __dirname2 =
|
|
30345
|
+
var __dirname2 = dirname9(fileURLToPath2(import.meta.url));
|
|
30313
30346
|
var EDITOR_RESTART_TIMEOUT_MS = 90000;
|
|
30314
30347
|
var HOLD_PING_MS = 2000;
|
|
30315
30348
|
var SLOWEST_FRAME_RATE = 20;
|
|
@@ -30596,7 +30629,7 @@ function aboveTheRunner(entry) {
|
|
|
30596
30629
|
}
|
|
30597
30630
|
function elsewhereIn(projectPath, asked) {
|
|
30598
30631
|
const wanted = asked.replace(/^res:\/\//, "").replace(/\/+$/, "");
|
|
30599
|
-
if (wanted === "" || existsSync9(
|
|
30632
|
+
if (wanted === "" || existsSync9(join15(projectPath, wanted))) {
|
|
30600
30633
|
return "";
|
|
30601
30634
|
}
|
|
30602
30635
|
const root = (wanted.split("/")[0] ?? "").toLowerCase();
|
|
@@ -30610,7 +30643,7 @@ function elsewhereIn(projectPath, asked) {
|
|
|
30610
30643
|
return near.length === 0 ? "" : ` The project does have ${there}.`;
|
|
30611
30644
|
}
|
|
30612
30645
|
function hasMainScene(projectFile) {
|
|
30613
|
-
const scene = parseProjectGodot(
|
|
30646
|
+
const scene = parseProjectGodot(readFileSync15(projectFile, "utf8"))["application"]?.["run/main_scene"];
|
|
30614
30647
|
return typeof scene === "string" && scene !== "";
|
|
30615
30648
|
}
|
|
30616
30649
|
function runtimeAddonAnnouncesEditors(projectPath) {
|
|
@@ -30618,13 +30651,13 @@ function runtimeAddonAnnouncesEditors(projectPath) {
|
|
|
30618
30651
|
return false;
|
|
30619
30652
|
}
|
|
30620
30653
|
try {
|
|
30621
|
-
return
|
|
30654
|
+
return readFileSync15(join15(projectPath, RUNTIME_AUTOLOAD.path), "utf8").includes('"editor_pid"');
|
|
30622
30655
|
} catch {
|
|
30623
30656
|
return false;
|
|
30624
30657
|
}
|
|
30625
30658
|
}
|
|
30626
30659
|
function editorPlaysHeadless(projectFile) {
|
|
30627
|
-
const runArgs = parseProjectGodot(
|
|
30660
|
+
const runArgs = parseProjectGodot(readFileSync15(projectFile, "utf8"))["editor"]?.["run/main_run_args"];
|
|
30628
30661
|
return typeof runArgs === "string" && /(?:^|\s)--headless(?:\s|$)/.test(runArgs);
|
|
30629
30662
|
}
|
|
30630
30663
|
function camelCased(params) {
|
|
@@ -30640,7 +30673,7 @@ function camelCased(params) {
|
|
|
30640
30673
|
class GodotServer {
|
|
30641
30674
|
mcp;
|
|
30642
30675
|
locator = new GodotLocator;
|
|
30643
|
-
operationsScript =
|
|
30676
|
+
operationsScript = join15(__dirname2, "godot", "operations", "godot_operations.gd");
|
|
30644
30677
|
godotBridge;
|
|
30645
30678
|
tools = buildToolDefinitions();
|
|
30646
30679
|
activeProcess = null;
|
|
@@ -30658,8 +30691,6 @@ class GodotServer {
|
|
|
30658
30691
|
successorWatch = null;
|
|
30659
30692
|
lastProjectPath = null;
|
|
30660
30693
|
shutdownInitiated = false;
|
|
30661
|
-
shortListEditors = new Map;
|
|
30662
|
-
cacheLastRebuilt = new Map;
|
|
30663
30694
|
launchedEditor = null;
|
|
30664
30695
|
launchedFrom = null;
|
|
30665
30696
|
ownProject;
|
|
@@ -31307,7 +31338,7 @@ class GodotServer {
|
|
|
31307
31338
|
if (path === undefined) {
|
|
31308
31339
|
return { ok: false, response: this.createErrorResponse("projectPath is required.") };
|
|
31309
31340
|
}
|
|
31310
|
-
const file =
|
|
31341
|
+
const file = join15(path, "project.godot");
|
|
31311
31342
|
if (!existsSync9(file)) {
|
|
31312
31343
|
return {
|
|
31313
31344
|
ok: false,
|
|
@@ -31531,7 +31562,7 @@ class GodotServer {
|
|
|
31531
31562
|
if (!engine.ok) {
|
|
31532
31563
|
return engine.response;
|
|
31533
31564
|
}
|
|
31534
|
-
const application = parseProjectGodot(
|
|
31565
|
+
const application = parseProjectGodot(readFileSync15(project.value.file, "utf8"))["application"] ?? {};
|
|
31535
31566
|
const name = application["config/name"];
|
|
31536
31567
|
const mainScene = application["run/main_scene"];
|
|
31537
31568
|
const info = {
|
|
@@ -31592,18 +31623,18 @@ class GodotServer {
|
|
|
31592
31623
|
if (!engine.ok) {
|
|
31593
31624
|
return engine.response;
|
|
31594
31625
|
}
|
|
31595
|
-
const folder =
|
|
31626
|
+
const folder = dirname9(output.absolutePath);
|
|
31596
31627
|
try {
|
|
31597
|
-
|
|
31628
|
+
mkdirSync9(folder, { recursive: true });
|
|
31598
31629
|
} catch (error) {
|
|
31599
|
-
return this.createErrorResponse(`Cannot create ${
|
|
31630
|
+
return this.createErrorResponse(`Cannot create ${dirname9(output.relativePath)} to export into: ${errorMessage(error)}`);
|
|
31600
31631
|
}
|
|
31601
31632
|
const debug = readBoolean(args, "debug") ?? false;
|
|
31602
|
-
const exportLogs = mkdtempSync2(
|
|
31633
|
+
const exportLogs = mkdtempSync2(join15(tmpdir4(), "gdharness-export-"));
|
|
31603
31634
|
const exportArgs = [
|
|
31604
31635
|
"--headless",
|
|
31605
31636
|
"--log-file",
|
|
31606
|
-
|
|
31637
|
+
join15(exportLogs, "engine.log"),
|
|
31607
31638
|
"--path",
|
|
31608
31639
|
project.value.path,
|
|
31609
31640
|
debug ? "--export-debug" : "--export-release",
|
|
@@ -31661,7 +31692,7 @@ class GodotServer {
|
|
|
31661
31692
|
return contained.response;
|
|
31662
31693
|
}
|
|
31663
31694
|
const runner = "addons/gdUnit4/bin/GdUnitCmdTool.gd";
|
|
31664
|
-
if (!existsSync9(
|
|
31695
|
+
if (!existsSync9(join15(project.value.path, runner))) {
|
|
31665
31696
|
return this.createErrorResponse(`gdUnit4 is not installed in this project: no ${runner}.`, [
|
|
31666
31697
|
"Install gdUnit4 under addons/gdUnit4, from https://github.com/godot-gdunit-labs/gdUnit4"
|
|
31667
31698
|
]);
|
|
@@ -31696,7 +31727,7 @@ class GodotServer {
|
|
|
31696
31727
|
];
|
|
31697
31728
|
const timeoutMs = readPositiveNumber(args, "timeoutMs") ?? 600000;
|
|
31698
31729
|
this.logDebug(`Running tests: ${engine.value} ${cmdArgs.join(" ")}`);
|
|
31699
|
-
const userData = mkdtempSync2(
|
|
31730
|
+
const userData = mkdtempSync2(join15(tmpdir4(), "gdharness-tests-"));
|
|
31700
31731
|
const run = this.spawnGame(engine.value, cmdArgs, userDataIn(userData));
|
|
31701
31732
|
const hung = await new Promise((resolve) => {
|
|
31702
31733
|
const timer = setTimeout(() => {
|
|
@@ -31712,19 +31743,22 @@ class GodotServer {
|
|
|
31712
31743
|
resolve(false);
|
|
31713
31744
|
});
|
|
31714
31745
|
});
|
|
31715
|
-
const reportsDir =
|
|
31746
|
+
const reportsDir = join15(project.value.path, ".godot", ...ours.split("/"));
|
|
31716
31747
|
let report = null;
|
|
31717
31748
|
let reportProblem = null;
|
|
31718
31749
|
try {
|
|
31719
31750
|
const written = existsSync9(reportsDir) ? readdirSync6(reportsDir).filter((name) => name.startsWith("report_")).sort((a, b) => Number(a.slice("report_".length)) - Number(b.slice("report_".length))) : [];
|
|
31720
31751
|
const newest = written.at(-1);
|
|
31721
31752
|
if (newest !== undefined) {
|
|
31722
|
-
report = parseJUnit(
|
|
31753
|
+
report = parseJUnit(readFileSync15(join15(reportsDir, newest, "results.xml"), "utf8"));
|
|
31723
31754
|
}
|
|
31724
31755
|
} catch (error) {
|
|
31725
31756
|
reportProblem = errorMessage(error);
|
|
31726
31757
|
} finally {
|
|
31727
31758
|
discard(reportsDir, userData);
|
|
31759
|
+
try {
|
|
31760
|
+
rmdirSync(dirname9(reportsDir));
|
|
31761
|
+
} catch {}
|
|
31728
31762
|
}
|
|
31729
31763
|
const engineEntries = run.log.select({ severity: "warning", sinceLastCall: false, limit: 200 }).entries.map(aboveTheRunner);
|
|
31730
31764
|
const verdicts = {
|
|
@@ -31859,7 +31893,7 @@ class GodotServer {
|
|
|
31859
31893
|
return null;
|
|
31860
31894
|
}
|
|
31861
31895
|
try {
|
|
31862
|
-
return
|
|
31896
|
+
return readFileSync15(contained.absolutePath, "utf8");
|
|
31863
31897
|
} catch {
|
|
31864
31898
|
return null;
|
|
31865
31899
|
}
|
|
@@ -31897,8 +31931,15 @@ class GodotServer {
|
|
|
31897
31931
|
};
|
|
31898
31932
|
}
|
|
31899
31933
|
editorWritesAShortList(projectPath) {
|
|
31900
|
-
const
|
|
31901
|
-
return
|
|
31934
|
+
const recorded = readClassNote(projectPath)?.shortListEditorPid;
|
|
31935
|
+
return recorded !== undefined && recorded === (this.godotBridge.getStatus().editorPid ?? null);
|
|
31936
|
+
}
|
|
31937
|
+
noteAShortListEditor(projectPath) {
|
|
31938
|
+
const note = readClassNote(projectPath);
|
|
31939
|
+
writeClassNote(projectPath, {
|
|
31940
|
+
rebuiltWith: note?.rebuiltWith ?? [],
|
|
31941
|
+
shortListEditorPid: this.godotBridge.getStatus().editorPid ?? null
|
|
31942
|
+
});
|
|
31902
31943
|
}
|
|
31903
31944
|
sourceOfClass(projectPath, resourcePath) {
|
|
31904
31945
|
const contained = resolveWithinProject(projectPath, resourcePath);
|
|
@@ -31906,7 +31947,7 @@ class GodotServer {
|
|
|
31906
31947
|
return null;
|
|
31907
31948
|
}
|
|
31908
31949
|
try {
|
|
31909
|
-
return
|
|
31950
|
+
return readFileSync15(contained.absolutePath, "utf8");
|
|
31910
31951
|
} catch {
|
|
31911
31952
|
return null;
|
|
31912
31953
|
}
|
|
@@ -32135,7 +32176,7 @@ class GodotServer {
|
|
|
32135
32176
|
return null;
|
|
32136
32177
|
}
|
|
32137
32178
|
try {
|
|
32138
|
-
return settingKeys(
|
|
32179
|
+
return settingKeys(readFileSync15(join15(projectPath, "project.godot"), "utf8"));
|
|
32139
32180
|
} catch {
|
|
32140
32181
|
return null;
|
|
32141
32182
|
}
|
|
@@ -32377,7 +32418,7 @@ class GodotServer {
|
|
|
32377
32418
|
}
|
|
32378
32419
|
async runtimeUp(projectPath, before, budget, withArgs) {
|
|
32379
32420
|
const budgetMs = budget.ms;
|
|
32380
|
-
if (!existsSync9(
|
|
32421
|
+
if (!existsSync9(join15(projectPath, RUNTIME_AUTOLOAD.path))) {
|
|
32381
32422
|
return runtimeVerdict(null, { addon: false, budgetMs, heldAt: null, running: false, withArgs });
|
|
32382
32423
|
}
|
|
32383
32424
|
let asking = null;
|
|
@@ -33250,7 +33291,7 @@ class GodotServer {
|
|
|
33250
33291
|
const after = projectPath === "" || busy ? null : cachedClasses(projectPath);
|
|
33251
33292
|
const lost = before === null || after === null ? [] : [...before.keys()].filter((name) => !after.has(name));
|
|
33252
33293
|
if (lost.length > 0) {
|
|
33253
|
-
this.
|
|
33294
|
+
this.noteAShortListEditor(projectPath);
|
|
33254
33295
|
}
|
|
33255
33296
|
const atMissingPaths = after === null ? [] : cachedAtMissingPaths(after, projectPath);
|
|
33256
33297
|
const restored = lost.length > 0 || atMissingPaths.length > 0 ? await this.rebuildCacheAfterLoss(projectPath, lost) : [];
|
|
@@ -33316,9 +33357,8 @@ class GodotServer {
|
|
|
33316
33357
|
return now === null ? [] : lost.filter((name) => now.has(name));
|
|
33317
33358
|
}
|
|
33318
33359
|
async rebuildClassCache(projectPath) {
|
|
33319
|
-
const key = resolve5(projectPath);
|
|
33320
33360
|
const before = cachedClasses(projectPath);
|
|
33321
|
-
const
|
|
33361
|
+
const noted = readClassNote(projectPath);
|
|
33322
33362
|
const rebuilt = await this.operation("refresh_class_cache", {}, projectPath);
|
|
33323
33363
|
if (!rebuilt.ok) {
|
|
33324
33364
|
return rebuilt;
|
|
@@ -33327,12 +33367,15 @@ class GodotServer {
|
|
|
33327
33367
|
if (after === null) {
|
|
33328
33368
|
return rebuilt;
|
|
33329
33369
|
}
|
|
33330
|
-
|
|
33331
|
-
const
|
|
33332
|
-
|
|
33370
|
+
const lost = noted === null || before === null ? [] : noted.rebuiltWith.filter((name) => !before.has(name) && after.has(name)).sort();
|
|
33371
|
+
const seenLosing = lost.length > 0 && this.godotBridge.isConnected();
|
|
33372
|
+
writeClassNote(projectPath, {
|
|
33373
|
+
rebuiltWith: [...after.keys()].sort(),
|
|
33374
|
+
...seenLosing ? { shortListEditorPid: this.godotBridge.getStatus().editorPid ?? null } : noted?.shortListEditorPid === undefined ? {} : { shortListEditorPid: noted.shortListEditorPid }
|
|
33375
|
+
});
|
|
33376
|
+
if (!seenLosing) {
|
|
33333
33377
|
return rebuilt;
|
|
33334
33378
|
}
|
|
33335
|
-
this.shortListEditors.set(key, this.godotBridge.getStatus().editorPid);
|
|
33336
33379
|
const them = lost.length === 1 ? "it" : "them";
|
|
33337
33380
|
return {
|
|
33338
33381
|
...rebuilt,
|
|
@@ -33489,8 +33532,8 @@ class GodotServer {
|
|
|
33489
33532
|
]);
|
|
33490
33533
|
}
|
|
33491
33534
|
const expectsScreenshot = command === "capture_screenshot" || command === "capture_viewport";
|
|
33492
|
-
const screenshotDir = expectsScreenshot ? mkdtempSync2(
|
|
33493
|
-
const screenshotPath = screenshotDir ?
|
|
33535
|
+
const screenshotDir = expectsScreenshot ? mkdtempSync2(join15(tmpdir4(), "gdharness-runtime-screenshot-")) : null;
|
|
33536
|
+
const screenshotPath = screenshotDir ? join15(screenshotDir, "capture.png") : null;
|
|
33494
33537
|
try {
|
|
33495
33538
|
const reply = await runtimeRequest(choice.endpoint, command, screenshotPath ? { ...params, output_path: screenshotPath } : params, timeoutMs);
|
|
33496
33539
|
if (!reply.ok) {
|
|
@@ -33514,7 +33557,7 @@ class GodotServer {
|
|
|
33514
33557
|
type: "text",
|
|
33515
33558
|
text: `Screenshot captured: ${dimensions}${chosenAmongSeveral ? ` from pid ${own}, the game this server holds` : ""}`
|
|
33516
33559
|
},
|
|
33517
|
-
{ type: "image", data:
|
|
33560
|
+
{ type: "image", data: readFileSync15(screenshotPath).toString("base64"), mimeType: "image/png" }
|
|
33518
33561
|
]
|
|
33519
33562
|
};
|
|
33520
33563
|
} finally {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gdharness",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.9",
|
|
4
4
|
"mcpName": "io.github.Aureliolo/gdharness",
|
|
5
5
|
"description": "A harness for driving a Godot 4 project from an agent: editor addons, a runtime bridge into the running game, an MCP server in front of them, and a CLI that installs and diagnoses the Godot side.",
|
|
6
6
|
"type": "module",
|