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/cli.js
CHANGED
|
@@ -7819,11 +7819,14 @@ function dictionary(entries) {
|
|
|
7819
7819
|
|
|
7820
7820
|
// src/call-signal.ts
|
|
7821
7821
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
7822
|
-
function withCallSignal(signal, answer) {
|
|
7823
|
-
return signal === undefined ? answer() : current.run(signal, answer);
|
|
7822
|
+
function withCallSignal(signal, answer, progress) {
|
|
7823
|
+
return signal === undefined && progress === undefined ? answer() : current.run({ signal, progress }, answer);
|
|
7824
7824
|
}
|
|
7825
7825
|
function callSignal() {
|
|
7826
|
-
return current.getStore();
|
|
7826
|
+
return current.getStore()?.signal;
|
|
7827
|
+
}
|
|
7828
|
+
function callProgress() {
|
|
7829
|
+
return current.getStore()?.progress;
|
|
7827
7830
|
}
|
|
7828
7831
|
var current;
|
|
7829
7832
|
var init_call_signal = __esm(() => {
|
|
@@ -17588,7 +17591,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
17588
17591
|
},
|
|
17589
17592
|
{
|
|
17590
17593
|
name: "project_test",
|
|
17591
|
-
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.",
|
|
17594
|
+
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.",
|
|
17592
17595
|
parameters: {
|
|
17593
17596
|
projectPath: PROJECT_PATH,
|
|
17594
17597
|
path: {
|
|
@@ -18413,7 +18416,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
18413
18416
|
requires: [],
|
|
18414
18417
|
operations: {
|
|
18415
18418
|
click: {
|
|
18416
|
-
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
|
|
18419
|
+
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`,
|
|
18417
18420
|
requires: []
|
|
18418
18421
|
},
|
|
18419
18422
|
choose: {
|
|
@@ -18586,6 +18589,106 @@ var init_headless_operations = __esm(() => {
|
|
|
18586
18589
|
});
|
|
18587
18590
|
});
|
|
18588
18591
|
|
|
18592
|
+
// src/tool-reference.ts
|
|
18593
|
+
function namedType(declared) {
|
|
18594
|
+
if (typeof declared === "string") {
|
|
18595
|
+
return declared;
|
|
18596
|
+
}
|
|
18597
|
+
if (Array.isArray(declared) && declared.every((kind) => typeof kind === "string")) {
|
|
18598
|
+
return declared.join(" or ");
|
|
18599
|
+
}
|
|
18600
|
+
return "any";
|
|
18601
|
+
}
|
|
18602
|
+
function argumentShape(schema) {
|
|
18603
|
+
const named = namedType(schema["type"]);
|
|
18604
|
+
const items = schema["items"];
|
|
18605
|
+
const held = typeof items === "object" && items !== null && !Array.isArray(items) ? namedType(items["type"]) : "any";
|
|
18606
|
+
return named.split(" or ").map((kind) => kind === "array" && held !== "any" ? `array of ${held}s` : kind).join(" or ");
|
|
18607
|
+
}
|
|
18608
|
+
function headlessSection() {
|
|
18609
|
+
const everyOp = new Map;
|
|
18610
|
+
for (const table of [HEADLESS_OPERATIONS, ENGINE_PASSES]) {
|
|
18611
|
+
for (const [tool, ops] of Object.entries(table)) {
|
|
18612
|
+
everyOp.set(tool, [...everyOp.get(tool) ?? [], ...Object.keys(ops)]);
|
|
18613
|
+
}
|
|
18614
|
+
}
|
|
18615
|
+
const byTool = [...everyOp].map(([tool, ops]) => `\`${tool}\` (${ops.join(", ")})`).sort();
|
|
18616
|
+
return [
|
|
18617
|
+
"## What a gate can call",
|
|
18618
|
+
"",
|
|
18619
|
+
"These answer from a short-lived headless engine: no editor window, no running game, and no",
|
|
18620
|
+
"process or port outliving the answer. They are the only calls an automatic check can make at",
|
|
18621
|
+
"all, because every other tool needs an editor somebody has open or a game somebody is running.",
|
|
18622
|
+
"",
|
|
18623
|
+
...byTool.map((line) => `- ${line}`),
|
|
18624
|
+
"",
|
|
18625
|
+
"Needing nothing is not the same as changing nothing, and a gate wants both. Most of these",
|
|
18626
|
+
"write to the project because that is what they are for: `script_edit create` writes a file,",
|
|
18627
|
+
"`project_settings set` rewrites `project.godot`, `project_import reimport` and `refresh_classes`",
|
|
18628
|
+
"write under `.godot`, and `project_import refresh_uids` writes a `.uid` beside each script that",
|
|
18629
|
+
"had none. The reading ops are the ones to gate on, and which is which is in each",
|
|
18630
|
+
"op's own summary below rather than assumed from this list.",
|
|
18631
|
+
"",
|
|
18632
|
+
"Everything else needs the editor (`editor_*`, `debug_*`, `scene_*`, `resource_*`) or a running",
|
|
18633
|
+
"game (`runtime_*`).",
|
|
18634
|
+
"",
|
|
18635
|
+
'One of these can be pointed the other way: `project_settings get` takes `from: "editor"` to ask',
|
|
18636
|
+
"the open editor instead of the file, which is a different reading and costs no engine start. A",
|
|
18637
|
+
"gate leaves it alone, because there is no editor on a build machine and the call is refused",
|
|
18638
|
+
"rather than answered from the file.",
|
|
18639
|
+
""
|
|
18640
|
+
];
|
|
18641
|
+
}
|
|
18642
|
+
function renderToolsMarkdown() {
|
|
18643
|
+
const lines = [
|
|
18644
|
+
"# Tools",
|
|
18645
|
+
"",
|
|
18646
|
+
`${TOOL_SPECS.length} tools, named \`domain_verb\`. A tool that does several related things takes an`,
|
|
18647
|
+
"`op`. An unknown op or argument is refused with the valid set listed. Generated from the server.",
|
|
18648
|
+
"",
|
|
18649
|
+
`${projectPathSentence()}: the \`runtime_*\` and \`debug_*\` ones pick between running games`,
|
|
18650
|
+
"instead, and the others answer about the editor on this server. The pick, when a call names",
|
|
18651
|
+
`neither \`projectPath\` nor \`pid\`: ${theGamePicked()}. An argument a tool does not`,
|
|
18652
|
+
"declare is refused rather than ignored. Answers are read from the engine after the change, not",
|
|
18653
|
+
"echoed from the request. Engine stderr comes back under `engine_messages`.",
|
|
18654
|
+
"",
|
|
18655
|
+
...headlessSection()
|
|
18656
|
+
];
|
|
18657
|
+
for (const tool of TOOL_SPECS) {
|
|
18658
|
+
lines.push(`## ${tool.name}`, "", tool.description, "");
|
|
18659
|
+
const operations = Object.entries(tool.operations ?? {});
|
|
18660
|
+
if (operations.length > 0) {
|
|
18661
|
+
for (const [op, spec] of operations) {
|
|
18662
|
+
const needs = [...tool.requires, ...spec.requires];
|
|
18663
|
+
const isDefault = tool.defaultOperation === op ? " (default)" : "";
|
|
18664
|
+
const wants = needs.length > 0 ? ` Needs: ${needs.map((name) => `\`${name}\``).join(", ")}.` : "";
|
|
18665
|
+
const spare = argumentsOf(tool, op).filter((name) => !needs.includes(name) && name !== "projectPath");
|
|
18666
|
+
const takes = spare.length > 0 ? ` Takes: ${spare.map((name) => `\`${name}\``).join(", ")}.` : "";
|
|
18667
|
+
lines.push(`- \`op: ${op}\`${isDefault}: ${spec.summary}.${wants}${takes}`);
|
|
18668
|
+
}
|
|
18669
|
+
lines.push("");
|
|
18670
|
+
} else if (tool.requires.length > 0) {
|
|
18671
|
+
lines.push(`Needs: ${tool.requires.map((name) => `\`${name}\``).join(", ")}.`, "");
|
|
18672
|
+
}
|
|
18673
|
+
const parameters = Object.entries(tool.parameters).filter(([name]) => name !== "projectPath");
|
|
18674
|
+
if (parameters.length > 0) {
|
|
18675
|
+
lines.push("Arguments:", "");
|
|
18676
|
+
for (const [name, schema] of parameters) {
|
|
18677
|
+
const type = argumentShape(schema);
|
|
18678
|
+
const note = typeof schema["description"] === "string" ? `: ${schema["description"]}` : "";
|
|
18679
|
+
lines.push(`- \`${name}\` (${type})${note}`);
|
|
18680
|
+
}
|
|
18681
|
+
lines.push("");
|
|
18682
|
+
}
|
|
18683
|
+
}
|
|
18684
|
+
return lines.join(`
|
|
18685
|
+
`);
|
|
18686
|
+
}
|
|
18687
|
+
var init_tool_reference = __esm(() => {
|
|
18688
|
+
init_headless_operations();
|
|
18689
|
+
init_tool_definitions();
|
|
18690
|
+
});
|
|
18691
|
+
|
|
18589
18692
|
// node_modules/zod/v3/helpers/util.js
|
|
18590
18693
|
var util, objectUtil, ZodParsedType, getParsedType = (data) => {
|
|
18591
18694
|
const t = typeof data;
|
|
@@ -34035,6 +34138,42 @@ var init_config_pin = __esm(() => {
|
|
|
34035
34138
|
PINNED = /\bgdharness@(\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?)\b/g;
|
|
34036
34139
|
});
|
|
34037
34140
|
|
|
34141
|
+
// src/cut-test-run.ts
|
|
34142
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync9, readFileSync as readFileSync16, rmSync as rmSync8, writeFileSync as writeFileSync11 } from "node:fs";
|
|
34143
|
+
import { dirname as dirname12, join as join18 } from "node:path";
|
|
34144
|
+
function keptAt(projectPath) {
|
|
34145
|
+
return join18(projectPath, ".godot", "gdharness-reports", "cut-run.json");
|
|
34146
|
+
}
|
|
34147
|
+
function keepCutTestRun(projectPath, run) {
|
|
34148
|
+
const path = keptAt(projectPath);
|
|
34149
|
+
try {
|
|
34150
|
+
mkdirSync9(dirname12(path), { recursive: true });
|
|
34151
|
+
writeFileSync11(path, `${JSON.stringify(run, null, 2)}
|
|
34152
|
+
`, "utf8");
|
|
34153
|
+
} catch {}
|
|
34154
|
+
}
|
|
34155
|
+
function takeCutTestRun(projectPath) {
|
|
34156
|
+
const path = keptAt(projectPath);
|
|
34157
|
+
if (!existsSync14(path)) {
|
|
34158
|
+
return null;
|
|
34159
|
+
}
|
|
34160
|
+
try {
|
|
34161
|
+
const run = JSON.parse(readFileSync16(path, "utf8"));
|
|
34162
|
+
rmSync8(path, { force: true });
|
|
34163
|
+
return typeof run.cutAt === "string" && Array.isArray(run.paths) && run.paths.every((path) => typeof path === "string") ? run : null;
|
|
34164
|
+
} catch {
|
|
34165
|
+
rmSync8(path, { force: true });
|
|
34166
|
+
return null;
|
|
34167
|
+
}
|
|
34168
|
+
}
|
|
34169
|
+
function cutTestRunNote(run) {
|
|
34170
|
+
const finished = run.suitesFinished?.length ?? 0;
|
|
34171
|
+
const failing = run.suitesFailing?.length ?? 0;
|
|
34172
|
+
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`}`;
|
|
34173
|
+
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.`;
|
|
34174
|
+
}
|
|
34175
|
+
var init_cut_test_run = () => {};
|
|
34176
|
+
|
|
34038
34177
|
// src/framing.ts
|
|
34039
34178
|
function frame(message) {
|
|
34040
34179
|
const body = Buffer.from(JSON.stringify(message), "utf8");
|
|
@@ -34823,25 +34962,25 @@ var init_desktop = () => {};
|
|
|
34823
34962
|
// src/editor-log.ts
|
|
34824
34963
|
import {
|
|
34825
34964
|
closeSync,
|
|
34826
|
-
existsSync as
|
|
34827
|
-
mkdirSync as
|
|
34965
|
+
existsSync as existsSync15,
|
|
34966
|
+
mkdirSync as mkdirSync10,
|
|
34828
34967
|
openSync,
|
|
34829
|
-
readFileSync as
|
|
34968
|
+
readFileSync as readFileSync17,
|
|
34830
34969
|
readSync,
|
|
34831
|
-
rmSync as
|
|
34970
|
+
rmSync as rmSync9,
|
|
34832
34971
|
statSync as statSync7,
|
|
34833
|
-
writeFileSync as
|
|
34972
|
+
writeFileSync as writeFileSync12
|
|
34834
34973
|
} from "node:fs";
|
|
34835
|
-
import { dirname as
|
|
34974
|
+
import { dirname as dirname13, join as join19 } from "node:path";
|
|
34836
34975
|
function editorLogPath(projectPath) {
|
|
34837
|
-
return
|
|
34976
|
+
return join19(projectPath, ".godot", "gdharness-editor.log");
|
|
34838
34977
|
}
|
|
34839
34978
|
function editorLogNotePath(projectPath) {
|
|
34840
|
-
return
|
|
34979
|
+
return join19(projectPath, ".godot", "gdharness-editor.json");
|
|
34841
34980
|
}
|
|
34842
34981
|
function readEditorLogNote(projectPath) {
|
|
34843
34982
|
try {
|
|
34844
|
-
const fields = asParams(JSON.parse(
|
|
34983
|
+
const fields = asParams(JSON.parse(readFileSync17(editorLogNotePath(projectPath), "utf8")));
|
|
34845
34984
|
const pid = readNumber(fields, "pid");
|
|
34846
34985
|
const startedAt = readString(fields, "startedAt");
|
|
34847
34986
|
return pid !== undefined && Number.isInteger(pid) && pid > 0 && startedAt !== undefined ? { pid, startedAt } : null;
|
|
@@ -34852,16 +34991,16 @@ function readEditorLogNote(projectPath) {
|
|
|
34852
34991
|
function clearEditorLog(projectPath) {
|
|
34853
34992
|
try {
|
|
34854
34993
|
const path = editorLogPath(projectPath);
|
|
34855
|
-
|
|
34856
|
-
|
|
34857
|
-
|
|
34994
|
+
mkdirSync10(dirname13(path), { recursive: true });
|
|
34995
|
+
rmSync9(path, { force: true });
|
|
34996
|
+
rmSync9(editorLogNotePath(projectPath), { force: true });
|
|
34858
34997
|
} catch {}
|
|
34859
34998
|
}
|
|
34860
34999
|
function writeEditorLogNote(projectPath, pid) {
|
|
34861
35000
|
try {
|
|
34862
35001
|
const path = editorLogNotePath(projectPath);
|
|
34863
|
-
|
|
34864
|
-
|
|
35002
|
+
mkdirSync10(dirname13(path), { recursive: true });
|
|
35003
|
+
writeFileSync12(path, JSON.stringify({ pid, startedAt: new Date().toISOString() }, null, 2), "utf8");
|
|
34865
35004
|
} catch {}
|
|
34866
35005
|
}
|
|
34867
35006
|
function theConsoleWasNotCaptured(why, projectPath) {
|
|
@@ -34882,7 +35021,7 @@ function editorConsole(projectPath, editorPid, openedByAServer) {
|
|
|
34882
35021
|
return { kind: "older server", notedPid: noted?.pid ?? null };
|
|
34883
35022
|
}
|
|
34884
35023
|
const path = editorLogPath(projectPath);
|
|
34885
|
-
if (!
|
|
35024
|
+
if (!existsSync15(path)) {
|
|
34886
35025
|
return { kind: "not written" };
|
|
34887
35026
|
}
|
|
34888
35027
|
const log = new GameLog;
|
|
@@ -34894,7 +35033,7 @@ function editorConsole(projectPath, editorPid, openedByAServer) {
|
|
|
34894
35033
|
function theFirstOf(path) {
|
|
34895
35034
|
const whole = statSync7(path).size;
|
|
34896
35035
|
if (whole <= MOST_CONSOLE_BYTES) {
|
|
34897
|
-
return { text:
|
|
35036
|
+
return { text: readFileSync17(path, "utf8") };
|
|
34898
35037
|
}
|
|
34899
35038
|
const handle = openSync(path, "r");
|
|
34900
35039
|
try {
|
|
@@ -38841,7 +38980,51 @@ function orphansPrinted(printed) {
|
|
|
38841
38980
|
suites
|
|
38842
38981
|
};
|
|
38843
38982
|
}
|
|
38844
|
-
|
|
38983
|
+
function finishesASuite(line) {
|
|
38984
|
+
return SUITE_COUNTS.test(line) && !line.includes(OVERALL);
|
|
38985
|
+
}
|
|
38986
|
+
function suitesPrinted(printed) {
|
|
38987
|
+
const finished = [];
|
|
38988
|
+
let running = null;
|
|
38989
|
+
let failed = [];
|
|
38990
|
+
for (const line of printed) {
|
|
38991
|
+
const started = RUNNING_SUITE.exec(line);
|
|
38992
|
+
if (started?.[1] !== undefined) {
|
|
38993
|
+
running = started[1];
|
|
38994
|
+
failed = [];
|
|
38995
|
+
continue;
|
|
38996
|
+
}
|
|
38997
|
+
if (running === null) {
|
|
38998
|
+
continue;
|
|
38999
|
+
}
|
|
39000
|
+
const failing = CASE_FAILED.exec(line)?.[1];
|
|
39001
|
+
if (failing !== undefined) {
|
|
39002
|
+
failed.push(failing);
|
|
39003
|
+
continue;
|
|
39004
|
+
}
|
|
39005
|
+
const counted = finishesASuite(line) ? SUITE_COUNTS.exec(line) : null;
|
|
39006
|
+
if (counted !== null) {
|
|
39007
|
+
finished.push({
|
|
39008
|
+
path: running,
|
|
39009
|
+
cases: Number(counted[1]),
|
|
39010
|
+
errors: Number(counted[2]),
|
|
39011
|
+
failures: Number(counted[3]),
|
|
39012
|
+
failed
|
|
39013
|
+
});
|
|
39014
|
+
running = null;
|
|
39015
|
+
}
|
|
39016
|
+
}
|
|
39017
|
+
return { finished, running };
|
|
39018
|
+
}
|
|
39019
|
+
function testProgressMessage(printed, elapsedMs) {
|
|
39020
|
+
const { finished, running } = suitesPrinted(printed);
|
|
39021
|
+
const failing = finished.filter((suite) => suite.errors + suite.failures > 0).length;
|
|
39022
|
+
const seconds = Math.floor(elapsedMs / 1000);
|
|
39023
|
+
const took = seconds < 60 ? `${seconds}s` : `${Math.floor(seconds / 60)}m ${seconds % 60}s`;
|
|
39024
|
+
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`}`;
|
|
39025
|
+
return `${done}${running === null ? "" : `; running ${running}`}; ${took} in.`;
|
|
39026
|
+
}
|
|
39027
|
+
var MalformedReportError, ENTITIES, ATTRIBUTE, HOOK_FAILURE, NOTHING_RAN, NO_SUCH_PATH, DISCOVERY_FAILED = "Script errors were detected during test discovery!", ESCAPE, LOCATION = `'[^'\\n]*' in \\S+:\\d+`, IGNORING_CASE = " \\(ignoring case\\)", PRINTED_ACTUAL, QUOTED_EQUALITY, COLOUR2, SPELLED, BOTH_SIDES_ALIKE_NOTE = "[gdharness: the expected and the actual printed alike because gdUnit4 drops the marks that tell them apart when it writes its report, and the runner printed no copy of this one, so neither side above is the value the assertion found.]", VALUE_NOT_RECOVERED_NOTE = '[gdharness: the value after "but was" may not be the one the assertion found. gdUnit4 writes a failing string into its report as a character diff against the expected one with the marks dropped, which merges the two, and the runner printed no copy of this one to read the value back from.]', RENDERED_AWAY_NOTE = '[gdharness: the two sides print alike, so what tells them apart is something gdUnit4 does not print. The usual cause is BBCode in the expected string, which gdUnit4 renders as formatting instead of showing; the value after "but was" is the one the assertion found.]', MASKED_BRACKET = "[lb]", PRINTED_DICTIONARY, RUNNING_SUITE, ORPHANS, OVERALL = "Overall Summary", SUITE_COUNTS, CASE_FAILED;
|
|
38845
39028
|
var init_junit = __esm(() => {
|
|
38846
39029
|
MalformedReportError = class MalformedReportError extends Error {
|
|
38847
39030
|
constructor(message) {
|
|
@@ -38873,6 +39056,8 @@ var init_junit = __esm(() => {
|
|
|
38873
39056
|
PRINTED_DICTIONARY = /^\{\n\t[\s\S]*\n {2}\}$/;
|
|
38874
39057
|
RUNNING_SUITE = /Run Test Suite:\s*(res:\/\/.+?\.(?:gd|cs))(?=\s|$)/;
|
|
38875
39058
|
ORPHANS = /(\d+)\s+orphans/;
|
|
39059
|
+
SUITE_COUNTS = /(\d+)\s+test cases\s*\|\s*(\d+)\s+errors\s*\|\s*(\d+)\s+failures/;
|
|
39060
|
+
CASE_FAILED = /^\s*res:\/\/.+?\.(?:gd|cs) > (.+?) (?:FAILED|ERROR)(?:\s|$)/;
|
|
38876
39061
|
});
|
|
38877
39062
|
|
|
38878
39063
|
// src/keeper-channel.ts
|
|
@@ -38918,10 +39103,10 @@ function askTheKeeperToStop(address, withinMs) {
|
|
|
38918
39103
|
var init_keeper_channel = () => {};
|
|
38919
39104
|
|
|
38920
39105
|
// src/lsp_client.ts
|
|
38921
|
-
import { existsSync as
|
|
39106
|
+
import { existsSync as existsSync16, realpathSync as realpathSync4 } from "node:fs";
|
|
38922
39107
|
import { readFile, realpath } from "node:fs/promises";
|
|
38923
39108
|
import { createConnection as createConnection3 } from "node:net";
|
|
38924
|
-
import { dirname as
|
|
39109
|
+
import { dirname as dirname14, join as join20, resolve as resolve5 } from "node:path";
|
|
38925
39110
|
import { fileURLToPath as fileURLToPath3, pathToFileURL } from "node:url";
|
|
38926
39111
|
function diagnosticsKey(uri) {
|
|
38927
39112
|
let decoded;
|
|
@@ -39053,7 +39238,7 @@ class GodotLSPClient {
|
|
|
39053
39238
|
if (this.initialized) {
|
|
39054
39239
|
return;
|
|
39055
39240
|
}
|
|
39056
|
-
const rootPath = this.rootPath ??
|
|
39241
|
+
const rootPath = this.rootPath ?? dirname14(resolve5(filePath));
|
|
39057
39242
|
await this.initialize(rootPath);
|
|
39058
39243
|
}
|
|
39059
39244
|
async sendRequest(method, params, timeoutMs = this.timeouts.requestMs) {
|
|
@@ -39464,7 +39649,7 @@ async function resolveLSPPaths(projectPathValue, scriptPathValue) {
|
|
|
39464
39649
|
} catch {
|
|
39465
39650
|
throw new ArgumentRefusal(`Project path does not exist: ${requestedProjectPath}`);
|
|
39466
39651
|
}
|
|
39467
|
-
if (!
|
|
39652
|
+
if (!existsSync16(join20(projectPath, "project.godot"))) {
|
|
39468
39653
|
throw new ArgumentRefusal(`Not a Godot project: ${projectPath}. Point projectPath at the directory holding project.godot.`);
|
|
39469
39654
|
}
|
|
39470
39655
|
const contained = resolveWithinProject(projectPath, scriptPathValue);
|
|
@@ -39673,13 +39858,13 @@ var init_process_time = __esm(() => {
|
|
|
39673
39858
|
});
|
|
39674
39859
|
|
|
39675
39860
|
// src/project-scan.ts
|
|
39676
|
-
import { existsSync as
|
|
39677
|
-
import { join as
|
|
39861
|
+
import { existsSync as existsSync17, readdirSync as readdirSync7, readFileSync as readFileSync18 } from "node:fs";
|
|
39862
|
+
import { join as join21 } from "node:path";
|
|
39678
39863
|
function skipped(name) {
|
|
39679
39864
|
return name.startsWith(".") || SKIPPED.has(name);
|
|
39680
39865
|
}
|
|
39681
39866
|
function steppedOver(directory) {
|
|
39682
|
-
return
|
|
39867
|
+
return existsSync17(join21(directory, ".gdignore"));
|
|
39683
39868
|
}
|
|
39684
39869
|
function projectStructure(projectPath) {
|
|
39685
39870
|
const structure = { scenes: 0, scripts: 0, assets: 0, other: 0 };
|
|
@@ -39692,7 +39877,7 @@ function projectStructure(projectPath) {
|
|
|
39692
39877
|
continue;
|
|
39693
39878
|
}
|
|
39694
39879
|
if (entry.isDirectory()) {
|
|
39695
|
-
visit(
|
|
39880
|
+
visit(join21(directory, entry.name));
|
|
39696
39881
|
} else if (entry.isFile()) {
|
|
39697
39882
|
const extension = entry.name.split(".").pop()?.toLowerCase() ?? "";
|
|
39698
39883
|
if (SCENE_EXTENSIONS.has(extension)) {
|
|
@@ -39722,14 +39907,14 @@ function scriptsWithoutUid(projectPath) {
|
|
|
39722
39907
|
}
|
|
39723
39908
|
const spelled = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
|
|
39724
39909
|
if (entry.isDirectory()) {
|
|
39725
|
-
visit(
|
|
39910
|
+
visit(join21(directory, entry.name), spelled);
|
|
39726
39911
|
continue;
|
|
39727
39912
|
}
|
|
39728
39913
|
if (!entry.isFile()) {
|
|
39729
39914
|
continue;
|
|
39730
39915
|
}
|
|
39731
39916
|
const extension = entry.name.split(".").pop()?.toLowerCase() ?? "";
|
|
39732
|
-
if (CARRIES_A_UID.has(extension) && !
|
|
39917
|
+
if (CARRIES_A_UID.has(extension) && !existsSync17(join21(directory, `${entry.name}.uid`))) {
|
|
39733
39918
|
found.push(spelled);
|
|
39734
39919
|
}
|
|
39735
39920
|
}
|
|
@@ -39750,7 +39935,7 @@ function projectTexts(projectPath) {
|
|
|
39750
39935
|
if (skipped(entry.name)) {
|
|
39751
39936
|
continue;
|
|
39752
39937
|
}
|
|
39753
|
-
const absolute =
|
|
39938
|
+
const absolute = join21(directory, entry.name);
|
|
39754
39939
|
const spelled = `${prefix}${entry.name}`;
|
|
39755
39940
|
if (entry.isDirectory()) {
|
|
39756
39941
|
visit(absolute, `${spelled}/`);
|
|
@@ -39760,7 +39945,7 @@ function projectTexts(projectPath) {
|
|
|
39760
39945
|
if (!entry.isFile() || BINARY_EXTENSIONS.has(extension)) {
|
|
39761
39946
|
continue;
|
|
39762
39947
|
}
|
|
39763
|
-
const bytes =
|
|
39948
|
+
const bytes = readFileSync18(absolute);
|
|
39764
39949
|
if (!isBinary(bytes)) {
|
|
39765
39950
|
found.push({ path: `res://${spelled}`, absolute, text: bytes.toString("utf8") });
|
|
39766
39951
|
}
|
|
@@ -39777,7 +39962,7 @@ function ignoredTexts(projectPath) {
|
|
|
39777
39962
|
if (skipped(entry.name)) {
|
|
39778
39963
|
continue;
|
|
39779
39964
|
}
|
|
39780
|
-
const absolute =
|
|
39965
|
+
const absolute = join21(directory, entry.name);
|
|
39781
39966
|
const spelled = `${prefix}${entry.name}`;
|
|
39782
39967
|
if (entry.isDirectory()) {
|
|
39783
39968
|
visit(absolute, `${spelled}/`, under);
|
|
@@ -39787,7 +39972,7 @@ function ignoredTexts(projectPath) {
|
|
|
39787
39972
|
if (!under || !entry.isFile() || BINARY_EXTENSIONS.has(extension) || entry.name === ".gdignore") {
|
|
39788
39973
|
continue;
|
|
39789
39974
|
}
|
|
39790
|
-
const bytes =
|
|
39975
|
+
const bytes = readFileSync18(absolute);
|
|
39791
39976
|
if (!isBinary(bytes)) {
|
|
39792
39977
|
found.push({ path: `res://${spelled}`, absolute, text: bytes.toString("utf8") });
|
|
39793
39978
|
}
|
|
@@ -39817,7 +40002,7 @@ function searchProject(projectPath, options) {
|
|
|
39817
40002
|
if (skipped(entry.name)) {
|
|
39818
40003
|
continue;
|
|
39819
40004
|
}
|
|
39820
|
-
const entryPath =
|
|
40005
|
+
const entryPath = join21(directory, entry.name);
|
|
39821
40006
|
if (entry.isDirectory()) {
|
|
39822
40007
|
visit(entryPath);
|
|
39823
40008
|
continue;
|
|
@@ -39826,7 +40011,7 @@ function searchProject(projectPath, options) {
|
|
|
39826
40011
|
if (!entry.isFile() || extensions !== null && !extensions.has(extension)) {
|
|
39827
40012
|
continue;
|
|
39828
40013
|
}
|
|
39829
|
-
const bytes =
|
|
40014
|
+
const bytes = readFileSync18(entryPath);
|
|
39830
40015
|
if (extensions === null && isBinary(bytes)) {
|
|
39831
40016
|
continue;
|
|
39832
40017
|
}
|
|
@@ -39903,8 +40088,8 @@ var init_project_scan = __esm(() => {
|
|
|
39903
40088
|
});
|
|
39904
40089
|
|
|
39905
40090
|
// src/rename.ts
|
|
39906
|
-
import { existsSync as
|
|
39907
|
-
import { dirname as
|
|
40091
|
+
import { existsSync as existsSync18, mkdirSync as mkdirSync11, readFileSync as readFileSync19, renameSync, unlinkSync as unlinkSync2, writeFileSync as writeFileSync13 } from "node:fs";
|
|
40092
|
+
import { dirname as dirname15, posix } from "node:path";
|
|
39908
40093
|
|
|
39909
40094
|
class Script {
|
|
39910
40095
|
path;
|
|
@@ -40605,25 +40790,25 @@ function rewritesOf(plan, texts, move) {
|
|
|
40605
40790
|
}
|
|
40606
40791
|
function applyRewrites(rewrites) {
|
|
40607
40792
|
for (const rewrite of rewrites) {
|
|
40608
|
-
const now =
|
|
40793
|
+
const now = readFileSync19(rewrite.absolute, "utf8");
|
|
40609
40794
|
if (now !== rewrite.before) {
|
|
40610
40795
|
throw new Error(`${rewrite.path} changed on disk while the rename was being worked out; nothing was written`);
|
|
40611
40796
|
}
|
|
40612
|
-
if (rewrite.writeTo !== rewrite.absolute &&
|
|
40797
|
+
if (rewrite.writeTo !== rewrite.absolute && existsSync18(rewrite.writeTo)) {
|
|
40613
40798
|
throw new Error(`${rewrite.writeTo} already exists; nothing was written`);
|
|
40614
40799
|
}
|
|
40615
40800
|
}
|
|
40616
40801
|
const done = [];
|
|
40617
40802
|
try {
|
|
40618
40803
|
for (const rewrite of rewrites) {
|
|
40619
|
-
|
|
40804
|
+
mkdirSync11(dirname15(rewrite.writeTo), { recursive: true });
|
|
40620
40805
|
const staging = `${rewrite.writeTo}.gdharness-rename`;
|
|
40621
|
-
|
|
40806
|
+
writeFileSync13(staging, rewrite.after, "utf8");
|
|
40622
40807
|
renameSync(staging, rewrite.writeTo);
|
|
40623
40808
|
let movedUid = false;
|
|
40624
40809
|
if (rewrite.writeTo !== rewrite.absolute) {
|
|
40625
40810
|
unlinkSync2(rewrite.absolute);
|
|
40626
|
-
if (
|
|
40811
|
+
if (existsSync18(`${rewrite.absolute}.uid`)) {
|
|
40627
40812
|
renameSync(`${rewrite.absolute}.uid`, `${rewrite.writeTo}.uid`);
|
|
40628
40813
|
movedUid = true;
|
|
40629
40814
|
}
|
|
@@ -40633,7 +40818,7 @@ function applyRewrites(rewrites) {
|
|
|
40633
40818
|
} catch (error) {
|
|
40634
40819
|
for (const { rewrite, movedUid } of done.reverse()) {
|
|
40635
40820
|
try {
|
|
40636
|
-
|
|
40821
|
+
writeFileSync13(rewrite.absolute, rewrite.before, "utf8");
|
|
40637
40822
|
if (rewrite.writeTo !== rewrite.absolute) {
|
|
40638
40823
|
unlinkSync2(rewrite.writeTo);
|
|
40639
40824
|
if (movedUid) {
|
|
@@ -40669,8 +40854,8 @@ var init_rename = __esm(() => {
|
|
|
40669
40854
|
});
|
|
40670
40855
|
|
|
40671
40856
|
// src/rename-symbol.ts
|
|
40672
|
-
import { existsSync as
|
|
40673
|
-
import { basename as basename5, dirname as
|
|
40857
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync12, readdirSync as readdirSync8, realpathSync as realpathSync5, rmSync as rmSync10, writeFileSync as writeFileSync14 } from "node:fs";
|
|
40858
|
+
import { basename as basename5, dirname as dirname16, join as join22, relative as relative4, resolve as resolve6 } from "node:path";
|
|
40674
40859
|
function resourcePathOf(projectPath, absolute) {
|
|
40675
40860
|
const inside = relative4(onDiskSpelling(projectPath), onDiskSpelling(absolute));
|
|
40676
40861
|
if (inside === "" || inside.startsWith("..") || /^[A-Za-z]:/.test(inside)) {
|
|
@@ -40683,9 +40868,9 @@ function onDiskSpelling(path) {
|
|
|
40683
40868
|
let at = resolve6(path);
|
|
40684
40869
|
for (;; ) {
|
|
40685
40870
|
try {
|
|
40686
|
-
return
|
|
40871
|
+
return join22(realpathSync5.native(at), ...missing.reverse());
|
|
40687
40872
|
} catch {
|
|
40688
|
-
const parent =
|
|
40873
|
+
const parent = dirname16(at);
|
|
40689
40874
|
if (parent === at) {
|
|
40690
40875
|
return resolve6(path);
|
|
40691
40876
|
}
|
|
@@ -40736,7 +40921,7 @@ async function renameSymbol(request, services) {
|
|
|
40736
40921
|
reason: `newScriptPath must be a .gd path inside the project, and ${request.newScriptPath} is not.`
|
|
40737
40922
|
};
|
|
40738
40923
|
}
|
|
40739
|
-
if (
|
|
40924
|
+
if (existsSync19(request.newScriptPath)) {
|
|
40740
40925
|
return { ok: false, reason: `${moveTo} already exists, so the script cannot be moved there.` };
|
|
40741
40926
|
}
|
|
40742
40927
|
}
|
|
@@ -40912,7 +41097,7 @@ async function renameSymbol(request, services) {
|
|
|
40912
41097
|
scripts,
|
|
40913
41098
|
from: declaringPath,
|
|
40914
41099
|
to: moveTo,
|
|
40915
|
-
exists: (path) =>
|
|
41100
|
+
exists: (path) => existsSync19(`${projectPath}/${path.slice("res://".length)}`)
|
|
40916
41101
|
}, plan);
|
|
40917
41102
|
}
|
|
40918
41103
|
plan = mentionIgnored(plan, ignoredTexts(projectPath), symbol, moveTo === null ? null : declaringPath);
|
|
@@ -41111,15 +41296,15 @@ ${mention.kind}`;
|
|
|
41111
41296
|
};
|
|
41112
41297
|
}
|
|
41113
41298
|
function writeLeftAloneDetail(projectPath, symbol, detail) {
|
|
41114
|
-
const directory =
|
|
41115
|
-
|
|
41299
|
+
const directory = join22(projectPath, ".godot", "gdharness-renames");
|
|
41300
|
+
mkdirSync12(directory, { recursive: true });
|
|
41116
41301
|
detailWrites += 1;
|
|
41117
|
-
const path =
|
|
41118
|
-
|
|
41302
|
+
const path = join22(directory, `${String(Date.now())}-${String(detailWrites).padStart(6, "0")}-${String(process.pid)}-${symbol}.json`);
|
|
41303
|
+
writeFileSync14(path, `${JSON.stringify(detail, null, 2)}
|
|
41119
41304
|
`, "utf8");
|
|
41120
41305
|
const kept = readdirSync8(directory).filter((name) => name.endsWith(".json")).sort().reverse();
|
|
41121
41306
|
for (const old of kept.slice(10)) {
|
|
41122
|
-
|
|
41307
|
+
rmSync10(join22(directory, old), { force: true });
|
|
41123
41308
|
}
|
|
41124
41309
|
return path;
|
|
41125
41310
|
}
|
|
@@ -41159,16 +41344,16 @@ var init_rename_symbol = __esm(() => {
|
|
|
41159
41344
|
});
|
|
41160
41345
|
|
|
41161
41346
|
// src/restart-note.ts
|
|
41162
|
-
import { mkdirSync as
|
|
41163
|
-
import { dirname as
|
|
41347
|
+
import { mkdirSync as mkdirSync13, readFileSync as readFileSync20, rmSync as rmSync11, writeFileSync as writeFileSync15 } from "node:fs";
|
|
41348
|
+
import { dirname as dirname17, join as join23 } from "node:path";
|
|
41164
41349
|
function restartNotePath(projectPath) {
|
|
41165
|
-
return
|
|
41350
|
+
return join23(projectPath, ".godot", "gdharness-restart.json");
|
|
41166
41351
|
}
|
|
41167
41352
|
function noteRestartBegun(note) {
|
|
41168
41353
|
const path = restartNotePath(note.projectPath);
|
|
41169
41354
|
try {
|
|
41170
|
-
|
|
41171
|
-
|
|
41355
|
+
mkdirSync13(dirname17(path), { recursive: true });
|
|
41356
|
+
writeFileSync15(path, `${JSON.stringify(note, null, 2)}
|
|
41172
41357
|
`, "utf8");
|
|
41173
41358
|
} catch (error) {
|
|
41174
41359
|
console.error(`[SERVER] Could not record the restart at ${path}: ${errorMessage(error)}`);
|
|
@@ -41176,7 +41361,7 @@ function noteRestartBegun(note) {
|
|
|
41176
41361
|
}
|
|
41177
41362
|
function restartOwed(projectPath) {
|
|
41178
41363
|
try {
|
|
41179
|
-
const parsed = JSON.parse(
|
|
41364
|
+
const parsed = JSON.parse(readFileSync20(restartNotePath(projectPath), "utf8"));
|
|
41180
41365
|
if (typeof parsed !== "object" || parsed === null) {
|
|
41181
41366
|
return null;
|
|
41182
41367
|
}
|
|
@@ -41191,7 +41376,7 @@ function restartOwed(projectPath) {
|
|
|
41191
41376
|
}
|
|
41192
41377
|
function restartSettled(projectPath) {
|
|
41193
41378
|
try {
|
|
41194
|
-
|
|
41379
|
+
rmSync11(restartNotePath(projectPath), { force: true });
|
|
41195
41380
|
} catch (error) {
|
|
41196
41381
|
console.error(`[SERVER] Could not clear the restart note: ${errorMessage(error)}`);
|
|
41197
41382
|
}
|
|
@@ -41204,28 +41389,28 @@ var init_restart_note = __esm(() => {
|
|
|
41204
41389
|
import { execFile as execFile4 } from "node:child_process";
|
|
41205
41390
|
import { createHash as createHash4 } from "node:crypto";
|
|
41206
41391
|
import {
|
|
41207
|
-
mkdirSync as
|
|
41392
|
+
mkdirSync as mkdirSync14,
|
|
41208
41393
|
openSync as openSync2,
|
|
41209
41394
|
readdirSync as readdirSync9,
|
|
41210
|
-
readFileSync as
|
|
41395
|
+
readFileSync as readFileSync21,
|
|
41211
41396
|
realpathSync as realpathSync6,
|
|
41212
|
-
rmSync as
|
|
41397
|
+
rmSync as rmSync12,
|
|
41213
41398
|
statSync as statSync8,
|
|
41214
|
-
writeFileSync as
|
|
41399
|
+
writeFileSync as writeFileSync16
|
|
41215
41400
|
} from "node:fs";
|
|
41216
|
-
import { basename as basename6, dirname as
|
|
41401
|
+
import { basename as basename6, dirname as dirname18, join as join24, resolve as resolve7 } from "node:path";
|
|
41217
41402
|
import { promisify as promisify4 } from "node:util";
|
|
41218
41403
|
function runsDirectory() {
|
|
41219
|
-
return
|
|
41404
|
+
return join24(runtimeDirectory(), "runs");
|
|
41220
41405
|
}
|
|
41221
41406
|
function realAsFarAsItExists(path) {
|
|
41222
41407
|
const missing = [];
|
|
41223
41408
|
let at = path;
|
|
41224
41409
|
for (;; ) {
|
|
41225
41410
|
try {
|
|
41226
|
-
return
|
|
41411
|
+
return join24(realpathSync6.native(at), ...missing);
|
|
41227
41412
|
} catch {
|
|
41228
|
-
const parent =
|
|
41413
|
+
const parent = dirname18(at);
|
|
41229
41414
|
if (parent === at) {
|
|
41230
41415
|
return path;
|
|
41231
41416
|
}
|
|
@@ -41240,26 +41425,26 @@ function projectKey(projectPath) {
|
|
|
41240
41425
|
return createHash4("sha256").update(folded.replaceAll("\\", "/")).digest("hex").slice(0, 16);
|
|
41241
41426
|
}
|
|
41242
41427
|
function runRecordPath(projectPath, directory = runtimeDirectory()) {
|
|
41243
|
-
return
|
|
41428
|
+
return join24(directory, "runs", `note-${projectKey(projectPath)}.json`);
|
|
41244
41429
|
}
|
|
41245
41430
|
function openTranscript(startedAt) {
|
|
41246
|
-
|
|
41247
|
-
const path =
|
|
41431
|
+
mkdirSync14(runsDirectory(), { recursive: true });
|
|
41432
|
+
const path = join24(runsDirectory(), `run-${startedAt}.log`);
|
|
41248
41433
|
return { path, fd: openSync2(path, "a") };
|
|
41249
41434
|
}
|
|
41250
41435
|
function noteIsAbout(note, playing) {
|
|
41251
41436
|
return note.play !== undefined && playing !== null && note.play.play === playing.play && note.play.editorPid === playing.editorPid;
|
|
41252
41437
|
}
|
|
41253
41438
|
function editorNotePath(projectPath, directory = runtimeDirectory()) {
|
|
41254
|
-
return
|
|
41439
|
+
return join24(directory, "runs", `editor-${projectKey(projectPath)}.json`);
|
|
41255
41440
|
}
|
|
41256
41441
|
function writeEditorRunNote(note) {
|
|
41257
|
-
|
|
41258
|
-
|
|
41442
|
+
mkdirSync14(runsDirectory(), { recursive: true });
|
|
41443
|
+
writeFileSync16(editorNotePath(note.projectPath), JSON.stringify(note, null, 2), "utf8");
|
|
41259
41444
|
}
|
|
41260
41445
|
function readEditorRunNote(projectPath) {
|
|
41261
41446
|
for (const directory of runtimeDirectories()) {
|
|
41262
|
-
const note = editorNoteAt(editorNotePath(projectPath, directory)) ?? editorNoteAt(
|
|
41447
|
+
const note = editorNoteAt(editorNotePath(projectPath, directory)) ?? editorNoteAt(join24(directory, "runs", "editor-run.json"));
|
|
41263
41448
|
if (note !== null && isSameDirectory(note.projectPath, projectPath)) {
|
|
41264
41449
|
return note;
|
|
41265
41450
|
}
|
|
@@ -41269,7 +41454,7 @@ function readEditorRunNote(projectPath) {
|
|
|
41269
41454
|
function editorNoteAt(path) {
|
|
41270
41455
|
let parsed;
|
|
41271
41456
|
try {
|
|
41272
|
-
parsed = JSON.parse(
|
|
41457
|
+
parsed = JSON.parse(readFileSync21(path, "utf8"));
|
|
41273
41458
|
} catch {
|
|
41274
41459
|
return null;
|
|
41275
41460
|
}
|
|
@@ -41293,7 +41478,7 @@ function readRunRecord(projectPath) {
|
|
|
41293
41478
|
if (own !== null) {
|
|
41294
41479
|
return own;
|
|
41295
41480
|
}
|
|
41296
|
-
const shared = recordAt(
|
|
41481
|
+
const shared = recordAt(join24(directory, "runs", SHARED_NOTE));
|
|
41297
41482
|
if (shared !== null && shared.projectPath !== "" && isSameDirectory(shared.projectPath, projectPath)) {
|
|
41298
41483
|
return shared;
|
|
41299
41484
|
}
|
|
@@ -41305,13 +41490,13 @@ function everyRunRecord() {
|
|
|
41305
41490
|
for (const directory of runtimeDirectories()) {
|
|
41306
41491
|
let entries;
|
|
41307
41492
|
try {
|
|
41308
|
-
entries = readdirSync9(
|
|
41493
|
+
entries = readdirSync9(join24(directory, "runs"));
|
|
41309
41494
|
} catch {
|
|
41310
41495
|
continue;
|
|
41311
41496
|
}
|
|
41312
41497
|
for (const entry of entries) {
|
|
41313
41498
|
if (entry === SHARED_NOTE || entry.startsWith("note-") && entry.endsWith(".json")) {
|
|
41314
|
-
const record = recordAt(
|
|
41499
|
+
const record = recordAt(join24(directory, "runs", entry));
|
|
41315
41500
|
if (record !== null) {
|
|
41316
41501
|
found.push(record);
|
|
41317
41502
|
}
|
|
@@ -41323,7 +41508,7 @@ function everyRunRecord() {
|
|
|
41323
41508
|
function recordAt(path) {
|
|
41324
41509
|
let parsed;
|
|
41325
41510
|
try {
|
|
41326
|
-
parsed = JSON.parse(
|
|
41511
|
+
parsed = JSON.parse(readFileSync21(path, "utf8"));
|
|
41327
41512
|
} catch {
|
|
41328
41513
|
return null;
|
|
41329
41514
|
}
|
|
@@ -41374,7 +41559,7 @@ async function runningAs(pid) {
|
|
|
41374
41559
|
const began = await startedAt(pid, null);
|
|
41375
41560
|
const when = began === null ? {} : { startedAt: began };
|
|
41376
41561
|
if (process.platform === "linux") {
|
|
41377
|
-
const raw =
|
|
41562
|
+
const raw = readFileSync21(`/proc/${pid}/cmdline`, "utf8").replaceAll("\x00", " ").trim();
|
|
41378
41563
|
return raw === "" ? null : { kind: "commandLine", text: raw, ...when };
|
|
41379
41564
|
}
|
|
41380
41565
|
const args = (await execFileAsync("ps", ["-p", String(pid), "-o", "args="], { encoding: "utf8", timeout: 15000 })).stdout.trim();
|
|
@@ -41519,12 +41704,12 @@ function judgeAnnouncedGame(running, projectPath, announcedAt) {
|
|
|
41519
41704
|
}
|
|
41520
41705
|
function clearRunRecord(projectPath) {
|
|
41521
41706
|
for (const directory of runtimeDirectories()) {
|
|
41522
|
-
const shared =
|
|
41707
|
+
const shared = join24(directory, "runs", SHARED_NOTE);
|
|
41523
41708
|
const sharedRecord = recordAt(shared);
|
|
41524
41709
|
const ours = sharedRecord !== null && sharedRecord.projectPath !== "" && isSameDirectory(sharedRecord.projectPath, projectPath);
|
|
41525
41710
|
for (const path of [runRecordPath(projectPath, directory), ...ours ? [shared] : []]) {
|
|
41526
41711
|
try {
|
|
41527
|
-
|
|
41712
|
+
rmSync12(path, { force: true });
|
|
41528
41713
|
} catch {}
|
|
41529
41714
|
}
|
|
41530
41715
|
}
|
|
@@ -41549,12 +41734,12 @@ function sweepTranscripts(keepMs = 24 * 60 * 60 * 1000, now = Date.now()) {
|
|
|
41549
41734
|
if (!entry.startsWith("run-") || !entry.endsWith(".log")) {
|
|
41550
41735
|
continue;
|
|
41551
41736
|
}
|
|
41552
|
-
const path =
|
|
41737
|
+
const path = join24(directory, entry);
|
|
41553
41738
|
if (kept.has(path) || ageMs(path, now) < keepMs) {
|
|
41554
41739
|
continue;
|
|
41555
41740
|
}
|
|
41556
41741
|
try {
|
|
41557
|
-
|
|
41742
|
+
rmSync12(path);
|
|
41558
41743
|
} catch {}
|
|
41559
41744
|
}
|
|
41560
41745
|
}
|
|
@@ -41569,7 +41754,7 @@ var init_run_record = __esm(() => {
|
|
|
41569
41754
|
});
|
|
41570
41755
|
|
|
41571
41756
|
// src/script-origins.ts
|
|
41572
|
-
import { join as
|
|
41757
|
+
import { join as join25 } from "node:path";
|
|
41573
41758
|
function unresolvedClassIn(message) {
|
|
41574
41759
|
return UNRESOLVED.exec(message)?.[1] ?? null;
|
|
41575
41760
|
}
|
|
@@ -41622,7 +41807,7 @@ function checkWithEngine(godotPath, projectPath) {
|
|
|
41622
41807
|
const ran = await runEngine(godotPath, [
|
|
41623
41808
|
"--headless",
|
|
41624
41809
|
"--log-file",
|
|
41625
|
-
|
|
41810
|
+
join25(logDir, "engine.log"),
|
|
41626
41811
|
"--path",
|
|
41627
41812
|
projectPath,
|
|
41628
41813
|
"--check-only",
|
|
@@ -41674,17 +41859,17 @@ import {
|
|
|
41674
41859
|
closeSync as closeSync2,
|
|
41675
41860
|
constants,
|
|
41676
41861
|
copyFileSync,
|
|
41677
|
-
existsSync as
|
|
41862
|
+
existsSync as existsSync20,
|
|
41678
41863
|
fstatSync,
|
|
41679
|
-
mkdirSync as
|
|
41864
|
+
mkdirSync as mkdirSync15,
|
|
41680
41865
|
openSync as openSync3,
|
|
41681
41866
|
readdirSync as readdirSync10,
|
|
41682
|
-
readFileSync as
|
|
41867
|
+
readFileSync as readFileSync22,
|
|
41683
41868
|
readSync as readSync2,
|
|
41684
41869
|
rmdirSync as rmdirSync2,
|
|
41685
41870
|
statSync as statSync9
|
|
41686
41871
|
} from "node:fs";
|
|
41687
|
-
import { basename as basename7, dirname as
|
|
41872
|
+
import { basename as basename7, dirname as dirname19, extname as extname2, isAbsolute as isAbsolute3, join as join26, normalize as normalize2, relative as relative5 } from "node:path";
|
|
41688
41873
|
import { setTimeout as delay3 } from "node:timers/promises";
|
|
41689
41874
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
41690
41875
|
import { promisify as promisify5 } from "node:util";
|
|
@@ -41705,16 +41890,16 @@ function captureDestinationRefusal(outputPath, projects) {
|
|
|
41705
41890
|
if (extname2(outputPath).toLowerCase() !== ".png") {
|
|
41706
41891
|
return `outputPath ${outputPath} does not end in .png, which is what a capture is.`;
|
|
41707
41892
|
}
|
|
41708
|
-
const directory = realPathOr(
|
|
41709
|
-
if (!
|
|
41710
|
-
return `outputPath's directory ${
|
|
41893
|
+
const directory = realPathOr(dirname19(outputPath));
|
|
41894
|
+
if (!existsSync20(directory)) {
|
|
41895
|
+
return `outputPath's directory ${dirname19(outputPath)} does not exist; a capture creates no directories.`;
|
|
41711
41896
|
}
|
|
41712
|
-
const where =
|
|
41897
|
+
const where = join26(directory, basename7(outputPath));
|
|
41713
41898
|
const inside = projects.find((project) => project !== "" && isWithinRoot(realPathOr(project), where));
|
|
41714
41899
|
if (inside !== undefined) {
|
|
41715
41900
|
return `outputPath ${outputPath} is inside the project at ${inside}; a capture is saved outside it, so it never becomes part of the game.`;
|
|
41716
41901
|
}
|
|
41717
|
-
if (
|
|
41902
|
+
if (existsSync20(where)) {
|
|
41718
41903
|
return `outputPath ${outputPath} already exists; a capture is not written over a file. Name a new one.`;
|
|
41719
41904
|
}
|
|
41720
41905
|
return null;
|
|
@@ -42187,16 +42372,60 @@ function settingsNamedBy(op, args) {
|
|
|
42187
42372
|
}
|
|
42188
42373
|
}
|
|
42189
42374
|
function foldedScriptErrors(errors) {
|
|
42190
|
-
const
|
|
42375
|
+
const unresolved = new Map;
|
|
42376
|
+
for (const error of errors) {
|
|
42377
|
+
const name = unresolvedClassIn(error.message);
|
|
42378
|
+
if (name !== null) {
|
|
42379
|
+
unresolved.set(error.path, (unresolved.get(error.path) ?? new Set).add(name));
|
|
42380
|
+
}
|
|
42381
|
+
}
|
|
42382
|
+
const follows = (error) => {
|
|
42383
|
+
const names = unresolved.get(error.path);
|
|
42384
|
+
if (names === undefined) {
|
|
42385
|
+
return false;
|
|
42386
|
+
}
|
|
42387
|
+
const reparsed = /Could not parse global class "([A-Za-z_][A-Za-z0-9_]*)"/.exec(error.message)?.[1];
|
|
42388
|
+
return error.message.includes('the inferred type "Variant"') || reparsed !== undefined && names.has(reparsed);
|
|
42389
|
+
};
|
|
42390
|
+
const scriptErrors = errors.filter((error) => !error.message.includes(DEPENDED_FAILED) && !follows(error));
|
|
42191
42391
|
const own = new Set(scriptErrors.map((error) => error.path));
|
|
42192
42392
|
return {
|
|
42193
42393
|
scriptErrors,
|
|
42194
|
-
failedThroughDependency: [...new Set(errors.map((error) => error.path))].filter((path) => !own.has(path))
|
|
42394
|
+
failedThroughDependency: [...new Set(errors.map((error) => error.path))].filter((path) => !own.has(path)),
|
|
42395
|
+
followFromUnresolved: errors.filter(follows)
|
|
42195
42396
|
};
|
|
42196
42397
|
}
|
|
42197
42398
|
function nonEmpty(lists) {
|
|
42198
42399
|
return Object.fromEntries(Object.entries(lists).filter(([, list]) => list.length > 0));
|
|
42199
42400
|
}
|
|
42401
|
+
function withPreviousCut(answer, previous) {
|
|
42402
|
+
const content = answer.content.map((part) => ({ ...part }));
|
|
42403
|
+
const last = content.at(-1);
|
|
42404
|
+
let json = null;
|
|
42405
|
+
try {
|
|
42406
|
+
const parsed = JSON.parse(last?.text ?? "");
|
|
42407
|
+
json = typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? { ...parsed } : null;
|
|
42408
|
+
} catch {}
|
|
42409
|
+
if (last !== undefined && json !== null) {
|
|
42410
|
+
last.text = answerJson({ ...json, previousRunCut: previous });
|
|
42411
|
+
}
|
|
42412
|
+
const note = cutTestRunNote(previous);
|
|
42413
|
+
const first = content[0];
|
|
42414
|
+
if (first === undefined || first === last && json !== null || typeof first.text !== "string") {
|
|
42415
|
+
content.unshift({ type: "text", text: note });
|
|
42416
|
+
} else {
|
|
42417
|
+
first.text = `${first.text} ${note}`;
|
|
42418
|
+
}
|
|
42419
|
+
return { ...answer, content };
|
|
42420
|
+
}
|
|
42421
|
+
function finishedBeforeTheEnd(finished, failing, running) {
|
|
42422
|
+
if (finished === 0 && running === null) {
|
|
42423
|
+
return "";
|
|
42424
|
+
}
|
|
42425
|
+
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`}`;
|
|
42426
|
+
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"}`;
|
|
42427
|
+
return ` Before it ended, ${got}${running === null ? "" : `${finished === 0 ? " and" : ", and"} ${running} was running`}${listed}.`;
|
|
42428
|
+
}
|
|
42200
42429
|
function scriptErrorsNote(errors, {
|
|
42201
42430
|
origins,
|
|
42202
42431
|
unconfirmed
|
|
@@ -42204,7 +42433,11 @@ function scriptErrorsNote(errors, {
|
|
|
42204
42433
|
origins: [],
|
|
42205
42434
|
unconfirmed: []
|
|
42206
42435
|
}) {
|
|
42207
|
-
const {
|
|
42436
|
+
const {
|
|
42437
|
+
scriptErrors: own,
|
|
42438
|
+
failedThroughDependency: dependents,
|
|
42439
|
+
followFromUnresolved: consequences
|
|
42440
|
+
} = foldedScriptErrors(errors);
|
|
42208
42441
|
const causes = new Map;
|
|
42209
42442
|
for (const error of own) {
|
|
42210
42443
|
if (!causes.has(error.path)) {
|
|
@@ -42231,12 +42464,10 @@ function scriptErrorsNote(errors, {
|
|
|
42231
42464
|
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(", ")}.`;
|
|
42232
42465
|
const more = own.length - causes.size;
|
|
42233
42466
|
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.` : "";
|
|
42234
|
-
|
|
42235
|
-
|
|
42236
|
-
|
|
42237
|
-
|
|
42238
|
-
rest
|
|
42239
|
-
].filter((part) => part !== "").join(" ");
|
|
42467
|
+
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.` : "";
|
|
42468
|
+
const unnamed = first.length;
|
|
42469
|
+
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.`;
|
|
42470
|
+
return [opening, named, following, rest, after].filter((part) => part !== "").join(" ");
|
|
42240
42471
|
}
|
|
42241
42472
|
function timedOutVerdict(timeoutMs, silentForMs) {
|
|
42242
42473
|
const quiet = Math.round(silentForMs);
|
|
@@ -42294,7 +42525,7 @@ function aboveTheRunner(entry) {
|
|
|
42294
42525
|
}
|
|
42295
42526
|
function elsewhereIn(projectPath, asked) {
|
|
42296
42527
|
const wanted = asked.replace(/^res:\/\//, "").replace(/\/+$/, "");
|
|
42297
|
-
if (wanted === "" ||
|
|
42528
|
+
if (wanted === "" || existsSync20(join26(projectPath, wanted))) {
|
|
42298
42529
|
return "";
|
|
42299
42530
|
}
|
|
42300
42531
|
const root = (wanted.split("/")[0] ?? "").toLowerCase();
|
|
@@ -42338,7 +42569,7 @@ function testPathsIn(projectPath, given) {
|
|
|
42338
42569
|
};
|
|
42339
42570
|
}
|
|
42340
42571
|
function hasMainScene(projectFile) {
|
|
42341
|
-
const scene = parseProjectGodot(
|
|
42572
|
+
const scene = parseProjectGodot(readFileSync22(projectFile, "utf8"))["application"]?.["run/main_scene"];
|
|
42342
42573
|
return typeof scene === "string" && scene !== "";
|
|
42343
42574
|
}
|
|
42344
42575
|
function runtimeAddonAnnouncesEditors(projectPath) {
|
|
@@ -42346,13 +42577,13 @@ function runtimeAddonAnnouncesEditors(projectPath) {
|
|
|
42346
42577
|
return false;
|
|
42347
42578
|
}
|
|
42348
42579
|
try {
|
|
42349
|
-
return
|
|
42580
|
+
return readFileSync22(join26(projectPath, RUNTIME_AUTOLOAD.path), "utf8").includes('"editor_pid"');
|
|
42350
42581
|
} catch {
|
|
42351
42582
|
return false;
|
|
42352
42583
|
}
|
|
42353
42584
|
}
|
|
42354
42585
|
function editorPlaysHeadless(projectFile) {
|
|
42355
|
-
const runArgs = parseProjectGodot(
|
|
42586
|
+
const runArgs = parseProjectGodot(readFileSync22(projectFile, "utf8"))["editor"]?.["run/main_run_args"];
|
|
42356
42587
|
return typeof runArgs === "string" && /(?:^|\s)--headless(?:\s|$)/.test(runArgs);
|
|
42357
42588
|
}
|
|
42358
42589
|
function camelCased(params) {
|
|
@@ -42371,7 +42602,7 @@ function needsItsOwnProcess(asked) {
|
|
|
42371
42602
|
class GodotServer {
|
|
42372
42603
|
mcp;
|
|
42373
42604
|
locator = new GodotLocator;
|
|
42374
|
-
operationsScript =
|
|
42605
|
+
operationsScript = join26(__dirname2, "godot", "operations", "godot_operations.gd");
|
|
42375
42606
|
godotBridge;
|
|
42376
42607
|
tools = buildToolDefinitions();
|
|
42377
42608
|
activeProcess = null;
|
|
@@ -42674,11 +42905,20 @@ class GodotServer {
|
|
|
42674
42905
|
return checked.response;
|
|
42675
42906
|
}
|
|
42676
42907
|
const named = args["projectPath"];
|
|
42677
|
-
if (typeof named === "string" &&
|
|
42908
|
+
if (typeof named === "string" && existsSync20(join26(named, "project.godot"))) {
|
|
42678
42909
|
this.lastProjectPath = named;
|
|
42679
42910
|
}
|
|
42680
42911
|
this.updates.refresh();
|
|
42681
|
-
const
|
|
42912
|
+
const token = request.params._meta?.progressToken;
|
|
42913
|
+
const progress = token === undefined ? undefined : (done, message) => {
|
|
42914
|
+
extra.sendNotification({
|
|
42915
|
+
method: "notifications/progress",
|
|
42916
|
+
params: { progressToken: token, progress: done, message }
|
|
42917
|
+
}).catch((error) => {
|
|
42918
|
+
this.logDebug(`Progress for ${spec.name} was not sent: ${errorMessage(error)}`);
|
|
42919
|
+
});
|
|
42920
|
+
};
|
|
42921
|
+
const answer = await withCallSignal(extra.signal, () => this.answered(spec.name, checked.op ?? "", args), progress);
|
|
42682
42922
|
return this.withFeedbackNotice(this.withUpdateNotice(answer));
|
|
42683
42923
|
});
|
|
42684
42924
|
}
|
|
@@ -42805,7 +43045,14 @@ class GodotServer {
|
|
|
42805
43045
|
const asked = readString(args, "op");
|
|
42806
43046
|
const named = asked !== undefined && spec.operations?.[asked] !== undefined ? asked : null;
|
|
42807
43047
|
const wanted = named === null ? [...known] : [...argumentsOf(spec, named), "op"];
|
|
42808
|
-
const
|
|
43048
|
+
const shaped = wanted.map((name) => {
|
|
43049
|
+
const schema = spec.parameters[name];
|
|
43050
|
+
if (schema !== undefined) {
|
|
43051
|
+
return `${name} (${argumentShape(schema)})`;
|
|
43052
|
+
}
|
|
43053
|
+
return name === "op" && named === null && spec.operations !== undefined ? `op (${Object.keys(spec.operations).join(", ")})` : name;
|
|
43054
|
+
});
|
|
43055
|
+
const takes = wanted.length === 0 ? "It takes no arguments." : `It takes: ${shaped.join(", ")}.`;
|
|
42809
43056
|
const where = named === null ? spec.name : `${spec.name} ${named}`;
|
|
42810
43057
|
return {
|
|
42811
43058
|
ok: false,
|
|
@@ -43148,8 +43395,8 @@ class GodotServer {
|
|
|
43148
43395
|
if (path === undefined) {
|
|
43149
43396
|
return { ok: false, response: this.createErrorResponse("projectPath is required.") };
|
|
43150
43397
|
}
|
|
43151
|
-
const file =
|
|
43152
|
-
if (!
|
|
43398
|
+
const file = join26(path, "project.godot");
|
|
43399
|
+
if (!existsSync20(file)) {
|
|
43153
43400
|
return {
|
|
43154
43401
|
ok: false,
|
|
43155
43402
|
response: this.createErrorResponse(`Not a Godot project: ${path}`, [
|
|
@@ -43760,7 +44007,7 @@ class GodotServer {
|
|
|
43760
44007
|
if (!engine.ok) {
|
|
43761
44008
|
return engine.response;
|
|
43762
44009
|
}
|
|
43763
|
-
const application = parseProjectGodot(
|
|
44010
|
+
const application = parseProjectGodot(readFileSync22(project.value.file, "utf8"))["application"] ?? {};
|
|
43764
44011
|
const name = application["config/name"];
|
|
43765
44012
|
const mainScene = application["run/main_scene"];
|
|
43766
44013
|
const info = {
|
|
@@ -43816,18 +44063,18 @@ class GodotServer {
|
|
|
43816
44063
|
if (!engine.ok) {
|
|
43817
44064
|
return engine.response;
|
|
43818
44065
|
}
|
|
43819
|
-
const folder =
|
|
44066
|
+
const folder = dirname19(output.absolutePath);
|
|
43820
44067
|
try {
|
|
43821
|
-
|
|
44068
|
+
mkdirSync15(folder, { recursive: true });
|
|
43822
44069
|
} catch (error) {
|
|
43823
|
-
return this.createErrorResponse(`Cannot create ${
|
|
44070
|
+
return this.createErrorResponse(`Cannot create ${dirname19(output.relativePath)} to export into: ${errorMessage(error)}`);
|
|
43824
44071
|
}
|
|
43825
44072
|
const debug = readBoolean(args, "debug") ?? false;
|
|
43826
44073
|
const exportLogs = scratchDirectory("export");
|
|
43827
44074
|
const exportArgs = [
|
|
43828
44075
|
"--headless",
|
|
43829
44076
|
"--log-file",
|
|
43830
|
-
|
|
44077
|
+
join26(exportLogs, "engine.log"),
|
|
43831
44078
|
"--path",
|
|
43832
44079
|
project.value.path,
|
|
43833
44080
|
debug ? "--export-debug" : "--export-release",
|
|
@@ -43835,7 +44082,7 @@ class GodotServer {
|
|
|
43835
44082
|
output.absolutePath
|
|
43836
44083
|
];
|
|
43837
44084
|
this.logDebug(`Exporting: ${engine.value} ${exportArgs.join(" ")}`);
|
|
43838
|
-
const writtenAt = () =>
|
|
44085
|
+
const writtenAt = () => existsSync20(output.absolutePath) ? statSync9(output.absolutePath).mtimeMs : null;
|
|
43839
44086
|
const before = writtenAt();
|
|
43840
44087
|
let ending;
|
|
43841
44088
|
try {
|
|
@@ -43848,7 +44095,38 @@ class GodotServer {
|
|
|
43848
44095
|
}
|
|
43849
44096
|
return exportAnswer(ending, { preset, outputPath: output.relativePath, debug }, before, writtenAt());
|
|
43850
44097
|
}
|
|
44098
|
+
reportTestProgress(log) {
|
|
44099
|
+
const progress = callProgress();
|
|
44100
|
+
if (progress === undefined) {
|
|
44101
|
+
return;
|
|
44102
|
+
}
|
|
44103
|
+
const began = Date.now();
|
|
44104
|
+
let read = 0;
|
|
44105
|
+
let lastSent = began;
|
|
44106
|
+
const ticker = setInterval(() => {
|
|
44107
|
+
const entries = log.all;
|
|
44108
|
+
const finishedOne = entries.slice(read).some((entry) => finishesASuite(entry.text));
|
|
44109
|
+
read = entries.length;
|
|
44110
|
+
const now = Date.now();
|
|
44111
|
+
if (!finishedOne && now - lastSent < TEST_HEARTBEAT_MS) {
|
|
44112
|
+
return;
|
|
44113
|
+
}
|
|
44114
|
+
lastSent = now;
|
|
44115
|
+
progress(Math.floor((now - began) / 1000), testProgressMessage(entries.map((entry) => entry.text), now - began));
|
|
44116
|
+
}, 1000);
|
|
44117
|
+
ticker.unref();
|
|
44118
|
+
return ticker;
|
|
44119
|
+
}
|
|
43851
44120
|
async handleRunTests(args) {
|
|
44121
|
+
const answer = await this.runTests(args);
|
|
44122
|
+
const project = this.project(args);
|
|
44123
|
+
if (!project.ok || callSignal()?.aborted === true) {
|
|
44124
|
+
return answer;
|
|
44125
|
+
}
|
|
44126
|
+
const previous = takeCutTestRun(project.value.path);
|
|
44127
|
+
return previous === null ? answer : withPreviousCut(answer, previous);
|
|
44128
|
+
}
|
|
44129
|
+
async runTests(args) {
|
|
43852
44130
|
const project = this.project(args);
|
|
43853
44131
|
if (!project.ok) {
|
|
43854
44132
|
return project.response;
|
|
@@ -43857,7 +44135,7 @@ class GodotServer {
|
|
|
43857
44135
|
if (!paths.ok) {
|
|
43858
44136
|
return this.createErrorResponse(paths.reason, PATH_SOLUTIONS);
|
|
43859
44137
|
}
|
|
43860
|
-
const missing = paths.value.filter((path) => !
|
|
44138
|
+
const missing = paths.value.filter((path) => !existsSync20(join26(project.value.path, path.slice("res://".length))));
|
|
43861
44139
|
const asked = paths.value.filter((path) => !missing.includes(path));
|
|
43862
44140
|
if (asked.length === 0) {
|
|
43863
44141
|
const verdict = `nothing at ${missing.join(" and ")}`;
|
|
@@ -43873,7 +44151,7 @@ class GodotServer {
|
|
|
43873
44151
|
};
|
|
43874
44152
|
}
|
|
43875
44153
|
const runner = "addons/gdUnit4/bin/GdUnitCmdTool.gd";
|
|
43876
|
-
if (!
|
|
44154
|
+
if (!existsSync20(join26(project.value.path, runner))) {
|
|
43877
44155
|
return this.createErrorResponse(`gdUnit4 is not installed in this project: no ${runner}.`, [
|
|
43878
44156
|
"Install gdUnit4 under addons/gdUnit4, from https://github.com/godot-gdunit-labs/gdUnit4"
|
|
43879
44157
|
]);
|
|
@@ -43918,6 +44196,7 @@ class GodotServer {
|
|
|
43918
44196
|
run.process.stderr?.on("data", () => {
|
|
43919
44197
|
lastSaid = Date.now();
|
|
43920
44198
|
});
|
|
44199
|
+
const reporting = this.reportTestProgress(run.log);
|
|
43921
44200
|
let silentForMs = 0;
|
|
43922
44201
|
const timedOut = await new Promise((resolve) => {
|
|
43923
44202
|
const timer = setTimeout(() => {
|
|
@@ -43934,25 +44213,26 @@ class GodotServer {
|
|
|
43934
44213
|
resolve(false);
|
|
43935
44214
|
});
|
|
43936
44215
|
});
|
|
44216
|
+
clearInterval(reporting);
|
|
43937
44217
|
if (timedOut && run.process.pid !== undefined) {
|
|
43938
44218
|
await untilGone([run.process.pid]);
|
|
43939
44219
|
}
|
|
43940
44220
|
await run.settled;
|
|
43941
|
-
const reportsDir =
|
|
44221
|
+
const reportsDir = join26(project.value.path, ".godot", ...ours.split("/"));
|
|
43942
44222
|
let report = null;
|
|
43943
44223
|
let reportProblem = null;
|
|
43944
44224
|
try {
|
|
43945
|
-
const written =
|
|
44225
|
+
const written = existsSync20(reportsDir) ? readdirSync10(reportsDir).filter((name) => name.startsWith("report_")).sort((a, b) => Number(a.slice("report_".length)) - Number(b.slice("report_".length))) : [];
|
|
43946
44226
|
const newest = written.at(-1);
|
|
43947
44227
|
if (newest !== undefined) {
|
|
43948
|
-
report = parseJUnit(
|
|
44228
|
+
report = parseJUnit(readFileSync22(join26(reportsDir, newest, "results.xml"), "utf8"));
|
|
43949
44229
|
}
|
|
43950
44230
|
} catch (error) {
|
|
43951
44231
|
reportProblem = errorMessage(error);
|
|
43952
44232
|
} finally {
|
|
43953
44233
|
discard(reportsDir, userData);
|
|
43954
44234
|
try {
|
|
43955
|
-
rmdirSync2(
|
|
44235
|
+
rmdirSync2(dirname19(reportsDir));
|
|
43956
44236
|
} catch {}
|
|
43957
44237
|
}
|
|
43958
44238
|
const engineEntries = run.log.select({ severity: "warning", sinceLastCall: false, limit: 200 }).entries.map(aboveTheRunner);
|
|
@@ -43989,9 +44269,28 @@ class GodotServer {
|
|
|
43989
44269
|
printed: echoed.entries.map((entry) => entry.text),
|
|
43990
44270
|
...echoed.omitted > 0 ? { printedOmitted: echoed.omitted } : {}
|
|
43991
44271
|
};
|
|
44272
|
+
const sofar = report === null ? suitesPrinted(said) : null;
|
|
43992
44273
|
if (report === null) {
|
|
43993
|
-
const
|
|
44274
|
+
const finished = sofar?.finished ?? [];
|
|
44275
|
+
const running = sofar?.running ?? null;
|
|
44276
|
+
const failing = finished.filter((suite) => suite.errors + suite.failures > 0);
|
|
44277
|
+
const atTheEnd = {
|
|
44278
|
+
...nonEmpty({
|
|
44279
|
+
suitesFinished: finished.map((suite) => suite.path),
|
|
44280
|
+
suitesFailing: failing.map(({ path, errors, failures, failed }) => ({
|
|
44281
|
+
path,
|
|
44282
|
+
errors,
|
|
44283
|
+
failures,
|
|
44284
|
+
failed
|
|
44285
|
+
}))
|
|
44286
|
+
}),
|
|
44287
|
+
...running === null ? {} : { suiteRunning: running }
|
|
44288
|
+
};
|
|
44289
|
+
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)}`;
|
|
43994
44290
|
const note = missingNote === undefined ? said : `${said} ${missingNote}`;
|
|
44291
|
+
if (callSignal()?.aborted === true) {
|
|
44292
|
+
keepCutTestRun(project.value.path, { cutAt: new Date().toISOString(), paths: asked, ...atTheEnd });
|
|
44293
|
+
}
|
|
43995
44294
|
return {
|
|
43996
44295
|
content: [
|
|
43997
44296
|
{ type: "text", text: note },
|
|
@@ -44007,6 +44306,7 @@ class GodotServer {
|
|
|
44007
44306
|
...timedOut ? { timedOut, silentForMs } : {},
|
|
44008
44307
|
...missing.length > 0 ? { missing } : {},
|
|
44009
44308
|
...nonEmpty(foldedScriptErrors(scriptErrors)),
|
|
44309
|
+
...atTheEnd,
|
|
44010
44310
|
arguments: cmdArgs,
|
|
44011
44311
|
entries: forAnswer(withoutListed(printed, scriptErrors).slice(0, 60).map(aboveTheRunner)),
|
|
44012
44312
|
...printedLines,
|
|
@@ -44141,11 +44441,11 @@ class GodotServer {
|
|
|
44141
44441
|
const cached = cachedClasses(projectPath);
|
|
44142
44442
|
const contradicted = cached === null ? [] : contradictedDiagnostics(messages, cached, (resourcePath) => {
|
|
44143
44443
|
const contained = resolveWithinProject(projectPath, resourcePath);
|
|
44144
|
-
if (!contained.ok || !
|
|
44444
|
+
if (!contained.ok || !existsSync20(contained.absolutePath)) {
|
|
44145
44445
|
return null;
|
|
44146
44446
|
}
|
|
44147
44447
|
try {
|
|
44148
|
-
return
|
|
44448
|
+
return readFileSync22(contained.absolutePath, "utf8");
|
|
44149
44449
|
} catch {
|
|
44150
44450
|
return null;
|
|
44151
44451
|
}
|
|
@@ -44201,11 +44501,11 @@ class GodotServer {
|
|
|
44201
44501
|
}
|
|
44202
44502
|
sourceOfClass(projectPath, resourcePath) {
|
|
44203
44503
|
const contained = resolveWithinProject(projectPath, resourcePath);
|
|
44204
|
-
if (!contained.ok || !
|
|
44504
|
+
if (!contained.ok || !existsSync20(contained.absolutePath)) {
|
|
44205
44505
|
return null;
|
|
44206
44506
|
}
|
|
44207
44507
|
try {
|
|
44208
|
-
return
|
|
44508
|
+
return readFileSync22(contained.absolutePath, "utf8");
|
|
44209
44509
|
} catch {
|
|
44210
44510
|
return null;
|
|
44211
44511
|
}
|
|
@@ -44516,7 +44816,7 @@ class GodotServer {
|
|
|
44516
44816
|
return null;
|
|
44517
44817
|
}
|
|
44518
44818
|
try {
|
|
44519
|
-
return settingKeys(
|
|
44819
|
+
return settingKeys(readFileSync22(join26(projectPath, "project.godot"), "utf8"));
|
|
44520
44820
|
} catch {
|
|
44521
44821
|
return null;
|
|
44522
44822
|
}
|
|
@@ -44838,7 +45138,7 @@ class GodotServer {
|
|
|
44838
45138
|
}
|
|
44839
45139
|
async runtimeUp(projectPath, before, budget, withArgs) {
|
|
44840
45140
|
const budgetMs = budget.ms;
|
|
44841
|
-
if (!
|
|
45141
|
+
if (!existsSync20(join26(projectPath, RUNTIME_AUTOLOAD.path))) {
|
|
44842
45142
|
return runtimeVerdict(null, {
|
|
44843
45143
|
addon: false,
|
|
44844
45144
|
budgetMs,
|
|
@@ -45097,7 +45397,7 @@ class GodotServer {
|
|
|
45097
45397
|
return 0;
|
|
45098
45398
|
}
|
|
45099
45399
|
try {
|
|
45100
|
-
return reportAlreadyInTranscript(
|
|
45400
|
+
return reportAlreadyInTranscript(readFileSync22(game.transcript), readFileSync22(report));
|
|
45101
45401
|
} catch (error) {
|
|
45102
45402
|
this.logDebug(`Could not compare a picked-up run's report with its transcript: ${errorMessage(error)}`);
|
|
45103
45403
|
return 0;
|
|
@@ -45855,7 +46155,7 @@ class GodotServer {
|
|
|
45855
46155
|
notes.push(endedWithoutACode({
|
|
45856
46156
|
throughEditor: run.throughEditor,
|
|
45857
46157
|
announced: announced !== undefined || run.announcedPid !== undefined,
|
|
45858
|
-
couldAnnounce: run.projectPath !== null &&
|
|
46158
|
+
couldAnnounce: run.projectPath !== null && existsSync20(join26(run.projectPath, RUNTIME_AUTOLOAD.path)),
|
|
45859
46159
|
endedHere: typeof run.endedHere === "string",
|
|
45860
46160
|
pickedUp: run.pickedUp === true
|
|
45861
46161
|
}));
|
|
@@ -46416,7 +46716,7 @@ class GodotServer {
|
|
|
46416
46716
|
if (!scriptPath.ok) {
|
|
46417
46717
|
return this.createErrorResponse(scriptPath.reason);
|
|
46418
46718
|
}
|
|
46419
|
-
if (!
|
|
46719
|
+
if (!existsSync20(scriptPath.absolutePath)) {
|
|
46420
46720
|
return this.createErrorResponse(`Script file does not exist: ${scriptPath.absolutePath}`);
|
|
46421
46721
|
}
|
|
46422
46722
|
const asked = readNonEmptyString(args, "newScriptPath");
|
|
@@ -46704,7 +47004,7 @@ class GodotServer {
|
|
|
46704
47004
|
async ownRunOnItsWay(projectPath, budgetMs) {
|
|
46705
47005
|
const run = await this.currentRun();
|
|
46706
47006
|
const project = projectPath ?? run?.projectPath ?? null;
|
|
46707
|
-
if (run === null || project === null || run.announcedPid !== undefined || run.projectPath !== null && !isSameDirectory(run.projectPath, project) || !
|
|
47007
|
+
if (run === null || project === null || run.announcedPid !== undefined || run.projectPath !== null && !isSameDirectory(run.projectPath, project) || !existsSync20(join26(project, RUNTIME_AUTOLOAD.path)) || !await this.runStillGoing(run)) {
|
|
46708
47008
|
return null;
|
|
46709
47009
|
}
|
|
46710
47010
|
const already = runtimesAnnounced().running;
|
|
@@ -46788,7 +47088,7 @@ class GodotServer {
|
|
|
46788
47088
|
}
|
|
46789
47089
|
}
|
|
46790
47090
|
const screenshotDir = expectsScreenshot ? scratchDirectory("runtime-screenshot") : null;
|
|
46791
|
-
const screenshotPath = screenshotDir ?
|
|
47091
|
+
const screenshotPath = screenshotDir ? join26(screenshotDir, "capture.png") : null;
|
|
46792
47092
|
const staleNote = this.runtimeStaleNote(choice.endpoint);
|
|
46793
47093
|
const stale = staleNote === undefined ? {} : { addonIsStale: true, staleNote };
|
|
46794
47094
|
const refused = (message) => this.createErrorResponse(staleNote === undefined ? message : `${message}
|
|
@@ -46837,7 +47137,7 @@ ${staleNote}`);
|
|
|
46837
47137
|
|
|
46838
47138
|
${staleNote}`}`
|
|
46839
47139
|
},
|
|
46840
|
-
{ type: "image", data:
|
|
47140
|
+
{ type: "image", data: readFileSync22(screenshotPath).toString("base64"), mimeType: "image/png" }
|
|
46841
47141
|
]
|
|
46842
47142
|
};
|
|
46843
47143
|
} finally {
|
|
@@ -46938,7 +47238,7 @@ async function runGodotServer() {
|
|
|
46938
47238
|
const server = new GodotServer;
|
|
46939
47239
|
await server.run();
|
|
46940
47240
|
}
|
|
46941
|
-
var UPDATE_NOTICE_EVERY = 500, FEEDBACK_NOTICE_EVERY = 250, run4, __dirname2, GREETING_WAIT_MS = 1e4, HOLD_PING_MS = 2000, SLOWEST_FRAME_RATE = 20, PLAY_STARTS_WITHIN_MS = 30000, STOP_WAIT_MS = 1e4, KEEPER_ANSWER_MS = 15000, EXIT_REPORTED_WITHIN_MS = 2000, CONSOLE_WRAPPER, LONGEST_BOOT_NOTED_MS, MOST_GROUPS = 20, VALUE_NAMES, BRIDGE_RETRY_MS = 2000, SUCCESSOR_CHECK_MS = 1e4, CONSOLE_WATCH_MS = 250, BOUNDARY_WAIT_MS = 2000, HANDOVER_MS, HANDOVER_POLL_MS = 250, CACHE_WRITE_MS = 2000, SCAN_WAIT_MS = 30000, STREAMS_GRACE_MS = 2000, PLAYING_STATUS_MS = 1000, SCAN_START_MS = 2000, SCAN_SETTLED_MS = 500, REIMPORT_TIMEOUT_MS = 300000, AUDIO_BUS_OPS, STALLED_AFTER_MS = 30000, DEPENDED_FAILED = "Failed to compile depended scripts", WAIT_FOR_RUN_MS = 600000, RUN_POLL_MS = 250, PATH_SOLUTIONS, PROJECT_FILE_ARGUMENTS, DEBUG_STATE_CALLS, PROJECT_INFO_SECTIONS2, RUNNER_DIRECTORY = "addons/gdUnit4/";
|
|
47241
|
+
var UPDATE_NOTICE_EVERY = 500, FEEDBACK_NOTICE_EVERY = 250, run4, __dirname2, GREETING_WAIT_MS = 1e4, HOLD_PING_MS = 2000, SLOWEST_FRAME_RATE = 20, PLAY_STARTS_WITHIN_MS = 30000, STOP_WAIT_MS = 1e4, KEEPER_ANSWER_MS = 15000, EXIT_REPORTED_WITHIN_MS = 2000, CONSOLE_WRAPPER, LONGEST_BOOT_NOTED_MS, MOST_GROUPS = 20, VALUE_NAMES, BRIDGE_RETRY_MS = 2000, SUCCESSOR_CHECK_MS = 1e4, CONSOLE_WATCH_MS = 250, BOUNDARY_WAIT_MS = 2000, HANDOVER_MS, HANDOVER_POLL_MS = 250, CACHE_WRITE_MS = 2000, SCAN_WAIT_MS = 30000, STREAMS_GRACE_MS = 2000, PLAYING_STATUS_MS = 1000, SCAN_START_MS = 2000, SCAN_SETTLED_MS = 500, REIMPORT_TIMEOUT_MS = 300000, AUDIO_BUS_OPS, STALLED_AFTER_MS = 30000, TEST_HEARTBEAT_MS = 20000, DEPENDED_FAILED = "Failed to compile depended scripts", WAIT_FOR_RUN_MS = 600000, RUN_POLL_MS = 250, PATH_SOLUTIONS, PROJECT_FILE_ARGUMENTS, DEBUG_STATE_CALLS, PROJECT_INFO_SECTIONS2, RUNNER_DIRECTORY = "addons/gdUnit4/";
|
|
46942
47242
|
var init_server3 = __esm(() => {
|
|
46943
47243
|
init_mcp();
|
|
46944
47244
|
init_stdio2();
|
|
@@ -46952,6 +47252,7 @@ var init_server3 = __esm(() => {
|
|
|
46952
47252
|
init_class_cache();
|
|
46953
47253
|
init_class_note();
|
|
46954
47254
|
init_config_pin();
|
|
47255
|
+
init_cut_test_run();
|
|
46955
47256
|
init_dap_client();
|
|
46956
47257
|
init_desktop();
|
|
46957
47258
|
init_editor_log();
|
|
@@ -46983,9 +47284,10 @@ var init_server3 = __esm(() => {
|
|
|
46983
47284
|
init_server_version();
|
|
46984
47285
|
init_setup();
|
|
46985
47286
|
init_tool_definitions();
|
|
47287
|
+
init_tool_reference();
|
|
46986
47288
|
init_update_check();
|
|
46987
47289
|
run4 = promisify5(execFile5);
|
|
46988
|
-
__dirname2 =
|
|
47290
|
+
__dirname2 = dirname19(fileURLToPath5(import.meta.url));
|
|
46989
47291
|
CONSOLE_WRAPPER = /_console(\.exe)?$/i;
|
|
46990
47292
|
LONGEST_BOOT_NOTED_MS = 5 * 60000;
|
|
46991
47293
|
VALUE_NAMES = {
|
|
@@ -47035,8 +47337,8 @@ var init_server3 = __esm(() => {
|
|
|
47035
47337
|
init_bridge_announce();
|
|
47036
47338
|
init_errors();
|
|
47037
47339
|
init_godot_path();
|
|
47038
|
-
import { existsSync as
|
|
47039
|
-
import { join as
|
|
47340
|
+
import { existsSync as existsSync21 } from "node:fs";
|
|
47341
|
+
import { join as join27, resolve as resolve8 } from "node:path";
|
|
47040
47342
|
|
|
47041
47343
|
// src/harnesses.ts
|
|
47042
47344
|
import { existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync2, rmSync as rmSync2, statSync as statSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
@@ -47976,102 +48278,9 @@ init_setup();
|
|
|
47976
48278
|
// src/skill.ts
|
|
47977
48279
|
init_setup();
|
|
47978
48280
|
init_tool_definitions();
|
|
48281
|
+
init_tool_reference();
|
|
47979
48282
|
import { existsSync as existsSync11, mkdirSync as mkdirSync5, readdirSync as readdirSync5, rmSync as rmSync6, writeFileSync as writeFileSync7 } from "node:fs";
|
|
47980
48283
|
import { basename as basename4, dirname as dirname6, join as join11 } from "node:path";
|
|
47981
|
-
|
|
47982
|
-
// src/tool-reference.ts
|
|
47983
|
-
init_headless_operations();
|
|
47984
|
-
init_tool_definitions();
|
|
47985
|
-
function namedType(declared) {
|
|
47986
|
-
if (typeof declared === "string") {
|
|
47987
|
-
return declared;
|
|
47988
|
-
}
|
|
47989
|
-
if (Array.isArray(declared) && declared.every((kind) => typeof kind === "string")) {
|
|
47990
|
-
return declared.join(" or ");
|
|
47991
|
-
}
|
|
47992
|
-
return "any";
|
|
47993
|
-
}
|
|
47994
|
-
function headlessSection() {
|
|
47995
|
-
const everyOp = new Map;
|
|
47996
|
-
for (const table of [HEADLESS_OPERATIONS, ENGINE_PASSES]) {
|
|
47997
|
-
for (const [tool, ops] of Object.entries(table)) {
|
|
47998
|
-
everyOp.set(tool, [...everyOp.get(tool) ?? [], ...Object.keys(ops)]);
|
|
47999
|
-
}
|
|
48000
|
-
}
|
|
48001
|
-
const byTool = [...everyOp].map(([tool, ops]) => `\`${tool}\` (${ops.join(", ")})`).sort();
|
|
48002
|
-
return [
|
|
48003
|
-
"## What a gate can call",
|
|
48004
|
-
"",
|
|
48005
|
-
"These answer from a short-lived headless engine: no editor window, no running game, and no",
|
|
48006
|
-
"process or port outliving the answer. They are the only calls an automatic check can make at",
|
|
48007
|
-
"all, because every other tool needs an editor somebody has open or a game somebody is running.",
|
|
48008
|
-
"",
|
|
48009
|
-
...byTool.map((line) => `- ${line}`),
|
|
48010
|
-
"",
|
|
48011
|
-
"Needing nothing is not the same as changing nothing, and a gate wants both. Most of these",
|
|
48012
|
-
"write to the project because that is what they are for: `script_edit create` writes a file,",
|
|
48013
|
-
"`project_settings set` rewrites `project.godot`, `project_import reimport` and `refresh_classes`",
|
|
48014
|
-
"write under `.godot`, and `project_import refresh_uids` writes a `.uid` beside each script that",
|
|
48015
|
-
"had none. The reading ops are the ones to gate on, and which is which is in each",
|
|
48016
|
-
"op's own summary below rather than assumed from this list.",
|
|
48017
|
-
"",
|
|
48018
|
-
"Everything else needs the editor (`editor_*`, `debug_*`, `scene_*`, `resource_*`) or a running",
|
|
48019
|
-
"game (`runtime_*`).",
|
|
48020
|
-
"",
|
|
48021
|
-
'One of these can be pointed the other way: `project_settings get` takes `from: "editor"` to ask',
|
|
48022
|
-
"the open editor instead of the file, which is a different reading and costs no engine start. A",
|
|
48023
|
-
"gate leaves it alone, because there is no editor on a build machine and the call is refused",
|
|
48024
|
-
"rather than answered from the file.",
|
|
48025
|
-
""
|
|
48026
|
-
];
|
|
48027
|
-
}
|
|
48028
|
-
function renderToolsMarkdown() {
|
|
48029
|
-
const lines = [
|
|
48030
|
-
"# Tools",
|
|
48031
|
-
"",
|
|
48032
|
-
`${TOOL_SPECS.length} tools, named \`domain_verb\`. A tool that does several related things takes an`,
|
|
48033
|
-
"`op`. An unknown op or argument is refused with the valid set listed. Generated from the server.",
|
|
48034
|
-
"",
|
|
48035
|
-
`${projectPathSentence()}: the \`runtime_*\` and \`debug_*\` ones pick between running games`,
|
|
48036
|
-
"instead, and the others answer about the editor on this server. The pick, when a call names",
|
|
48037
|
-
`neither \`projectPath\` nor \`pid\`: ${theGamePicked()}. An argument a tool does not`,
|
|
48038
|
-
"declare is refused rather than ignored. Answers are read from the engine after the change, not",
|
|
48039
|
-
"echoed from the request. Engine stderr comes back under `engine_messages`.",
|
|
48040
|
-
"",
|
|
48041
|
-
...headlessSection()
|
|
48042
|
-
];
|
|
48043
|
-
for (const tool of TOOL_SPECS) {
|
|
48044
|
-
lines.push(`## ${tool.name}`, "", tool.description, "");
|
|
48045
|
-
const operations = Object.entries(tool.operations ?? {});
|
|
48046
|
-
if (operations.length > 0) {
|
|
48047
|
-
for (const [op, spec] of operations) {
|
|
48048
|
-
const needs = [...tool.requires, ...spec.requires];
|
|
48049
|
-
const isDefault = tool.defaultOperation === op ? " (default)" : "";
|
|
48050
|
-
const wants = needs.length > 0 ? ` Needs: ${needs.map((name) => `\`${name}\``).join(", ")}.` : "";
|
|
48051
|
-
const spare = argumentsOf(tool, op).filter((name) => !needs.includes(name) && name !== "projectPath");
|
|
48052
|
-
const takes = spare.length > 0 ? ` Takes: ${spare.map((name) => `\`${name}\``).join(", ")}.` : "";
|
|
48053
|
-
lines.push(`- \`op: ${op}\`${isDefault}: ${spec.summary}.${wants}${takes}`);
|
|
48054
|
-
}
|
|
48055
|
-
lines.push("");
|
|
48056
|
-
} else if (tool.requires.length > 0) {
|
|
48057
|
-
lines.push(`Needs: ${tool.requires.map((name) => `\`${name}\``).join(", ")}.`, "");
|
|
48058
|
-
}
|
|
48059
|
-
const parameters = Object.entries(tool.parameters).filter(([name]) => name !== "projectPath");
|
|
48060
|
-
if (parameters.length > 0) {
|
|
48061
|
-
lines.push("Arguments:", "");
|
|
48062
|
-
for (const [name, schema] of parameters) {
|
|
48063
|
-
const type = namedType(schema["type"]);
|
|
48064
|
-
const note = typeof schema["description"] === "string" ? `: ${schema["description"]}` : "";
|
|
48065
|
-
lines.push(`- \`${name}\` (${type})${note}`);
|
|
48066
|
-
}
|
|
48067
|
-
lines.push("");
|
|
48068
|
-
}
|
|
48069
|
-
}
|
|
48070
|
-
return lines.join(`
|
|
48071
|
-
`);
|
|
48072
|
-
}
|
|
48073
|
-
|
|
48074
|
-
// src/skill.ts
|
|
48075
48284
|
var SKILL_NAME = "gdharness";
|
|
48076
48285
|
var SHARED_SKILLS = join11(".agents", "skills");
|
|
48077
48286
|
function skillMarkdown(version) {
|
|
@@ -48367,7 +48576,7 @@ class UsageError extends Refusal {
|
|
|
48367
48576
|
function projectArgument(at) {
|
|
48368
48577
|
const named = args.slice(at).find((value) => !value.startsWith("--"));
|
|
48369
48578
|
const projectPath = resolve8(named ?? ".");
|
|
48370
|
-
if (!
|
|
48579
|
+
if (!existsSync21(join27(projectPath, "project.godot"))) {
|
|
48371
48580
|
throw new UsageError(`Not a Godot project: ${projectPath} holds no project.godot.`);
|
|
48372
48581
|
}
|
|
48373
48582
|
return projectPath;
|
|
@@ -48412,7 +48621,7 @@ async function registerRuntime(godot, projectPath) {
|
|
|
48412
48621
|
return;
|
|
48413
48622
|
}
|
|
48414
48623
|
console.log(`${RUNTIME_AUTOLOAD.name} autoload left at ${named}, which this project registers itself`);
|
|
48415
|
-
if (!
|
|
48624
|
+
if (!existsSync21(join27(projectPath, (named ?? "").replace(/^res:\/\//, "")))) {
|
|
48416
48625
|
console.log(` that file is not in this project, so it boots with a missing script: restore it, or point the entry at res://${RUNTIME_AUTOLOAD.path}`);
|
|
48417
48626
|
}
|
|
48418
48627
|
}
|
|
@@ -48544,7 +48753,7 @@ async function setup() {
|
|
|
48544
48753
|
console.log("no harness configured; name one yourself, and gdharness harnesses lists them all");
|
|
48545
48754
|
}
|
|
48546
48755
|
if (!args.includes("--no-skill")) {
|
|
48547
|
-
const directories = skillDirectories(chosen.harnesses, projectPath,
|
|
48756
|
+
const directories = skillDirectories(chosen.harnesses, projectPath, existsSync21);
|
|
48548
48757
|
for (const written of writeSkill(directories, getLocalVersion())) {
|
|
48549
48758
|
console.log(`skill: ${written.replaced ? "replaced" : "written"} ${written.path}`);
|
|
48550
48759
|
}
|
|
@@ -48658,7 +48867,7 @@ async function upgrade() {
|
|
|
48658
48867
|
moved.push(took.from);
|
|
48659
48868
|
}
|
|
48660
48869
|
}
|
|
48661
|
-
for (const written of writeSkill(skillDirectories(already, projectPath,
|
|
48870
|
+
for (const written of writeSkill(skillDirectories(already, projectPath, existsSync21), version)) {
|
|
48662
48871
|
console.log(`skill: ${written.replaced ? "replaced" : "written"} ${written.path}`);
|
|
48663
48872
|
}
|
|
48664
48873
|
console.log(`
|