gdharness 1.1.47 → 1.1.49
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/index.js
CHANGED
|
@@ -10249,17 +10249,17 @@ import {
|
|
|
10249
10249
|
closeSync as closeSync2,
|
|
10250
10250
|
constants,
|
|
10251
10251
|
copyFileSync,
|
|
10252
|
-
existsSync as
|
|
10252
|
+
existsSync as existsSync17,
|
|
10253
10253
|
fstatSync,
|
|
10254
|
-
mkdirSync as
|
|
10254
|
+
mkdirSync as mkdirSync13,
|
|
10255
10255
|
openSync as openSync3,
|
|
10256
10256
|
readdirSync as readdirSync9,
|
|
10257
|
-
readFileSync as
|
|
10257
|
+
readFileSync as readFileSync20,
|
|
10258
10258
|
readSync as readSync2,
|
|
10259
10259
|
rmdirSync as rmdirSync2,
|
|
10260
10260
|
statSync as statSync8
|
|
10261
10261
|
} from "node:fs";
|
|
10262
|
-
import { basename as basename5, dirname as
|
|
10262
|
+
import { basename as basename5, dirname as dirname15, extname as extname2, isAbsolute as isAbsolute3, join as join22, normalize as normalize2, relative as relative4 } from "node:path";
|
|
10263
10263
|
import { setTimeout as delay3 } from "node:timers/promises";
|
|
10264
10264
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
10265
10265
|
import { promisify as promisify5 } from "node:util";
|
|
@@ -26178,11 +26178,14 @@ function withdrawBridge(path) {
|
|
|
26178
26178
|
// src/call-signal.ts
|
|
26179
26179
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
26180
26180
|
var current = new AsyncLocalStorage;
|
|
26181
|
-
function withCallSignal(signal, answer) {
|
|
26182
|
-
return signal === undefined ? answer() : current.run(signal, answer);
|
|
26181
|
+
function withCallSignal(signal, answer, progress) {
|
|
26182
|
+
return signal === undefined && progress === undefined ? answer() : current.run({ signal, progress }, answer);
|
|
26183
26183
|
}
|
|
26184
26184
|
function callSignal() {
|
|
26185
|
-
return current.getStore();
|
|
26185
|
+
return current.getStore()?.signal;
|
|
26186
|
+
}
|
|
26187
|
+
function callProgress() {
|
|
26188
|
+
return current.getStore()?.progress;
|
|
26186
26189
|
}
|
|
26187
26190
|
|
|
26188
26191
|
// src/class-cache.ts
|
|
@@ -26945,6 +26948,41 @@ function configDisagrees(projectPath, serverVersion) {
|
|
|
26945
26948
|
return `.mcp.json in this project names gdharness@${others.join(" and gdharness@")}, and this server ` + `is ${serverVersion}. Whichever is meant to be right, nothing will change version until the ` + "MCP server is reconnected, and a pin kept anywhere else does not reach this file.";
|
|
26946
26949
|
}
|
|
26947
26950
|
|
|
26951
|
+
// src/cut-test-run.ts
|
|
26952
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync6, readFileSync as readFileSync10, rmSync as rmSync3, writeFileSync as writeFileSync6 } from "node:fs";
|
|
26953
|
+
import { dirname as dirname6, join as join9 } from "node:path";
|
|
26954
|
+
function keptAt(projectPath) {
|
|
26955
|
+
return join9(projectPath, ".godot", "gdharness-reports", "cut-run.json");
|
|
26956
|
+
}
|
|
26957
|
+
function keepCutTestRun(projectPath, run) {
|
|
26958
|
+
const path = keptAt(projectPath);
|
|
26959
|
+
try {
|
|
26960
|
+
mkdirSync6(dirname6(path), { recursive: true });
|
|
26961
|
+
writeFileSync6(path, `${JSON.stringify(run, null, 2)}
|
|
26962
|
+
`, "utf8");
|
|
26963
|
+
} catch {}
|
|
26964
|
+
}
|
|
26965
|
+
function takeCutTestRun(projectPath) {
|
|
26966
|
+
const path = keptAt(projectPath);
|
|
26967
|
+
if (!existsSync5(path)) {
|
|
26968
|
+
return null;
|
|
26969
|
+
}
|
|
26970
|
+
try {
|
|
26971
|
+
const run = JSON.parse(readFileSync10(path, "utf8"));
|
|
26972
|
+
rmSync3(path, { force: true });
|
|
26973
|
+
return typeof run.cutAt === "string" && Array.isArray(run.paths) && run.paths.every((path) => typeof path === "string") ? run : null;
|
|
26974
|
+
} catch {
|
|
26975
|
+
rmSync3(path, { force: true });
|
|
26976
|
+
return null;
|
|
26977
|
+
}
|
|
26978
|
+
}
|
|
26979
|
+
function cutTestRunNote(run) {
|
|
26980
|
+
const finished = run.suitesFinished?.length ?? 0;
|
|
26981
|
+
const failing = run.suitesFailing?.length ?? 0;
|
|
26982
|
+
const got = finished === 0 ? "before any suite had finished" : `after ${finished === 1 ? "one suite" : `${finished} suites`} had finished${failing === 0 ? "" : finished === 1 ? ", with failures" : `, ${failing} of them with failures`}`;
|
|
26983
|
+
return `The test run before this one in this project, of ${run.paths.join(", ")}, was cut off by its client at ${run.cutAt} ${got}${run.suiteRunning === undefined ? "" : `, while ${run.suiteRunning} was running`}; what it finished is under previousRunCut.`;
|
|
26984
|
+
}
|
|
26985
|
+
|
|
26948
26986
|
// src/dap_client.ts
|
|
26949
26987
|
import { createConnection as createConnection2 } from "node:net";
|
|
26950
26988
|
import { setTimeout as delay2 } from "node:timers/promises";
|
|
@@ -27737,16 +27775,16 @@ function dictionary(entries) {
|
|
|
27737
27775
|
// src/editor-log.ts
|
|
27738
27776
|
import {
|
|
27739
27777
|
closeSync,
|
|
27740
|
-
existsSync as
|
|
27741
|
-
mkdirSync as
|
|
27778
|
+
existsSync as existsSync6,
|
|
27779
|
+
mkdirSync as mkdirSync7,
|
|
27742
27780
|
openSync,
|
|
27743
|
-
readFileSync as
|
|
27781
|
+
readFileSync as readFileSync11,
|
|
27744
27782
|
readSync,
|
|
27745
|
-
rmSync as
|
|
27783
|
+
rmSync as rmSync4,
|
|
27746
27784
|
statSync as statSync3,
|
|
27747
|
-
writeFileSync as
|
|
27785
|
+
writeFileSync as writeFileSync7
|
|
27748
27786
|
} from "node:fs";
|
|
27749
|
-
import { dirname as
|
|
27787
|
+
import { dirname as dirname7, join as join10 } from "node:path";
|
|
27750
27788
|
|
|
27751
27789
|
// src/game-log.ts
|
|
27752
27790
|
import { StringDecoder } from "node:string_decoder";
|
|
@@ -27864,14 +27902,14 @@ function answersTo(entry, severity, contains) {
|
|
|
27864
27902
|
|
|
27865
27903
|
// src/editor-log.ts
|
|
27866
27904
|
function editorLogPath(projectPath) {
|
|
27867
|
-
return
|
|
27905
|
+
return join10(projectPath, ".godot", "gdharness-editor.log");
|
|
27868
27906
|
}
|
|
27869
27907
|
function editorLogNotePath(projectPath) {
|
|
27870
|
-
return
|
|
27908
|
+
return join10(projectPath, ".godot", "gdharness-editor.json");
|
|
27871
27909
|
}
|
|
27872
27910
|
function readEditorLogNote(projectPath) {
|
|
27873
27911
|
try {
|
|
27874
|
-
const fields = asParams(JSON.parse(
|
|
27912
|
+
const fields = asParams(JSON.parse(readFileSync11(editorLogNotePath(projectPath), "utf8")));
|
|
27875
27913
|
const pid = readNumber(fields, "pid");
|
|
27876
27914
|
const startedAt = readString(fields, "startedAt");
|
|
27877
27915
|
return pid !== undefined && Number.isInteger(pid) && pid > 0 && startedAt !== undefined ? { pid, startedAt } : null;
|
|
@@ -27882,16 +27920,16 @@ function readEditorLogNote(projectPath) {
|
|
|
27882
27920
|
function clearEditorLog(projectPath) {
|
|
27883
27921
|
try {
|
|
27884
27922
|
const path = editorLogPath(projectPath);
|
|
27885
|
-
|
|
27886
|
-
|
|
27887
|
-
|
|
27923
|
+
mkdirSync7(dirname7(path), { recursive: true });
|
|
27924
|
+
rmSync4(path, { force: true });
|
|
27925
|
+
rmSync4(editorLogNotePath(projectPath), { force: true });
|
|
27888
27926
|
} catch {}
|
|
27889
27927
|
}
|
|
27890
27928
|
function writeEditorLogNote(projectPath, pid) {
|
|
27891
27929
|
try {
|
|
27892
27930
|
const path = editorLogNotePath(projectPath);
|
|
27893
|
-
|
|
27894
|
-
|
|
27931
|
+
mkdirSync7(dirname7(path), { recursive: true });
|
|
27932
|
+
writeFileSync7(path, JSON.stringify({ pid, startedAt: new Date().toISOString() }, null, 2), "utf8");
|
|
27895
27933
|
} catch {}
|
|
27896
27934
|
}
|
|
27897
27935
|
function theConsoleWasNotCaptured(why, projectPath) {
|
|
@@ -27912,7 +27950,7 @@ function editorConsole(projectPath, editorPid, openedByAServer) {
|
|
|
27912
27950
|
return { kind: "older server", notedPid: noted?.pid ?? null };
|
|
27913
27951
|
}
|
|
27914
27952
|
const path = editorLogPath(projectPath);
|
|
27915
|
-
if (!
|
|
27953
|
+
if (!existsSync6(path)) {
|
|
27916
27954
|
return { kind: "not written" };
|
|
27917
27955
|
}
|
|
27918
27956
|
const log = new GameLog;
|
|
@@ -27925,7 +27963,7 @@ var MOST_CONSOLE_BYTES = 8 * 1024 * 1024;
|
|
|
27925
27963
|
function theFirstOf(path) {
|
|
27926
27964
|
const whole = statSync3(path).size;
|
|
27927
27965
|
if (whole <= MOST_CONSOLE_BYTES) {
|
|
27928
|
-
return { text:
|
|
27966
|
+
return { text: readFileSync11(path, "utf8") };
|
|
27929
27967
|
}
|
|
27930
27968
|
const handle = openSync(path, "r");
|
|
27931
27969
|
try {
|
|
@@ -28693,14 +28731,14 @@ function getDefaultBridge(ownProject = null) {
|
|
|
28693
28731
|
|
|
28694
28732
|
// src/godot-path.ts
|
|
28695
28733
|
import { execFile as execFile2 } from "node:child_process";
|
|
28696
|
-
import { existsSync as
|
|
28734
|
+
import { existsSync as existsSync8 } from "node:fs";
|
|
28697
28735
|
import { normalize } from "node:path";
|
|
28698
28736
|
import { promisify as promisify2 } from "node:util";
|
|
28699
28737
|
|
|
28700
28738
|
// src/detection.ts
|
|
28701
|
-
import { existsSync as
|
|
28739
|
+
import { existsSync as existsSync7, readdirSync as readdirSync3, statSync as statSync4 } from "node:fs";
|
|
28702
28740
|
import { homedir as homedir2 } from "node:os";
|
|
28703
|
-
import { join as
|
|
28741
|
+
import { join as join11 } from "node:path";
|
|
28704
28742
|
function resolveHomeDirectory() {
|
|
28705
28743
|
try {
|
|
28706
28744
|
return homedir2();
|
|
@@ -28709,7 +28747,7 @@ function resolveHomeDirectory() {
|
|
|
28709
28747
|
}
|
|
28710
28748
|
}
|
|
28711
28749
|
function scanDirectoryForGodotBinaries(directory, platform) {
|
|
28712
|
-
if (!directory || !
|
|
28750
|
+
if (!directory || !existsSync7(directory)) {
|
|
28713
28751
|
return [];
|
|
28714
28752
|
}
|
|
28715
28753
|
let entries;
|
|
@@ -28724,7 +28762,7 @@ function scanDirectoryForGodotBinaries(directory, platform) {
|
|
|
28724
28762
|
if (!pattern.test(name)) {
|
|
28725
28763
|
continue;
|
|
28726
28764
|
}
|
|
28727
|
-
const fullPath =
|
|
28765
|
+
const fullPath = join11(directory, name);
|
|
28728
28766
|
try {
|
|
28729
28767
|
const stat = statSync4(fullPath);
|
|
28730
28768
|
if (stat.isFile()) {
|
|
@@ -28733,7 +28771,7 @@ function scanDirectoryForGodotBinaries(directory, platform) {
|
|
|
28733
28771
|
} catch {}
|
|
28734
28772
|
}
|
|
28735
28773
|
matches.sort((a, b) => b.mtime - a.mtime);
|
|
28736
|
-
return matches.map((m) =>
|
|
28774
|
+
return matches.map((m) => join11(directory, m.name));
|
|
28737
28775
|
}
|
|
28738
28776
|
function conventionalPaths(platform, home) {
|
|
28739
28777
|
const paths = ["godot"];
|
|
@@ -28836,7 +28874,7 @@ class GodotLocator {
|
|
|
28836
28874
|
return known;
|
|
28837
28875
|
}
|
|
28838
28876
|
let reason;
|
|
28839
|
-
if (path !== "godot" && !
|
|
28877
|
+
if (path !== "godot" && !existsSync8(path)) {
|
|
28840
28878
|
reason = "does not exist";
|
|
28841
28879
|
} else {
|
|
28842
28880
|
try {
|
|
@@ -28852,33 +28890,33 @@ class GodotLocator {
|
|
|
28852
28890
|
}
|
|
28853
28891
|
|
|
28854
28892
|
// src/headless.ts
|
|
28855
|
-
import { existsSync as
|
|
28856
|
-
import { dirname as
|
|
28893
|
+
import { existsSync as existsSync11, readFileSync as readFileSync13, realpathSync as realpathSync2, writeFileSync as writeFileSync9 } from "node:fs";
|
|
28894
|
+
import { dirname as dirname8, join as join14 } from "node:path";
|
|
28857
28895
|
|
|
28858
28896
|
// src/reimport.ts
|
|
28859
28897
|
import { createHash as createHash2 } from "node:crypto";
|
|
28860
|
-
import { existsSync as
|
|
28861
|
-
import { basename as basename2, join as
|
|
28898
|
+
import { existsSync as existsSync9, readFileSync as readFileSync12, rmSync as rmSync5, statSync as statSync5, utimesSync, writeFileSync as writeFileSync8 } from "node:fs";
|
|
28899
|
+
import { basename as basename2, join as join12 } from "node:path";
|
|
28862
28900
|
function forceNextImport(projectPath, resourcePath) {
|
|
28863
28901
|
const relative = resourcePath.replace(/^res:\/\//, "");
|
|
28864
|
-
const sidecar =
|
|
28865
|
-
if (!
|
|
28902
|
+
const sidecar = join12(projectPath, `${relative}.import`);
|
|
28903
|
+
if (!existsSync9(sidecar)) {
|
|
28866
28904
|
return;
|
|
28867
28905
|
}
|
|
28868
28906
|
const { atime, mtimeMs } = statSync5(sidecar);
|
|
28869
|
-
|
|
28907
|
+
writeFileSync8(sidecar, readFileSync12(sidecar, "utf8").replace(/^valid=false\r?\n/m, ""));
|
|
28870
28908
|
utimesSync(sidecar, atime, new Date(mtimeMs + 2000));
|
|
28871
|
-
|
|
28909
|
+
rmSync5(importRecord(projectPath, resourcePath), { force: true });
|
|
28872
28910
|
}
|
|
28873
28911
|
function importRecord(projectPath, resourcePath) {
|
|
28874
28912
|
const digest = createHash2("md5").update(resourcePath).digest("hex");
|
|
28875
|
-
return
|
|
28913
|
+
return join12(projectPath, ".godot", "imported", `${basename2(resourcePath)}-${digest}.md5`);
|
|
28876
28914
|
}
|
|
28877
28915
|
|
|
28878
28916
|
// src/scratch.ts
|
|
28879
|
-
import { existsSync as
|
|
28917
|
+
import { existsSync as existsSync10, mkdtempSync, readdirSync as readdirSync4, rmSync as rmSync6 } from "node:fs";
|
|
28880
28918
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
28881
|
-
import { join as
|
|
28919
|
+
import { join as join13 } from "node:path";
|
|
28882
28920
|
import process7 from "node:process";
|
|
28883
28921
|
var SWEPT = { recursive: true, force: true };
|
|
28884
28922
|
var AGAIN_AFTER_MS = [250, 500, 1000, 2000, 4000, 8000, 16000];
|
|
@@ -28893,7 +28931,7 @@ var KINDS = [
|
|
|
28893
28931
|
"tests"
|
|
28894
28932
|
];
|
|
28895
28933
|
function scratchDirectory(kind) {
|
|
28896
|
-
return mkdtempSync(
|
|
28934
|
+
return mkdtempSync(join13(tmpdir3(), `gdharness-${kind}-${process7.pid}-`));
|
|
28897
28935
|
}
|
|
28898
28936
|
function sweepAbandonedScratch(directory = tmpdir3()) {
|
|
28899
28937
|
let entries;
|
|
@@ -28909,7 +28947,7 @@ function sweepAbandonedScratch(directory = tmpdir3()) {
|
|
|
28909
28947
|
continue;
|
|
28910
28948
|
}
|
|
28911
28949
|
try {
|
|
28912
|
-
remove(
|
|
28950
|
+
remove(join13(directory, entry));
|
|
28913
28951
|
} catch {}
|
|
28914
28952
|
}
|
|
28915
28953
|
}
|
|
@@ -28935,7 +28973,7 @@ function discard(...paths) {
|
|
|
28935
28973
|
return left;
|
|
28936
28974
|
}
|
|
28937
28975
|
function remove(path) {
|
|
28938
|
-
|
|
28976
|
+
rmSync6(path, SWEPT);
|
|
28939
28977
|
}
|
|
28940
28978
|
function tryAgain(path, attempt) {
|
|
28941
28979
|
if (attempt === 0) {
|
|
@@ -28947,7 +28985,7 @@ function tryAgain(path, attempt) {
|
|
|
28947
28985
|
lastTryOnExit();
|
|
28948
28986
|
}
|
|
28949
28987
|
const after = AGAIN_AFTER_MS[attempt];
|
|
28950
|
-
if (after === undefined || !
|
|
28988
|
+
if (after === undefined || !existsSync10(path)) {
|
|
28951
28989
|
waiting.delete(path);
|
|
28952
28990
|
if (after === undefined) {
|
|
28953
28991
|
atExit.add(path);
|
|
@@ -29027,7 +29065,7 @@ function reason(log, problems) {
|
|
|
29027
29065
|
}
|
|
29028
29066
|
function scriptFault(problems, script) {
|
|
29029
29067
|
const spelled = (path) => `(${path.replaceAll("\\", "/").toLowerCase()}/`;
|
|
29030
|
-
const directories = [...new Set([spelled(
|
|
29068
|
+
const directories = [...new Set([spelled(dirname8(script)), spelled(resolvedOr(dirname8(script)))])];
|
|
29031
29069
|
return problems.find((entry) => {
|
|
29032
29070
|
const at = entry.detail.find((line) => line.startsWith("at: "))?.replaceAll("\\", "/").toLowerCase();
|
|
29033
29071
|
return entry.severity === "error" && at !== undefined && directories.some((directory) => at.includes(directory));
|
|
@@ -29083,7 +29121,7 @@ async function importOnce(godotPath, projectPath) {
|
|
|
29083
29121
|
ran = await runEngine(godotPath, [
|
|
29084
29122
|
"--headless",
|
|
29085
29123
|
"--log-file",
|
|
29086
|
-
|
|
29124
|
+
join14(logDir, "engine.log"),
|
|
29087
29125
|
"--path",
|
|
29088
29126
|
projectPath,
|
|
29089
29127
|
"--import"
|
|
@@ -29103,13 +29141,13 @@ async function importOnce(godotPath, projectPath) {
|
|
|
29103
29141
|
}
|
|
29104
29142
|
async function runOperation(engine, operation, params, projectPath) {
|
|
29105
29143
|
const paramsDir = scratchDirectory("params");
|
|
29106
|
-
const paramsFile =
|
|
29107
|
-
const answerFile =
|
|
29108
|
-
|
|
29144
|
+
const paramsFile = join14(paramsDir, `${operation}.json`);
|
|
29145
|
+
const answerFile = join14(paramsDir, "answer.json");
|
|
29146
|
+
writeFileSync9(paramsFile, JSON.stringify(snakeCased(params)), "utf8");
|
|
29109
29147
|
const args = [
|
|
29110
29148
|
"--headless",
|
|
29111
29149
|
"--log-file",
|
|
29112
|
-
|
|
29150
|
+
join14(paramsDir, "engine.log"),
|
|
29113
29151
|
"--path",
|
|
29114
29152
|
projectPath,
|
|
29115
29153
|
"--script",
|
|
@@ -29160,10 +29198,10 @@ async function runOperation(engine, operation, params, projectPath) {
|
|
|
29160
29198
|
};
|
|
29161
29199
|
}
|
|
29162
29200
|
function answerIn(file) {
|
|
29163
|
-
if (!
|
|
29201
|
+
if (!existsSync11(file)) {
|
|
29164
29202
|
return { kind: "none" };
|
|
29165
29203
|
}
|
|
29166
|
-
const text =
|
|
29204
|
+
const text = readFileSync13(file, "utf8");
|
|
29167
29205
|
try {
|
|
29168
29206
|
const parsed = JSON.parse(text);
|
|
29169
29207
|
return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? { kind: "answer", payload: parsed } : { kind: "unreadable", why: `it is JSON but not an object: ${text.slice(0, 200)}` };
|
|
@@ -29579,6 +29617,52 @@ function orphansPrinted(printed) {
|
|
|
29579
29617
|
suites
|
|
29580
29618
|
};
|
|
29581
29619
|
}
|
|
29620
|
+
var SUITE_COUNTS = /(\d+)\s+test cases\s*\|\s*(\d+)\s+errors\s*\|\s*(\d+)\s+failures/;
|
|
29621
|
+
var CASE_FAILED = /^\s*res:\/\/.+?\.(?:gd|cs) > (.+?) (?:FAILED|ERROR)(?:\s|$)/;
|
|
29622
|
+
function finishesASuite(line) {
|
|
29623
|
+
return SUITE_COUNTS.test(line) && !line.includes(OVERALL);
|
|
29624
|
+
}
|
|
29625
|
+
function suitesPrinted(printed) {
|
|
29626
|
+
const finished = [];
|
|
29627
|
+
let running = null;
|
|
29628
|
+
let failed = [];
|
|
29629
|
+
for (const line of printed) {
|
|
29630
|
+
const started = RUNNING_SUITE.exec(line);
|
|
29631
|
+
if (started?.[1] !== undefined) {
|
|
29632
|
+
running = started[1];
|
|
29633
|
+
failed = [];
|
|
29634
|
+
continue;
|
|
29635
|
+
}
|
|
29636
|
+
if (running === null) {
|
|
29637
|
+
continue;
|
|
29638
|
+
}
|
|
29639
|
+
const failing = CASE_FAILED.exec(line)?.[1];
|
|
29640
|
+
if (failing !== undefined) {
|
|
29641
|
+
failed.push(failing);
|
|
29642
|
+
continue;
|
|
29643
|
+
}
|
|
29644
|
+
const counted = finishesASuite(line) ? SUITE_COUNTS.exec(line) : null;
|
|
29645
|
+
if (counted !== null) {
|
|
29646
|
+
finished.push({
|
|
29647
|
+
path: running,
|
|
29648
|
+
cases: Number(counted[1]),
|
|
29649
|
+
errors: Number(counted[2]),
|
|
29650
|
+
failures: Number(counted[3]),
|
|
29651
|
+
failed
|
|
29652
|
+
});
|
|
29653
|
+
running = null;
|
|
29654
|
+
}
|
|
29655
|
+
}
|
|
29656
|
+
return { finished, running };
|
|
29657
|
+
}
|
|
29658
|
+
function testProgressMessage(printed, elapsedMs) {
|
|
29659
|
+
const { finished, running } = suitesPrinted(printed);
|
|
29660
|
+
const failing = finished.filter((suite) => suite.errors + suite.failures > 0).length;
|
|
29661
|
+
const seconds = Math.floor(elapsedMs / 1000);
|
|
29662
|
+
const took = seconds < 60 ? `${seconds}s` : `${Math.floor(seconds / 60)}m ${seconds % 60}s`;
|
|
29663
|
+
const done = finished.length === 0 ? "No suite has finished" : `${finished.length === 1 ? "One suite has" : `${finished.length} suites have`} finished${failing === 0 ? "" : `, ${failing} with failures`}`;
|
|
29664
|
+
return `${done}${running === null ? "" : `; running ${running}`}; ${took} in.`;
|
|
29665
|
+
}
|
|
29582
29666
|
|
|
29583
29667
|
// src/keeper-channel.ts
|
|
29584
29668
|
import { connect, createServer as createServer2 } from "node:net";
|
|
@@ -29622,10 +29706,10 @@ function askTheKeeperToStop(address, withinMs) {
|
|
|
29622
29706
|
}
|
|
29623
29707
|
|
|
29624
29708
|
// src/lsp_client.ts
|
|
29625
|
-
import { existsSync as
|
|
29709
|
+
import { existsSync as existsSync12, realpathSync as realpathSync3 } from "node:fs";
|
|
29626
29710
|
import { readFile, realpath } from "node:fs/promises";
|
|
29627
29711
|
import { createConnection as createConnection3 } from "node:net";
|
|
29628
|
-
import { dirname as
|
|
29712
|
+
import { dirname as dirname9, join as join15, resolve as resolve3 } from "node:path";
|
|
29629
29713
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
29630
29714
|
var DEFAULT_LSP_PORT = 6005;
|
|
29631
29715
|
var DIAGNOSTICS_TIMEOUT_MS = 5000;
|
|
@@ -29782,7 +29866,7 @@ class GodotLSPClient {
|
|
|
29782
29866
|
if (this.initialized) {
|
|
29783
29867
|
return;
|
|
29784
29868
|
}
|
|
29785
|
-
const rootPath = this.rootPath ??
|
|
29869
|
+
const rootPath = this.rootPath ?? dirname9(resolve3(filePath));
|
|
29786
29870
|
await this.initialize(rootPath);
|
|
29787
29871
|
}
|
|
29788
29872
|
async sendRequest(method, params, timeoutMs = this.timeouts.requestMs) {
|
|
@@ -30193,7 +30277,7 @@ async function resolveLSPPaths(projectPathValue, scriptPathValue) {
|
|
|
30193
30277
|
} catch {
|
|
30194
30278
|
throw new ArgumentRefusal(`Project path does not exist: ${requestedProjectPath}`);
|
|
30195
30279
|
}
|
|
30196
|
-
if (!
|
|
30280
|
+
if (!existsSync12(join15(projectPath, "project.godot"))) {
|
|
30197
30281
|
throw new ArgumentRefusal(`Not a Godot project: ${projectPath}. Point projectPath at the directory holding project.godot.`);
|
|
30198
30282
|
}
|
|
30199
30283
|
const contained = resolveWithinProject(projectPath, scriptPathValue);
|
|
@@ -30370,14 +30454,14 @@ function secondsFromClock(printed) {
|
|
|
30370
30454
|
}
|
|
30371
30455
|
|
|
30372
30456
|
// src/project-scan.ts
|
|
30373
|
-
import { existsSync as
|
|
30374
|
-
import { join as
|
|
30457
|
+
import { existsSync as existsSync13, readdirSync as readdirSync5, readFileSync as readFileSync14 } from "node:fs";
|
|
30458
|
+
import { join as join16 } from "node:path";
|
|
30375
30459
|
var SKIPPED = new Set(["node_modules"]);
|
|
30376
30460
|
function skipped(name) {
|
|
30377
30461
|
return name.startsWith(".") || SKIPPED.has(name);
|
|
30378
30462
|
}
|
|
30379
30463
|
function steppedOver(directory) {
|
|
30380
|
-
return
|
|
30464
|
+
return existsSync13(join16(directory, ".gdignore"));
|
|
30381
30465
|
}
|
|
30382
30466
|
var SCENE_EXTENSIONS = new Set(["tscn", "scn"]);
|
|
30383
30467
|
var ASSET_EXTENSIONS = new Set([
|
|
@@ -30421,7 +30505,7 @@ function projectStructure(projectPath) {
|
|
|
30421
30505
|
continue;
|
|
30422
30506
|
}
|
|
30423
30507
|
if (entry.isDirectory()) {
|
|
30424
|
-
visit(
|
|
30508
|
+
visit(join16(directory, entry.name));
|
|
30425
30509
|
} else if (entry.isFile()) {
|
|
30426
30510
|
const extension = entry.name.split(".").pop()?.toLowerCase() ?? "";
|
|
30427
30511
|
if (SCENE_EXTENSIONS.has(extension)) {
|
|
@@ -30452,14 +30536,14 @@ function scriptsWithoutUid(projectPath) {
|
|
|
30452
30536
|
}
|
|
30453
30537
|
const spelled = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
|
|
30454
30538
|
if (entry.isDirectory()) {
|
|
30455
|
-
visit(
|
|
30539
|
+
visit(join16(directory, entry.name), spelled);
|
|
30456
30540
|
continue;
|
|
30457
30541
|
}
|
|
30458
30542
|
if (!entry.isFile()) {
|
|
30459
30543
|
continue;
|
|
30460
30544
|
}
|
|
30461
30545
|
const extension = entry.name.split(".").pop()?.toLowerCase() ?? "";
|
|
30462
|
-
if (CARRIES_A_UID.has(extension) && !
|
|
30546
|
+
if (CARRIES_A_UID.has(extension) && !existsSync13(join16(directory, `${entry.name}.uid`))) {
|
|
30463
30547
|
found.push(spelled);
|
|
30464
30548
|
}
|
|
30465
30549
|
}
|
|
@@ -30491,7 +30575,7 @@ function projectTexts(projectPath) {
|
|
|
30491
30575
|
if (skipped(entry.name)) {
|
|
30492
30576
|
continue;
|
|
30493
30577
|
}
|
|
30494
|
-
const absolute =
|
|
30578
|
+
const absolute = join16(directory, entry.name);
|
|
30495
30579
|
const spelled = `${prefix}${entry.name}`;
|
|
30496
30580
|
if (entry.isDirectory()) {
|
|
30497
30581
|
visit(absolute, `${spelled}/`);
|
|
@@ -30501,7 +30585,7 @@ function projectTexts(projectPath) {
|
|
|
30501
30585
|
if (!entry.isFile() || BINARY_EXTENSIONS.has(extension)) {
|
|
30502
30586
|
continue;
|
|
30503
30587
|
}
|
|
30504
|
-
const bytes =
|
|
30588
|
+
const bytes = readFileSync14(absolute);
|
|
30505
30589
|
if (!isBinary(bytes)) {
|
|
30506
30590
|
found.push({ path: `res://${spelled}`, absolute, text: bytes.toString("utf8") });
|
|
30507
30591
|
}
|
|
@@ -30518,7 +30602,7 @@ function ignoredTexts(projectPath) {
|
|
|
30518
30602
|
if (skipped(entry.name)) {
|
|
30519
30603
|
continue;
|
|
30520
30604
|
}
|
|
30521
|
-
const absolute =
|
|
30605
|
+
const absolute = join16(directory, entry.name);
|
|
30522
30606
|
const spelled = `${prefix}${entry.name}`;
|
|
30523
30607
|
if (entry.isDirectory()) {
|
|
30524
30608
|
visit(absolute, `${spelled}/`, under);
|
|
@@ -30528,7 +30612,7 @@ function ignoredTexts(projectPath) {
|
|
|
30528
30612
|
if (!under || !entry.isFile() || BINARY_EXTENSIONS.has(extension) || entry.name === ".gdignore") {
|
|
30529
30613
|
continue;
|
|
30530
30614
|
}
|
|
30531
|
-
const bytes =
|
|
30615
|
+
const bytes = readFileSync14(absolute);
|
|
30532
30616
|
if (!isBinary(bytes)) {
|
|
30533
30617
|
found.push({ path: `res://${spelled}`, absolute, text: bytes.toString("utf8") });
|
|
30534
30618
|
}
|
|
@@ -30558,7 +30642,7 @@ function searchProject(projectPath, options) {
|
|
|
30558
30642
|
if (skipped(entry.name)) {
|
|
30559
30643
|
continue;
|
|
30560
30644
|
}
|
|
30561
|
-
const entryPath =
|
|
30645
|
+
const entryPath = join16(directory, entry.name);
|
|
30562
30646
|
if (entry.isDirectory()) {
|
|
30563
30647
|
visit(entryPath);
|
|
30564
30648
|
continue;
|
|
@@ -30567,7 +30651,7 @@ function searchProject(projectPath, options) {
|
|
|
30567
30651
|
if (!entry.isFile() || extensions !== null && !extensions.has(extension)) {
|
|
30568
30652
|
continue;
|
|
30569
30653
|
}
|
|
30570
|
-
const bytes =
|
|
30654
|
+
const bytes = readFileSync14(entryPath);
|
|
30571
30655
|
if (extensions === null && isBinary(bytes)) {
|
|
30572
30656
|
continue;
|
|
30573
30657
|
}
|
|
@@ -30597,8 +30681,8 @@ function searchProject(projectPath, options) {
|
|
|
30597
30681
|
}
|
|
30598
30682
|
|
|
30599
30683
|
// src/rename.ts
|
|
30600
|
-
import { existsSync as
|
|
30601
|
-
import { dirname as
|
|
30684
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync8, readFileSync as readFileSync15, renameSync, unlinkSync as unlinkSync2, writeFileSync as writeFileSync10 } from "node:fs";
|
|
30685
|
+
import { dirname as dirname10, posix } from "node:path";
|
|
30602
30686
|
class Script {
|
|
30603
30687
|
path;
|
|
30604
30688
|
text;
|
|
@@ -31320,25 +31404,25 @@ function rewritesOf(plan, texts, move) {
|
|
|
31320
31404
|
}
|
|
31321
31405
|
function applyRewrites(rewrites) {
|
|
31322
31406
|
for (const rewrite of rewrites) {
|
|
31323
|
-
const now =
|
|
31407
|
+
const now = readFileSync15(rewrite.absolute, "utf8");
|
|
31324
31408
|
if (now !== rewrite.before) {
|
|
31325
31409
|
throw new Error(`${rewrite.path} changed on disk while the rename was being worked out; nothing was written`);
|
|
31326
31410
|
}
|
|
31327
|
-
if (rewrite.writeTo !== rewrite.absolute &&
|
|
31411
|
+
if (rewrite.writeTo !== rewrite.absolute && existsSync14(rewrite.writeTo)) {
|
|
31328
31412
|
throw new Error(`${rewrite.writeTo} already exists; nothing was written`);
|
|
31329
31413
|
}
|
|
31330
31414
|
}
|
|
31331
31415
|
const done = [];
|
|
31332
31416
|
try {
|
|
31333
31417
|
for (const rewrite of rewrites) {
|
|
31334
|
-
|
|
31418
|
+
mkdirSync8(dirname10(rewrite.writeTo), { recursive: true });
|
|
31335
31419
|
const staging = `${rewrite.writeTo}.gdharness-rename`;
|
|
31336
|
-
|
|
31420
|
+
writeFileSync10(staging, rewrite.after, "utf8");
|
|
31337
31421
|
renameSync(staging, rewrite.writeTo);
|
|
31338
31422
|
let movedUid = false;
|
|
31339
31423
|
if (rewrite.writeTo !== rewrite.absolute) {
|
|
31340
31424
|
unlinkSync2(rewrite.absolute);
|
|
31341
|
-
if (
|
|
31425
|
+
if (existsSync14(`${rewrite.absolute}.uid`)) {
|
|
31342
31426
|
renameSync(`${rewrite.absolute}.uid`, `${rewrite.writeTo}.uid`);
|
|
31343
31427
|
movedUid = true;
|
|
31344
31428
|
}
|
|
@@ -31348,7 +31432,7 @@ function applyRewrites(rewrites) {
|
|
|
31348
31432
|
} catch (error) {
|
|
31349
31433
|
for (const { rewrite, movedUid } of done.reverse()) {
|
|
31350
31434
|
try {
|
|
31351
|
-
|
|
31435
|
+
writeFileSync10(rewrite.absolute, rewrite.before, "utf8");
|
|
31352
31436
|
if (rewrite.writeTo !== rewrite.absolute) {
|
|
31353
31437
|
unlinkSync2(rewrite.writeTo);
|
|
31354
31438
|
if (movedUid) {
|
|
@@ -31362,11 +31446,11 @@ function applyRewrites(rewrites) {
|
|
|
31362
31446
|
}
|
|
31363
31447
|
|
|
31364
31448
|
// src/rename-symbol.ts
|
|
31365
|
-
import { existsSync as
|
|
31366
|
-
import { basename as basename3, dirname as
|
|
31449
|
+
import { existsSync as existsSync15, mkdirSync as mkdirSync9, readdirSync as readdirSync6, realpathSync as realpathSync5, rmSync as rmSync7, writeFileSync as writeFileSync11 } from "node:fs";
|
|
31450
|
+
import { basename as basename3, dirname as dirname11, join as join17, relative as relative3, resolve as resolve5 } from "node:path";
|
|
31367
31451
|
|
|
31368
31452
|
// src/resources.ts
|
|
31369
|
-
import { readFileSync as
|
|
31453
|
+
import { readFileSync as readFileSync16, realpathSync as realpathSync4 } from "node:fs";
|
|
31370
31454
|
import { extname, resolve as resolve4 } from "node:path";
|
|
31371
31455
|
var STATIC_RESOURCES = [
|
|
31372
31456
|
{
|
|
@@ -31576,7 +31660,7 @@ function readResourceText(uri, getProjectPath) {
|
|
|
31576
31660
|
const parsedUri = parseGodotUri(uri);
|
|
31577
31661
|
if (parsedUri.kind === "project-info") {
|
|
31578
31662
|
const projectFilePath = resolveProjectFile(projectPath, "project.godot");
|
|
31579
|
-
const rawProject =
|
|
31663
|
+
const rawProject = readFileSync16(projectFilePath, "utf-8");
|
|
31580
31664
|
const parsedProject = parseProjectGodot(rawProject);
|
|
31581
31665
|
return {
|
|
31582
31666
|
mimeType: "application/json",
|
|
@@ -31585,7 +31669,7 @@ function readResourceText(uri, getProjectPath) {
|
|
|
31585
31669
|
}
|
|
31586
31670
|
const filePath = resolveProjectFile(projectPath, parsedUri.resourcePath);
|
|
31587
31671
|
ensureAllowedExtension(parsedUri.kind, filePath);
|
|
31588
|
-
const text =
|
|
31672
|
+
const text = readFileSync16(filePath, "utf-8");
|
|
31589
31673
|
return {
|
|
31590
31674
|
mimeType: parsedUri.kind === "script" ? "text/x-gdscript" : "text/plain",
|
|
31591
31675
|
text
|
|
@@ -31643,9 +31727,9 @@ function onDiskSpelling(path) {
|
|
|
31643
31727
|
let at = resolve5(path);
|
|
31644
31728
|
for (;; ) {
|
|
31645
31729
|
try {
|
|
31646
|
-
return
|
|
31730
|
+
return join17(realpathSync5.native(at), ...missing.reverse());
|
|
31647
31731
|
} catch {
|
|
31648
|
-
const parent =
|
|
31732
|
+
const parent = dirname11(at);
|
|
31649
31733
|
if (parent === at) {
|
|
31650
31734
|
return resolve5(path);
|
|
31651
31735
|
}
|
|
@@ -31696,7 +31780,7 @@ async function renameSymbol(request, services) {
|
|
|
31696
31780
|
reason: `newScriptPath must be a .gd path inside the project, and ${request.newScriptPath} is not.`
|
|
31697
31781
|
};
|
|
31698
31782
|
}
|
|
31699
|
-
if (
|
|
31783
|
+
if (existsSync15(request.newScriptPath)) {
|
|
31700
31784
|
return { ok: false, reason: `${moveTo} already exists, so the script cannot be moved there.` };
|
|
31701
31785
|
}
|
|
31702
31786
|
}
|
|
@@ -31872,7 +31956,7 @@ async function renameSymbol(request, services) {
|
|
|
31872
31956
|
scripts,
|
|
31873
31957
|
from: declaringPath,
|
|
31874
31958
|
to: moveTo,
|
|
31875
|
-
exists: (path) =>
|
|
31959
|
+
exists: (path) => existsSync15(`${projectPath}/${path.slice("res://".length)}`)
|
|
31876
31960
|
}, plan);
|
|
31877
31961
|
}
|
|
31878
31962
|
plan = mentionIgnored(plan, ignoredTexts(projectPath), symbol, moveTo === null ? null : declaringPath);
|
|
@@ -32073,15 +32157,15 @@ ${mention.kind}`;
|
|
|
32073
32157
|
var LEFT_ALONE_ANSWER_LINES = 300;
|
|
32074
32158
|
var detailWrites = 0;
|
|
32075
32159
|
function writeLeftAloneDetail(projectPath, symbol, detail) {
|
|
32076
|
-
const directory =
|
|
32077
|
-
|
|
32160
|
+
const directory = join17(projectPath, ".godot", "gdharness-renames");
|
|
32161
|
+
mkdirSync9(directory, { recursive: true });
|
|
32078
32162
|
detailWrites += 1;
|
|
32079
|
-
const path =
|
|
32080
|
-
|
|
32163
|
+
const path = join17(directory, `${String(Date.now())}-${String(detailWrites).padStart(6, "0")}-${String(process.pid)}-${symbol}.json`);
|
|
32164
|
+
writeFileSync11(path, `${JSON.stringify(detail, null, 2)}
|
|
32081
32165
|
`, "utf8");
|
|
32082
32166
|
const kept = readdirSync6(directory).filter((name) => name.endsWith(".json")).sort().reverse();
|
|
32083
32167
|
for (const old of kept.slice(10)) {
|
|
32084
|
-
|
|
32168
|
+
rmSync7(join17(directory, old), { force: true });
|
|
32085
32169
|
}
|
|
32086
32170
|
return path;
|
|
32087
32171
|
}
|
|
@@ -32104,16 +32188,16 @@ function summaryOf(edits, mentions, rewrites, left) {
|
|
|
32104
32188
|
}
|
|
32105
32189
|
|
|
32106
32190
|
// src/restart-note.ts
|
|
32107
|
-
import { mkdirSync as
|
|
32108
|
-
import { dirname as
|
|
32191
|
+
import { mkdirSync as mkdirSync10, readFileSync as readFileSync17, rmSync as rmSync8, writeFileSync as writeFileSync12 } from "node:fs";
|
|
32192
|
+
import { dirname as dirname12, join as join18 } from "node:path";
|
|
32109
32193
|
function restartNotePath(projectPath) {
|
|
32110
|
-
return
|
|
32194
|
+
return join18(projectPath, ".godot", "gdharness-restart.json");
|
|
32111
32195
|
}
|
|
32112
32196
|
function noteRestartBegun(note) {
|
|
32113
32197
|
const path = restartNotePath(note.projectPath);
|
|
32114
32198
|
try {
|
|
32115
|
-
|
|
32116
|
-
|
|
32199
|
+
mkdirSync10(dirname12(path), { recursive: true });
|
|
32200
|
+
writeFileSync12(path, `${JSON.stringify(note, null, 2)}
|
|
32117
32201
|
`, "utf8");
|
|
32118
32202
|
} catch (error) {
|
|
32119
32203
|
console.error(`[SERVER] Could not record the restart at ${path}: ${errorMessage(error)}`);
|
|
@@ -32121,7 +32205,7 @@ function noteRestartBegun(note) {
|
|
|
32121
32205
|
}
|
|
32122
32206
|
function restartOwed(projectPath) {
|
|
32123
32207
|
try {
|
|
32124
|
-
const parsed = JSON.parse(
|
|
32208
|
+
const parsed = JSON.parse(readFileSync17(restartNotePath(projectPath), "utf8"));
|
|
32125
32209
|
if (typeof parsed !== "object" || parsed === null) {
|
|
32126
32210
|
return null;
|
|
32127
32211
|
}
|
|
@@ -32136,7 +32220,7 @@ function restartOwed(projectPath) {
|
|
|
32136
32220
|
}
|
|
32137
32221
|
function restartSettled(projectPath) {
|
|
32138
32222
|
try {
|
|
32139
|
-
|
|
32223
|
+
rmSync8(restartNotePath(projectPath), { force: true });
|
|
32140
32224
|
} catch (error) {
|
|
32141
32225
|
console.error(`[SERVER] Could not clear the restart note: ${errorMessage(error)}`);
|
|
32142
32226
|
}
|
|
@@ -32146,29 +32230,29 @@ function restartSettled(projectPath) {
|
|
|
32146
32230
|
import { execFile as execFile4 } from "node:child_process";
|
|
32147
32231
|
import { createHash as createHash3 } from "node:crypto";
|
|
32148
32232
|
import {
|
|
32149
|
-
mkdirSync as
|
|
32233
|
+
mkdirSync as mkdirSync11,
|
|
32150
32234
|
openSync as openSync2,
|
|
32151
32235
|
readdirSync as readdirSync7,
|
|
32152
|
-
readFileSync as
|
|
32236
|
+
readFileSync as readFileSync18,
|
|
32153
32237
|
realpathSync as realpathSync6,
|
|
32154
|
-
rmSync as
|
|
32238
|
+
rmSync as rmSync9,
|
|
32155
32239
|
statSync as statSync6,
|
|
32156
|
-
writeFileSync as
|
|
32240
|
+
writeFileSync as writeFileSync13
|
|
32157
32241
|
} from "node:fs";
|
|
32158
|
-
import { basename as basename4, dirname as
|
|
32242
|
+
import { basename as basename4, dirname as dirname13, join as join19, resolve as resolve6 } from "node:path";
|
|
32159
32243
|
import { promisify as promisify4 } from "node:util";
|
|
32160
32244
|
var execFileAsync = promisify4(execFile4);
|
|
32161
32245
|
function runsDirectory() {
|
|
32162
|
-
return
|
|
32246
|
+
return join19(runtimeDirectory(), "runs");
|
|
32163
32247
|
}
|
|
32164
32248
|
function realAsFarAsItExists(path) {
|
|
32165
32249
|
const missing = [];
|
|
32166
32250
|
let at = path;
|
|
32167
32251
|
for (;; ) {
|
|
32168
32252
|
try {
|
|
32169
|
-
return
|
|
32253
|
+
return join19(realpathSync6.native(at), ...missing);
|
|
32170
32254
|
} catch {
|
|
32171
|
-
const parent =
|
|
32255
|
+
const parent = dirname13(at);
|
|
32172
32256
|
if (parent === at) {
|
|
32173
32257
|
return path;
|
|
32174
32258
|
}
|
|
@@ -32183,27 +32267,27 @@ function projectKey(projectPath) {
|
|
|
32183
32267
|
return createHash3("sha256").update(folded.replaceAll("\\", "/")).digest("hex").slice(0, 16);
|
|
32184
32268
|
}
|
|
32185
32269
|
function runRecordPath(projectPath, directory = runtimeDirectory()) {
|
|
32186
|
-
return
|
|
32270
|
+
return join19(directory, "runs", `note-${projectKey(projectPath)}.json`);
|
|
32187
32271
|
}
|
|
32188
32272
|
var SHARED_NOTE = "run.json";
|
|
32189
32273
|
function openTranscript(startedAt) {
|
|
32190
|
-
|
|
32191
|
-
const path =
|
|
32274
|
+
mkdirSync11(runsDirectory(), { recursive: true });
|
|
32275
|
+
const path = join19(runsDirectory(), `run-${startedAt}.log`);
|
|
32192
32276
|
return { path, fd: openSync2(path, "a") };
|
|
32193
32277
|
}
|
|
32194
32278
|
function noteIsAbout(note, playing) {
|
|
32195
32279
|
return note.play !== undefined && playing !== null && note.play.play === playing.play && note.play.editorPid === playing.editorPid;
|
|
32196
32280
|
}
|
|
32197
32281
|
function editorNotePath(projectPath, directory = runtimeDirectory()) {
|
|
32198
|
-
return
|
|
32282
|
+
return join19(directory, "runs", `editor-${projectKey(projectPath)}.json`);
|
|
32199
32283
|
}
|
|
32200
32284
|
function writeEditorRunNote(note) {
|
|
32201
|
-
|
|
32202
|
-
|
|
32285
|
+
mkdirSync11(runsDirectory(), { recursive: true });
|
|
32286
|
+
writeFileSync13(editorNotePath(note.projectPath), JSON.stringify(note, null, 2), "utf8");
|
|
32203
32287
|
}
|
|
32204
32288
|
function readEditorRunNote(projectPath) {
|
|
32205
32289
|
for (const directory of runtimeDirectories()) {
|
|
32206
|
-
const note = editorNoteAt(editorNotePath(projectPath, directory)) ?? editorNoteAt(
|
|
32290
|
+
const note = editorNoteAt(editorNotePath(projectPath, directory)) ?? editorNoteAt(join19(directory, "runs", "editor-run.json"));
|
|
32207
32291
|
if (note !== null && isSameDirectory(note.projectPath, projectPath)) {
|
|
32208
32292
|
return note;
|
|
32209
32293
|
}
|
|
@@ -32213,7 +32297,7 @@ function readEditorRunNote(projectPath) {
|
|
|
32213
32297
|
function editorNoteAt(path) {
|
|
32214
32298
|
let parsed;
|
|
32215
32299
|
try {
|
|
32216
|
-
parsed = JSON.parse(
|
|
32300
|
+
parsed = JSON.parse(readFileSync18(path, "utf8"));
|
|
32217
32301
|
} catch {
|
|
32218
32302
|
return null;
|
|
32219
32303
|
}
|
|
@@ -32237,7 +32321,7 @@ function readRunRecord(projectPath) {
|
|
|
32237
32321
|
if (own !== null) {
|
|
32238
32322
|
return own;
|
|
32239
32323
|
}
|
|
32240
|
-
const shared = recordAt(
|
|
32324
|
+
const shared = recordAt(join19(directory, "runs", SHARED_NOTE));
|
|
32241
32325
|
if (shared !== null && shared.projectPath !== "" && isSameDirectory(shared.projectPath, projectPath)) {
|
|
32242
32326
|
return shared;
|
|
32243
32327
|
}
|
|
@@ -32249,13 +32333,13 @@ function everyRunRecord() {
|
|
|
32249
32333
|
for (const directory of runtimeDirectories()) {
|
|
32250
32334
|
let entries;
|
|
32251
32335
|
try {
|
|
32252
|
-
entries = readdirSync7(
|
|
32336
|
+
entries = readdirSync7(join19(directory, "runs"));
|
|
32253
32337
|
} catch {
|
|
32254
32338
|
continue;
|
|
32255
32339
|
}
|
|
32256
32340
|
for (const entry of entries) {
|
|
32257
32341
|
if (entry === SHARED_NOTE || entry.startsWith("note-") && entry.endsWith(".json")) {
|
|
32258
|
-
const record = recordAt(
|
|
32342
|
+
const record = recordAt(join19(directory, "runs", entry));
|
|
32259
32343
|
if (record !== null) {
|
|
32260
32344
|
found.push(record);
|
|
32261
32345
|
}
|
|
@@ -32267,7 +32351,7 @@ function everyRunRecord() {
|
|
|
32267
32351
|
function recordAt(path) {
|
|
32268
32352
|
let parsed;
|
|
32269
32353
|
try {
|
|
32270
|
-
parsed = JSON.parse(
|
|
32354
|
+
parsed = JSON.parse(readFileSync18(path, "utf8"));
|
|
32271
32355
|
} catch {
|
|
32272
32356
|
return null;
|
|
32273
32357
|
}
|
|
@@ -32318,7 +32402,7 @@ async function runningAs(pid) {
|
|
|
32318
32402
|
const began = await startedAt(pid, null);
|
|
32319
32403
|
const when = began === null ? {} : { startedAt: began };
|
|
32320
32404
|
if (process.platform === "linux") {
|
|
32321
|
-
const raw =
|
|
32405
|
+
const raw = readFileSync18(`/proc/${pid}/cmdline`, "utf8").replaceAll("\x00", " ").trim();
|
|
32322
32406
|
return raw === "" ? null : { kind: "commandLine", text: raw, ...when };
|
|
32323
32407
|
}
|
|
32324
32408
|
const args = (await execFileAsync("ps", ["-p", String(pid), "-o", "args="], { encoding: "utf8", timeout: 15000 })).stdout.trim();
|
|
@@ -32464,12 +32548,12 @@ function judgeAnnouncedGame(running, projectPath, announcedAt) {
|
|
|
32464
32548
|
}
|
|
32465
32549
|
function clearRunRecord(projectPath) {
|
|
32466
32550
|
for (const directory of runtimeDirectories()) {
|
|
32467
|
-
const shared =
|
|
32551
|
+
const shared = join19(directory, "runs", SHARED_NOTE);
|
|
32468
32552
|
const sharedRecord = recordAt(shared);
|
|
32469
32553
|
const ours = sharedRecord !== null && sharedRecord.projectPath !== "" && isSameDirectory(sharedRecord.projectPath, projectPath);
|
|
32470
32554
|
for (const path of [runRecordPath(projectPath, directory), ...ours ? [shared] : []]) {
|
|
32471
32555
|
try {
|
|
32472
|
-
|
|
32556
|
+
rmSync9(path, { force: true });
|
|
32473
32557
|
} catch {}
|
|
32474
32558
|
}
|
|
32475
32559
|
}
|
|
@@ -32494,18 +32578,18 @@ function sweepTranscripts(keepMs = 24 * 60 * 60 * 1000, now = Date.now()) {
|
|
|
32494
32578
|
if (!entry.startsWith("run-") || !entry.endsWith(".log")) {
|
|
32495
32579
|
continue;
|
|
32496
32580
|
}
|
|
32497
|
-
const path =
|
|
32581
|
+
const path = join19(directory, entry);
|
|
32498
32582
|
if (kept.has(path) || ageMs(path, now) < keepMs) {
|
|
32499
32583
|
continue;
|
|
32500
32584
|
}
|
|
32501
32585
|
try {
|
|
32502
|
-
|
|
32586
|
+
rmSync9(path);
|
|
32503
32587
|
} catch {}
|
|
32504
32588
|
}
|
|
32505
32589
|
}
|
|
32506
32590
|
|
|
32507
32591
|
// src/script-origins.ts
|
|
32508
|
-
import { join as
|
|
32592
|
+
import { join as join20 } from "node:path";
|
|
32509
32593
|
var UNRESOLVED = /Could not resolve class "([A-Za-z_][A-Za-z0-9_]*)", because of a parser error/;
|
|
32510
32594
|
function unresolvedClassIn(message) {
|
|
32511
32595
|
return UNRESOLVED.exec(message)?.[1] ?? null;
|
|
@@ -32561,7 +32645,7 @@ function checkWithEngine(godotPath, projectPath) {
|
|
|
32561
32645
|
const ran = await runEngine(godotPath, [
|
|
32562
32646
|
"--headless",
|
|
32563
32647
|
"--log-file",
|
|
32564
|
-
|
|
32648
|
+
join20(logDir, "engine.log"),
|
|
32565
32649
|
"--path",
|
|
32566
32650
|
projectPath,
|
|
32567
32651
|
"--check-only",
|
|
@@ -32602,16 +32686,16 @@ import { spawnSync } from "node:child_process";
|
|
|
32602
32686
|
import { createHash as createHash4 } from "node:crypto";
|
|
32603
32687
|
import {
|
|
32604
32688
|
cpSync,
|
|
32605
|
-
existsSync as
|
|
32606
|
-
mkdirSync as
|
|
32689
|
+
existsSync as existsSync16,
|
|
32690
|
+
mkdirSync as mkdirSync12,
|
|
32607
32691
|
readdirSync as readdirSync8,
|
|
32608
|
-
readFileSync as
|
|
32692
|
+
readFileSync as readFileSync19,
|
|
32609
32693
|
rmdirSync,
|
|
32610
|
-
rmSync as
|
|
32694
|
+
rmSync as rmSync10,
|
|
32611
32695
|
statSync as statSync7,
|
|
32612
|
-
writeFileSync as
|
|
32696
|
+
writeFileSync as writeFileSync14
|
|
32613
32697
|
} from "node:fs";
|
|
32614
|
-
import { dirname as
|
|
32698
|
+
import { dirname as dirname14, join as join21 } from "node:path";
|
|
32615
32699
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
32616
32700
|
var ADDONS = ["gdharness_editor", "gdharness_runtime", "auto_reload"];
|
|
32617
32701
|
var EDITOR_PLUGINS = ["gdharness_editor", "auto_reload"];
|
|
@@ -32635,21 +32719,21 @@ function addonsDigest(names, from) {
|
|
|
32635
32719
|
if (name === VERSION_MARKER || name === DIGEST_MARKER) {
|
|
32636
32720
|
continue;
|
|
32637
32721
|
}
|
|
32638
|
-
const path =
|
|
32722
|
+
const path = join21(directory, name);
|
|
32639
32723
|
const inside = `${relative}/${name}`;
|
|
32640
32724
|
if (statSync7(path).isDirectory()) {
|
|
32641
32725
|
walk(path, inside);
|
|
32642
32726
|
} else {
|
|
32643
32727
|
hash.update(`${inside}
|
|
32644
32728
|
`);
|
|
32645
|
-
hash.update(
|
|
32729
|
+
hash.update(readFileSync19(path));
|
|
32646
32730
|
hash.update(`
|
|
32647
32731
|
`);
|
|
32648
32732
|
}
|
|
32649
32733
|
}
|
|
32650
32734
|
};
|
|
32651
32735
|
for (const name of names) {
|
|
32652
|
-
walk(
|
|
32736
|
+
walk(join21(from, name), name);
|
|
32653
32737
|
}
|
|
32654
32738
|
return hash.digest("hex");
|
|
32655
32739
|
}
|
|
@@ -32682,22 +32766,22 @@ function installedRuntimeDigest(projectPath) {
|
|
|
32682
32766
|
return digestMarker(projectPath, RUNTIME_ADDON);
|
|
32683
32767
|
}
|
|
32684
32768
|
function digestMarker(projectPath, addon) {
|
|
32685
|
-
const marker =
|
|
32686
|
-
if (!
|
|
32769
|
+
const marker = join21(projectPath, "addons", addon, DIGEST_MARKER);
|
|
32770
|
+
if (!existsSync16(marker)) {
|
|
32687
32771
|
return null;
|
|
32688
32772
|
}
|
|
32689
|
-
const said =
|
|
32773
|
+
const said = readFileSync19(marker, "utf8").trim();
|
|
32690
32774
|
return said === "" ? null : said;
|
|
32691
32775
|
}
|
|
32692
32776
|
function shippedAddonsDirectory() {
|
|
32693
|
-
return
|
|
32777
|
+
return join21(dirname14(fileURLToPath3(import.meta.url)), "godot", "addons");
|
|
32694
32778
|
}
|
|
32695
32779
|
function installedAddonVersion(projectPath) {
|
|
32696
|
-
const marker =
|
|
32697
|
-
if (!
|
|
32780
|
+
const marker = join21(projectPath, "addons", ADDONS[0], VERSION_MARKER);
|
|
32781
|
+
if (!existsSync16(marker)) {
|
|
32698
32782
|
return null;
|
|
32699
32783
|
}
|
|
32700
|
-
const said =
|
|
32784
|
+
const said = readFileSync19(marker, "utf8").trim();
|
|
32701
32785
|
return said === "" ? null : said;
|
|
32702
32786
|
}
|
|
32703
32787
|
function loaderAmong(projectPath, named) {
|
|
@@ -32708,7 +32792,7 @@ function loaderAmong(projectPath, named) {
|
|
|
32708
32792
|
}
|
|
32709
32793
|
let source = null;
|
|
32710
32794
|
try {
|
|
32711
|
-
source =
|
|
32795
|
+
source = readFileSync19(join21(projectPath, path), "utf8");
|
|
32712
32796
|
} catch {}
|
|
32713
32797
|
if (source?.includes(RUNTIME_AUTOLOAD.path)) {
|
|
32714
32798
|
return { name: entry, path, exists: true };
|
|
@@ -32750,10 +32834,10 @@ function enabledPlugins(settings) {
|
|
|
32750
32834
|
function inspectProject(projectPath) {
|
|
32751
32835
|
const problems = [];
|
|
32752
32836
|
const addons = ADDONS.map((name) => {
|
|
32753
|
-
const directory =
|
|
32754
|
-
const marker =
|
|
32755
|
-
const installed =
|
|
32756
|
-
const version =
|
|
32837
|
+
const directory = join21(projectPath, "addons", name);
|
|
32838
|
+
const marker = join21(directory, VERSION_MARKER);
|
|
32839
|
+
const installed = existsSync16(directory);
|
|
32840
|
+
const version = existsSync16(marker) ? readFileSync19(marker, "utf8").trim() : null;
|
|
32757
32841
|
const current = version === SERVER_VERSION;
|
|
32758
32842
|
if (!installed) {
|
|
32759
32843
|
problems.push(`addons/${name} is not installed; run gdharness setup`);
|
|
@@ -32762,7 +32846,7 @@ function inspectProject(projectPath) {
|
|
|
32762
32846
|
}
|
|
32763
32847
|
return { name, installed, version, current };
|
|
32764
32848
|
});
|
|
32765
|
-
const settings = parseProjectGodot(
|
|
32849
|
+
const settings = parseProjectGodot(readFileSync19(join21(projectPath, "project.godot"), "utf8"));
|
|
32766
32850
|
const pluginsEnabled = enabledPlugins(settings);
|
|
32767
32851
|
for (const name of EDITOR_PLUGINS) {
|
|
32768
32852
|
if (!pluginsEnabled.includes(name)) {
|
|
@@ -32778,7 +32862,7 @@ function inspectProject(projectPath) {
|
|
|
32778
32862
|
const tracked = trackedByGit(projectPath, [...named.values()]);
|
|
32779
32863
|
const addonMissing = (path) => addons.some((addon) => !addon.installed && path.startsWith(`addons/${addon.name}/`));
|
|
32780
32864
|
for (const [name, path] of named) {
|
|
32781
|
-
if (!
|
|
32865
|
+
if (!existsSync16(join21(projectPath, path))) {
|
|
32782
32866
|
if (!addonMissing(path)) {
|
|
32783
32867
|
problems.push(`the ${name} autoload names res://${path}, which is not in this project, so it boots with a missing script; restore that file, or point the autoload at one that is here`);
|
|
32784
32868
|
}
|
|
@@ -33184,7 +33268,7 @@ var TOOL_SPECS = [
|
|
|
33184
33268
|
},
|
|
33185
33269
|
{
|
|
33186
33270
|
name: "project_test",
|
|
33187
|
-
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, with the classes each addon declares counted under addedInAddons and changedInAddons rather than named: 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 script that does not load stops gdUnit4 before any suite runs: its errors come back in full under scriptErrors, and the scripts that failed only because one they depend on did are listed by path under failedThroughDependency. When they failed on a class whose own script has a parser error, which gdUnit4 never names, that script is checked by the engine and its error comes first under scriptErrors, with the class it declares under declares. 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.",
|
|
33271
|
+
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, with the classes each addon declares counted under addedInAddons and changedInAddons rather than named: 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 script that does not load stops gdUnit4 before any suite runs: its errors come back in full under scriptErrors, and the scripts that failed only because one they depend on did are listed by path under failedThroughDependency. When they failed on a class whose own script has a parser error, which gdUnit4 never names, that script is checked by the engine and its error comes first under scriptErrors, with the class it declares under declares, and the errors a script has only because that class did not resolve, which the engine reads as Variant, are kept apart under followFromUnresolved. A client that sends a progress token is told as each suite finishes, and at least every twenty seconds through a long one, so an idle limit does not end a long tier. A run that ends without a report, at its timeout or otherwise, names the suites it finished under suitesFinished, the failing ones with their failed cases under suitesFailing, and the one it was in under suiteRunning. When a client cuts a run off, that is kept in the project and the next project_test there carries it under previousRunCut. 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.",
|
|
33188
33272
|
parameters: {
|
|
33189
33273
|
projectPath: PROJECT_PATH,
|
|
33190
33274
|
path: {
|
|
@@ -34009,7 +34093,7 @@ var TOOL_SPECS = [
|
|
|
34009
34093
|
requires: [],
|
|
34010
34094
|
operations: {
|
|
34011
34095
|
click: {
|
|
34012
|
-
summary: `press and release on a Control, a frame apart, and answer with what was under the pointer and what became of the control: in_tree, removed or freed. The control is named by nodePath, or by says with the words on it, found under nodePath when both are given, and the answer then says under found which match it was. Words that are part of a RichTextLabel are pressed where they are drawn rather than at the label's middle, so a link among them is the one pressed: found.pressedOn says "the words", or "the middle of the label" with a note saying why they could not be placed, and link_pressed
|
|
34096
|
+
summary: `press and release on a Control, a frame apart, and answer with what was under the pointer and what became of the control: in_tree, removed or freed. The control is named by nodePath, or by says with the words on it, found under nodePath when both are given, and the answer then says under found which match it was. Words that are part of a RichTextLabel are pressed where they are drawn rather than at the label's middle, so a link among them is the one pressed: found.pressedOn says "the words", or "the middle of the label" with a note saying why they could not be placed, and on a rich label link_pressed says whether the press reached a link, with link_meta giving the meta the label reports clicked, an empty one included. A control out of sight inside a ScrollContainer is scrolled to first, each container out from it a frame after the one inside, and scrolled_into_view says whether a scroll value changed. A disabled button, and a control that does not process (the game paused, say), are refused: the pointer reaches them and the press does nothing. A 3D node is clicked where it is drawn, and landed then says the interface did not swallow the press: no control under the pointer stopped it, a control letting the pointer pass included, and every viewport on the way out from the room had the pointer over what shows the room`,
|
|
34013
34097
|
requires: []
|
|
34014
34098
|
},
|
|
34015
34099
|
choose: {
|
|
@@ -34184,11 +34268,28 @@ function buildToolDefinitions() {
|
|
|
34184
34268
|
});
|
|
34185
34269
|
}
|
|
34186
34270
|
|
|
34271
|
+
// src/tool-reference.ts
|
|
34272
|
+
function namedType(declared) {
|
|
34273
|
+
if (typeof declared === "string") {
|
|
34274
|
+
return declared;
|
|
34275
|
+
}
|
|
34276
|
+
if (Array.isArray(declared) && declared.every((kind) => typeof kind === "string")) {
|
|
34277
|
+
return declared.join(" or ");
|
|
34278
|
+
}
|
|
34279
|
+
return "any";
|
|
34280
|
+
}
|
|
34281
|
+
function argumentShape(schema) {
|
|
34282
|
+
const named = namedType(schema["type"]);
|
|
34283
|
+
const items = schema["items"];
|
|
34284
|
+
const held = typeof items === "object" && items !== null && !Array.isArray(items) ? namedType(items["type"]) : "any";
|
|
34285
|
+
return named.split(" or ").map((kind) => kind === "array" && held !== "any" ? `array of ${held}s` : kind).join(" or ");
|
|
34286
|
+
}
|
|
34287
|
+
|
|
34187
34288
|
// src/server.ts
|
|
34188
34289
|
var UPDATE_NOTICE_EVERY = 500;
|
|
34189
34290
|
var FEEDBACK_NOTICE_EVERY = 250;
|
|
34190
34291
|
var run4 = promisify5(execFile5);
|
|
34191
|
-
var __dirname2 =
|
|
34292
|
+
var __dirname2 = dirname15(fileURLToPath4(import.meta.url));
|
|
34192
34293
|
function editorRestartTimeoutMs() {
|
|
34193
34294
|
const override = Number.parseInt(envValue("GDHARNESS_EDITOR_RESTART_TIMEOUT_MS") ?? "", 10);
|
|
34194
34295
|
return Number.isInteger(override) && override > 0 ? override : 90000;
|
|
@@ -34209,16 +34310,16 @@ function captureDestinationRefusal(outputPath, projects) {
|
|
|
34209
34310
|
if (extname2(outputPath).toLowerCase() !== ".png") {
|
|
34210
34311
|
return `outputPath ${outputPath} does not end in .png, which is what a capture is.`;
|
|
34211
34312
|
}
|
|
34212
|
-
const directory = realPathOr(
|
|
34213
|
-
if (!
|
|
34214
|
-
return `outputPath's directory ${
|
|
34313
|
+
const directory = realPathOr(dirname15(outputPath));
|
|
34314
|
+
if (!existsSync17(directory)) {
|
|
34315
|
+
return `outputPath's directory ${dirname15(outputPath)} does not exist; a capture creates no directories.`;
|
|
34215
34316
|
}
|
|
34216
|
-
const where =
|
|
34317
|
+
const where = join22(directory, basename5(outputPath));
|
|
34217
34318
|
const inside = projects.find((project) => project !== "" && isWithinRoot(realPathOr(project), where));
|
|
34218
34319
|
if (inside !== undefined) {
|
|
34219
34320
|
return `outputPath ${outputPath} is inside the project at ${inside}; a capture is saved outside it, so it never becomes part of the game.`;
|
|
34220
34321
|
}
|
|
34221
|
-
if (
|
|
34322
|
+
if (existsSync17(where)) {
|
|
34222
34323
|
return `outputPath ${outputPath} already exists; a capture is not written over a file. Name a new one.`;
|
|
34223
34324
|
}
|
|
34224
34325
|
return null;
|
|
@@ -34722,18 +34823,63 @@ function settingsNamedBy(op, args) {
|
|
|
34722
34823
|
}
|
|
34723
34824
|
}
|
|
34724
34825
|
var STALLED_AFTER_MS = 30000;
|
|
34826
|
+
var TEST_HEARTBEAT_MS = 20000;
|
|
34725
34827
|
var DEPENDED_FAILED = "Failed to compile depended scripts";
|
|
34726
34828
|
function foldedScriptErrors(errors) {
|
|
34727
|
-
const
|
|
34829
|
+
const unresolved = new Map;
|
|
34830
|
+
for (const error of errors) {
|
|
34831
|
+
const name = unresolvedClassIn(error.message);
|
|
34832
|
+
if (name !== null) {
|
|
34833
|
+
unresolved.set(error.path, (unresolved.get(error.path) ?? new Set).add(name));
|
|
34834
|
+
}
|
|
34835
|
+
}
|
|
34836
|
+
const follows = (error) => {
|
|
34837
|
+
const names = unresolved.get(error.path);
|
|
34838
|
+
if (names === undefined) {
|
|
34839
|
+
return false;
|
|
34840
|
+
}
|
|
34841
|
+
const reparsed = /Could not parse global class "([A-Za-z_][A-Za-z0-9_]*)"/.exec(error.message)?.[1];
|
|
34842
|
+
return error.message.includes('the inferred type "Variant"') || reparsed !== undefined && names.has(reparsed);
|
|
34843
|
+
};
|
|
34844
|
+
const scriptErrors = errors.filter((error) => !error.message.includes(DEPENDED_FAILED) && !follows(error));
|
|
34728
34845
|
const own = new Set(scriptErrors.map((error) => error.path));
|
|
34729
34846
|
return {
|
|
34730
34847
|
scriptErrors,
|
|
34731
|
-
failedThroughDependency: [...new Set(errors.map((error) => error.path))].filter((path) => !own.has(path))
|
|
34848
|
+
failedThroughDependency: [...new Set(errors.map((error) => error.path))].filter((path) => !own.has(path)),
|
|
34849
|
+
followFromUnresolved: errors.filter(follows)
|
|
34732
34850
|
};
|
|
34733
34851
|
}
|
|
34734
34852
|
function nonEmpty(lists) {
|
|
34735
34853
|
return Object.fromEntries(Object.entries(lists).filter(([, list]) => list.length > 0));
|
|
34736
34854
|
}
|
|
34855
|
+
function withPreviousCut(answer, previous) {
|
|
34856
|
+
const content = answer.content.map((part) => ({ ...part }));
|
|
34857
|
+
const last = content.at(-1);
|
|
34858
|
+
let json = null;
|
|
34859
|
+
try {
|
|
34860
|
+
const parsed = JSON.parse(last?.text ?? "");
|
|
34861
|
+
json = typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? { ...parsed } : null;
|
|
34862
|
+
} catch {}
|
|
34863
|
+
if (last !== undefined && json !== null) {
|
|
34864
|
+
last.text = answerJson({ ...json, previousRunCut: previous });
|
|
34865
|
+
}
|
|
34866
|
+
const note = cutTestRunNote(previous);
|
|
34867
|
+
const first = content[0];
|
|
34868
|
+
if (first === undefined || first === last && json !== null || typeof first.text !== "string") {
|
|
34869
|
+
content.unshift({ type: "text", text: note });
|
|
34870
|
+
} else {
|
|
34871
|
+
first.text = `${first.text} ${note}`;
|
|
34872
|
+
}
|
|
34873
|
+
return { ...answer, content };
|
|
34874
|
+
}
|
|
34875
|
+
function finishedBeforeTheEnd(finished, failing, running) {
|
|
34876
|
+
if (finished === 0 && running === null) {
|
|
34877
|
+
return "";
|
|
34878
|
+
}
|
|
34879
|
+
const got = finished === 0 ? "no suite had finished" : `${finished === 1 ? "one suite" : `${finished} suites`} finished${failing === 0 ? "" : finished === 1 ? ", with failures" : `, ${failing} of them with failures`}`;
|
|
34880
|
+
const listed = finished === 0 ? "" : `; ${finished === 1 ? "it is" : "they are"} under suitesFinished${failing === 0 ? "" : ", with the failing cases under suitesFailing"}, so a run of the rest need not repeat ${finished === 1 ? "it" : "them"}`;
|
|
34881
|
+
return ` Before it ended, ${got}${running === null ? "" : `${finished === 0 ? " and" : ", and"} ${running} was running`}${listed}.`;
|
|
34882
|
+
}
|
|
34737
34883
|
function scriptErrorsNote(errors, {
|
|
34738
34884
|
origins,
|
|
34739
34885
|
unconfirmed
|
|
@@ -34741,7 +34887,11 @@ function scriptErrorsNote(errors, {
|
|
|
34741
34887
|
origins: [],
|
|
34742
34888
|
unconfirmed: []
|
|
34743
34889
|
}) {
|
|
34744
|
-
const {
|
|
34890
|
+
const {
|
|
34891
|
+
scriptErrors: own,
|
|
34892
|
+
failedThroughDependency: dependents,
|
|
34893
|
+
followFromUnresolved: consequences
|
|
34894
|
+
} = foldedScriptErrors(errors);
|
|
34745
34895
|
const causes = new Map;
|
|
34746
34896
|
for (const error of own) {
|
|
34747
34897
|
if (!causes.has(error.path)) {
|
|
@@ -34768,12 +34918,10 @@ function scriptErrorsNote(errors, {
|
|
|
34768
34918
|
const following = dependents.length === 0 ? "" : causes.size === 0 ? `${dependents.join(", ")} ${dependents.length === 1 ? "depends" : "depend"} on a script that did not compile, and gdUnit4 named none with an error of its own: script_diagnostics on one of them finds it.` : `${dependents.length === 1 ? "One more" : `${dependents.length} more`} failed only because a script ${dependents.length === 1 ? "it depends" : "they depend"} on did: ${dependents.join(", ")}.`;
|
|
34769
34919
|
const more = own.length - causes.size;
|
|
34770
34920
|
const rest = more > 0 ? `${more === 1 ? "One more error" : `${more} more errors`} in ${causes.size === 1 ? "that script" : "those scripts"} ${more === 1 ? "is" : "are"} under scriptErrors.` : "";
|
|
34771
|
-
|
|
34772
|
-
|
|
34773
|
-
|
|
34774
|
-
|
|
34775
|
-
rest
|
|
34776
|
-
].filter((part) => part !== "").join(" ");
|
|
34921
|
+
const after = consequences.length > 0 ? `${consequences.length === 1 ? "One more error comes" : `${consequences.length} more errors come`} only from a class that did not resolve, which the engine reads as Variant, and ${consequences.length === 1 ? "is" : "are"} under followFromUnresolved.` : "";
|
|
34922
|
+
const unnamed = first.length;
|
|
34923
|
+
const opening = unnamed === 0 ? `No tests ran: gdUnit4 could not load ${scripts === 1 ? "one script" : `${scripts} scripts`} while looking for suites, and it runs no suite at all when one fails to load.` : `No tests ran: ${scripts + unnamed} scripts did not compile, and gdUnit4 runs no suite at all when one fails to load. It could not load ${scripts === 1 ? "one" : scripts} of them while looking for suites, and ${unnamed === 1 ? "the one it did not name comes" : `the ${unnamed} it did not name come`} first.`;
|
|
34924
|
+
return [opening, named, following, rest, after].filter((part) => part !== "").join(" ");
|
|
34777
34925
|
}
|
|
34778
34926
|
function timedOutVerdict(timeoutMs, silentForMs) {
|
|
34779
34927
|
const quiet = Math.round(silentForMs);
|
|
@@ -34863,7 +35011,7 @@ function aboveTheRunner(entry) {
|
|
|
34863
35011
|
}
|
|
34864
35012
|
function elsewhereIn(projectPath, asked) {
|
|
34865
35013
|
const wanted = asked.replace(/^res:\/\//, "").replace(/\/+$/, "");
|
|
34866
|
-
if (wanted === "" ||
|
|
35014
|
+
if (wanted === "" || existsSync17(join22(projectPath, wanted))) {
|
|
34867
35015
|
return "";
|
|
34868
35016
|
}
|
|
34869
35017
|
const root = (wanted.split("/")[0] ?? "").toLowerCase();
|
|
@@ -34907,7 +35055,7 @@ function testPathsIn(projectPath, given) {
|
|
|
34907
35055
|
};
|
|
34908
35056
|
}
|
|
34909
35057
|
function hasMainScene(projectFile) {
|
|
34910
|
-
const scene = parseProjectGodot(
|
|
35058
|
+
const scene = parseProjectGodot(readFileSync20(projectFile, "utf8"))["application"]?.["run/main_scene"];
|
|
34911
35059
|
return typeof scene === "string" && scene !== "";
|
|
34912
35060
|
}
|
|
34913
35061
|
function runtimeAddonAnnouncesEditors(projectPath) {
|
|
@@ -34915,13 +35063,13 @@ function runtimeAddonAnnouncesEditors(projectPath) {
|
|
|
34915
35063
|
return false;
|
|
34916
35064
|
}
|
|
34917
35065
|
try {
|
|
34918
|
-
return
|
|
35066
|
+
return readFileSync20(join22(projectPath, RUNTIME_AUTOLOAD.path), "utf8").includes('"editor_pid"');
|
|
34919
35067
|
} catch {
|
|
34920
35068
|
return false;
|
|
34921
35069
|
}
|
|
34922
35070
|
}
|
|
34923
35071
|
function editorPlaysHeadless(projectFile) {
|
|
34924
|
-
const runArgs = parseProjectGodot(
|
|
35072
|
+
const runArgs = parseProjectGodot(readFileSync20(projectFile, "utf8"))["editor"]?.["run/main_run_args"];
|
|
34925
35073
|
return typeof runArgs === "string" && /(?:^|\s)--headless(?:\s|$)/.test(runArgs);
|
|
34926
35074
|
}
|
|
34927
35075
|
function camelCased(params) {
|
|
@@ -34940,7 +35088,7 @@ function needsItsOwnProcess(asked) {
|
|
|
34940
35088
|
class GodotServer {
|
|
34941
35089
|
mcp;
|
|
34942
35090
|
locator = new GodotLocator;
|
|
34943
|
-
operationsScript =
|
|
35091
|
+
operationsScript = join22(__dirname2, "godot", "operations", "godot_operations.gd");
|
|
34944
35092
|
godotBridge;
|
|
34945
35093
|
tools = buildToolDefinitions();
|
|
34946
35094
|
activeProcess = null;
|
|
@@ -35243,11 +35391,20 @@ class GodotServer {
|
|
|
35243
35391
|
return checked.response;
|
|
35244
35392
|
}
|
|
35245
35393
|
const named = args["projectPath"];
|
|
35246
|
-
if (typeof named === "string" &&
|
|
35394
|
+
if (typeof named === "string" && existsSync17(join22(named, "project.godot"))) {
|
|
35247
35395
|
this.lastProjectPath = named;
|
|
35248
35396
|
}
|
|
35249
35397
|
this.updates.refresh();
|
|
35250
|
-
const
|
|
35398
|
+
const token = request.params._meta?.progressToken;
|
|
35399
|
+
const progress = token === undefined ? undefined : (done, message) => {
|
|
35400
|
+
extra.sendNotification({
|
|
35401
|
+
method: "notifications/progress",
|
|
35402
|
+
params: { progressToken: token, progress: done, message }
|
|
35403
|
+
}).catch((error) => {
|
|
35404
|
+
this.logDebug(`Progress for ${spec.name} was not sent: ${errorMessage(error)}`);
|
|
35405
|
+
});
|
|
35406
|
+
};
|
|
35407
|
+
const answer = await withCallSignal(extra.signal, () => this.answered(spec.name, checked.op ?? "", args), progress);
|
|
35251
35408
|
return this.withFeedbackNotice(this.withUpdateNotice(answer));
|
|
35252
35409
|
});
|
|
35253
35410
|
}
|
|
@@ -35374,7 +35531,14 @@ class GodotServer {
|
|
|
35374
35531
|
const asked = readString(args, "op");
|
|
35375
35532
|
const named = asked !== undefined && spec.operations?.[asked] !== undefined ? asked : null;
|
|
35376
35533
|
const wanted = named === null ? [...known] : [...argumentsOf(spec, named), "op"];
|
|
35377
|
-
const
|
|
35534
|
+
const shaped = wanted.map((name) => {
|
|
35535
|
+
const schema = spec.parameters[name];
|
|
35536
|
+
if (schema !== undefined) {
|
|
35537
|
+
return `${name} (${argumentShape(schema)})`;
|
|
35538
|
+
}
|
|
35539
|
+
return name === "op" && named === null && spec.operations !== undefined ? `op (${Object.keys(spec.operations).join(", ")})` : name;
|
|
35540
|
+
});
|
|
35541
|
+
const takes = wanted.length === 0 ? "It takes no arguments." : `It takes: ${shaped.join(", ")}.`;
|
|
35378
35542
|
const where = named === null ? spec.name : `${spec.name} ${named}`;
|
|
35379
35543
|
return {
|
|
35380
35544
|
ok: false,
|
|
@@ -35717,8 +35881,8 @@ class GodotServer {
|
|
|
35717
35881
|
if (path === undefined) {
|
|
35718
35882
|
return { ok: false, response: this.createErrorResponse("projectPath is required.") };
|
|
35719
35883
|
}
|
|
35720
|
-
const file =
|
|
35721
|
-
if (!
|
|
35884
|
+
const file = join22(path, "project.godot");
|
|
35885
|
+
if (!existsSync17(file)) {
|
|
35722
35886
|
return {
|
|
35723
35887
|
ok: false,
|
|
35724
35888
|
response: this.createErrorResponse(`Not a Godot project: ${path}`, [
|
|
@@ -36329,7 +36493,7 @@ class GodotServer {
|
|
|
36329
36493
|
if (!engine.ok) {
|
|
36330
36494
|
return engine.response;
|
|
36331
36495
|
}
|
|
36332
|
-
const application = parseProjectGodot(
|
|
36496
|
+
const application = parseProjectGodot(readFileSync20(project.value.file, "utf8"))["application"] ?? {};
|
|
36333
36497
|
const name = application["config/name"];
|
|
36334
36498
|
const mainScene = application["run/main_scene"];
|
|
36335
36499
|
const info = {
|
|
@@ -36385,18 +36549,18 @@ class GodotServer {
|
|
|
36385
36549
|
if (!engine.ok) {
|
|
36386
36550
|
return engine.response;
|
|
36387
36551
|
}
|
|
36388
|
-
const folder =
|
|
36552
|
+
const folder = dirname15(output.absolutePath);
|
|
36389
36553
|
try {
|
|
36390
|
-
|
|
36554
|
+
mkdirSync13(folder, { recursive: true });
|
|
36391
36555
|
} catch (error) {
|
|
36392
|
-
return this.createErrorResponse(`Cannot create ${
|
|
36556
|
+
return this.createErrorResponse(`Cannot create ${dirname15(output.relativePath)} to export into: ${errorMessage(error)}`);
|
|
36393
36557
|
}
|
|
36394
36558
|
const debug = readBoolean(args, "debug") ?? false;
|
|
36395
36559
|
const exportLogs = scratchDirectory("export");
|
|
36396
36560
|
const exportArgs = [
|
|
36397
36561
|
"--headless",
|
|
36398
36562
|
"--log-file",
|
|
36399
|
-
|
|
36563
|
+
join22(exportLogs, "engine.log"),
|
|
36400
36564
|
"--path",
|
|
36401
36565
|
project.value.path,
|
|
36402
36566
|
debug ? "--export-debug" : "--export-release",
|
|
@@ -36404,7 +36568,7 @@ class GodotServer {
|
|
|
36404
36568
|
output.absolutePath
|
|
36405
36569
|
];
|
|
36406
36570
|
this.logDebug(`Exporting: ${engine.value} ${exportArgs.join(" ")}`);
|
|
36407
|
-
const writtenAt = () =>
|
|
36571
|
+
const writtenAt = () => existsSync17(output.absolutePath) ? statSync8(output.absolutePath).mtimeMs : null;
|
|
36408
36572
|
const before = writtenAt();
|
|
36409
36573
|
let ending;
|
|
36410
36574
|
try {
|
|
@@ -36417,7 +36581,38 @@ class GodotServer {
|
|
|
36417
36581
|
}
|
|
36418
36582
|
return exportAnswer(ending, { preset, outputPath: output.relativePath, debug }, before, writtenAt());
|
|
36419
36583
|
}
|
|
36584
|
+
reportTestProgress(log) {
|
|
36585
|
+
const progress = callProgress();
|
|
36586
|
+
if (progress === undefined) {
|
|
36587
|
+
return;
|
|
36588
|
+
}
|
|
36589
|
+
const began = Date.now();
|
|
36590
|
+
let read = 0;
|
|
36591
|
+
let lastSent = began;
|
|
36592
|
+
const ticker = setInterval(() => {
|
|
36593
|
+
const entries = log.all;
|
|
36594
|
+
const finishedOne = entries.slice(read).some((entry) => finishesASuite(entry.text));
|
|
36595
|
+
read = entries.length;
|
|
36596
|
+
const now = Date.now();
|
|
36597
|
+
if (!finishedOne && now - lastSent < TEST_HEARTBEAT_MS) {
|
|
36598
|
+
return;
|
|
36599
|
+
}
|
|
36600
|
+
lastSent = now;
|
|
36601
|
+
progress(Math.floor((now - began) / 1000), testProgressMessage(entries.map((entry) => entry.text), now - began));
|
|
36602
|
+
}, 1000);
|
|
36603
|
+
ticker.unref();
|
|
36604
|
+
return ticker;
|
|
36605
|
+
}
|
|
36420
36606
|
async handleRunTests(args) {
|
|
36607
|
+
const answer = await this.runTests(args);
|
|
36608
|
+
const project = this.project(args);
|
|
36609
|
+
if (!project.ok || callSignal()?.aborted === true) {
|
|
36610
|
+
return answer;
|
|
36611
|
+
}
|
|
36612
|
+
const previous = takeCutTestRun(project.value.path);
|
|
36613
|
+
return previous === null ? answer : withPreviousCut(answer, previous);
|
|
36614
|
+
}
|
|
36615
|
+
async runTests(args) {
|
|
36421
36616
|
const project = this.project(args);
|
|
36422
36617
|
if (!project.ok) {
|
|
36423
36618
|
return project.response;
|
|
@@ -36426,7 +36621,7 @@ class GodotServer {
|
|
|
36426
36621
|
if (!paths.ok) {
|
|
36427
36622
|
return this.createErrorResponse(paths.reason, PATH_SOLUTIONS);
|
|
36428
36623
|
}
|
|
36429
|
-
const missing = paths.value.filter((path) => !
|
|
36624
|
+
const missing = paths.value.filter((path) => !existsSync17(join22(project.value.path, path.slice("res://".length))));
|
|
36430
36625
|
const asked = paths.value.filter((path) => !missing.includes(path));
|
|
36431
36626
|
if (asked.length === 0) {
|
|
36432
36627
|
const verdict = `nothing at ${missing.join(" and ")}`;
|
|
@@ -36442,7 +36637,7 @@ class GodotServer {
|
|
|
36442
36637
|
};
|
|
36443
36638
|
}
|
|
36444
36639
|
const runner = "addons/gdUnit4/bin/GdUnitCmdTool.gd";
|
|
36445
|
-
if (!
|
|
36640
|
+
if (!existsSync17(join22(project.value.path, runner))) {
|
|
36446
36641
|
return this.createErrorResponse(`gdUnit4 is not installed in this project: no ${runner}.`, [
|
|
36447
36642
|
"Install gdUnit4 under addons/gdUnit4, from https://github.com/godot-gdunit-labs/gdUnit4"
|
|
36448
36643
|
]);
|
|
@@ -36487,6 +36682,7 @@ class GodotServer {
|
|
|
36487
36682
|
run.process.stderr?.on("data", () => {
|
|
36488
36683
|
lastSaid = Date.now();
|
|
36489
36684
|
});
|
|
36685
|
+
const reporting = this.reportTestProgress(run.log);
|
|
36490
36686
|
let silentForMs = 0;
|
|
36491
36687
|
const timedOut = await new Promise((resolve) => {
|
|
36492
36688
|
const timer = setTimeout(() => {
|
|
@@ -36503,25 +36699,26 @@ class GodotServer {
|
|
|
36503
36699
|
resolve(false);
|
|
36504
36700
|
});
|
|
36505
36701
|
});
|
|
36702
|
+
clearInterval(reporting);
|
|
36506
36703
|
if (timedOut && run.process.pid !== undefined) {
|
|
36507
36704
|
await untilGone([run.process.pid]);
|
|
36508
36705
|
}
|
|
36509
36706
|
await run.settled;
|
|
36510
|
-
const reportsDir =
|
|
36707
|
+
const reportsDir = join22(project.value.path, ".godot", ...ours.split("/"));
|
|
36511
36708
|
let report = null;
|
|
36512
36709
|
let reportProblem = null;
|
|
36513
36710
|
try {
|
|
36514
|
-
const written =
|
|
36711
|
+
const written = existsSync17(reportsDir) ? readdirSync9(reportsDir).filter((name) => name.startsWith("report_")).sort((a, b) => Number(a.slice("report_".length)) - Number(b.slice("report_".length))) : [];
|
|
36515
36712
|
const newest = written.at(-1);
|
|
36516
36713
|
if (newest !== undefined) {
|
|
36517
|
-
report = parseJUnit(
|
|
36714
|
+
report = parseJUnit(readFileSync20(join22(reportsDir, newest, "results.xml"), "utf8"));
|
|
36518
36715
|
}
|
|
36519
36716
|
} catch (error) {
|
|
36520
36717
|
reportProblem = errorMessage(error);
|
|
36521
36718
|
} finally {
|
|
36522
36719
|
discard(reportsDir, userData);
|
|
36523
36720
|
try {
|
|
36524
|
-
rmdirSync2(
|
|
36721
|
+
rmdirSync2(dirname15(reportsDir));
|
|
36525
36722
|
} catch {}
|
|
36526
36723
|
}
|
|
36527
36724
|
const engineEntries = run.log.select({ severity: "warning", sinceLastCall: false, limit: 200 }).entries.map(aboveTheRunner);
|
|
@@ -36558,9 +36755,28 @@ class GodotServer {
|
|
|
36558
36755
|
printed: echoed.entries.map((entry) => entry.text),
|
|
36559
36756
|
...echoed.omitted > 0 ? { printedOmitted: echoed.omitted } : {}
|
|
36560
36757
|
};
|
|
36758
|
+
const sofar = report === null ? suitesPrinted(said) : null;
|
|
36561
36759
|
if (report === null) {
|
|
36562
|
-
const
|
|
36760
|
+
const finished = sofar?.finished ?? [];
|
|
36761
|
+
const running = sofar?.running ?? null;
|
|
36762
|
+
const failing = finished.filter((suite) => suite.errors + suite.failures > 0);
|
|
36763
|
+
const atTheEnd = {
|
|
36764
|
+
...nonEmpty({
|
|
36765
|
+
suitesFinished: finished.map((suite) => suite.path),
|
|
36766
|
+
suitesFailing: failing.map(({ path, errors, failures, failed }) => ({
|
|
36767
|
+
path,
|
|
36768
|
+
errors,
|
|
36769
|
+
failures,
|
|
36770
|
+
failed
|
|
36771
|
+
}))
|
|
36772
|
+
}),
|
|
36773
|
+
...running === null ? {} : { suiteRunning: running }
|
|
36774
|
+
};
|
|
36775
|
+
const said = nothingRan !== null ? `No tests ran: ${ranAs}.` : scriptErrors.length > 0 ? scriptErrorsNote(printedErrors, declaring) : reportProblem !== null ? `The test run's report could not be read (${reportProblem}); the engine's own verdict was ${ranAs}.` : `The test run wrote no report (${ranAs}).${finishedBeforeTheEnd(finished.length, failing.length, running)}`;
|
|
36563
36776
|
const note = missingNote === undefined ? said : `${said} ${missingNote}`;
|
|
36777
|
+
if (callSignal()?.aborted === true) {
|
|
36778
|
+
keepCutTestRun(project.value.path, { cutAt: new Date().toISOString(), paths: asked, ...atTheEnd });
|
|
36779
|
+
}
|
|
36564
36780
|
return {
|
|
36565
36781
|
content: [
|
|
36566
36782
|
{ type: "text", text: note },
|
|
@@ -36576,6 +36792,7 @@ class GodotServer {
|
|
|
36576
36792
|
...timedOut ? { timedOut, silentForMs } : {},
|
|
36577
36793
|
...missing.length > 0 ? { missing } : {},
|
|
36578
36794
|
...nonEmpty(foldedScriptErrors(scriptErrors)),
|
|
36795
|
+
...atTheEnd,
|
|
36579
36796
|
arguments: cmdArgs,
|
|
36580
36797
|
entries: forAnswer(withoutListed(printed, scriptErrors).slice(0, 60).map(aboveTheRunner)),
|
|
36581
36798
|
...printedLines,
|
|
@@ -36710,11 +36927,11 @@ class GodotServer {
|
|
|
36710
36927
|
const cached = cachedClasses(projectPath);
|
|
36711
36928
|
const contradicted = cached === null ? [] : contradictedDiagnostics(messages, cached, (resourcePath) => {
|
|
36712
36929
|
const contained = resolveWithinProject(projectPath, resourcePath);
|
|
36713
|
-
if (!contained.ok || !
|
|
36930
|
+
if (!contained.ok || !existsSync17(contained.absolutePath)) {
|
|
36714
36931
|
return null;
|
|
36715
36932
|
}
|
|
36716
36933
|
try {
|
|
36717
|
-
return
|
|
36934
|
+
return readFileSync20(contained.absolutePath, "utf8");
|
|
36718
36935
|
} catch {
|
|
36719
36936
|
return null;
|
|
36720
36937
|
}
|
|
@@ -36770,11 +36987,11 @@ class GodotServer {
|
|
|
36770
36987
|
}
|
|
36771
36988
|
sourceOfClass(projectPath, resourcePath) {
|
|
36772
36989
|
const contained = resolveWithinProject(projectPath, resourcePath);
|
|
36773
|
-
if (!contained.ok || !
|
|
36990
|
+
if (!contained.ok || !existsSync17(contained.absolutePath)) {
|
|
36774
36991
|
return null;
|
|
36775
36992
|
}
|
|
36776
36993
|
try {
|
|
36777
|
-
return
|
|
36994
|
+
return readFileSync20(contained.absolutePath, "utf8");
|
|
36778
36995
|
} catch {
|
|
36779
36996
|
return null;
|
|
36780
36997
|
}
|
|
@@ -37085,7 +37302,7 @@ class GodotServer {
|
|
|
37085
37302
|
return null;
|
|
37086
37303
|
}
|
|
37087
37304
|
try {
|
|
37088
|
-
return settingKeys(
|
|
37305
|
+
return settingKeys(readFileSync20(join22(projectPath, "project.godot"), "utf8"));
|
|
37089
37306
|
} catch {
|
|
37090
37307
|
return null;
|
|
37091
37308
|
}
|
|
@@ -37407,7 +37624,7 @@ class GodotServer {
|
|
|
37407
37624
|
}
|
|
37408
37625
|
async runtimeUp(projectPath, before, budget, withArgs) {
|
|
37409
37626
|
const budgetMs = budget.ms;
|
|
37410
|
-
if (!
|
|
37627
|
+
if (!existsSync17(join22(projectPath, RUNTIME_AUTOLOAD.path))) {
|
|
37411
37628
|
return runtimeVerdict(null, {
|
|
37412
37629
|
addon: false,
|
|
37413
37630
|
budgetMs,
|
|
@@ -37666,7 +37883,7 @@ class GodotServer {
|
|
|
37666
37883
|
return 0;
|
|
37667
37884
|
}
|
|
37668
37885
|
try {
|
|
37669
|
-
return reportAlreadyInTranscript(
|
|
37886
|
+
return reportAlreadyInTranscript(readFileSync20(game.transcript), readFileSync20(report));
|
|
37670
37887
|
} catch (error) {
|
|
37671
37888
|
this.logDebug(`Could not compare a picked-up run's report with its transcript: ${errorMessage(error)}`);
|
|
37672
37889
|
return 0;
|
|
@@ -38424,7 +38641,7 @@ class GodotServer {
|
|
|
38424
38641
|
notes.push(endedWithoutACode({
|
|
38425
38642
|
throughEditor: run.throughEditor,
|
|
38426
38643
|
announced: announced !== undefined || run.announcedPid !== undefined,
|
|
38427
|
-
couldAnnounce: run.projectPath !== null &&
|
|
38644
|
+
couldAnnounce: run.projectPath !== null && existsSync17(join22(run.projectPath, RUNTIME_AUTOLOAD.path)),
|
|
38428
38645
|
endedHere: typeof run.endedHere === "string",
|
|
38429
38646
|
pickedUp: run.pickedUp === true
|
|
38430
38647
|
}));
|
|
@@ -38985,7 +39202,7 @@ class GodotServer {
|
|
|
38985
39202
|
if (!scriptPath.ok) {
|
|
38986
39203
|
return this.createErrorResponse(scriptPath.reason);
|
|
38987
39204
|
}
|
|
38988
|
-
if (!
|
|
39205
|
+
if (!existsSync17(scriptPath.absolutePath)) {
|
|
38989
39206
|
return this.createErrorResponse(`Script file does not exist: ${scriptPath.absolutePath}`);
|
|
38990
39207
|
}
|
|
38991
39208
|
const asked = readNonEmptyString(args, "newScriptPath");
|
|
@@ -39273,7 +39490,7 @@ class GodotServer {
|
|
|
39273
39490
|
async ownRunOnItsWay(projectPath, budgetMs) {
|
|
39274
39491
|
const run = await this.currentRun();
|
|
39275
39492
|
const project = projectPath ?? run?.projectPath ?? null;
|
|
39276
|
-
if (run === null || project === null || run.announcedPid !== undefined || run.projectPath !== null && !isSameDirectory(run.projectPath, project) || !
|
|
39493
|
+
if (run === null || project === null || run.announcedPid !== undefined || run.projectPath !== null && !isSameDirectory(run.projectPath, project) || !existsSync17(join22(project, RUNTIME_AUTOLOAD.path)) || !await this.runStillGoing(run)) {
|
|
39277
39494
|
return null;
|
|
39278
39495
|
}
|
|
39279
39496
|
const already = runtimesAnnounced().running;
|
|
@@ -39357,7 +39574,7 @@ class GodotServer {
|
|
|
39357
39574
|
}
|
|
39358
39575
|
}
|
|
39359
39576
|
const screenshotDir = expectsScreenshot ? scratchDirectory("runtime-screenshot") : null;
|
|
39360
|
-
const screenshotPath = screenshotDir ?
|
|
39577
|
+
const screenshotPath = screenshotDir ? join22(screenshotDir, "capture.png") : null;
|
|
39361
39578
|
const staleNote = this.runtimeStaleNote(choice.endpoint);
|
|
39362
39579
|
const stale = staleNote === undefined ? {} : { addonIsStale: true, staleNote };
|
|
39363
39580
|
const refused = (message) => this.createErrorResponse(staleNote === undefined ? message : `${message}
|
|
@@ -39406,7 +39623,7 @@ ${staleNote}`);
|
|
|
39406
39623
|
|
|
39407
39624
|
${staleNote}`}`
|
|
39408
39625
|
},
|
|
39409
|
-
{ type: "image", data:
|
|
39626
|
+
{ type: "image", data: readFileSync20(screenshotPath).toString("base64"), mimeType: "image/png" }
|
|
39410
39627
|
]
|
|
39411
39628
|
};
|
|
39412
39629
|
} finally {
|