gdharness 1.0.6 → 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 +167 -79
- package/build/godot/addons/gdharness_runtime/runtime_autoload.gd +28 -5
- package/build/index.js +180 -94
- package/package.json +1 -1
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
|
|
|
@@ -29468,7 +29499,7 @@ var TOOL_SPECS = [
|
|
|
29468
29499
|
requires: []
|
|
29469
29500
|
},
|
|
29470
29501
|
refresh_classes: {
|
|
29471
|
-
summary: 'rewrite .godot/global_script_class_cache.cfg from the class_name declarations on disk, and name under unseenByEditor any class the editor open on this project still cannot resolve: rewriting the file does not reach the list a running editor loaded, so "added: []" means the file was already right rather than that nothing is wrong',
|
|
29502
|
+
summary: 'rewrite .godot/global_script_class_cache.cfg from the class_name declarations on disk, and name under unseenByEditor any class the editor open on this project still cannot resolve: rewriting the file does not reach the list a running editor loaded, so "added: []" means the file was already right rather than that nothing is wrong. A class the cache held after the last rebuild and had lost when this one began is named under lostSinceLastRebuild: the editor wrote the file without it in between, on a save or a scan, and will again until restarted',
|
|
29472
29503
|
requires: []
|
|
29473
29504
|
}
|
|
29474
29505
|
}
|
|
@@ -29493,7 +29524,7 @@ var TOOL_SPECS = [
|
|
|
29493
29524
|
},
|
|
29494
29525
|
{
|
|
29495
29526
|
name: "project_test",
|
|
29496
|
-
description: "Runs the project's gdUnit4 tests headless and answers with every case that did not pass: where it is, and what the assertion said. Suites where everything passed are counted rather than listed, and an engine message keeps the frames above gdUnit4 rather than the twenty inside it, so a clean tier answers in a few lines. The class list is rebuilt first, so a suite written a moment ago is found. On Windows and Linux the run gets a user:// of its own, so a suite that saves a game writes nowhere near the saves of the copy somebody plays. On macOS the engine reads user:// off HOME and the run writes where the player keeps theirs, which the answer says under savesNote rather than leaving to be found in a save list. A run that found nothing to run is never called a pass: gdUnit4 exits cleanly for one, so the answer says so and names the path it looked in. A run where nothing failed but nodes were left in the tree comes back under warnings, with the count per suite: gdUnit4 decides its verdict on those and keeps them out of its report, so they are read off what it printed. Needs gdUnit4 under addons/gdUnit4.",
|
|
29527
|
+
description: "Runs the project's gdUnit4 tests headless and answers with every case that did not pass: where it is, and what the assertion said. Suites where everything passed are counted rather than listed, and an engine message keeps the frames above gdUnit4 rather than the twenty inside it, so a clean tier answers in a few lines. The class list is rebuilt first, so a suite written a moment ago is found, and the rebuild's answer is under classes: a class it puts back that the cache had after the last rebuild is named under classes.lostSinceLastRebuild, which is the editor writing its shorter list over the file on every save until restarted. On Windows and Linux the run gets a user:// of its own, so a suite that saves a game writes nowhere near the saves of the copy somebody plays. On macOS the engine reads user:// off HOME and the run writes where the player keeps theirs, which the answer says under savesNote rather than leaving to be found in a save list. A run that found nothing to run is never called a pass: gdUnit4 exits cleanly for one, so the answer says so and names the path it looked in. A run where nothing failed but nodes were left in the tree comes back under warnings, with the count per suite: gdUnit4 decides its verdict on those and keeps them out of its report, so they are read off what it printed. Needs gdUnit4 under addons/gdUnit4.",
|
|
29497
29528
|
parameters: {
|
|
29498
29529
|
projectPath: PROJECT_PATH,
|
|
29499
29530
|
path: {
|
|
@@ -29710,7 +29741,7 @@ var TOOL_SPECS = [
|
|
|
29710
29741
|
},
|
|
29711
29742
|
{
|
|
29712
29743
|
name: "script_diagnostics",
|
|
29713
|
-
description: "Errors and warnings for a script from the editor's language server, and whether the script is clean. Needs the editor running. Diagnostics are checked against the project's own files, and the two that the files disprove are named rather than passed on: a member the class cache's file declares comes back under contradictedByTheFile, and a class this project declares that the diagnostic could not resolve comes back under typesTheEditorHasNotLoaded, with inTheClassCache saying whether a launched game would resolve it. staleAnalysis then says what to do, and for both states it is editor_rescan first: it asks the editor already running, starts nothing, and has cleared each in every reproduction measured. What follows differs: for a stale analysed type, editor_rescan with reloadScript recompiles the held copy and editor_launch restart is the last resort that costs a window; for a class missing from the cache, project_import refresh_classes rewrites the cache from disk at the cost of a short headless engine, which matters beside a running bench.",
|
|
29744
|
+
description: "Errors and warnings for a script from the editor's language server, and whether the script is clean. Needs the editor running. Diagnostics are checked against the project's own files, and the two that the files disprove are named rather than passed on: a member the class cache's file declares comes back under contradictedByTheFile, and a class this project declares that the diagnostic could not resolve comes back under typesTheEditorHasNotLoaded, with inTheClassCache saying whether a launched game would resolve it. staleAnalysis then says what to do, and for both states it is editor_rescan first: it asks the editor already running, starts nothing, and has cleared each in every reproduction measured. The one exception is named when it applies: an editor whose last scan wrote the class cache shorter than the file holds a list behind the files and writes it on every scan, so on that editor the rescan loads nothing and staleAnalysis says editor_launch restart instead. What follows differs: for a stale analysed type, editor_rescan with reloadScript recompiles the held copy and editor_launch restart is the last resort that costs a window; for a class missing from the cache, project_import refresh_classes rewrites the cache from disk at the cost of a short headless engine, which matters beside a running bench.",
|
|
29714
29745
|
parameters: {
|
|
29715
29746
|
projectPath: PROJECT_PATH,
|
|
29716
29747
|
scriptPath: SCRIPT_PATH
|
|
@@ -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;
|
|
@@ -31071,8 +31102,15 @@ class GodotServer {
|
|
|
31071
31102
|
const contained = this.containProjectFiles(asks);
|
|
31072
31103
|
return contained.ok ? await this.handleViaBridge(editorSide, contained.value) : contained.response;
|
|
31073
31104
|
}
|
|
31074
|
-
|
|
31075
|
-
|
|
31105
|
+
if (headless !== "refresh_class_cache") {
|
|
31106
|
+
return await this.headless(headless, answerable);
|
|
31107
|
+
}
|
|
31108
|
+
const project = this.project(answerable);
|
|
31109
|
+
if (!project.ok) {
|
|
31110
|
+
return project.response;
|
|
31111
|
+
}
|
|
31112
|
+
const answered = this.answer(await this.rebuildClassCache(project.value.path));
|
|
31113
|
+
return this.godotBridge.isConnected() ? await this.alsoSayWhatTheEditorCannotSee(answered, args) : answered;
|
|
31076
31114
|
}
|
|
31077
31115
|
const { op: _op, ...arguments_ } = args;
|
|
31078
31116
|
const bridge = async (command, extra = {}) => {
|
|
@@ -31298,7 +31336,7 @@ class GodotServer {
|
|
|
31298
31336
|
if (path === undefined) {
|
|
31299
31337
|
return { ok: false, response: this.createErrorResponse("projectPath is required.") };
|
|
31300
31338
|
}
|
|
31301
|
-
const file =
|
|
31339
|
+
const file = join15(path, "project.godot");
|
|
31302
31340
|
if (!existsSync9(file)) {
|
|
31303
31341
|
return {
|
|
31304
31342
|
ok: false,
|
|
@@ -31522,7 +31560,7 @@ class GodotServer {
|
|
|
31522
31560
|
if (!engine.ok) {
|
|
31523
31561
|
return engine.response;
|
|
31524
31562
|
}
|
|
31525
|
-
const application = parseProjectGodot(
|
|
31563
|
+
const application = parseProjectGodot(readFileSync15(project.value.file, "utf8"))["application"] ?? {};
|
|
31526
31564
|
const name = application["config/name"];
|
|
31527
31565
|
const mainScene = application["run/main_scene"];
|
|
31528
31566
|
const info = {
|
|
@@ -31583,18 +31621,18 @@ class GodotServer {
|
|
|
31583
31621
|
if (!engine.ok) {
|
|
31584
31622
|
return engine.response;
|
|
31585
31623
|
}
|
|
31586
|
-
const folder =
|
|
31624
|
+
const folder = dirname9(output.absolutePath);
|
|
31587
31625
|
try {
|
|
31588
|
-
|
|
31626
|
+
mkdirSync9(folder, { recursive: true });
|
|
31589
31627
|
} catch (error) {
|
|
31590
|
-
return this.createErrorResponse(`Cannot create ${
|
|
31628
|
+
return this.createErrorResponse(`Cannot create ${dirname9(output.relativePath)} to export into: ${errorMessage(error)}`);
|
|
31591
31629
|
}
|
|
31592
31630
|
const debug = readBoolean(args, "debug") ?? false;
|
|
31593
|
-
const exportLogs = mkdtempSync2(
|
|
31631
|
+
const exportLogs = mkdtempSync2(join15(tmpdir4(), "gdharness-export-"));
|
|
31594
31632
|
const exportArgs = [
|
|
31595
31633
|
"--headless",
|
|
31596
31634
|
"--log-file",
|
|
31597
|
-
|
|
31635
|
+
join15(exportLogs, "engine.log"),
|
|
31598
31636
|
"--path",
|
|
31599
31637
|
project.value.path,
|
|
31600
31638
|
debug ? "--export-debug" : "--export-release",
|
|
@@ -31652,7 +31690,7 @@ class GodotServer {
|
|
|
31652
31690
|
return contained.response;
|
|
31653
31691
|
}
|
|
31654
31692
|
const runner = "addons/gdUnit4/bin/GdUnitCmdTool.gd";
|
|
31655
|
-
if (!existsSync9(
|
|
31693
|
+
if (!existsSync9(join15(project.value.path, runner))) {
|
|
31656
31694
|
return this.createErrorResponse(`gdUnit4 is not installed in this project: no ${runner}.`, [
|
|
31657
31695
|
"Install gdUnit4 under addons/gdUnit4, from https://github.com/godot-gdunit-labs/gdUnit4"
|
|
31658
31696
|
]);
|
|
@@ -31661,7 +31699,7 @@ class GodotServer {
|
|
|
31661
31699
|
if (!engine.ok) {
|
|
31662
31700
|
return engine.response;
|
|
31663
31701
|
}
|
|
31664
|
-
const classes = await this.
|
|
31702
|
+
const classes = await this.rebuildClassCache(project.value.path);
|
|
31665
31703
|
if (!classes.ok) {
|
|
31666
31704
|
return this.answer(classes);
|
|
31667
31705
|
}
|
|
@@ -31687,7 +31725,7 @@ class GodotServer {
|
|
|
31687
31725
|
];
|
|
31688
31726
|
const timeoutMs = readPositiveNumber(args, "timeoutMs") ?? 600000;
|
|
31689
31727
|
this.logDebug(`Running tests: ${engine.value} ${cmdArgs.join(" ")}`);
|
|
31690
|
-
const userData = mkdtempSync2(
|
|
31728
|
+
const userData = mkdtempSync2(join15(tmpdir4(), "gdharness-tests-"));
|
|
31691
31729
|
const run = this.spawnGame(engine.value, cmdArgs, userDataIn(userData));
|
|
31692
31730
|
const hung = await new Promise((resolve) => {
|
|
31693
31731
|
const timer = setTimeout(() => {
|
|
@@ -31703,14 +31741,14 @@ class GodotServer {
|
|
|
31703
31741
|
resolve(false);
|
|
31704
31742
|
});
|
|
31705
31743
|
});
|
|
31706
|
-
const reportsDir =
|
|
31744
|
+
const reportsDir = join15(project.value.path, ".godot", ...ours.split("/"));
|
|
31707
31745
|
let report = null;
|
|
31708
31746
|
let reportProblem = null;
|
|
31709
31747
|
try {
|
|
31710
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))) : [];
|
|
31711
31749
|
const newest = written.at(-1);
|
|
31712
31750
|
if (newest !== undefined) {
|
|
31713
|
-
report = parseJUnit(
|
|
31751
|
+
report = parseJUnit(readFileSync15(join15(reportsDir, newest, "results.xml"), "utf8"));
|
|
31714
31752
|
}
|
|
31715
31753
|
} catch (error) {
|
|
31716
31754
|
reportProblem = errorMessage(error);
|
|
@@ -31850,7 +31888,7 @@ class GodotServer {
|
|
|
31850
31888
|
return null;
|
|
31851
31889
|
}
|
|
31852
31890
|
try {
|
|
31853
|
-
return
|
|
31891
|
+
return readFileSync15(contained.absolutePath, "utf8");
|
|
31854
31892
|
} catch {
|
|
31855
31893
|
return null;
|
|
31856
31894
|
}
|
|
@@ -31878,7 +31916,8 @@ class GodotServer {
|
|
|
31878
31916
|
}
|
|
31879
31917
|
const stale = unloaded.filter((type) => type.inTheClassCache);
|
|
31880
31918
|
if (stale.length > 0) {
|
|
31881
|
-
|
|
31919
|
+
const remedy = this.editorWritesAShortList(projectPath) ? "editor_rescan will not clear it here: this editor wrote the class cache from a list shorter than the file on its last scan and writes the same list on every scan, so editor_launch restart is what loads it." : "editor_rescan clears it, measured against a real editor with waiting the same length ruled out; editor_launch restart also does, and costs more.";
|
|
31920
|
+
notes.push(`${stale.map((type) => type.type).join(", ")} ${stale.length === 1 ? "is" : "are"} in the class cache and still unresolved here, which is the editor's loaded list being behind rather than anything on disk. A game launched now reads the cache and resolves ${stale.length === 1 ? "it" : "them"}, so the run and the diagnostics disagree. ${remedy}`);
|
|
31882
31921
|
}
|
|
31883
31922
|
return {
|
|
31884
31923
|
...contradicted.length === 0 ? {} : { contradictedByTheFile: contradicted },
|
|
@@ -31886,13 +31925,24 @@ class GodotServer {
|
|
|
31886
31925
|
...notes.length === 0 ? {} : { staleAnalysis: notes.join(" ") }
|
|
31887
31926
|
};
|
|
31888
31927
|
}
|
|
31928
|
+
editorWritesAShortList(projectPath) {
|
|
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
|
+
});
|
|
31938
|
+
}
|
|
31889
31939
|
sourceOfClass(projectPath, resourcePath) {
|
|
31890
31940
|
const contained = resolveWithinProject(projectPath, resourcePath);
|
|
31891
31941
|
if (!contained.ok || !existsSync9(contained.absolutePath)) {
|
|
31892
31942
|
return null;
|
|
31893
31943
|
}
|
|
31894
31944
|
try {
|
|
31895
|
-
return
|
|
31945
|
+
return readFileSync15(contained.absolutePath, "utf8");
|
|
31896
31946
|
} catch {
|
|
31897
31947
|
return null;
|
|
31898
31948
|
}
|
|
@@ -32121,7 +32171,7 @@ class GodotServer {
|
|
|
32121
32171
|
return null;
|
|
32122
32172
|
}
|
|
32123
32173
|
try {
|
|
32124
|
-
return settingKeys(
|
|
32174
|
+
return settingKeys(readFileSync15(join15(projectPath, "project.godot"), "utf8"));
|
|
32125
32175
|
} catch {
|
|
32126
32176
|
return null;
|
|
32127
32177
|
}
|
|
@@ -32363,7 +32413,7 @@ class GodotServer {
|
|
|
32363
32413
|
}
|
|
32364
32414
|
async runtimeUp(projectPath, before, budget, withArgs) {
|
|
32365
32415
|
const budgetMs = budget.ms;
|
|
32366
|
-
if (!existsSync9(
|
|
32416
|
+
if (!existsSync9(join15(projectPath, RUNTIME_AUTOLOAD.path))) {
|
|
32367
32417
|
return runtimeVerdict(null, { addon: false, budgetMs, heldAt: null, running: false, withArgs });
|
|
32368
32418
|
}
|
|
32369
32419
|
let asking = null;
|
|
@@ -33235,6 +33285,9 @@ class GodotServer {
|
|
|
33235
33285
|
}
|
|
33236
33286
|
const after = projectPath === "" || busy ? null : cachedClasses(projectPath);
|
|
33237
33287
|
const lost = before === null || after === null ? [] : [...before.keys()].filter((name) => !after.has(name));
|
|
33288
|
+
if (lost.length > 0) {
|
|
33289
|
+
this.noteAShortListEditor(projectPath);
|
|
33290
|
+
}
|
|
33238
33291
|
const atMissingPaths = after === null ? [] : cachedAtMissingPaths(after, projectPath);
|
|
33239
33292
|
const restored = lost.length > 0 || atMissingPaths.length > 0 ? await this.rebuildCacheAfterLoss(projectPath, lost) : [];
|
|
33240
33293
|
const rebuilt = atMissingPaths.length > 0 ? cachedClasses(projectPath) : null;
|
|
@@ -33290,7 +33343,7 @@ class GodotServer {
|
|
|
33290
33343
|
if (projectPath === "") {
|
|
33291
33344
|
return [];
|
|
33292
33345
|
}
|
|
33293
|
-
const rebuilt = await this.
|
|
33346
|
+
const rebuilt = await this.rebuildClassCache(projectPath);
|
|
33294
33347
|
if (!rebuilt.ok) {
|
|
33295
33348
|
this.logDebug(`Could not rebuild the class cache after a scan: ${rebuilt.message}`);
|
|
33296
33349
|
return [];
|
|
@@ -33298,6 +33351,36 @@ class GodotServer {
|
|
|
33298
33351
|
const now = cachedClasses(projectPath);
|
|
33299
33352
|
return now === null ? [] : lost.filter((name) => now.has(name));
|
|
33300
33353
|
}
|
|
33354
|
+
async rebuildClassCache(projectPath) {
|
|
33355
|
+
const before = cachedClasses(projectPath);
|
|
33356
|
+
const noted = readClassNote(projectPath);
|
|
33357
|
+
const rebuilt = await this.operation("refresh_class_cache", {}, projectPath);
|
|
33358
|
+
if (!rebuilt.ok) {
|
|
33359
|
+
return rebuilt;
|
|
33360
|
+
}
|
|
33361
|
+
const after = cachedClasses(projectPath);
|
|
33362
|
+
if (after === null) {
|
|
33363
|
+
return rebuilt;
|
|
33364
|
+
}
|
|
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) {
|
|
33372
|
+
return rebuilt;
|
|
33373
|
+
}
|
|
33374
|
+
const them = lost.length === 1 ? "it" : "them";
|
|
33375
|
+
return {
|
|
33376
|
+
...rebuilt,
|
|
33377
|
+
payload: {
|
|
33378
|
+
...rebuilt.payload,
|
|
33379
|
+
lostSinceLastRebuild: lost,
|
|
33380
|
+
note: `The cache held ${lost.join(", ")} after the last rebuild and not when this one began, so the editor holding this project wrote it without ${them} in between: it holds a list shorter than the files and writes that list on every save and scan, and each rebuild puts ${them} back. editor_launch restart is what ends it.`
|
|
33381
|
+
}
|
|
33382
|
+
};
|
|
33383
|
+
}
|
|
33301
33384
|
async alsoSayWhatTheEditorCannotSee(answered, args) {
|
|
33302
33385
|
const first = answered.content[0];
|
|
33303
33386
|
if (answered.isError || first?.type !== "text" || typeof first.text !== "string") {
|
|
@@ -33308,15 +33391,18 @@ class GodotServer {
|
|
|
33308
33391
|
if (checked.unseen.length === 0 && stillHeld.length === 0 && checked.unchecked === undefined) {
|
|
33309
33392
|
return answered;
|
|
33310
33393
|
}
|
|
33311
|
-
const
|
|
33394
|
+
const payload = asParams(JSON.parse(first.text));
|
|
33395
|
+
const said = readString(payload, "note");
|
|
33396
|
+
const notes = said === undefined ? [] : [said];
|
|
33312
33397
|
if (checked.unseen.length > 0) {
|
|
33313
|
-
|
|
33398
|
+
const projectPath = typeof args["projectPath"] === "string" ? args["projectPath"] : "";
|
|
33399
|
+
notes.push(projectPath !== "" && this.editorWritesAShortList(projectPath) ? "The cache on disk is right now, and the editor holding this project is still not resolving these: rewriting the file does not reach the list it already loaded. editor_rescan will not reach it either on this editor, whose last scan wrote the cache from a list shorter than the file and would again; editor_launch restart is what loads them." : "The cache on disk is right now, and the editor holding this project is still not resolving these: rewriting the file does not reach the list it already loaded. editor_rescan does, on its own and with no change to the declaring script; editor_launch restart also does, and costs more.");
|
|
33314
33400
|
}
|
|
33315
33401
|
if (stillHeld.length > 0) {
|
|
33316
33402
|
notes.push(`The editor is still holding ${stillHeld.join(", ")}, which this project no longer declares, and it will write that list back over the cache this call just corrected. The entry returns pointing at a script that is gone, and the next engine to walk get_global_class_list() dies on "File not found". A rescan does not clear this one: it picks up a class that has appeared and does not drop one that has gone. editor_launch restart does.`);
|
|
33317
33403
|
}
|
|
33318
33404
|
return this.jsonTextResponse({
|
|
33319
|
-
...
|
|
33405
|
+
...payload,
|
|
33320
33406
|
unseenByEditor: checked.unseen.length > 0 ? checked.unseen : undefined,
|
|
33321
33407
|
stillHeldByEditor: stillHeld.length > 0 ? stillHeld : undefined,
|
|
33322
33408
|
classesUnchecked: checked.unchecked,
|
|
@@ -33441,8 +33527,8 @@ class GodotServer {
|
|
|
33441
33527
|
]);
|
|
33442
33528
|
}
|
|
33443
33529
|
const expectsScreenshot = command === "capture_screenshot" || command === "capture_viewport";
|
|
33444
|
-
const screenshotDir = expectsScreenshot ? mkdtempSync2(
|
|
33445
|
-
const screenshotPath = screenshotDir ?
|
|
33530
|
+
const screenshotDir = expectsScreenshot ? mkdtempSync2(join15(tmpdir4(), "gdharness-runtime-screenshot-")) : null;
|
|
33531
|
+
const screenshotPath = screenshotDir ? join15(screenshotDir, "capture.png") : null;
|
|
33446
33532
|
try {
|
|
33447
33533
|
const reply = await runtimeRequest(choice.endpoint, command, screenshotPath ? { ...params, output_path: screenshotPath } : params, timeoutMs);
|
|
33448
33534
|
if (!reply.ok) {
|
|
@@ -33466,7 +33552,7 @@ class GodotServer {
|
|
|
33466
33552
|
type: "text",
|
|
33467
33553
|
text: `Screenshot captured: ${dimensions}${chosenAmongSeveral ? ` from pid ${own}, the game this server holds` : ""}`
|
|
33468
33554
|
},
|
|
33469
|
-
{ type: "image", data:
|
|
33555
|
+
{ type: "image", data: readFileSync15(screenshotPath).toString("base64"), mimeType: "image/png" }
|
|
33470
33556
|
]
|
|
33471
33557
|
};
|
|
33472
33558
|
} finally {
|