gdharness 1.0.7 → 1.0.8
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 +120 -80
- package/build/index.js +132 -94
- package/package.json +1 -1
package/build/cli.js
CHANGED
|
@@ -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="], {
|
|
@@ -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
|
}
|
|
@@ -37607,16 +37639,16 @@ import {
|
|
|
37607
37639
|
closeSync,
|
|
37608
37640
|
existsSync as existsSync14,
|
|
37609
37641
|
fstatSync,
|
|
37610
|
-
mkdirSync as
|
|
37642
|
+
mkdirSync as mkdirSync12,
|
|
37611
37643
|
mkdtempSync as mkdtempSync3,
|
|
37612
37644
|
openSync as openSync2,
|
|
37613
37645
|
readdirSync as readdirSync7,
|
|
37614
|
-
readFileSync as
|
|
37646
|
+
readFileSync as readFileSync19,
|
|
37615
37647
|
readSync,
|
|
37616
37648
|
statSync as statSync5
|
|
37617
37649
|
} from "node:fs";
|
|
37618
37650
|
import { tmpdir as tmpdir5 } from "node:os";
|
|
37619
|
-
import { basename as basename4, dirname as
|
|
37651
|
+
import { basename as basename4, dirname as dirname14, join as join21, normalize as normalize3, relative as relative3 } from "node:path";
|
|
37620
37652
|
import { setTimeout as delay3 } from "node:timers/promises";
|
|
37621
37653
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
37622
37654
|
import { promisify as promisify7 } from "node:util";
|
|
@@ -37858,7 +37890,7 @@ function aboveTheRunner(entry) {
|
|
|
37858
37890
|
}
|
|
37859
37891
|
function elsewhereIn(projectPath, asked) {
|
|
37860
37892
|
const wanted = asked.replace(/^res:\/\//, "").replace(/\/+$/, "");
|
|
37861
|
-
if (wanted === "" || existsSync14(
|
|
37893
|
+
if (wanted === "" || existsSync14(join21(projectPath, wanted))) {
|
|
37862
37894
|
return "";
|
|
37863
37895
|
}
|
|
37864
37896
|
const root = (wanted.split("/")[0] ?? "").toLowerCase();
|
|
@@ -37872,7 +37904,7 @@ function elsewhereIn(projectPath, asked) {
|
|
|
37872
37904
|
return near.length === 0 ? "" : ` The project does have ${there}.`;
|
|
37873
37905
|
}
|
|
37874
37906
|
function hasMainScene(projectFile) {
|
|
37875
|
-
const scene = parseProjectGodot(
|
|
37907
|
+
const scene = parseProjectGodot(readFileSync19(projectFile, "utf8"))["application"]?.["run/main_scene"];
|
|
37876
37908
|
return typeof scene === "string" && scene !== "";
|
|
37877
37909
|
}
|
|
37878
37910
|
function runtimeAddonAnnouncesEditors(projectPath) {
|
|
@@ -37880,13 +37912,13 @@ function runtimeAddonAnnouncesEditors(projectPath) {
|
|
|
37880
37912
|
return false;
|
|
37881
37913
|
}
|
|
37882
37914
|
try {
|
|
37883
|
-
return
|
|
37915
|
+
return readFileSync19(join21(projectPath, RUNTIME_AUTOLOAD2.path), "utf8").includes('"editor_pid"');
|
|
37884
37916
|
} catch {
|
|
37885
37917
|
return false;
|
|
37886
37918
|
}
|
|
37887
37919
|
}
|
|
37888
37920
|
function editorPlaysHeadless(projectFile) {
|
|
37889
|
-
const runArgs = parseProjectGodot(
|
|
37921
|
+
const runArgs = parseProjectGodot(readFileSync19(projectFile, "utf8"))["editor"]?.["run/main_run_args"];
|
|
37890
37922
|
return typeof runArgs === "string" && /(?:^|\s)--headless(?:\s|$)/.test(runArgs);
|
|
37891
37923
|
}
|
|
37892
37924
|
function camelCased(params) {
|
|
@@ -37902,7 +37934,7 @@ function camelCased(params) {
|
|
|
37902
37934
|
class GodotServer {
|
|
37903
37935
|
mcp;
|
|
37904
37936
|
locator = new GodotLocator2;
|
|
37905
|
-
operationsScript =
|
|
37937
|
+
operationsScript = join21(__dirname2, "godot", "operations", "godot_operations.gd");
|
|
37906
37938
|
godotBridge;
|
|
37907
37939
|
tools = buildToolDefinitions();
|
|
37908
37940
|
activeProcess = null;
|
|
@@ -37920,8 +37952,6 @@ class GodotServer {
|
|
|
37920
37952
|
successorWatch = null;
|
|
37921
37953
|
lastProjectPath = null;
|
|
37922
37954
|
shutdownInitiated = false;
|
|
37923
|
-
shortListEditors = new Map;
|
|
37924
|
-
cacheLastRebuilt = new Map;
|
|
37925
37955
|
launchedEditor = null;
|
|
37926
37956
|
launchedFrom = null;
|
|
37927
37957
|
ownProject;
|
|
@@ -38569,7 +38599,7 @@ class GodotServer {
|
|
|
38569
38599
|
if (path === undefined) {
|
|
38570
38600
|
return { ok: false, response: this.createErrorResponse("projectPath is required.") };
|
|
38571
38601
|
}
|
|
38572
|
-
const file =
|
|
38602
|
+
const file = join21(path, "project.godot");
|
|
38573
38603
|
if (!existsSync14(file)) {
|
|
38574
38604
|
return {
|
|
38575
38605
|
ok: false,
|
|
@@ -38793,7 +38823,7 @@ class GodotServer {
|
|
|
38793
38823
|
if (!engine.ok) {
|
|
38794
38824
|
return engine.response;
|
|
38795
38825
|
}
|
|
38796
|
-
const application = parseProjectGodot(
|
|
38826
|
+
const application = parseProjectGodot(readFileSync19(project.value.file, "utf8"))["application"] ?? {};
|
|
38797
38827
|
const name = application["config/name"];
|
|
38798
38828
|
const mainScene = application["run/main_scene"];
|
|
38799
38829
|
const info = {
|
|
@@ -38854,18 +38884,18 @@ class GodotServer {
|
|
|
38854
38884
|
if (!engine.ok) {
|
|
38855
38885
|
return engine.response;
|
|
38856
38886
|
}
|
|
38857
|
-
const folder =
|
|
38887
|
+
const folder = dirname14(output.absolutePath);
|
|
38858
38888
|
try {
|
|
38859
|
-
|
|
38889
|
+
mkdirSync12(folder, { recursive: true });
|
|
38860
38890
|
} catch (error) {
|
|
38861
|
-
return this.createErrorResponse(`Cannot create ${
|
|
38891
|
+
return this.createErrorResponse(`Cannot create ${dirname14(output.relativePath)} to export into: ${errorMessage(error)}`);
|
|
38862
38892
|
}
|
|
38863
38893
|
const debug = readBoolean(args, "debug") ?? false;
|
|
38864
|
-
const exportLogs = mkdtempSync3(
|
|
38894
|
+
const exportLogs = mkdtempSync3(join21(tmpdir5(), "gdharness-export-"));
|
|
38865
38895
|
const exportArgs = [
|
|
38866
38896
|
"--headless",
|
|
38867
38897
|
"--log-file",
|
|
38868
|
-
|
|
38898
|
+
join21(exportLogs, "engine.log"),
|
|
38869
38899
|
"--path",
|
|
38870
38900
|
project.value.path,
|
|
38871
38901
|
debug ? "--export-debug" : "--export-release",
|
|
@@ -38923,7 +38953,7 @@ class GodotServer {
|
|
|
38923
38953
|
return contained.response;
|
|
38924
38954
|
}
|
|
38925
38955
|
const runner = "addons/gdUnit4/bin/GdUnitCmdTool.gd";
|
|
38926
|
-
if (!existsSync14(
|
|
38956
|
+
if (!existsSync14(join21(project.value.path, runner))) {
|
|
38927
38957
|
return this.createErrorResponse(`gdUnit4 is not installed in this project: no ${runner}.`, [
|
|
38928
38958
|
"Install gdUnit4 under addons/gdUnit4, from https://github.com/godot-gdunit-labs/gdUnit4"
|
|
38929
38959
|
]);
|
|
@@ -38958,7 +38988,7 @@ class GodotServer {
|
|
|
38958
38988
|
];
|
|
38959
38989
|
const timeoutMs = readPositiveNumber(args, "timeoutMs") ?? 600000;
|
|
38960
38990
|
this.logDebug(`Running tests: ${engine.value} ${cmdArgs.join(" ")}`);
|
|
38961
|
-
const userData = mkdtempSync3(
|
|
38991
|
+
const userData = mkdtempSync3(join21(tmpdir5(), "gdharness-tests-"));
|
|
38962
38992
|
const run = this.spawnGame(engine.value, cmdArgs, userDataIn(userData));
|
|
38963
38993
|
const hung = await new Promise((resolve) => {
|
|
38964
38994
|
const timer = setTimeout(() => {
|
|
@@ -38974,14 +39004,14 @@ class GodotServer {
|
|
|
38974
39004
|
resolve(false);
|
|
38975
39005
|
});
|
|
38976
39006
|
});
|
|
38977
|
-
const reportsDir =
|
|
39007
|
+
const reportsDir = join21(project.value.path, ".godot", ...ours.split("/"));
|
|
38978
39008
|
let report = null;
|
|
38979
39009
|
let reportProblem = null;
|
|
38980
39010
|
try {
|
|
38981
39011
|
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
39012
|
const newest = written.at(-1);
|
|
38983
39013
|
if (newest !== undefined) {
|
|
38984
|
-
report = parseJUnit(
|
|
39014
|
+
report = parseJUnit(readFileSync19(join21(reportsDir, newest, "results.xml"), "utf8"));
|
|
38985
39015
|
}
|
|
38986
39016
|
} catch (error) {
|
|
38987
39017
|
reportProblem = errorMessage(error);
|
|
@@ -39121,7 +39151,7 @@ class GodotServer {
|
|
|
39121
39151
|
return null;
|
|
39122
39152
|
}
|
|
39123
39153
|
try {
|
|
39124
|
-
return
|
|
39154
|
+
return readFileSync19(contained.absolutePath, "utf8");
|
|
39125
39155
|
} catch {
|
|
39126
39156
|
return null;
|
|
39127
39157
|
}
|
|
@@ -39159,8 +39189,15 @@ class GodotServer {
|
|
|
39159
39189
|
};
|
|
39160
39190
|
}
|
|
39161
39191
|
editorWritesAShortList(projectPath) {
|
|
39162
|
-
const
|
|
39163
|
-
return
|
|
39192
|
+
const recorded = readClassNote(projectPath)?.shortListEditorPid;
|
|
39193
|
+
return recorded !== undefined && recorded === (this.godotBridge.getStatus().editorPid ?? null);
|
|
39194
|
+
}
|
|
39195
|
+
noteAShortListEditor(projectPath) {
|
|
39196
|
+
const note = readClassNote(projectPath);
|
|
39197
|
+
writeClassNote(projectPath, {
|
|
39198
|
+
rebuiltWith: note?.rebuiltWith ?? [],
|
|
39199
|
+
shortListEditorPid: this.godotBridge.getStatus().editorPid ?? null
|
|
39200
|
+
});
|
|
39164
39201
|
}
|
|
39165
39202
|
sourceOfClass(projectPath, resourcePath) {
|
|
39166
39203
|
const contained = resolveWithinProject(projectPath, resourcePath);
|
|
@@ -39168,7 +39205,7 @@ class GodotServer {
|
|
|
39168
39205
|
return null;
|
|
39169
39206
|
}
|
|
39170
39207
|
try {
|
|
39171
|
-
return
|
|
39208
|
+
return readFileSync19(contained.absolutePath, "utf8");
|
|
39172
39209
|
} catch {
|
|
39173
39210
|
return null;
|
|
39174
39211
|
}
|
|
@@ -39397,7 +39434,7 @@ class GodotServer {
|
|
|
39397
39434
|
return null;
|
|
39398
39435
|
}
|
|
39399
39436
|
try {
|
|
39400
|
-
return settingKeys(
|
|
39437
|
+
return settingKeys(readFileSync19(join21(projectPath, "project.godot"), "utf8"));
|
|
39401
39438
|
} catch {
|
|
39402
39439
|
return null;
|
|
39403
39440
|
}
|
|
@@ -39639,7 +39676,7 @@ class GodotServer {
|
|
|
39639
39676
|
}
|
|
39640
39677
|
async runtimeUp(projectPath, before, budget, withArgs) {
|
|
39641
39678
|
const budgetMs = budget.ms;
|
|
39642
|
-
if (!existsSync14(
|
|
39679
|
+
if (!existsSync14(join21(projectPath, RUNTIME_AUTOLOAD2.path))) {
|
|
39643
39680
|
return runtimeVerdict(null, { addon: false, budgetMs, heldAt: null, running: false, withArgs });
|
|
39644
39681
|
}
|
|
39645
39682
|
let asking = null;
|
|
@@ -40512,7 +40549,7 @@ class GodotServer {
|
|
|
40512
40549
|
const after = projectPath === "" || busy ? null : cachedClasses(projectPath);
|
|
40513
40550
|
const lost = before === null || after === null ? [] : [...before.keys()].filter((name) => !after.has(name));
|
|
40514
40551
|
if (lost.length > 0) {
|
|
40515
|
-
this.
|
|
40552
|
+
this.noteAShortListEditor(projectPath);
|
|
40516
40553
|
}
|
|
40517
40554
|
const atMissingPaths = after === null ? [] : cachedAtMissingPaths(after, projectPath);
|
|
40518
40555
|
const restored = lost.length > 0 || atMissingPaths.length > 0 ? await this.rebuildCacheAfterLoss(projectPath, lost) : [];
|
|
@@ -40578,9 +40615,8 @@ class GodotServer {
|
|
|
40578
40615
|
return now === null ? [] : lost.filter((name) => now.has(name));
|
|
40579
40616
|
}
|
|
40580
40617
|
async rebuildClassCache(projectPath) {
|
|
40581
|
-
const key = resolve5(projectPath);
|
|
40582
40618
|
const before = cachedClasses(projectPath);
|
|
40583
|
-
const
|
|
40619
|
+
const noted = readClassNote(projectPath);
|
|
40584
40620
|
const rebuilt = await this.operation("refresh_class_cache", {}, projectPath);
|
|
40585
40621
|
if (!rebuilt.ok) {
|
|
40586
40622
|
return rebuilt;
|
|
@@ -40589,12 +40625,15 @@ class GodotServer {
|
|
|
40589
40625
|
if (after === null) {
|
|
40590
40626
|
return rebuilt;
|
|
40591
40627
|
}
|
|
40592
|
-
|
|
40593
|
-
const
|
|
40594
|
-
|
|
40628
|
+
const lost = noted === null || before === null ? [] : noted.rebuiltWith.filter((name) => !before.has(name) && after.has(name)).sort();
|
|
40629
|
+
const seenLosing = lost.length > 0 && this.godotBridge.isConnected();
|
|
40630
|
+
writeClassNote(projectPath, {
|
|
40631
|
+
rebuiltWith: [...after.keys()].sort(),
|
|
40632
|
+
...seenLosing ? { shortListEditorPid: this.godotBridge.getStatus().editorPid ?? null } : noted?.shortListEditorPid === undefined ? {} : { shortListEditorPid: noted.shortListEditorPid }
|
|
40633
|
+
});
|
|
40634
|
+
if (!seenLosing) {
|
|
40595
40635
|
return rebuilt;
|
|
40596
40636
|
}
|
|
40597
|
-
this.shortListEditors.set(key, this.godotBridge.getStatus().editorPid);
|
|
40598
40637
|
const them = lost.length === 1 ? "it" : "them";
|
|
40599
40638
|
return {
|
|
40600
40639
|
...rebuilt,
|
|
@@ -40751,8 +40790,8 @@ class GodotServer {
|
|
|
40751
40790
|
]);
|
|
40752
40791
|
}
|
|
40753
40792
|
const expectsScreenshot = command === "capture_screenshot" || command === "capture_viewport";
|
|
40754
|
-
const screenshotDir = expectsScreenshot ? mkdtempSync3(
|
|
40755
|
-
const screenshotPath = screenshotDir ?
|
|
40793
|
+
const screenshotDir = expectsScreenshot ? mkdtempSync3(join21(tmpdir5(), "gdharness-runtime-screenshot-")) : null;
|
|
40794
|
+
const screenshotPath = screenshotDir ? join21(screenshotDir, "capture.png") : null;
|
|
40756
40795
|
try {
|
|
40757
40796
|
const reply = await runtimeRequest(choice.endpoint, command, screenshotPath ? { ...params, output_path: screenshotPath } : params, timeoutMs);
|
|
40758
40797
|
if (!reply.ok) {
|
|
@@ -40776,7 +40815,7 @@ class GodotServer {
|
|
|
40776
40815
|
type: "text",
|
|
40777
40816
|
text: `Screenshot captured: ${dimensions}${chosenAmongSeveral ? ` from pid ${own}, the game this server holds` : ""}`
|
|
40778
40817
|
},
|
|
40779
|
-
{ type: "image", data:
|
|
40818
|
+
{ type: "image", data: readFileSync19(screenshotPath).toString("base64"), mimeType: "image/png" }
|
|
40780
40819
|
]
|
|
40781
40820
|
};
|
|
40782
40821
|
} finally {
|
|
@@ -40852,6 +40891,7 @@ var init_server3 = __esm(() => {
|
|
|
40852
40891
|
init_breakpoint_note();
|
|
40853
40892
|
init_bridge_announce();
|
|
40854
40893
|
init_class_cache();
|
|
40894
|
+
init_class_note();
|
|
40855
40895
|
init_config_pin();
|
|
40856
40896
|
init_dap_client();
|
|
40857
40897
|
init_errors();
|
|
@@ -40878,7 +40918,7 @@ var init_server3 = __esm(() => {
|
|
|
40878
40918
|
init_tool_definitions();
|
|
40879
40919
|
init_update_check();
|
|
40880
40920
|
run7 = promisify7(execFile7);
|
|
40881
|
-
__dirname2 =
|
|
40921
|
+
__dirname2 = dirname14(fileURLToPath4(import.meta.url));
|
|
40882
40922
|
CONSOLE_WRAPPER = /_console(\.exe)?$/i;
|
|
40883
40923
|
LONGEST_BOOT_NOTED_MS = 5 * 60000;
|
|
40884
40924
|
VALUE_NAMES = {
|
|
@@ -40919,7 +40959,7 @@ var init_server3 = __esm(() => {
|
|
|
40919
40959
|
|
|
40920
40960
|
// src/cli.ts
|
|
40921
40961
|
import { existsSync as existsSync15 } from "fs";
|
|
40922
|
-
import { join as
|
|
40962
|
+
import { join as join22, resolve as resolve5 } from "path";
|
|
40923
40963
|
|
|
40924
40964
|
// src/errors.ts
|
|
40925
40965
|
class Refusal extends Error {
|
|
@@ -42623,8 +42663,8 @@ class UsageError extends Refusal {
|
|
|
42623
42663
|
}
|
|
42624
42664
|
function projectArgument(at) {
|
|
42625
42665
|
const named = args.slice(at).find((value) => !value.startsWith("--"));
|
|
42626
|
-
const projectPath =
|
|
42627
|
-
if (!existsSync15(
|
|
42666
|
+
const projectPath = resolve5(named ?? ".");
|
|
42667
|
+
if (!existsSync15(join22(projectPath, "project.godot"))) {
|
|
42628
42668
|
throw new UsageError(`Not a Godot project: ${projectPath} holds no project.godot.`);
|
|
42629
42669
|
}
|
|
42630
42670
|
return projectPath;
|
|
@@ -42663,7 +42703,7 @@ async function registerRuntime(godot, projectPath) {
|
|
|
42663
42703
|
return;
|
|
42664
42704
|
}
|
|
42665
42705
|
console.log(`${RUNTIME_AUTOLOAD.name} autoload left at ${named}, which this project registers itself`);
|
|
42666
|
-
if (!existsSync15(
|
|
42706
|
+
if (!existsSync15(join22(projectPath, (named ?? "").replace(/^res:\/\//, "")))) {
|
|
42667
42707
|
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
42708
|
}
|
|
42669
42709
|
}
|
package/build/index.js
CHANGED
|
@@ -10175,16 +10175,16 @@ 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
10184
|
statSync as statSync5
|
|
10185
10185
|
} from "node:fs";
|
|
10186
10186
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
10187
|
-
import { basename as basename3, dirname as
|
|
10187
|
+
import { basename as basename3, dirname as dirname9, join as join15, normalize as normalize2, relative as relative3 } from "node:path";
|
|
10188
10188
|
import { setTimeout as delay3 } from "node:timers/promises";
|
|
10189
10189
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
10190
10190
|
import { promisify as promisify5 } from "node:util";
|
|
@@ -25814,18 +25814,49 @@ function unseenByEditor(projectPath, editorHolds) {
|
|
|
25814
25814
|
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
25815
|
}
|
|
25816
25816
|
|
|
25817
|
+
// src/class-note.ts
|
|
25818
|
+
import { mkdirSync as mkdirSync5, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "node:fs";
|
|
25819
|
+
import { dirname as dirname5, join as join7 } from "node:path";
|
|
25820
|
+
function classNotePath(projectPath) {
|
|
25821
|
+
return join7(projectPath, ".godot", "gdharness-classes.json");
|
|
25822
|
+
}
|
|
25823
|
+
function readClassNote(projectPath) {
|
|
25824
|
+
try {
|
|
25825
|
+
const fields = asParams(JSON.parse(readFileSync8(classNotePath(projectPath), "utf8")));
|
|
25826
|
+
const rebuiltWith = readArray(fields, "rebuiltWith");
|
|
25827
|
+
if (rebuiltWith === undefined || !rebuiltWith.every((name) => typeof name === "string")) {
|
|
25828
|
+
return null;
|
|
25829
|
+
}
|
|
25830
|
+
const pid = readNumber(fields, "shortListEditorPid");
|
|
25831
|
+
return {
|
|
25832
|
+
rebuiltWith: rebuiltWith.map(String),
|
|
25833
|
+
...Object.hasOwn(fields, "shortListEditorPid") ? { shortListEditorPid: pid ?? null } : {}
|
|
25834
|
+
};
|
|
25835
|
+
} catch {
|
|
25836
|
+
return null;
|
|
25837
|
+
}
|
|
25838
|
+
}
|
|
25839
|
+
function writeClassNote(projectPath, note) {
|
|
25840
|
+
try {
|
|
25841
|
+
const path = classNotePath(projectPath);
|
|
25842
|
+
mkdirSync5(dirname5(path), { recursive: true });
|
|
25843
|
+
writeFileSync5(path, `${JSON.stringify(note, null, 2)}
|
|
25844
|
+
`, "utf8");
|
|
25845
|
+
} catch {}
|
|
25846
|
+
}
|
|
25847
|
+
|
|
25817
25848
|
// src/config-pin.ts
|
|
25818
|
-
import { existsSync as existsSync4, readFileSync as
|
|
25819
|
-
import { join as
|
|
25849
|
+
import { existsSync as existsSync4, readFileSync as readFileSync9 } from "node:fs";
|
|
25850
|
+
import { join as join8 } from "node:path";
|
|
25820
25851
|
var PINNED = /\bgdharness@(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)\b/g;
|
|
25821
25852
|
function versionsNamedInConfig(projectPath) {
|
|
25822
|
-
const config =
|
|
25853
|
+
const config = join8(projectPath, ".mcp.json");
|
|
25823
25854
|
if (!existsSync4(config)) {
|
|
25824
25855
|
return [];
|
|
25825
25856
|
}
|
|
25826
25857
|
let text;
|
|
25827
25858
|
try {
|
|
25828
|
-
text =
|
|
25859
|
+
text = readFileSync9(config, "utf8");
|
|
25829
25860
|
JSON.parse(text);
|
|
25830
25861
|
} catch {
|
|
25831
25862
|
return [];
|
|
@@ -27192,7 +27223,7 @@ import { promisify as promisify2 } from "node:util";
|
|
|
27192
27223
|
// src/detection.ts
|
|
27193
27224
|
import { existsSync as existsSync5, readdirSync as readdirSync3, statSync as statSync3 } from "node:fs";
|
|
27194
27225
|
import { homedir as homedir2 } from "node:os";
|
|
27195
|
-
import { join as
|
|
27226
|
+
import { join as join9 } from "node:path";
|
|
27196
27227
|
function resolveHomeDirectory() {
|
|
27197
27228
|
try {
|
|
27198
27229
|
return homedir2();
|
|
@@ -27216,7 +27247,7 @@ function scanDirectoryForGodotBinaries(directory, platform) {
|
|
|
27216
27247
|
if (!pattern.test(name)) {
|
|
27217
27248
|
continue;
|
|
27218
27249
|
}
|
|
27219
|
-
const fullPath =
|
|
27250
|
+
const fullPath = join9(directory, name);
|
|
27220
27251
|
try {
|
|
27221
27252
|
const stat = statSync3(fullPath);
|
|
27222
27253
|
if (stat.isFile()) {
|
|
@@ -27225,7 +27256,7 @@ function scanDirectoryForGodotBinaries(directory, platform) {
|
|
|
27225
27256
|
} catch {}
|
|
27226
27257
|
}
|
|
27227
27258
|
matches.sort((a, b) => b.mtime - a.mtime);
|
|
27228
|
-
return matches.map((m) =>
|
|
27259
|
+
return matches.map((m) => join9(directory, m.name));
|
|
27229
27260
|
}
|
|
27230
27261
|
function conventionalPaths(platform, home) {
|
|
27231
27262
|
const paths = ["godot"];
|
|
@@ -27345,9 +27376,9 @@ class GodotLocator {
|
|
|
27345
27376
|
|
|
27346
27377
|
// src/headless.ts
|
|
27347
27378
|
import { execFile as execFile3 } from "node:child_process";
|
|
27348
|
-
import { mkdtempSync, writeFileSync as
|
|
27379
|
+
import { mkdtempSync, writeFileSync as writeFileSync6 } from "node:fs";
|
|
27349
27380
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
27350
|
-
import { join as
|
|
27381
|
+
import { join as join10 } from "node:path";
|
|
27351
27382
|
import { promisify as promisify3 } from "node:util";
|
|
27352
27383
|
|
|
27353
27384
|
// src/scratch.ts
|
|
@@ -27413,12 +27444,12 @@ function reason(stdout, stderr) {
|
|
|
27413
27444
|
return stdout.trim().split(/\r?\n/).at(-1) ?? "no output at all";
|
|
27414
27445
|
}
|
|
27415
27446
|
async function runImport(godotPath, projectPath) {
|
|
27416
|
-
const logDir = mkdtempSync(
|
|
27447
|
+
const logDir = mkdtempSync(join10(tmpdir3(), "gdharness-import-"));
|
|
27417
27448
|
try {
|
|
27418
27449
|
const { stderr } = await run3(godotPath, [
|
|
27419
27450
|
"--headless",
|
|
27420
27451
|
"--log-file",
|
|
27421
|
-
|
|
27452
|
+
join10(logDir, "engine.log"),
|
|
27422
27453
|
"--path",
|
|
27423
27454
|
projectPath,
|
|
27424
27455
|
"--import"
|
|
@@ -27443,13 +27474,13 @@ async function runImport(godotPath, projectPath) {
|
|
|
27443
27474
|
}
|
|
27444
27475
|
}
|
|
27445
27476
|
async function runOperation(engine, operation, params, projectPath) {
|
|
27446
|
-
const paramsDir = mkdtempSync(
|
|
27447
|
-
const paramsFile =
|
|
27448
|
-
|
|
27477
|
+
const paramsDir = mkdtempSync(join10(tmpdir3(), "gdharness-params-"));
|
|
27478
|
+
const paramsFile = join10(paramsDir, `${operation}.json`);
|
|
27479
|
+
writeFileSync6(paramsFile, JSON.stringify(snakeCased(params)), "utf8");
|
|
27449
27480
|
const args = [
|
|
27450
27481
|
"--headless",
|
|
27451
27482
|
"--log-file",
|
|
27452
|
-
|
|
27483
|
+
join10(paramsDir, "engine.log"),
|
|
27453
27484
|
"--path",
|
|
27454
27485
|
projectPath,
|
|
27455
27486
|
"--script",
|
|
@@ -27878,7 +27909,7 @@ function orphansPrinted(printed) {
|
|
|
27878
27909
|
import { realpathSync as realpathSync2 } from "node:fs";
|
|
27879
27910
|
import { readFile, realpath } from "node:fs/promises";
|
|
27880
27911
|
import { createConnection as createConnection3 } from "node:net";
|
|
27881
|
-
import { dirname as
|
|
27912
|
+
import { dirname as dirname6, resolve as resolve3 } from "node:path";
|
|
27882
27913
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
27883
27914
|
var DEFAULT_LSP_PORT = 6005;
|
|
27884
27915
|
var DIAGNOSTICS_TIMEOUT_MS = 5000;
|
|
@@ -28008,7 +28039,7 @@ class GodotLSPClient {
|
|
|
28008
28039
|
if (this.initialized) {
|
|
28009
28040
|
return;
|
|
28010
28041
|
}
|
|
28011
|
-
const rootPath = this.rootPath ??
|
|
28042
|
+
const rootPath = this.rootPath ?? dirname6(resolve3(filePath));
|
|
28012
28043
|
await this.initialize(rootPath);
|
|
28013
28044
|
}
|
|
28014
28045
|
async sendRequest(method, params) {
|
|
@@ -28444,14 +28475,14 @@ function secondsFromClock(printed) {
|
|
|
28444
28475
|
}
|
|
28445
28476
|
|
|
28446
28477
|
// src/project-scan.ts
|
|
28447
|
-
import { existsSync as existsSync7, readdirSync as readdirSync4, readFileSync as
|
|
28448
|
-
import { join as
|
|
28478
|
+
import { existsSync as existsSync7, readdirSync as readdirSync4, readFileSync as readFileSync10 } from "node:fs";
|
|
28479
|
+
import { join as join11 } from "node:path";
|
|
28449
28480
|
var SKIPPED = new Set(["node_modules"]);
|
|
28450
28481
|
function skipped(name) {
|
|
28451
28482
|
return name.startsWith(".") || SKIPPED.has(name);
|
|
28452
28483
|
}
|
|
28453
28484
|
function steppedOver(directory) {
|
|
28454
|
-
return existsSync7(
|
|
28485
|
+
return existsSync7(join11(directory, ".gdignore"));
|
|
28455
28486
|
}
|
|
28456
28487
|
var ASSET_EXTENSIONS = new Set(["png", "jpg", "jpeg", "webp", "svg", "ttf", "otf", "wav", "mp3", "ogg"]);
|
|
28457
28488
|
function projectStructure(projectPath) {
|
|
@@ -28465,7 +28496,7 @@ function projectStructure(projectPath) {
|
|
|
28465
28496
|
continue;
|
|
28466
28497
|
}
|
|
28467
28498
|
if (entry.isDirectory()) {
|
|
28468
|
-
visit(
|
|
28499
|
+
visit(join11(directory, entry.name));
|
|
28469
28500
|
} else if (entry.isFile()) {
|
|
28470
28501
|
const extension = entry.name.split(".").pop()?.toLowerCase() ?? "";
|
|
28471
28502
|
if (extension === "tscn") {
|
|
@@ -28496,14 +28527,14 @@ function scriptsWithoutUid(projectPath) {
|
|
|
28496
28527
|
}
|
|
28497
28528
|
const spelled = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
|
|
28498
28529
|
if (entry.isDirectory()) {
|
|
28499
|
-
visit(
|
|
28530
|
+
visit(join11(directory, entry.name), spelled);
|
|
28500
28531
|
continue;
|
|
28501
28532
|
}
|
|
28502
28533
|
if (!entry.isFile()) {
|
|
28503
28534
|
continue;
|
|
28504
28535
|
}
|
|
28505
28536
|
const extension = entry.name.split(".").pop()?.toLowerCase() ?? "";
|
|
28506
|
-
if (CARRIES_A_UID.has(extension) && !existsSync7(
|
|
28537
|
+
if (CARRIES_A_UID.has(extension) && !existsSync7(join11(directory, `${entry.name}.uid`))) {
|
|
28507
28538
|
found.push(spelled);
|
|
28508
28539
|
}
|
|
28509
28540
|
}
|
|
@@ -28538,7 +28569,7 @@ function searchProject(projectPath, options) {
|
|
|
28538
28569
|
if (skipped(entry.name)) {
|
|
28539
28570
|
continue;
|
|
28540
28571
|
}
|
|
28541
|
-
const entryPath =
|
|
28572
|
+
const entryPath = join11(directory, entry.name);
|
|
28542
28573
|
if (entry.isDirectory()) {
|
|
28543
28574
|
visit(entryPath);
|
|
28544
28575
|
continue;
|
|
@@ -28549,7 +28580,7 @@ function searchProject(projectPath, options) {
|
|
|
28549
28580
|
}
|
|
28550
28581
|
result.summary.files_searched += 1;
|
|
28551
28582
|
const matches = [];
|
|
28552
|
-
for (const [index, line] of
|
|
28583
|
+
for (const [index, line] of readFileSync10(entryPath, "utf8").split(`
|
|
28553
28584
|
`).entries()) {
|
|
28554
28585
|
if (full()) {
|
|
28555
28586
|
break;
|
|
@@ -28572,7 +28603,7 @@ function searchProject(projectPath, options) {
|
|
|
28572
28603
|
}
|
|
28573
28604
|
|
|
28574
28605
|
// src/resources.ts
|
|
28575
|
-
import { readFileSync as
|
|
28606
|
+
import { readFileSync as readFileSync11 } from "node:fs";
|
|
28576
28607
|
import { extname, resolve as resolve4 } from "node:path";
|
|
28577
28608
|
var STATIC_RESOURCES = [
|
|
28578
28609
|
{
|
|
@@ -28767,7 +28798,7 @@ function readResourceText(uri, getProjectPath) {
|
|
|
28767
28798
|
const parsedUri = parseGodotUri(uri);
|
|
28768
28799
|
if (parsedUri.kind === "project-info") {
|
|
28769
28800
|
const projectFilePath = resolveProjectFile(projectPath, "project.godot");
|
|
28770
|
-
const rawProject =
|
|
28801
|
+
const rawProject = readFileSync11(projectFilePath, "utf-8");
|
|
28771
28802
|
const parsedProject = parseProjectGodot(rawProject);
|
|
28772
28803
|
return {
|
|
28773
28804
|
mimeType: "application/json",
|
|
@@ -28776,7 +28807,7 @@ function readResourceText(uri, getProjectPath) {
|
|
|
28776
28807
|
}
|
|
28777
28808
|
const filePath = resolveProjectFile(projectPath, parsedUri.resourcePath);
|
|
28778
28809
|
ensureAllowedExtension(parsedUri.kind, filePath);
|
|
28779
|
-
const text =
|
|
28810
|
+
const text = readFileSync11(filePath, "utf-8");
|
|
28780
28811
|
return {
|
|
28781
28812
|
mimeType: parsedUri.kind === "script" ? "text/x-gdscript" : "text/plain",
|
|
28782
28813
|
text
|
|
@@ -28813,16 +28844,16 @@ function setupResourceHandlers(mcp, getProjectPath) {
|
|
|
28813
28844
|
}
|
|
28814
28845
|
|
|
28815
28846
|
// src/restart-note.ts
|
|
28816
|
-
import { mkdirSync as
|
|
28817
|
-
import { dirname as
|
|
28847
|
+
import { mkdirSync as mkdirSync6, readFileSync as readFileSync12, rmSync as rmSync4, writeFileSync as writeFileSync7 } from "node:fs";
|
|
28848
|
+
import { dirname as dirname7, join as join12 } from "node:path";
|
|
28818
28849
|
function restartNotePath(projectPath) {
|
|
28819
|
-
return
|
|
28850
|
+
return join12(projectPath, ".godot", "gdharness-restart.json");
|
|
28820
28851
|
}
|
|
28821
28852
|
function noteRestartBegun(note) {
|
|
28822
28853
|
const path = restartNotePath(note.projectPath);
|
|
28823
28854
|
try {
|
|
28824
|
-
|
|
28825
|
-
|
|
28855
|
+
mkdirSync6(dirname7(path), { recursive: true });
|
|
28856
|
+
writeFileSync7(path, `${JSON.stringify(note, null, 2)}
|
|
28826
28857
|
`, "utf8");
|
|
28827
28858
|
} catch (error) {
|
|
28828
28859
|
console.error(`[SERVER] Could not record the restart at ${path}: ${errorMessage(error)}`);
|
|
@@ -28830,7 +28861,7 @@ function noteRestartBegun(note) {
|
|
|
28830
28861
|
}
|
|
28831
28862
|
function restartOwed(projectPath) {
|
|
28832
28863
|
try {
|
|
28833
|
-
const parsed = JSON.parse(
|
|
28864
|
+
const parsed = JSON.parse(readFileSync12(restartNotePath(projectPath), "utf8"));
|
|
28834
28865
|
if (typeof parsed !== "object" || parsed === null) {
|
|
28835
28866
|
return null;
|
|
28836
28867
|
}
|
|
@@ -28853,36 +28884,36 @@ function restartSettled(projectPath) {
|
|
|
28853
28884
|
|
|
28854
28885
|
// src/run-record.ts
|
|
28855
28886
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
28856
|
-
import { mkdirSync as
|
|
28857
|
-
import { basename as basename2, join as
|
|
28887
|
+
import { mkdirSync as mkdirSync7, openSync, readdirSync as readdirSync5, readFileSync as readFileSync13, rmSync as rmSync5, statSync as statSync4, writeFileSync as writeFileSync8 } from "node:fs";
|
|
28888
|
+
import { basename as basename2, join as join13 } from "node:path";
|
|
28858
28889
|
function runsDirectory() {
|
|
28859
|
-
return
|
|
28890
|
+
return join13(runtimeDirectory(), "runs");
|
|
28860
28891
|
}
|
|
28861
28892
|
function recordPath() {
|
|
28862
|
-
return
|
|
28893
|
+
return join13(runsDirectory(), "run.json");
|
|
28863
28894
|
}
|
|
28864
28895
|
function recordPaths() {
|
|
28865
|
-
return runtimeDirectories().map((directory) =>
|
|
28896
|
+
return runtimeDirectories().map((directory) => join13(directory, "runs", "run.json"));
|
|
28866
28897
|
}
|
|
28867
28898
|
function openTranscript(startedAt) {
|
|
28868
|
-
|
|
28869
|
-
const path =
|
|
28899
|
+
mkdirSync7(runsDirectory(), { recursive: true });
|
|
28900
|
+
const path = join13(runsDirectory(), `run-${startedAt}.log`);
|
|
28870
28901
|
return { path, fd: openSync(path, "a") };
|
|
28871
28902
|
}
|
|
28872
28903
|
function writeRunRecord(record) {
|
|
28873
|
-
|
|
28874
|
-
|
|
28904
|
+
mkdirSync7(runsDirectory(), { recursive: true });
|
|
28905
|
+
writeFileSync8(recordPath(), JSON.stringify(record, null, 2), "utf8");
|
|
28875
28906
|
}
|
|
28876
28907
|
function editorNotePath() {
|
|
28877
|
-
return
|
|
28908
|
+
return join13(runsDirectory(), "editor-run.json");
|
|
28878
28909
|
}
|
|
28879
28910
|
function writeEditorRunNote(note) {
|
|
28880
|
-
|
|
28881
|
-
|
|
28911
|
+
mkdirSync7(runsDirectory(), { recursive: true });
|
|
28912
|
+
writeFileSync8(editorNotePath(), JSON.stringify(note, null, 2), "utf8");
|
|
28882
28913
|
}
|
|
28883
28914
|
function readEditorRunNote() {
|
|
28884
28915
|
for (const directory of runtimeDirectories()) {
|
|
28885
|
-
const note = editorNoteAt(
|
|
28916
|
+
const note = editorNoteAt(join13(directory, "runs", "editor-run.json"));
|
|
28886
28917
|
if (note !== null) {
|
|
28887
28918
|
return note;
|
|
28888
28919
|
}
|
|
@@ -28892,7 +28923,7 @@ function readEditorRunNote() {
|
|
|
28892
28923
|
function editorNoteAt(path) {
|
|
28893
28924
|
let parsed;
|
|
28894
28925
|
try {
|
|
28895
|
-
parsed = JSON.parse(
|
|
28926
|
+
parsed = JSON.parse(readFileSync13(path, "utf8"));
|
|
28896
28927
|
} catch {
|
|
28897
28928
|
return null;
|
|
28898
28929
|
}
|
|
@@ -28915,7 +28946,7 @@ function recordRunEnded(pid, exitCode) {
|
|
|
28915
28946
|
return;
|
|
28916
28947
|
}
|
|
28917
28948
|
try {
|
|
28918
|
-
|
|
28949
|
+
writeFileSync8(path, JSON.stringify({ ...record, exitCode }, null, 2), "utf8");
|
|
28919
28950
|
} catch {}
|
|
28920
28951
|
}
|
|
28921
28952
|
function readRunRecord() {
|
|
@@ -28930,7 +28961,7 @@ function readRunRecord() {
|
|
|
28930
28961
|
function recordAt(path) {
|
|
28931
28962
|
let parsed;
|
|
28932
28963
|
try {
|
|
28933
|
-
parsed = JSON.parse(
|
|
28964
|
+
parsed = JSON.parse(readFileSync13(path, "utf8"));
|
|
28934
28965
|
} catch {
|
|
28935
28966
|
return null;
|
|
28936
28967
|
}
|
|
@@ -28975,7 +29006,7 @@ function runningAs(pid) {
|
|
|
28975
29006
|
const began = startedAt(pid, null);
|
|
28976
29007
|
const when = began === null ? {} : { startedAt: began };
|
|
28977
29008
|
if (process.platform === "linux") {
|
|
28978
|
-
const raw =
|
|
29009
|
+
const raw = readFileSync13(`/proc/${pid}/cmdline`, "utf8").replaceAll("\x00", " ").trim();
|
|
28979
29010
|
return raw === "" ? null : { kind: "commandLine", text: raw, ...when };
|
|
28980
29011
|
}
|
|
28981
29012
|
const args = execFileSync2("ps", ["-p", String(pid), "-o", "args="], {
|
|
@@ -29128,7 +29159,7 @@ function sweepTranscripts(keepMs = 24 * 60 * 60 * 1000, now = Date.now()) {
|
|
|
29128
29159
|
if (!entry.startsWith("run-") || !entry.endsWith(".log")) {
|
|
29129
29160
|
continue;
|
|
29130
29161
|
}
|
|
29131
|
-
const path =
|
|
29162
|
+
const path = join13(directory, entry);
|
|
29132
29163
|
if (path === keep || ageMs(path, now) < keepMs) {
|
|
29133
29164
|
continue;
|
|
29134
29165
|
}
|
|
@@ -29139,8 +29170,8 @@ function sweepTranscripts(keepMs = 24 * 60 * 60 * 1000, now = Date.now()) {
|
|
|
29139
29170
|
}
|
|
29140
29171
|
|
|
29141
29172
|
// src/setup.ts
|
|
29142
|
-
import { cpSync, existsSync as existsSync8, mkdirSync as
|
|
29143
|
-
import { dirname as
|
|
29173
|
+
import { cpSync, existsSync as existsSync8, mkdirSync as mkdirSync8, readFileSync as readFileSync14, rmSync as rmSync6, writeFileSync as writeFileSync9 } from "node:fs";
|
|
29174
|
+
import { dirname as dirname8, join as join14 } from "node:path";
|
|
29144
29175
|
var ADDONS = ["gdharness_editor", "gdharness_runtime", "auto_reload"];
|
|
29145
29176
|
var RUNTIME_AUTOLOAD = {
|
|
29146
29177
|
name: "GdharnessRuntime",
|
|
@@ -29148,11 +29179,11 @@ var RUNTIME_AUTOLOAD = {
|
|
|
29148
29179
|
};
|
|
29149
29180
|
var VERSION_MARKER = ".gdharness-version";
|
|
29150
29181
|
function installedAddonVersion(projectPath) {
|
|
29151
|
-
const marker =
|
|
29182
|
+
const marker = join14(projectPath, "addons", ADDONS[0], VERSION_MARKER);
|
|
29152
29183
|
if (!existsSync8(marker)) {
|
|
29153
29184
|
return null;
|
|
29154
29185
|
}
|
|
29155
|
-
const said =
|
|
29186
|
+
const said = readFileSync14(marker, "utf8").trim();
|
|
29156
29187
|
return said === "" ? null : said;
|
|
29157
29188
|
}
|
|
29158
29189
|
|
|
@@ -30309,7 +30340,7 @@ function buildToolDefinitions() {
|
|
|
30309
30340
|
var UPDATE_NOTICE_EVERY = 500;
|
|
30310
30341
|
var FEEDBACK_NOTICE_EVERY = 250;
|
|
30311
30342
|
var run5 = promisify5(execFile5);
|
|
30312
|
-
var __dirname2 =
|
|
30343
|
+
var __dirname2 = dirname9(fileURLToPath2(import.meta.url));
|
|
30313
30344
|
var EDITOR_RESTART_TIMEOUT_MS = 90000;
|
|
30314
30345
|
var HOLD_PING_MS = 2000;
|
|
30315
30346
|
var SLOWEST_FRAME_RATE = 20;
|
|
@@ -30596,7 +30627,7 @@ function aboveTheRunner(entry) {
|
|
|
30596
30627
|
}
|
|
30597
30628
|
function elsewhereIn(projectPath, asked) {
|
|
30598
30629
|
const wanted = asked.replace(/^res:\/\//, "").replace(/\/+$/, "");
|
|
30599
|
-
if (wanted === "" || existsSync9(
|
|
30630
|
+
if (wanted === "" || existsSync9(join15(projectPath, wanted))) {
|
|
30600
30631
|
return "";
|
|
30601
30632
|
}
|
|
30602
30633
|
const root = (wanted.split("/")[0] ?? "").toLowerCase();
|
|
@@ -30610,7 +30641,7 @@ function elsewhereIn(projectPath, asked) {
|
|
|
30610
30641
|
return near.length === 0 ? "" : ` The project does have ${there}.`;
|
|
30611
30642
|
}
|
|
30612
30643
|
function hasMainScene(projectFile) {
|
|
30613
|
-
const scene = parseProjectGodot(
|
|
30644
|
+
const scene = parseProjectGodot(readFileSync15(projectFile, "utf8"))["application"]?.["run/main_scene"];
|
|
30614
30645
|
return typeof scene === "string" && scene !== "";
|
|
30615
30646
|
}
|
|
30616
30647
|
function runtimeAddonAnnouncesEditors(projectPath) {
|
|
@@ -30618,13 +30649,13 @@ function runtimeAddonAnnouncesEditors(projectPath) {
|
|
|
30618
30649
|
return false;
|
|
30619
30650
|
}
|
|
30620
30651
|
try {
|
|
30621
|
-
return
|
|
30652
|
+
return readFileSync15(join15(projectPath, RUNTIME_AUTOLOAD.path), "utf8").includes('"editor_pid"');
|
|
30622
30653
|
} catch {
|
|
30623
30654
|
return false;
|
|
30624
30655
|
}
|
|
30625
30656
|
}
|
|
30626
30657
|
function editorPlaysHeadless(projectFile) {
|
|
30627
|
-
const runArgs = parseProjectGodot(
|
|
30658
|
+
const runArgs = parseProjectGodot(readFileSync15(projectFile, "utf8"))["editor"]?.["run/main_run_args"];
|
|
30628
30659
|
return typeof runArgs === "string" && /(?:^|\s)--headless(?:\s|$)/.test(runArgs);
|
|
30629
30660
|
}
|
|
30630
30661
|
function camelCased(params) {
|
|
@@ -30640,7 +30671,7 @@ function camelCased(params) {
|
|
|
30640
30671
|
class GodotServer {
|
|
30641
30672
|
mcp;
|
|
30642
30673
|
locator = new GodotLocator;
|
|
30643
|
-
operationsScript =
|
|
30674
|
+
operationsScript = join15(__dirname2, "godot", "operations", "godot_operations.gd");
|
|
30644
30675
|
godotBridge;
|
|
30645
30676
|
tools = buildToolDefinitions();
|
|
30646
30677
|
activeProcess = null;
|
|
@@ -30658,8 +30689,6 @@ class GodotServer {
|
|
|
30658
30689
|
successorWatch = null;
|
|
30659
30690
|
lastProjectPath = null;
|
|
30660
30691
|
shutdownInitiated = false;
|
|
30661
|
-
shortListEditors = new Map;
|
|
30662
|
-
cacheLastRebuilt = new Map;
|
|
30663
30692
|
launchedEditor = null;
|
|
30664
30693
|
launchedFrom = null;
|
|
30665
30694
|
ownProject;
|
|
@@ -31307,7 +31336,7 @@ class GodotServer {
|
|
|
31307
31336
|
if (path === undefined) {
|
|
31308
31337
|
return { ok: false, response: this.createErrorResponse("projectPath is required.") };
|
|
31309
31338
|
}
|
|
31310
|
-
const file =
|
|
31339
|
+
const file = join15(path, "project.godot");
|
|
31311
31340
|
if (!existsSync9(file)) {
|
|
31312
31341
|
return {
|
|
31313
31342
|
ok: false,
|
|
@@ -31531,7 +31560,7 @@ class GodotServer {
|
|
|
31531
31560
|
if (!engine.ok) {
|
|
31532
31561
|
return engine.response;
|
|
31533
31562
|
}
|
|
31534
|
-
const application = parseProjectGodot(
|
|
31563
|
+
const application = parseProjectGodot(readFileSync15(project.value.file, "utf8"))["application"] ?? {};
|
|
31535
31564
|
const name = application["config/name"];
|
|
31536
31565
|
const mainScene = application["run/main_scene"];
|
|
31537
31566
|
const info = {
|
|
@@ -31592,18 +31621,18 @@ class GodotServer {
|
|
|
31592
31621
|
if (!engine.ok) {
|
|
31593
31622
|
return engine.response;
|
|
31594
31623
|
}
|
|
31595
|
-
const folder =
|
|
31624
|
+
const folder = dirname9(output.absolutePath);
|
|
31596
31625
|
try {
|
|
31597
|
-
|
|
31626
|
+
mkdirSync9(folder, { recursive: true });
|
|
31598
31627
|
} catch (error) {
|
|
31599
|
-
return this.createErrorResponse(`Cannot create ${
|
|
31628
|
+
return this.createErrorResponse(`Cannot create ${dirname9(output.relativePath)} to export into: ${errorMessage(error)}`);
|
|
31600
31629
|
}
|
|
31601
31630
|
const debug = readBoolean(args, "debug") ?? false;
|
|
31602
|
-
const exportLogs = mkdtempSync2(
|
|
31631
|
+
const exportLogs = mkdtempSync2(join15(tmpdir4(), "gdharness-export-"));
|
|
31603
31632
|
const exportArgs = [
|
|
31604
31633
|
"--headless",
|
|
31605
31634
|
"--log-file",
|
|
31606
|
-
|
|
31635
|
+
join15(exportLogs, "engine.log"),
|
|
31607
31636
|
"--path",
|
|
31608
31637
|
project.value.path,
|
|
31609
31638
|
debug ? "--export-debug" : "--export-release",
|
|
@@ -31661,7 +31690,7 @@ class GodotServer {
|
|
|
31661
31690
|
return contained.response;
|
|
31662
31691
|
}
|
|
31663
31692
|
const runner = "addons/gdUnit4/bin/GdUnitCmdTool.gd";
|
|
31664
|
-
if (!existsSync9(
|
|
31693
|
+
if (!existsSync9(join15(project.value.path, runner))) {
|
|
31665
31694
|
return this.createErrorResponse(`gdUnit4 is not installed in this project: no ${runner}.`, [
|
|
31666
31695
|
"Install gdUnit4 under addons/gdUnit4, from https://github.com/godot-gdunit-labs/gdUnit4"
|
|
31667
31696
|
]);
|
|
@@ -31696,7 +31725,7 @@ class GodotServer {
|
|
|
31696
31725
|
];
|
|
31697
31726
|
const timeoutMs = readPositiveNumber(args, "timeoutMs") ?? 600000;
|
|
31698
31727
|
this.logDebug(`Running tests: ${engine.value} ${cmdArgs.join(" ")}`);
|
|
31699
|
-
const userData = mkdtempSync2(
|
|
31728
|
+
const userData = mkdtempSync2(join15(tmpdir4(), "gdharness-tests-"));
|
|
31700
31729
|
const run = this.spawnGame(engine.value, cmdArgs, userDataIn(userData));
|
|
31701
31730
|
const hung = await new Promise((resolve) => {
|
|
31702
31731
|
const timer = setTimeout(() => {
|
|
@@ -31712,14 +31741,14 @@ class GodotServer {
|
|
|
31712
31741
|
resolve(false);
|
|
31713
31742
|
});
|
|
31714
31743
|
});
|
|
31715
|
-
const reportsDir =
|
|
31744
|
+
const reportsDir = join15(project.value.path, ".godot", ...ours.split("/"));
|
|
31716
31745
|
let report = null;
|
|
31717
31746
|
let reportProblem = null;
|
|
31718
31747
|
try {
|
|
31719
31748
|
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
31749
|
const newest = written.at(-1);
|
|
31721
31750
|
if (newest !== undefined) {
|
|
31722
|
-
report = parseJUnit(
|
|
31751
|
+
report = parseJUnit(readFileSync15(join15(reportsDir, newest, "results.xml"), "utf8"));
|
|
31723
31752
|
}
|
|
31724
31753
|
} catch (error) {
|
|
31725
31754
|
reportProblem = errorMessage(error);
|
|
@@ -31859,7 +31888,7 @@ class GodotServer {
|
|
|
31859
31888
|
return null;
|
|
31860
31889
|
}
|
|
31861
31890
|
try {
|
|
31862
|
-
return
|
|
31891
|
+
return readFileSync15(contained.absolutePath, "utf8");
|
|
31863
31892
|
} catch {
|
|
31864
31893
|
return null;
|
|
31865
31894
|
}
|
|
@@ -31897,8 +31926,15 @@ class GodotServer {
|
|
|
31897
31926
|
};
|
|
31898
31927
|
}
|
|
31899
31928
|
editorWritesAShortList(projectPath) {
|
|
31900
|
-
const
|
|
31901
|
-
return
|
|
31929
|
+
const recorded = readClassNote(projectPath)?.shortListEditorPid;
|
|
31930
|
+
return recorded !== undefined && recorded === (this.godotBridge.getStatus().editorPid ?? null);
|
|
31931
|
+
}
|
|
31932
|
+
noteAShortListEditor(projectPath) {
|
|
31933
|
+
const note = readClassNote(projectPath);
|
|
31934
|
+
writeClassNote(projectPath, {
|
|
31935
|
+
rebuiltWith: note?.rebuiltWith ?? [],
|
|
31936
|
+
shortListEditorPid: this.godotBridge.getStatus().editorPid ?? null
|
|
31937
|
+
});
|
|
31902
31938
|
}
|
|
31903
31939
|
sourceOfClass(projectPath, resourcePath) {
|
|
31904
31940
|
const contained = resolveWithinProject(projectPath, resourcePath);
|
|
@@ -31906,7 +31942,7 @@ class GodotServer {
|
|
|
31906
31942
|
return null;
|
|
31907
31943
|
}
|
|
31908
31944
|
try {
|
|
31909
|
-
return
|
|
31945
|
+
return readFileSync15(contained.absolutePath, "utf8");
|
|
31910
31946
|
} catch {
|
|
31911
31947
|
return null;
|
|
31912
31948
|
}
|
|
@@ -32135,7 +32171,7 @@ class GodotServer {
|
|
|
32135
32171
|
return null;
|
|
32136
32172
|
}
|
|
32137
32173
|
try {
|
|
32138
|
-
return settingKeys(
|
|
32174
|
+
return settingKeys(readFileSync15(join15(projectPath, "project.godot"), "utf8"));
|
|
32139
32175
|
} catch {
|
|
32140
32176
|
return null;
|
|
32141
32177
|
}
|
|
@@ -32377,7 +32413,7 @@ class GodotServer {
|
|
|
32377
32413
|
}
|
|
32378
32414
|
async runtimeUp(projectPath, before, budget, withArgs) {
|
|
32379
32415
|
const budgetMs = budget.ms;
|
|
32380
|
-
if (!existsSync9(
|
|
32416
|
+
if (!existsSync9(join15(projectPath, RUNTIME_AUTOLOAD.path))) {
|
|
32381
32417
|
return runtimeVerdict(null, { addon: false, budgetMs, heldAt: null, running: false, withArgs });
|
|
32382
32418
|
}
|
|
32383
32419
|
let asking = null;
|
|
@@ -33250,7 +33286,7 @@ class GodotServer {
|
|
|
33250
33286
|
const after = projectPath === "" || busy ? null : cachedClasses(projectPath);
|
|
33251
33287
|
const lost = before === null || after === null ? [] : [...before.keys()].filter((name) => !after.has(name));
|
|
33252
33288
|
if (lost.length > 0) {
|
|
33253
|
-
this.
|
|
33289
|
+
this.noteAShortListEditor(projectPath);
|
|
33254
33290
|
}
|
|
33255
33291
|
const atMissingPaths = after === null ? [] : cachedAtMissingPaths(after, projectPath);
|
|
33256
33292
|
const restored = lost.length > 0 || atMissingPaths.length > 0 ? await this.rebuildCacheAfterLoss(projectPath, lost) : [];
|
|
@@ -33316,9 +33352,8 @@ class GodotServer {
|
|
|
33316
33352
|
return now === null ? [] : lost.filter((name) => now.has(name));
|
|
33317
33353
|
}
|
|
33318
33354
|
async rebuildClassCache(projectPath) {
|
|
33319
|
-
const key = resolve5(projectPath);
|
|
33320
33355
|
const before = cachedClasses(projectPath);
|
|
33321
|
-
const
|
|
33356
|
+
const noted = readClassNote(projectPath);
|
|
33322
33357
|
const rebuilt = await this.operation("refresh_class_cache", {}, projectPath);
|
|
33323
33358
|
if (!rebuilt.ok) {
|
|
33324
33359
|
return rebuilt;
|
|
@@ -33327,12 +33362,15 @@ class GodotServer {
|
|
|
33327
33362
|
if (after === null) {
|
|
33328
33363
|
return rebuilt;
|
|
33329
33364
|
}
|
|
33330
|
-
|
|
33331
|
-
const
|
|
33332
|
-
|
|
33365
|
+
const lost = noted === null || before === null ? [] : noted.rebuiltWith.filter((name) => !before.has(name) && after.has(name)).sort();
|
|
33366
|
+
const seenLosing = lost.length > 0 && this.godotBridge.isConnected();
|
|
33367
|
+
writeClassNote(projectPath, {
|
|
33368
|
+
rebuiltWith: [...after.keys()].sort(),
|
|
33369
|
+
...seenLosing ? { shortListEditorPid: this.godotBridge.getStatus().editorPid ?? null } : noted?.shortListEditorPid === undefined ? {} : { shortListEditorPid: noted.shortListEditorPid }
|
|
33370
|
+
});
|
|
33371
|
+
if (!seenLosing) {
|
|
33333
33372
|
return rebuilt;
|
|
33334
33373
|
}
|
|
33335
|
-
this.shortListEditors.set(key, this.godotBridge.getStatus().editorPid);
|
|
33336
33374
|
const them = lost.length === 1 ? "it" : "them";
|
|
33337
33375
|
return {
|
|
33338
33376
|
...rebuilt,
|
|
@@ -33489,8 +33527,8 @@ class GodotServer {
|
|
|
33489
33527
|
]);
|
|
33490
33528
|
}
|
|
33491
33529
|
const expectsScreenshot = command === "capture_screenshot" || command === "capture_viewport";
|
|
33492
|
-
const screenshotDir = expectsScreenshot ? mkdtempSync2(
|
|
33493
|
-
const screenshotPath = screenshotDir ?
|
|
33530
|
+
const screenshotDir = expectsScreenshot ? mkdtempSync2(join15(tmpdir4(), "gdharness-runtime-screenshot-")) : null;
|
|
33531
|
+
const screenshotPath = screenshotDir ? join15(screenshotDir, "capture.png") : null;
|
|
33494
33532
|
try {
|
|
33495
33533
|
const reply = await runtimeRequest(choice.endpoint, command, screenshotPath ? { ...params, output_path: screenshotPath } : params, timeoutMs);
|
|
33496
33534
|
if (!reply.ok) {
|
|
@@ -33514,7 +33552,7 @@ class GodotServer {
|
|
|
33514
33552
|
type: "text",
|
|
33515
33553
|
text: `Screenshot captured: ${dimensions}${chosenAmongSeveral ? ` from pid ${own}, the game this server holds` : ""}`
|
|
33516
33554
|
},
|
|
33517
|
-
{ type: "image", data:
|
|
33555
|
+
{ type: "image", data: readFileSync15(screenshotPath).toString("base64"), mimeType: "image/png" }
|
|
33518
33556
|
]
|
|
33519
33557
|
};
|
|
33520
33558
|
} finally {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gdharness",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.8",
|
|
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",
|