gdharness 1.1.51 → 1.1.53
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 +215 -53
- package/build/index.js +213 -51
- package/package.json +1 -1
package/build/cli.js
CHANGED
|
@@ -8544,7 +8544,8 @@ function sameCodeNote(addonVersion, serverVersion, addonDigest, shippedDigest) {
|
|
|
8544
8544
|
if (addonVersion === undefined || addonVersion === serverVersion || addonDigest === undefined || addonDigest === "" || addonDigest !== shippedDigest) {
|
|
8545
8545
|
return;
|
|
8546
8546
|
}
|
|
8547
|
-
|
|
8547
|
+
const same = `The editor loaded the ${addonVersion} addons, which are the same code this ${serverVersion} server ships, so nothing needs restarting`;
|
|
8548
|
+
return isNewer(addonVersion, serverVersion) ? `${same}. This server is the older of the two, which a reconnect in your harness settles once the project pins ${addonVersion}.` : `${same}; addonVersion is the version the editor loaded when it started.`;
|
|
8548
8549
|
}
|
|
8549
8550
|
function addonMismatch(addonVersion, serverVersion, addonDigest, shippedDigest, onDisk) {
|
|
8550
8551
|
if (!editorIsStale(addonVersion, serverVersion, addonDigest, shippedDigest)) {
|
|
@@ -17826,8 +17827,16 @@ var init_tool_definitions = __esm(() => {
|
|
|
17826
17827
|
type: "boolean",
|
|
17827
17828
|
description: "structure: also list what the script inherits, walking extends through the project's other scripts. Each such member carries inherited_from, the file declaring it, and the answer gains inherits_from, the chain that was walked. A name the script overrides is listed at both its lines rather than once. A native base is not walked, since it declares nothing in a file: editor_classes info answers for those. Default false."
|
|
17828
17829
|
},
|
|
17829
|
-
line: {
|
|
17830
|
-
|
|
17830
|
+
line: {
|
|
17831
|
+
type: "integer",
|
|
17832
|
+
minimum: 1,
|
|
17833
|
+
description: "completion, hover: the line, counted from 1 as the editor shows it."
|
|
17834
|
+
},
|
|
17835
|
+
character: {
|
|
17836
|
+
type: "integer",
|
|
17837
|
+
minimum: 1,
|
|
17838
|
+
description: "completion, hover: the column, counted from 1 as the editor shows it, with a tab counted as one. symbols gives each name at the line and column hover is asked at."
|
|
17839
|
+
}
|
|
17831
17840
|
},
|
|
17832
17841
|
requires: ["projectPath", "scriptPath"],
|
|
17833
17842
|
operations: {
|
|
@@ -17835,7 +17844,10 @@ var init_tool_definitions = __esm(() => {
|
|
|
17835
17844
|
summary: "functions, variables, signals, class_name and extends, read from the file",
|
|
17836
17845
|
requires: []
|
|
17837
17846
|
},
|
|
17838
|
-
symbols: {
|
|
17847
|
+
symbols: {
|
|
17848
|
+
summary: "document symbols from the language server, each with its kind, the line and column of its name, and the lastLine of its declaration",
|
|
17849
|
+
requires: []
|
|
17850
|
+
},
|
|
17839
17851
|
completion: { summary: "completions at a position", requires: ["line", "character"] },
|
|
17840
17852
|
hover: { summary: "hover text at a position", requires: ["line", "character"] }
|
|
17841
17853
|
},
|
|
@@ -17843,13 +17855,13 @@ var init_tool_definitions = __esm(() => {
|
|
|
17843
17855
|
},
|
|
17844
17856
|
{
|
|
17845
17857
|
name: "script_diagnostics",
|
|
17846
|
-
description: "Errors and warnings for a script, or for several, from the editor's language server, and whether each is clean. Needs the editor running. Diagnostics are checked against the project's own files, and the two that the files disprove are named rather than passed on: a member the class cache's file declares, a method, property, constant, static function or enum member, comes back under contradictedByTheFile, and a class this project declares that the diagnostic could not resolve comes back under typesTheEditorHasNotLoaded, with inTheClassCache saying whether a launched game would resolve it. staleAnalysis then says what to do, and for both states it is editor_rescan first: it asks the editor already running, starts nothing, and has cleared each in every reproduction measured. Two exceptions are named when they apply. For an enum member, constant or static function of a global class the reload comes first: in the one project that has reproduced a stale enum member, editor_rescan with reloadScript on the declaring script cleared it and a plain rescan did not. And an editor whose last scan wrote the class cache shorter than the file holds a list behind the files and writes it on every scan, so on that editor the rescan loads nothing and staleAnalysis says editor_launch restart instead. What follows differs: for a stale analysed type, editor_rescan with reloadScript recompiles the held copy and editor_launch restart is the last resort that costs a window; for a class missing from the cache, project_import refresh_classes rewrites the cache from disk at the cost of a short headless engine, which matters beside a running bench.",
|
|
17858
|
+
description: "Errors and warnings for a script, or for several, from the editor's language server, and whether each is clean. Needs the editor running. Each diagnostic carries its severity (error or warning), its message, and where it is as the editor shows it: line and column counted from 1, and endLine and endColumn just past its end. Diagnostics are checked against the project's own files, and the two that the files disprove are named rather than passed on: a member the class cache's file declares, a method, property, constant, static function or enum member, comes back under contradictedByTheFile, and a class this project declares that the diagnostic could not resolve comes back under typesTheEditorHasNotLoaded, with inTheClassCache saying whether a launched game would resolve it. staleAnalysis then says what to do, and for both states it is editor_rescan first: it asks the editor already running, starts nothing, and has cleared each in every reproduction measured. Two exceptions are named when they apply. For an enum member, constant or static function of a global class the reload comes first: in the one project that has reproduced a stale enum member, editor_rescan with reloadScript on the declaring script cleared it and a plain rescan did not. And an editor whose last scan wrote the class cache shorter than the file holds a list behind the files and writes it on every scan, so on that editor the rescan loads nothing and staleAnalysis says editor_launch restart instead. What follows differs: for a stale analysed type, editor_rescan with reloadScript recompiles the held copy and editor_launch restart is the last resort that costs a window; for a class missing from the cache, project_import refresh_classes rewrites the cache from disk at the cost of a short headless engine, which matters beside a running bench.",
|
|
17847
17859
|
parameters: {
|
|
17848
17860
|
projectPath: PROJECT_PATH,
|
|
17849
17861
|
scriptPath: {
|
|
17850
17862
|
type: ["string", "array"],
|
|
17851
17863
|
items: { type: "string" },
|
|
17852
|
-
description: 'A script inside the project, a folder, or a list of either, such as "scripts/player.gd" or ["scripts/economy", "ui/shop.gd"]. A folder is its .gd files at any depth, skipping what the engine skips. One script is answered as it always was
|
|
17864
|
+
description: 'A script inside the project, a folder, or a list of either, such as "scripts/player.gd" or ["scripts/economy", "ui/shop.gd"]. A folder is its .gd files at any depth, skipping what the engine skips. One script is answered as it always was. Several are answered together, with clean, errors and warnings for all of them: a script with nothing to report is named in nothingReported, and every other is answered under scripts in that same shape, including a script that could not be read, named with why while the rest are still asked about. Any number of scripts is one call, which reports progress as it goes.'
|
|
17853
17865
|
}
|
|
17854
17866
|
},
|
|
17855
17867
|
requires: ["projectPath", "scriptPath"]
|
|
@@ -38105,6 +38117,21 @@ var init_wrapper = __esm(() => {
|
|
|
38105
38117
|
import { randomUUID } from "node:crypto";
|
|
38106
38118
|
import { EventEmitter } from "node:events";
|
|
38107
38119
|
import http from "node:http";
|
|
38120
|
+
function closeReason(text, keep) {
|
|
38121
|
+
if (Buffer.byteLength(text) <= MOST_CLOSE_REASON_BYTES) {
|
|
38122
|
+
return text;
|
|
38123
|
+
}
|
|
38124
|
+
const characters = Array.from(text);
|
|
38125
|
+
const marker = "...";
|
|
38126
|
+
while (Buffer.byteLength(characters.join("")) + marker.length > MOST_CLOSE_REASON_BYTES) {
|
|
38127
|
+
if (keep === "end") {
|
|
38128
|
+
characters.shift();
|
|
38129
|
+
} else {
|
|
38130
|
+
characters.pop();
|
|
38131
|
+
}
|
|
38132
|
+
}
|
|
38133
|
+
return keep === "end" ? `${marker}${characters.join("")}` : `${characters.join("")}${marker}`;
|
|
38134
|
+
}
|
|
38108
38135
|
function resolveDefaultBridgePort() {
|
|
38109
38136
|
return portFromEnv("GDHARNESS_BRIDGE_PORT", DEFAULT_PORT);
|
|
38110
38137
|
}
|
|
@@ -38131,7 +38158,7 @@ function getDefaultBridge(ownProject = null) {
|
|
|
38131
38158
|
defaultBridge ??= new GodotBridge(resolveDefaultBridgePort(), resolveDefaultBridgeHost(), DEFAULT_TIMEOUT_MS, ownProject);
|
|
38132
38159
|
return defaultBridge;
|
|
38133
38160
|
}
|
|
38134
|
-
var DEFAULT_PORT = 6505, DEFAULT_HOST = "127.0.0.1", DEFAULT_TIMEOUT_MS = 30000, KEEPALIVE_INTERVAL_MS = 1e4, SECOND_CONNECTION_CLOSE_CODE = 4000, OTHER_PROJECT_CLOSE_CODE = 4001, ONE_SHOT_CLOSE_CODE = 4002, CONNECT_WINDOW_MS = 35000, GodotBridge, defaultBridge = null;
|
|
38161
|
+
var DEFAULT_PORT = 6505, DEFAULT_HOST = "127.0.0.1", DEFAULT_TIMEOUT_MS = 30000, KEEPALIVE_INTERVAL_MS = 1e4, SECOND_CONNECTION_CLOSE_CODE = 4000, MOST_CLOSE_REASON_BYTES = 123, OTHER_PROJECT_CLOSE_CODE = 4001, ONE_SHOT_CLOSE_CODE = 4002, CONNECT_WINDOW_MS = 35000, GodotBridge, defaultBridge = null;
|
|
38135
38162
|
var init_godot_bridge = __esm(() => {
|
|
38136
38163
|
init_wrapper();
|
|
38137
38164
|
init_errors();
|
|
@@ -38281,7 +38308,7 @@ var init_godot_bridge = __esm(() => {
|
|
|
38281
38308
|
const editor = identifying ? null : this.connectionInfo;
|
|
38282
38309
|
return {
|
|
38283
38310
|
host: this.host,
|
|
38284
|
-
port: this.
|
|
38311
|
+
port: this.boundPort,
|
|
38285
38312
|
connected: this.isConnected(),
|
|
38286
38313
|
projectPath: editor?.projectPath,
|
|
38287
38314
|
connectedAt: editor?.connectedAt,
|
|
@@ -38538,7 +38565,8 @@ var init_godot_bridge = __esm(() => {
|
|
|
38538
38565
|
this.socket = null;
|
|
38539
38566
|
this.connectionInfo = null;
|
|
38540
38567
|
this.stopKeepalive();
|
|
38541
|
-
|
|
38568
|
+
const fits = closeReason(said, "start") === said;
|
|
38569
|
+
stranger?.close(OTHER_PROJECT_CLOSE_CODE, fits ? said : closeReason(`this server serves ${this.ownProject ?? ""}`, "end"));
|
|
38542
38570
|
}
|
|
38543
38571
|
identify(pid) {
|
|
38544
38572
|
const known = this.oneShots.get(pid);
|
|
@@ -38577,7 +38605,7 @@ var init_godot_bridge = __esm(() => {
|
|
|
38577
38605
|
this.socket = null;
|
|
38578
38606
|
this.connectionInfo = null;
|
|
38579
38607
|
this.stopKeepalive();
|
|
38580
|
-
run?.close(ONE_SHOT_CLOSE_CODE, said);
|
|
38608
|
+
run?.close(ONE_SHOT_CLOSE_CODE, closeReason(said, "start"));
|
|
38581
38609
|
}
|
|
38582
38610
|
handleDisconnect(disconnectedSocket, reason) {
|
|
38583
38611
|
if (disconnectedSocket && this.socket && disconnectedSocket !== this.socket) {
|
|
@@ -39112,6 +39140,10 @@ import { readFile, realpath } from "node:fs/promises";
|
|
|
39112
39140
|
import { createConnection as createConnection3 } from "node:net";
|
|
39113
39141
|
import { dirname as dirname14, join as join20, resolve as resolve5 } from "node:path";
|
|
39114
39142
|
import { fileURLToPath as fileURLToPath3, pathToFileURL } from "node:url";
|
|
39143
|
+
function initializeTimeoutMs() {
|
|
39144
|
+
const override = Number.parseInt(envValue("GDHARNESS_LSP_INITIALIZE_TIMEOUT_MS") ?? "", 10);
|
|
39145
|
+
return Number.isInteger(override) && override > 0 ? override : 180000;
|
|
39146
|
+
}
|
|
39115
39147
|
function diagnosticsKey(uri) {
|
|
39116
39148
|
let decoded;
|
|
39117
39149
|
try {
|
|
@@ -39145,7 +39177,11 @@ class GodotLSPClient {
|
|
|
39145
39177
|
documentVersions = new Map;
|
|
39146
39178
|
documentTurns = new Map;
|
|
39147
39179
|
timeouts;
|
|
39148
|
-
constructor(port = portFromEnv("GDHARNESS_LSP_PORT", DEFAULT_LSP_PORT), host = "127.0.0.1", timeouts = {
|
|
39180
|
+
constructor(port = portFromEnv("GDHARNESS_LSP_PORT", DEFAULT_LSP_PORT), host = "127.0.0.1", timeouts = {
|
|
39181
|
+
requestMs: REQUEST_TIMEOUT_MS2,
|
|
39182
|
+
referencesMs: REFERENCES_TIMEOUT_MS,
|
|
39183
|
+
initializeMs: initializeTimeoutMs()
|
|
39184
|
+
}) {
|
|
39149
39185
|
this.port = port;
|
|
39150
39186
|
this.host = host;
|
|
39151
39187
|
this.timeouts = timeouts;
|
|
@@ -39455,7 +39491,7 @@ class GodotLSPClient {
|
|
|
39455
39491
|
name: resolvedRootPath
|
|
39456
39492
|
}
|
|
39457
39493
|
]
|
|
39458
|
-
});
|
|
39494
|
+
}, this.timeouts.initializeMs);
|
|
39459
39495
|
const served = this.servedWorkspace;
|
|
39460
39496
|
if (served !== null && !isSameDirectory(served, resolvedRootPath)) {
|
|
39461
39497
|
throw new AnotherProjectsServer(this.port, served, resolvedRootPath);
|
|
@@ -39641,10 +39677,78 @@ function normalizeLSPError(error, port) {
|
|
|
39641
39677
|
if (error.message.includes("ECONNREFUSED") || error.message.includes("Failed to connect to Godot LSP") || error.message.includes("socket closed")) {
|
|
39642
39678
|
return `Godot LSP is unavailable on port ${port}. Start the Godot editor and enable Language Server in Editor Settings, or set GDHARNESS_LSP_PORT to the port it serves.`;
|
|
39643
39679
|
}
|
|
39680
|
+
if (error instanceof LSPTimeout && error.method === "initialize") {
|
|
39681
|
+
return `The language server on port ${port} took the connection and did not answer initialize within ${String(error.seconds)}s. Godot reads every script in the project before it answers the first initialize after its editor starts, and goes on reading after this call stops waiting, so call again once the editor has settled.`;
|
|
39682
|
+
}
|
|
39644
39683
|
return error.message;
|
|
39645
39684
|
}
|
|
39646
39685
|
return String(error);
|
|
39647
39686
|
}
|
|
39687
|
+
function asRecord(value) {
|
|
39688
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
39689
|
+
}
|
|
39690
|
+
function named(value, names) {
|
|
39691
|
+
return typeof value === "number" && Number.isInteger(value) ? names[value - 1] ?? value : value;
|
|
39692
|
+
}
|
|
39693
|
+
function shownPosition(position) {
|
|
39694
|
+
const { line, character } = asRecord(position);
|
|
39695
|
+
return typeof line === "number" && typeof character === "number" ? { line: line + 1, column: character + 1 } : null;
|
|
39696
|
+
}
|
|
39697
|
+
function shownRange(range) {
|
|
39698
|
+
const start = shownPosition(asRecord(range)["start"]);
|
|
39699
|
+
const end = shownPosition(asRecord(range)["end"]);
|
|
39700
|
+
return {
|
|
39701
|
+
...start === null ? {} : { line: start.line, column: start.column },
|
|
39702
|
+
...end === null ? {} : { endLine: end.line, endColumn: end.column }
|
|
39703
|
+
};
|
|
39704
|
+
}
|
|
39705
|
+
function shownDiagnostic(entry) {
|
|
39706
|
+
const { range, severity, message, code } = asRecord(entry);
|
|
39707
|
+
return {
|
|
39708
|
+
...shownRange(range),
|
|
39709
|
+
severity: named(severity, SEVERITIES),
|
|
39710
|
+
message,
|
|
39711
|
+
...code === undefined || code === null || code === 0 || code === "" ? {} : { code }
|
|
39712
|
+
};
|
|
39713
|
+
}
|
|
39714
|
+
function shownSymbol(entry) {
|
|
39715
|
+
const symbol = asRecord(entry);
|
|
39716
|
+
const whole = asRecord(symbol["range"] ?? asRecord(symbol["location"])["range"]);
|
|
39717
|
+
const at = shownPosition(asRecord(symbol["selectionRange"] ?? whole)["start"]);
|
|
39718
|
+
const last = shownPosition(whole["end"]);
|
|
39719
|
+
const children = Array.isArray(symbol["children"]) ? symbol["children"] : [];
|
|
39720
|
+
const { documentation, native_class: nativeClass, deprecated, detail } = symbol;
|
|
39721
|
+
return {
|
|
39722
|
+
name: symbol["name"],
|
|
39723
|
+
kind: named(symbol["kind"], SYMBOL_KINDS),
|
|
39724
|
+
...typeof detail === "string" && detail !== "" ? { detail } : {},
|
|
39725
|
+
...at === null ? {} : { line: at.line, column: at.column },
|
|
39726
|
+
...last === null ? {} : { lastLine: last.line },
|
|
39727
|
+
...typeof documentation === "string" && documentation !== "" ? { documentation } : {},
|
|
39728
|
+
...typeof nativeClass === "string" && nativeClass !== "" ? { nativeClass } : {},
|
|
39729
|
+
...deprecated === true ? { deprecated } : {},
|
|
39730
|
+
...children.length > 0 ? { children: children.map(shownSymbol) } : {}
|
|
39731
|
+
};
|
|
39732
|
+
}
|
|
39733
|
+
function shownCompletion(entry) {
|
|
39734
|
+
const { data: _request, kind, ...rest } = asRecord(entry);
|
|
39735
|
+
return { ...rest, kind: named(kind, COMPLETION_KINDS) };
|
|
39736
|
+
}
|
|
39737
|
+
function shownHover(hover) {
|
|
39738
|
+
if (hover === null || typeof hover !== "object") {
|
|
39739
|
+
return hover;
|
|
39740
|
+
}
|
|
39741
|
+
const { range, ...rest } = asRecord(hover);
|
|
39742
|
+
return { ...rest, ...shownRange(range) };
|
|
39743
|
+
}
|
|
39744
|
+
function protocolPosition(args) {
|
|
39745
|
+
const line = args["line"];
|
|
39746
|
+
const character = args["character"];
|
|
39747
|
+
if (typeof line !== "number" || typeof character !== "number" || !Number.isInteger(line) || !Number.isInteger(character) || line < 1 || character < 1) {
|
|
39748
|
+
throw new ArgumentRefusal(`line and character are counted from 1, as the editor shows them; got line ${String(line)} and character ${String(character)}.`);
|
|
39749
|
+
}
|
|
39750
|
+
return { line: line - 1, character: character - 1 };
|
|
39751
|
+
}
|
|
39648
39752
|
async function resolveLSPPaths(projectPathValue, scriptPathValue) {
|
|
39649
39753
|
const requestedProjectPath = resolve5(projectPathValue);
|
|
39650
39754
|
let projectPath;
|
|
@@ -39691,29 +39795,21 @@ async function handleLSPTool(client, toolName, args) {
|
|
|
39691
39795
|
switch (toolName) {
|
|
39692
39796
|
case "lsp_get_diagnostics": {
|
|
39693
39797
|
const diagnostics = await client.getDiagnostics(scriptPath, content);
|
|
39694
|
-
return asToolResponse({ diagnostics });
|
|
39798
|
+
return asToolResponse({ diagnostics: diagnostics.map(shownDiagnostic) });
|
|
39695
39799
|
}
|
|
39696
39800
|
case "lsp_get_completions": {
|
|
39697
|
-
const line =
|
|
39698
|
-
const character = Number(parsedArgs["character"]);
|
|
39699
|
-
if (!Number.isFinite(line) || !Number.isFinite(character)) {
|
|
39700
|
-
throw new ArgumentRefusal("Arguments line and character must be numbers.");
|
|
39701
|
-
}
|
|
39801
|
+
const { line, character } = protocolPosition(parsedArgs);
|
|
39702
39802
|
const completions = await client.getCompletions(scriptPath, content, line, character);
|
|
39703
|
-
return asToolResponse({ completions });
|
|
39803
|
+
return asToolResponse({ completions: completions.map(shownCompletion) });
|
|
39704
39804
|
}
|
|
39705
39805
|
case "lsp_get_hover": {
|
|
39706
|
-
const line =
|
|
39707
|
-
const character = Number(parsedArgs["character"]);
|
|
39708
|
-
if (!Number.isFinite(line) || !Number.isFinite(character)) {
|
|
39709
|
-
throw new ArgumentRefusal("Arguments line and character must be numbers.");
|
|
39710
|
-
}
|
|
39806
|
+
const { line, character } = protocolPosition(parsedArgs);
|
|
39711
39807
|
const hover = await client.getHover(scriptPath, content, line, character);
|
|
39712
|
-
return asToolResponse({ hover });
|
|
39808
|
+
return asToolResponse({ hover: shownHover(hover) });
|
|
39713
39809
|
}
|
|
39714
39810
|
case "lsp_get_symbols": {
|
|
39715
39811
|
const symbols = await client.getDocumentSymbols(scriptPath, content);
|
|
39716
|
-
return asToolResponse({ symbols });
|
|
39812
|
+
return asToolResponse({ symbols: symbols.map(shownSymbol) });
|
|
39717
39813
|
}
|
|
39718
39814
|
default:
|
|
39719
39815
|
return asToolResponse({ error: `Unknown LSP tool: ${toolName}` });
|
|
@@ -39723,11 +39819,12 @@ async function handleLSPTool(client, toolName, args) {
|
|
|
39723
39819
|
error: normalizeLSPError(error, client.port),
|
|
39724
39820
|
...error instanceof AnotherProjectsServer ? { serves: error.serves } : {},
|
|
39725
39821
|
...error instanceof ArgumentRefusal ? { refusedArguments: true } : {},
|
|
39726
|
-
...error instanceof NoDiagnosticsPublished ? { aboutThisFile: true } : {}
|
|
39822
|
+
...error instanceof NoDiagnosticsPublished ? { aboutThisFile: true } : {},
|
|
39823
|
+
...error instanceof LSPTimeout && error.method === "initialize" ? { stillReading: true } : {}
|
|
39727
39824
|
});
|
|
39728
39825
|
}
|
|
39729
39826
|
}
|
|
39730
|
-
var DEFAULT_LSP_PORT = 6005, DIAGNOSTICS_TIMEOUT_MS = 5000, REQUEST_TIMEOUT_MS2 = 1e4, REFERENCES_TIMEOUT_MS = 180000, LSPTimeout, ArgumentRefusal, NoDiagnosticsPublished, AnotherProjectsServer;
|
|
39827
|
+
var DEFAULT_LSP_PORT = 6005, DIAGNOSTICS_TIMEOUT_MS = 5000, REQUEST_TIMEOUT_MS2 = 1e4, REFERENCES_TIMEOUT_MS = 180000, LSPTimeout, ArgumentRefusal, NoDiagnosticsPublished, AnotherProjectsServer, SEVERITIES, SYMBOL_KINDS, COMPLETION_KINDS;
|
|
39731
39828
|
var init_lsp_client = __esm(() => {
|
|
39732
39829
|
init_answer_json();
|
|
39733
39830
|
init_errors();
|
|
@@ -39754,6 +39851,62 @@ var init_lsp_client = __esm(() => {
|
|
|
39754
39851
|
this.serves = serves;
|
|
39755
39852
|
}
|
|
39756
39853
|
};
|
|
39854
|
+
SEVERITIES = ["error", "warning", "information", "hint"];
|
|
39855
|
+
SYMBOL_KINDS = [
|
|
39856
|
+
"file",
|
|
39857
|
+
"module",
|
|
39858
|
+
"namespace",
|
|
39859
|
+
"package",
|
|
39860
|
+
"class",
|
|
39861
|
+
"method",
|
|
39862
|
+
"property",
|
|
39863
|
+
"field",
|
|
39864
|
+
"constructor",
|
|
39865
|
+
"enum",
|
|
39866
|
+
"interface",
|
|
39867
|
+
"function",
|
|
39868
|
+
"variable",
|
|
39869
|
+
"constant",
|
|
39870
|
+
"string",
|
|
39871
|
+
"number",
|
|
39872
|
+
"boolean",
|
|
39873
|
+
"array",
|
|
39874
|
+
"object",
|
|
39875
|
+
"key",
|
|
39876
|
+
"null",
|
|
39877
|
+
"enum member",
|
|
39878
|
+
"struct",
|
|
39879
|
+
"event",
|
|
39880
|
+
"operator",
|
|
39881
|
+
"type parameter"
|
|
39882
|
+
];
|
|
39883
|
+
COMPLETION_KINDS = [
|
|
39884
|
+
"text",
|
|
39885
|
+
"method",
|
|
39886
|
+
"function",
|
|
39887
|
+
"constructor",
|
|
39888
|
+
"field",
|
|
39889
|
+
"variable",
|
|
39890
|
+
"class",
|
|
39891
|
+
"interface",
|
|
39892
|
+
"module",
|
|
39893
|
+
"property",
|
|
39894
|
+
"unit",
|
|
39895
|
+
"value",
|
|
39896
|
+
"enum",
|
|
39897
|
+
"keyword",
|
|
39898
|
+
"snippet",
|
|
39899
|
+
"color",
|
|
39900
|
+
"file",
|
|
39901
|
+
"reference",
|
|
39902
|
+
"folder",
|
|
39903
|
+
"enum member",
|
|
39904
|
+
"constant",
|
|
39905
|
+
"struct",
|
|
39906
|
+
"event",
|
|
39907
|
+
"operator",
|
|
39908
|
+
"type parameter"
|
|
39909
|
+
];
|
|
39757
39910
|
});
|
|
39758
39911
|
|
|
39759
39912
|
// src/outside.ts
|
|
@@ -42628,12 +42781,6 @@ function scriptsToDiagnose(projectPath, given) {
|
|
|
42628
42781
|
if (unique.length === 0) {
|
|
42629
42782
|
return { ok: false, reason: `No .gd script is under ${empty.join(" or ")}.` };
|
|
42630
42783
|
}
|
|
42631
|
-
if (unique.length > MOST_DIAGNOSED) {
|
|
42632
|
-
return {
|
|
42633
|
-
ok: false,
|
|
42634
|
-
reason: `scriptPath names ${unique.length} scripts, and one call asks about at most ${MOST_DIAGNOSED}, each a round trip through the language server: name narrower folders, or call again for the rest.`
|
|
42635
|
-
};
|
|
42636
|
-
}
|
|
42637
42784
|
return { ok: true, value: unique };
|
|
42638
42785
|
}
|
|
42639
42786
|
function isDirectory(path) {
|
|
@@ -42773,8 +42920,12 @@ class GodotServer {
|
|
|
42773
42920
|
console.error("gdharness running on stdio");
|
|
42774
42921
|
sweepAbandonedScratch();
|
|
42775
42922
|
try {
|
|
42776
|
-
|
|
42777
|
-
|
|
42923
|
+
try {
|
|
42924
|
+
await this.takeOverFromAPredecessor();
|
|
42925
|
+
await this.godotBridge.start();
|
|
42926
|
+
} finally {
|
|
42927
|
+
this.handingOverFrom = null;
|
|
42928
|
+
}
|
|
42778
42929
|
this.bridgeStartupError = null;
|
|
42779
42930
|
const bridgeStatus = this.godotBridge.getStatus();
|
|
42780
42931
|
console.error(`[SERVER] Godot Editor Bridge started on ${bridgeStatus.host}:${bridgeStatus.port}`);
|
|
@@ -42814,7 +42965,6 @@ class GodotServer {
|
|
|
42814
42965
|
await delay3(HANDOVER_POLL_MS);
|
|
42815
42966
|
}
|
|
42816
42967
|
}
|
|
42817
|
-
this.handingOverFrom = null;
|
|
42818
42968
|
this.notHandedOverBy = taken ? null : announced.pid;
|
|
42819
42969
|
}
|
|
42820
42970
|
keepTryingTheBridge() {
|
|
@@ -42846,10 +42996,9 @@ class GodotServer {
|
|
|
42846
42996
|
if (this.ownProject === null) {
|
|
42847
42997
|
return;
|
|
42848
42998
|
}
|
|
42849
|
-
const status = this.godotBridge.getStatus();
|
|
42850
42999
|
this.announcedAt = announceBridge(this.ownProject, {
|
|
42851
|
-
host:
|
|
42852
|
-
port:
|
|
43000
|
+
host: this.godotBridge.getStatus().host,
|
|
43001
|
+
port: this.godotBridge.port,
|
|
42853
43002
|
version: SERVER_VERSION
|
|
42854
43003
|
});
|
|
42855
43004
|
this.watchForASuccessor();
|
|
@@ -44499,11 +44648,16 @@ class GodotServer {
|
|
|
44499
44648
|
return "refusal" in one ? one.refusal : this.jsonTextResponse(this.markedIfStale(one.answer));
|
|
44500
44649
|
}
|
|
44501
44650
|
const progress = callProgress();
|
|
44502
|
-
const
|
|
44651
|
+
const cancelled = callSignal();
|
|
44652
|
+
const reported = [];
|
|
44653
|
+
const nothingReported = [];
|
|
44503
44654
|
let errors = 0;
|
|
44504
44655
|
let warnings = 0;
|
|
44505
44656
|
let unread = 0;
|
|
44506
44657
|
for (const [at, scriptPath] of scripts.value.entries()) {
|
|
44658
|
+
if (cancelled?.aborted === true) {
|
|
44659
|
+
return this.createErrorResponse(`Cancelled after ${at} of ${scripts.value.length} scripts.`);
|
|
44660
|
+
}
|
|
44507
44661
|
progress?.(at + 1, `${at + 1} of ${scripts.value.length}: ${scriptPath}`);
|
|
44508
44662
|
const one = await this.diagnosticsOf({ ...args, scriptPath });
|
|
44509
44663
|
if ("refusal" in one) {
|
|
@@ -44511,20 +44665,25 @@ class GodotServer {
|
|
|
44511
44665
|
return one.refusal;
|
|
44512
44666
|
}
|
|
44513
44667
|
unread += 1;
|
|
44514
|
-
|
|
44668
|
+
reported.push({ scriptPath, error: one.refusal.content[0]?.text ?? "not read" });
|
|
44515
44669
|
continue;
|
|
44516
44670
|
}
|
|
44517
44671
|
errors += Number(one.answer["errors"]);
|
|
44518
44672
|
warnings += Number(one.answer["warnings"]);
|
|
44519
|
-
|
|
44673
|
+
if (readArray(one.answer, "diagnostics")?.length === 0) {
|
|
44674
|
+
nothingReported.push(scriptPath);
|
|
44675
|
+
} else {
|
|
44676
|
+
reported.push(one.answer);
|
|
44677
|
+
}
|
|
44520
44678
|
}
|
|
44521
44679
|
return this.jsonTextResponse(this.markedIfStale({
|
|
44522
44680
|
clean: errors === 0 && unread === 0,
|
|
44523
|
-
scriptsChecked:
|
|
44681
|
+
scriptsChecked: scripts.value.length,
|
|
44524
44682
|
errors,
|
|
44525
44683
|
warnings,
|
|
44526
44684
|
...unread > 0 ? { scriptsNotRead: unread } : {},
|
|
44527
|
-
scripts:
|
|
44685
|
+
scripts: reported,
|
|
44686
|
+
nothingReported
|
|
44528
44687
|
}));
|
|
44529
44688
|
}
|
|
44530
44689
|
markedIfStale(answer) {
|
|
@@ -44538,6 +44697,9 @@ class GodotServer {
|
|
|
44538
44697
|
if (payload["refusedArguments"] === true) {
|
|
44539
44698
|
return { refusal: this.createErrorResponse(reason), perScript: true };
|
|
44540
44699
|
}
|
|
44700
|
+
if (payload["stillReading"] === true) {
|
|
44701
|
+
return { refusal: this.createErrorResponse(`Diagnostics unavailable: ${reason}`), perScript: false };
|
|
44702
|
+
}
|
|
44541
44703
|
if (typeof payload["serves"] === "string") {
|
|
44542
44704
|
return {
|
|
44543
44705
|
refusal: this.createErrorResponse(`Diagnostics unavailable: ${reason}`, [
|
|
@@ -44557,10 +44719,7 @@ class GodotServer {
|
|
|
44557
44719
|
};
|
|
44558
44720
|
}
|
|
44559
44721
|
const diagnostics = readArray(payload, "diagnostics") ?? [];
|
|
44560
|
-
const errors = diagnostics.filter((entry) =>
|
|
44561
|
-
const severity = asParams(entry)["severity"];
|
|
44562
|
-
return severity === 1 || severity === "error" || severity === "ERROR";
|
|
44563
|
-
}).length;
|
|
44722
|
+
const errors = diagnostics.filter((entry) => asParams(entry)["severity"] === "error").length;
|
|
44564
44723
|
return {
|
|
44565
44724
|
answer: {
|
|
44566
44725
|
scriptPath: readString(args, "scriptPath"),
|
|
@@ -44782,19 +44941,22 @@ class GodotServer {
|
|
|
44782
44941
|
return { note: owed, underway: restartUnderway(owed) };
|
|
44783
44942
|
}
|
|
44784
44943
|
async getEditorStatusPayload() {
|
|
44944
|
+
const wasConnected = this.godotBridge.getStatus().connected;
|
|
44945
|
+
const launchedWasUp = !wasConnected && await this.launchedEditorIsUp();
|
|
44946
|
+
const arrivingFound = wasConnected || this.ownProject === null ? [] : await this.editorsNotYetConnected(this.ownProject) ?? [];
|
|
44785
44947
|
const status = this.godotBridge.getStatus();
|
|
44786
|
-
const launchedIsUp = !status.connected &&
|
|
44948
|
+
const launchedIsUp = !status.connected && launchedWasUp;
|
|
44949
|
+
const arriving = status.connected ? [] : arrivingFound;
|
|
44787
44950
|
const isPortConflict = this.bridgeStartupError?.includes("EADDRINUSE") ?? false;
|
|
44788
44951
|
const greeted = hasSaidWhoItIs(status);
|
|
44789
44952
|
const stale = greeted && editorIsStale(status.addonVersion, SERVER_VERSION, status.addonDigest, shippedEditorDigest());
|
|
44790
44953
|
const unfinished = this.restartLeftUnfinished(status.connected);
|
|
44791
|
-
const arriving = status.connected || this.ownProject === null ? [] : await this.editorsNotYetConnected(this.ownProject) ?? [];
|
|
44792
44954
|
return {
|
|
44793
44955
|
...status,
|
|
44794
44956
|
serverVersion: SERVER_VERSION,
|
|
44795
44957
|
addonIsStale: greeted ? stale : undefined,
|
|
44796
44958
|
greeting: status.connected && !greeted ? "The editor has connected and has not yet said who it is: its project, pid, addon version and ports arrive with its greeting a moment after the socket opens, later while it imports. Ask again for them." : undefined,
|
|
44797
|
-
bridgeAvailable: this.bridgeStartupError === null,
|
|
44959
|
+
bridgeAvailable: this.bridgeStartupError === null && status.port !== null,
|
|
44798
44960
|
mayYetConnect: status.connected ? undefined : arriving.length > 0 ? true : unfinished !== null ? unfinished.underway : anEditorIsStillComing(status.listeningSince, launchedIsUp),
|
|
44799
44961
|
editorsNotYetConnected: arriving.length > 0 ? arriving : undefined,
|
|
44800
44962
|
restartInterrupted: unfinished === null || unfinished.underway || arriving.length > 0 ? undefined : {
|
|
@@ -47403,7 +47565,7 @@ async function runGodotServer() {
|
|
|
47403
47565
|
const server = new GodotServer;
|
|
47404
47566
|
await server.run();
|
|
47405
47567
|
}
|
|
47406
|
-
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/"
|
|
47568
|
+
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/";
|
|
47407
47569
|
var init_server3 = __esm(() => {
|
|
47408
47570
|
init_mcp();
|
|
47409
47571
|
init_stdio2();
|
package/build/index.js
CHANGED
|
@@ -10069,7 +10069,8 @@ function sameCodeNote(addonVersion, serverVersion, addonDigest, shippedDigest) {
|
|
|
10069
10069
|
if (addonVersion === undefined || addonVersion === serverVersion || addonDigest === undefined || addonDigest === "" || addonDigest !== shippedDigest) {
|
|
10070
10070
|
return;
|
|
10071
10071
|
}
|
|
10072
|
-
|
|
10072
|
+
const same = `The editor loaded the ${addonVersion} addons, which are the same code this ${serverVersion} server ships, so nothing needs restarting`;
|
|
10073
|
+
return isNewer(addonVersion, serverVersion) ? `${same}. This server is the older of the two, which a reconnect in your harness settles once the project pins ${addonVersion}.` : `${same}; addonVersion is the version the editor loaded when it started.`;
|
|
10073
10074
|
}
|
|
10074
10075
|
function addonMismatch(addonVersion, serverVersion, addonDigest, shippedDigest, onDisk) {
|
|
10075
10076
|
if (!editorIsStale(addonVersion, serverVersion, addonDigest, shippedDigest)) {
|
|
@@ -28180,6 +28181,22 @@ var DEFAULT_HOST = "127.0.0.1";
|
|
|
28180
28181
|
var DEFAULT_TIMEOUT_MS = 30000;
|
|
28181
28182
|
var KEEPALIVE_INTERVAL_MS = 1e4;
|
|
28182
28183
|
var SECOND_CONNECTION_CLOSE_CODE = 4000;
|
|
28184
|
+
var MOST_CLOSE_REASON_BYTES = 123;
|
|
28185
|
+
function closeReason(text, keep) {
|
|
28186
|
+
if (Buffer.byteLength(text) <= MOST_CLOSE_REASON_BYTES) {
|
|
28187
|
+
return text;
|
|
28188
|
+
}
|
|
28189
|
+
const characters = Array.from(text);
|
|
28190
|
+
const marker = "...";
|
|
28191
|
+
while (Buffer.byteLength(characters.join("")) + marker.length > MOST_CLOSE_REASON_BYTES) {
|
|
28192
|
+
if (keep === "end") {
|
|
28193
|
+
characters.shift();
|
|
28194
|
+
} else {
|
|
28195
|
+
characters.pop();
|
|
28196
|
+
}
|
|
28197
|
+
}
|
|
28198
|
+
return keep === "end" ? `${marker}${characters.join("")}` : `${characters.join("")}${marker}`;
|
|
28199
|
+
}
|
|
28183
28200
|
var OTHER_PROJECT_CLOSE_CODE = 4001;
|
|
28184
28201
|
var ONE_SHOT_CLOSE_CODE = 4002;
|
|
28185
28202
|
function resolveDefaultBridgePort() {
|
|
@@ -28350,7 +28367,7 @@ class GodotBridge extends EventEmitter {
|
|
|
28350
28367
|
const editor = identifying ? null : this.connectionInfo;
|
|
28351
28368
|
return {
|
|
28352
28369
|
host: this.host,
|
|
28353
|
-
port: this.
|
|
28370
|
+
port: this.boundPort,
|
|
28354
28371
|
connected: this.isConnected(),
|
|
28355
28372
|
projectPath: editor?.projectPath,
|
|
28356
28373
|
connectedAt: editor?.connectedAt,
|
|
@@ -28607,7 +28624,8 @@ class GodotBridge extends EventEmitter {
|
|
|
28607
28624
|
this.socket = null;
|
|
28608
28625
|
this.connectionInfo = null;
|
|
28609
28626
|
this.stopKeepalive();
|
|
28610
|
-
|
|
28627
|
+
const fits = closeReason(said, "start") === said;
|
|
28628
|
+
stranger?.close(OTHER_PROJECT_CLOSE_CODE, fits ? said : closeReason(`this server serves ${this.ownProject ?? ""}`, "end"));
|
|
28611
28629
|
}
|
|
28612
28630
|
identify(pid) {
|
|
28613
28631
|
const known = this.oneShots.get(pid);
|
|
@@ -28646,7 +28664,7 @@ class GodotBridge extends EventEmitter {
|
|
|
28646
28664
|
this.socket = null;
|
|
28647
28665
|
this.connectionInfo = null;
|
|
28648
28666
|
this.stopKeepalive();
|
|
28649
|
-
run?.close(ONE_SHOT_CLOSE_CODE, said);
|
|
28667
|
+
run?.close(ONE_SHOT_CLOSE_CODE, closeReason(said, "start"));
|
|
28650
28668
|
}
|
|
28651
28669
|
handleDisconnect(disconnectedSocket, reason) {
|
|
28652
28670
|
if (disconnectedSocket && this.socket && disconnectedSocket !== this.socket) {
|
|
@@ -29715,6 +29733,10 @@ var DEFAULT_LSP_PORT = 6005;
|
|
|
29715
29733
|
var DIAGNOSTICS_TIMEOUT_MS = 5000;
|
|
29716
29734
|
var REQUEST_TIMEOUT_MS2 = 1e4;
|
|
29717
29735
|
var REFERENCES_TIMEOUT_MS = 180000;
|
|
29736
|
+
function initializeTimeoutMs() {
|
|
29737
|
+
const override = Number.parseInt(envValue("GDHARNESS_LSP_INITIALIZE_TIMEOUT_MS") ?? "", 10);
|
|
29738
|
+
return Number.isInteger(override) && override > 0 ? override : 180000;
|
|
29739
|
+
}
|
|
29718
29740
|
|
|
29719
29741
|
class LSPTimeout extends Error {
|
|
29720
29742
|
method;
|
|
@@ -29772,7 +29794,11 @@ class GodotLSPClient {
|
|
|
29772
29794
|
documentVersions = new Map;
|
|
29773
29795
|
documentTurns = new Map;
|
|
29774
29796
|
timeouts;
|
|
29775
|
-
constructor(port = portFromEnv("GDHARNESS_LSP_PORT", DEFAULT_LSP_PORT), host = "127.0.0.1", timeouts = {
|
|
29797
|
+
constructor(port = portFromEnv("GDHARNESS_LSP_PORT", DEFAULT_LSP_PORT), host = "127.0.0.1", timeouts = {
|
|
29798
|
+
requestMs: REQUEST_TIMEOUT_MS2,
|
|
29799
|
+
referencesMs: REFERENCES_TIMEOUT_MS,
|
|
29800
|
+
initializeMs: initializeTimeoutMs()
|
|
29801
|
+
}) {
|
|
29776
29802
|
this.port = port;
|
|
29777
29803
|
this.host = host;
|
|
29778
29804
|
this.timeouts = timeouts;
|
|
@@ -30082,7 +30108,7 @@ class GodotLSPClient {
|
|
|
30082
30108
|
name: resolvedRootPath
|
|
30083
30109
|
}
|
|
30084
30110
|
]
|
|
30085
|
-
});
|
|
30111
|
+
}, this.timeouts.initializeMs);
|
|
30086
30112
|
const served = this.servedWorkspace;
|
|
30087
30113
|
if (served !== null && !isSameDirectory(served, resolvedRootPath)) {
|
|
30088
30114
|
throw new AnotherProjectsServer(this.port, served, resolvedRootPath);
|
|
@@ -30268,10 +30294,134 @@ function normalizeLSPError(error, port) {
|
|
|
30268
30294
|
if (error.message.includes("ECONNREFUSED") || error.message.includes("Failed to connect to Godot LSP") || error.message.includes("socket closed")) {
|
|
30269
30295
|
return `Godot LSP is unavailable on port ${port}. Start the Godot editor and enable Language Server in Editor Settings, or set GDHARNESS_LSP_PORT to the port it serves.`;
|
|
30270
30296
|
}
|
|
30297
|
+
if (error instanceof LSPTimeout && error.method === "initialize") {
|
|
30298
|
+
return `The language server on port ${port} took the connection and did not answer initialize within ${String(error.seconds)}s. Godot reads every script in the project before it answers the first initialize after its editor starts, and goes on reading after this call stops waiting, so call again once the editor has settled.`;
|
|
30299
|
+
}
|
|
30271
30300
|
return error.message;
|
|
30272
30301
|
}
|
|
30273
30302
|
return String(error);
|
|
30274
30303
|
}
|
|
30304
|
+
var SEVERITIES = ["error", "warning", "information", "hint"];
|
|
30305
|
+
var SYMBOL_KINDS = [
|
|
30306
|
+
"file",
|
|
30307
|
+
"module",
|
|
30308
|
+
"namespace",
|
|
30309
|
+
"package",
|
|
30310
|
+
"class",
|
|
30311
|
+
"method",
|
|
30312
|
+
"property",
|
|
30313
|
+
"field",
|
|
30314
|
+
"constructor",
|
|
30315
|
+
"enum",
|
|
30316
|
+
"interface",
|
|
30317
|
+
"function",
|
|
30318
|
+
"variable",
|
|
30319
|
+
"constant",
|
|
30320
|
+
"string",
|
|
30321
|
+
"number",
|
|
30322
|
+
"boolean",
|
|
30323
|
+
"array",
|
|
30324
|
+
"object",
|
|
30325
|
+
"key",
|
|
30326
|
+
"null",
|
|
30327
|
+
"enum member",
|
|
30328
|
+
"struct",
|
|
30329
|
+
"event",
|
|
30330
|
+
"operator",
|
|
30331
|
+
"type parameter"
|
|
30332
|
+
];
|
|
30333
|
+
var COMPLETION_KINDS = [
|
|
30334
|
+
"text",
|
|
30335
|
+
"method",
|
|
30336
|
+
"function",
|
|
30337
|
+
"constructor",
|
|
30338
|
+
"field",
|
|
30339
|
+
"variable",
|
|
30340
|
+
"class",
|
|
30341
|
+
"interface",
|
|
30342
|
+
"module",
|
|
30343
|
+
"property",
|
|
30344
|
+
"unit",
|
|
30345
|
+
"value",
|
|
30346
|
+
"enum",
|
|
30347
|
+
"keyword",
|
|
30348
|
+
"snippet",
|
|
30349
|
+
"color",
|
|
30350
|
+
"file",
|
|
30351
|
+
"reference",
|
|
30352
|
+
"folder",
|
|
30353
|
+
"enum member",
|
|
30354
|
+
"constant",
|
|
30355
|
+
"struct",
|
|
30356
|
+
"event",
|
|
30357
|
+
"operator",
|
|
30358
|
+
"type parameter"
|
|
30359
|
+
];
|
|
30360
|
+
function asRecord(value) {
|
|
30361
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
30362
|
+
}
|
|
30363
|
+
function named(value, names) {
|
|
30364
|
+
return typeof value === "number" && Number.isInteger(value) ? names[value - 1] ?? value : value;
|
|
30365
|
+
}
|
|
30366
|
+
function shownPosition(position) {
|
|
30367
|
+
const { line, character } = asRecord(position);
|
|
30368
|
+
return typeof line === "number" && typeof character === "number" ? { line: line + 1, column: character + 1 } : null;
|
|
30369
|
+
}
|
|
30370
|
+
function shownRange(range) {
|
|
30371
|
+
const start = shownPosition(asRecord(range)["start"]);
|
|
30372
|
+
const end = shownPosition(asRecord(range)["end"]);
|
|
30373
|
+
return {
|
|
30374
|
+
...start === null ? {} : { line: start.line, column: start.column },
|
|
30375
|
+
...end === null ? {} : { endLine: end.line, endColumn: end.column }
|
|
30376
|
+
};
|
|
30377
|
+
}
|
|
30378
|
+
function shownDiagnostic(entry) {
|
|
30379
|
+
const { range, severity, message, code } = asRecord(entry);
|
|
30380
|
+
return {
|
|
30381
|
+
...shownRange(range),
|
|
30382
|
+
severity: named(severity, SEVERITIES),
|
|
30383
|
+
message,
|
|
30384
|
+
...code === undefined || code === null || code === 0 || code === "" ? {} : { code }
|
|
30385
|
+
};
|
|
30386
|
+
}
|
|
30387
|
+
function shownSymbol(entry) {
|
|
30388
|
+
const symbol = asRecord(entry);
|
|
30389
|
+
const whole = asRecord(symbol["range"] ?? asRecord(symbol["location"])["range"]);
|
|
30390
|
+
const at = shownPosition(asRecord(symbol["selectionRange"] ?? whole)["start"]);
|
|
30391
|
+
const last = shownPosition(whole["end"]);
|
|
30392
|
+
const children = Array.isArray(symbol["children"]) ? symbol["children"] : [];
|
|
30393
|
+
const { documentation, native_class: nativeClass, deprecated, detail } = symbol;
|
|
30394
|
+
return {
|
|
30395
|
+
name: symbol["name"],
|
|
30396
|
+
kind: named(symbol["kind"], SYMBOL_KINDS),
|
|
30397
|
+
...typeof detail === "string" && detail !== "" ? { detail } : {},
|
|
30398
|
+
...at === null ? {} : { line: at.line, column: at.column },
|
|
30399
|
+
...last === null ? {} : { lastLine: last.line },
|
|
30400
|
+
...typeof documentation === "string" && documentation !== "" ? { documentation } : {},
|
|
30401
|
+
...typeof nativeClass === "string" && nativeClass !== "" ? { nativeClass } : {},
|
|
30402
|
+
...deprecated === true ? { deprecated } : {},
|
|
30403
|
+
...children.length > 0 ? { children: children.map(shownSymbol) } : {}
|
|
30404
|
+
};
|
|
30405
|
+
}
|
|
30406
|
+
function shownCompletion(entry) {
|
|
30407
|
+
const { data: _request, kind, ...rest } = asRecord(entry);
|
|
30408
|
+
return { ...rest, kind: named(kind, COMPLETION_KINDS) };
|
|
30409
|
+
}
|
|
30410
|
+
function shownHover(hover) {
|
|
30411
|
+
if (hover === null || typeof hover !== "object") {
|
|
30412
|
+
return hover;
|
|
30413
|
+
}
|
|
30414
|
+
const { range, ...rest } = asRecord(hover);
|
|
30415
|
+
return { ...rest, ...shownRange(range) };
|
|
30416
|
+
}
|
|
30417
|
+
function protocolPosition(args) {
|
|
30418
|
+
const line = args["line"];
|
|
30419
|
+
const character = args["character"];
|
|
30420
|
+
if (typeof line !== "number" || typeof character !== "number" || !Number.isInteger(line) || !Number.isInteger(character) || line < 1 || character < 1) {
|
|
30421
|
+
throw new ArgumentRefusal(`line and character are counted from 1, as the editor shows them; got line ${String(line)} and character ${String(character)}.`);
|
|
30422
|
+
}
|
|
30423
|
+
return { line: line - 1, character: character - 1 };
|
|
30424
|
+
}
|
|
30275
30425
|
async function resolveLSPPaths(projectPathValue, scriptPathValue) {
|
|
30276
30426
|
const requestedProjectPath = resolve3(projectPathValue);
|
|
30277
30427
|
let projectPath;
|
|
@@ -30318,29 +30468,21 @@ async function handleLSPTool(client, toolName, args) {
|
|
|
30318
30468
|
switch (toolName) {
|
|
30319
30469
|
case "lsp_get_diagnostics": {
|
|
30320
30470
|
const diagnostics = await client.getDiagnostics(scriptPath, content);
|
|
30321
|
-
return asToolResponse({ diagnostics });
|
|
30471
|
+
return asToolResponse({ diagnostics: diagnostics.map(shownDiagnostic) });
|
|
30322
30472
|
}
|
|
30323
30473
|
case "lsp_get_completions": {
|
|
30324
|
-
const line =
|
|
30325
|
-
const character = Number(parsedArgs["character"]);
|
|
30326
|
-
if (!Number.isFinite(line) || !Number.isFinite(character)) {
|
|
30327
|
-
throw new ArgumentRefusal("Arguments line and character must be numbers.");
|
|
30328
|
-
}
|
|
30474
|
+
const { line, character } = protocolPosition(parsedArgs);
|
|
30329
30475
|
const completions = await client.getCompletions(scriptPath, content, line, character);
|
|
30330
|
-
return asToolResponse({ completions });
|
|
30476
|
+
return asToolResponse({ completions: completions.map(shownCompletion) });
|
|
30331
30477
|
}
|
|
30332
30478
|
case "lsp_get_hover": {
|
|
30333
|
-
const line =
|
|
30334
|
-
const character = Number(parsedArgs["character"]);
|
|
30335
|
-
if (!Number.isFinite(line) || !Number.isFinite(character)) {
|
|
30336
|
-
throw new ArgumentRefusal("Arguments line and character must be numbers.");
|
|
30337
|
-
}
|
|
30479
|
+
const { line, character } = protocolPosition(parsedArgs);
|
|
30338
30480
|
const hover = await client.getHover(scriptPath, content, line, character);
|
|
30339
|
-
return asToolResponse({ hover });
|
|
30481
|
+
return asToolResponse({ hover: shownHover(hover) });
|
|
30340
30482
|
}
|
|
30341
30483
|
case "lsp_get_symbols": {
|
|
30342
30484
|
const symbols = await client.getDocumentSymbols(scriptPath, content);
|
|
30343
|
-
return asToolResponse({ symbols });
|
|
30485
|
+
return asToolResponse({ symbols: symbols.map(shownSymbol) });
|
|
30344
30486
|
}
|
|
30345
30487
|
default:
|
|
30346
30488
|
return asToolResponse({ error: `Unknown LSP tool: ${toolName}` });
|
|
@@ -30350,7 +30492,8 @@ async function handleLSPTool(client, toolName, args) {
|
|
|
30350
30492
|
error: normalizeLSPError(error, client.port),
|
|
30351
30493
|
...error instanceof AnotherProjectsServer ? { serves: error.serves } : {},
|
|
30352
30494
|
...error instanceof ArgumentRefusal ? { refusedArguments: true } : {},
|
|
30353
|
-
...error instanceof NoDiagnosticsPublished ? { aboutThisFile: true } : {}
|
|
30495
|
+
...error instanceof NoDiagnosticsPublished ? { aboutThisFile: true } : {},
|
|
30496
|
+
...error instanceof LSPTimeout && error.method === "initialize" ? { stillReading: true } : {}
|
|
30354
30497
|
});
|
|
30355
30498
|
}
|
|
30356
30499
|
}
|
|
@@ -33507,8 +33650,16 @@ var TOOL_SPECS = [
|
|
|
33507
33650
|
type: "boolean",
|
|
33508
33651
|
description: "structure: also list what the script inherits, walking extends through the project's other scripts. Each such member carries inherited_from, the file declaring it, and the answer gains inherits_from, the chain that was walked. A name the script overrides is listed at both its lines rather than once. A native base is not walked, since it declares nothing in a file: editor_classes info answers for those. Default false."
|
|
33509
33652
|
},
|
|
33510
|
-
line: {
|
|
33511
|
-
|
|
33653
|
+
line: {
|
|
33654
|
+
type: "integer",
|
|
33655
|
+
minimum: 1,
|
|
33656
|
+
description: "completion, hover: the line, counted from 1 as the editor shows it."
|
|
33657
|
+
},
|
|
33658
|
+
character: {
|
|
33659
|
+
type: "integer",
|
|
33660
|
+
minimum: 1,
|
|
33661
|
+
description: "completion, hover: the column, counted from 1 as the editor shows it, with a tab counted as one. symbols gives each name at the line and column hover is asked at."
|
|
33662
|
+
}
|
|
33512
33663
|
},
|
|
33513
33664
|
requires: ["projectPath", "scriptPath"],
|
|
33514
33665
|
operations: {
|
|
@@ -33516,7 +33667,10 @@ var TOOL_SPECS = [
|
|
|
33516
33667
|
summary: "functions, variables, signals, class_name and extends, read from the file",
|
|
33517
33668
|
requires: []
|
|
33518
33669
|
},
|
|
33519
|
-
symbols: {
|
|
33670
|
+
symbols: {
|
|
33671
|
+
summary: "document symbols from the language server, each with its kind, the line and column of its name, and the lastLine of its declaration",
|
|
33672
|
+
requires: []
|
|
33673
|
+
},
|
|
33520
33674
|
completion: { summary: "completions at a position", requires: ["line", "character"] },
|
|
33521
33675
|
hover: { summary: "hover text at a position", requires: ["line", "character"] }
|
|
33522
33676
|
},
|
|
@@ -33524,13 +33678,13 @@ var TOOL_SPECS = [
|
|
|
33524
33678
|
},
|
|
33525
33679
|
{
|
|
33526
33680
|
name: "script_diagnostics",
|
|
33527
|
-
description: "Errors and warnings for a script, or for several, from the editor's language server, and whether each is clean. Needs the editor running. Diagnostics are checked against the project's own files, and the two that the files disprove are named rather than passed on: a member the class cache's file declares, a method, property, constant, static function or enum member, comes back under contradictedByTheFile, and a class this project declares that the diagnostic could not resolve comes back under typesTheEditorHasNotLoaded, with inTheClassCache saying whether a launched game would resolve it. staleAnalysis then says what to do, and for both states it is editor_rescan first: it asks the editor already running, starts nothing, and has cleared each in every reproduction measured. Two exceptions are named when they apply. For an enum member, constant or static function of a global class the reload comes first: in the one project that has reproduced a stale enum member, editor_rescan with reloadScript on the declaring script cleared it and a plain rescan did not. And an editor whose last scan wrote the class cache shorter than the file holds a list behind the files and writes it on every scan, so on that editor the rescan loads nothing and staleAnalysis says editor_launch restart instead. What follows differs: for a stale analysed type, editor_rescan with reloadScript recompiles the held copy and editor_launch restart is the last resort that costs a window; for a class missing from the cache, project_import refresh_classes rewrites the cache from disk at the cost of a short headless engine, which matters beside a running bench.",
|
|
33681
|
+
description: "Errors and warnings for a script, or for several, from the editor's language server, and whether each is clean. Needs the editor running. Each diagnostic carries its severity (error or warning), its message, and where it is as the editor shows it: line and column counted from 1, and endLine and endColumn just past its end. Diagnostics are checked against the project's own files, and the two that the files disprove are named rather than passed on: a member the class cache's file declares, a method, property, constant, static function or enum member, comes back under contradictedByTheFile, and a class this project declares that the diagnostic could not resolve comes back under typesTheEditorHasNotLoaded, with inTheClassCache saying whether a launched game would resolve it. staleAnalysis then says what to do, and for both states it is editor_rescan first: it asks the editor already running, starts nothing, and has cleared each in every reproduction measured. Two exceptions are named when they apply. For an enum member, constant or static function of a global class the reload comes first: in the one project that has reproduced a stale enum member, editor_rescan with reloadScript on the declaring script cleared it and a plain rescan did not. And an editor whose last scan wrote the class cache shorter than the file holds a list behind the files and writes it on every scan, so on that editor the rescan loads nothing and staleAnalysis says editor_launch restart instead. What follows differs: for a stale analysed type, editor_rescan with reloadScript recompiles the held copy and editor_launch restart is the last resort that costs a window; for a class missing from the cache, project_import refresh_classes rewrites the cache from disk at the cost of a short headless engine, which matters beside a running bench.",
|
|
33528
33682
|
parameters: {
|
|
33529
33683
|
projectPath: PROJECT_PATH,
|
|
33530
33684
|
scriptPath: {
|
|
33531
33685
|
type: ["string", "array"],
|
|
33532
33686
|
items: { type: "string" },
|
|
33533
|
-
description: 'A script inside the project, a folder, or a list of either, such as "scripts/player.gd" or ["scripts/economy", "ui/shop.gd"]. A folder is its .gd files at any depth, skipping what the engine skips. One script is answered as it always was
|
|
33687
|
+
description: 'A script inside the project, a folder, or a list of either, such as "scripts/player.gd" or ["scripts/economy", "ui/shop.gd"]. A folder is its .gd files at any depth, skipping what the engine skips. One script is answered as it always was. Several are answered together, with clean, errors and warnings for all of them: a script with nothing to report is named in nothingReported, and every other is answered under scripts in that same shape, including a script that could not be read, named with why while the rest are still asked about. Any number of scripts is one call, which reports progress as it goes.'
|
|
33534
33688
|
}
|
|
33535
33689
|
},
|
|
33536
33690
|
requires: ["projectPath", "scriptPath"]
|
|
@@ -35072,7 +35226,6 @@ function testPathsIn(projectPath, given) {
|
|
|
35072
35226
|
value: unique.filter((path) => !unique.some((other) => other !== path && within(path, other))).map((path) => `res://${path}`)
|
|
35073
35227
|
};
|
|
35074
35228
|
}
|
|
35075
|
-
var MOST_DIAGNOSED = 300;
|
|
35076
35229
|
function scriptsToDiagnose(projectPath, given) {
|
|
35077
35230
|
const listed = Array.isArray(given);
|
|
35078
35231
|
if (!listed) {
|
|
@@ -35116,12 +35269,6 @@ function scriptsToDiagnose(projectPath, given) {
|
|
|
35116
35269
|
if (unique.length === 0) {
|
|
35117
35270
|
return { ok: false, reason: `No .gd script is under ${empty.join(" or ")}.` };
|
|
35118
35271
|
}
|
|
35119
|
-
if (unique.length > MOST_DIAGNOSED) {
|
|
35120
|
-
return {
|
|
35121
|
-
ok: false,
|
|
35122
|
-
reason: `scriptPath names ${unique.length} scripts, and one call asks about at most ${MOST_DIAGNOSED}, each a round trip through the language server: name narrower folders, or call again for the rest.`
|
|
35123
|
-
};
|
|
35124
|
-
}
|
|
35125
35272
|
return { ok: true, value: unique };
|
|
35126
35273
|
}
|
|
35127
35274
|
function isDirectory(path) {
|
|
@@ -35261,8 +35408,12 @@ class GodotServer {
|
|
|
35261
35408
|
console.error("gdharness running on stdio");
|
|
35262
35409
|
sweepAbandonedScratch();
|
|
35263
35410
|
try {
|
|
35264
|
-
|
|
35265
|
-
|
|
35411
|
+
try {
|
|
35412
|
+
await this.takeOverFromAPredecessor();
|
|
35413
|
+
await this.godotBridge.start();
|
|
35414
|
+
} finally {
|
|
35415
|
+
this.handingOverFrom = null;
|
|
35416
|
+
}
|
|
35266
35417
|
this.bridgeStartupError = null;
|
|
35267
35418
|
const bridgeStatus = this.godotBridge.getStatus();
|
|
35268
35419
|
console.error(`[SERVER] Godot Editor Bridge started on ${bridgeStatus.host}:${bridgeStatus.port}`);
|
|
@@ -35302,7 +35453,6 @@ class GodotServer {
|
|
|
35302
35453
|
await delay3(HANDOVER_POLL_MS);
|
|
35303
35454
|
}
|
|
35304
35455
|
}
|
|
35305
|
-
this.handingOverFrom = null;
|
|
35306
35456
|
this.notHandedOverBy = taken ? null : announced.pid;
|
|
35307
35457
|
}
|
|
35308
35458
|
keepTryingTheBridge() {
|
|
@@ -35334,10 +35484,9 @@ class GodotServer {
|
|
|
35334
35484
|
if (this.ownProject === null) {
|
|
35335
35485
|
return;
|
|
35336
35486
|
}
|
|
35337
|
-
const status = this.godotBridge.getStatus();
|
|
35338
35487
|
this.announcedAt = announceBridge(this.ownProject, {
|
|
35339
|
-
host:
|
|
35340
|
-
port:
|
|
35488
|
+
host: this.godotBridge.getStatus().host,
|
|
35489
|
+
port: this.godotBridge.port,
|
|
35341
35490
|
version: SERVER_VERSION
|
|
35342
35491
|
});
|
|
35343
35492
|
this.watchForASuccessor();
|
|
@@ -36987,11 +37136,16 @@ class GodotServer {
|
|
|
36987
37136
|
return "refusal" in one ? one.refusal : this.jsonTextResponse(this.markedIfStale(one.answer));
|
|
36988
37137
|
}
|
|
36989
37138
|
const progress = callProgress();
|
|
36990
|
-
const
|
|
37139
|
+
const cancelled = callSignal();
|
|
37140
|
+
const reported = [];
|
|
37141
|
+
const nothingReported = [];
|
|
36991
37142
|
let errors = 0;
|
|
36992
37143
|
let warnings = 0;
|
|
36993
37144
|
let unread = 0;
|
|
36994
37145
|
for (const [at, scriptPath] of scripts.value.entries()) {
|
|
37146
|
+
if (cancelled?.aborted === true) {
|
|
37147
|
+
return this.createErrorResponse(`Cancelled after ${at} of ${scripts.value.length} scripts.`);
|
|
37148
|
+
}
|
|
36995
37149
|
progress?.(at + 1, `${at + 1} of ${scripts.value.length}: ${scriptPath}`);
|
|
36996
37150
|
const one = await this.diagnosticsOf({ ...args, scriptPath });
|
|
36997
37151
|
if ("refusal" in one) {
|
|
@@ -36999,20 +37153,25 @@ class GodotServer {
|
|
|
36999
37153
|
return one.refusal;
|
|
37000
37154
|
}
|
|
37001
37155
|
unread += 1;
|
|
37002
|
-
|
|
37156
|
+
reported.push({ scriptPath, error: one.refusal.content[0]?.text ?? "not read" });
|
|
37003
37157
|
continue;
|
|
37004
37158
|
}
|
|
37005
37159
|
errors += Number(one.answer["errors"]);
|
|
37006
37160
|
warnings += Number(one.answer["warnings"]);
|
|
37007
|
-
|
|
37161
|
+
if (readArray(one.answer, "diagnostics")?.length === 0) {
|
|
37162
|
+
nothingReported.push(scriptPath);
|
|
37163
|
+
} else {
|
|
37164
|
+
reported.push(one.answer);
|
|
37165
|
+
}
|
|
37008
37166
|
}
|
|
37009
37167
|
return this.jsonTextResponse(this.markedIfStale({
|
|
37010
37168
|
clean: errors === 0 && unread === 0,
|
|
37011
|
-
scriptsChecked:
|
|
37169
|
+
scriptsChecked: scripts.value.length,
|
|
37012
37170
|
errors,
|
|
37013
37171
|
warnings,
|
|
37014
37172
|
...unread > 0 ? { scriptsNotRead: unread } : {},
|
|
37015
|
-
scripts:
|
|
37173
|
+
scripts: reported,
|
|
37174
|
+
nothingReported
|
|
37016
37175
|
}));
|
|
37017
37176
|
}
|
|
37018
37177
|
markedIfStale(answer) {
|
|
@@ -37026,6 +37185,9 @@ class GodotServer {
|
|
|
37026
37185
|
if (payload["refusedArguments"] === true) {
|
|
37027
37186
|
return { refusal: this.createErrorResponse(reason), perScript: true };
|
|
37028
37187
|
}
|
|
37188
|
+
if (payload["stillReading"] === true) {
|
|
37189
|
+
return { refusal: this.createErrorResponse(`Diagnostics unavailable: ${reason}`), perScript: false };
|
|
37190
|
+
}
|
|
37029
37191
|
if (typeof payload["serves"] === "string") {
|
|
37030
37192
|
return {
|
|
37031
37193
|
refusal: this.createErrorResponse(`Diagnostics unavailable: ${reason}`, [
|
|
@@ -37045,10 +37207,7 @@ class GodotServer {
|
|
|
37045
37207
|
};
|
|
37046
37208
|
}
|
|
37047
37209
|
const diagnostics = readArray(payload, "diagnostics") ?? [];
|
|
37048
|
-
const errors = diagnostics.filter((entry) =>
|
|
37049
|
-
const severity = asParams(entry)["severity"];
|
|
37050
|
-
return severity === 1 || severity === "error" || severity === "ERROR";
|
|
37051
|
-
}).length;
|
|
37210
|
+
const errors = diagnostics.filter((entry) => asParams(entry)["severity"] === "error").length;
|
|
37052
37211
|
return {
|
|
37053
37212
|
answer: {
|
|
37054
37213
|
scriptPath: readString(args, "scriptPath"),
|
|
@@ -37270,19 +37429,22 @@ class GodotServer {
|
|
|
37270
37429
|
return { note: owed, underway: restartUnderway(owed) };
|
|
37271
37430
|
}
|
|
37272
37431
|
async getEditorStatusPayload() {
|
|
37432
|
+
const wasConnected = this.godotBridge.getStatus().connected;
|
|
37433
|
+
const launchedWasUp = !wasConnected && await this.launchedEditorIsUp();
|
|
37434
|
+
const arrivingFound = wasConnected || this.ownProject === null ? [] : await this.editorsNotYetConnected(this.ownProject) ?? [];
|
|
37273
37435
|
const status = this.godotBridge.getStatus();
|
|
37274
|
-
const launchedIsUp = !status.connected &&
|
|
37436
|
+
const launchedIsUp = !status.connected && launchedWasUp;
|
|
37437
|
+
const arriving = status.connected ? [] : arrivingFound;
|
|
37275
37438
|
const isPortConflict = this.bridgeStartupError?.includes("EADDRINUSE") ?? false;
|
|
37276
37439
|
const greeted = hasSaidWhoItIs(status);
|
|
37277
37440
|
const stale = greeted && editorIsStale(status.addonVersion, SERVER_VERSION, status.addonDigest, shippedEditorDigest());
|
|
37278
37441
|
const unfinished = this.restartLeftUnfinished(status.connected);
|
|
37279
|
-
const arriving = status.connected || this.ownProject === null ? [] : await this.editorsNotYetConnected(this.ownProject) ?? [];
|
|
37280
37442
|
return {
|
|
37281
37443
|
...status,
|
|
37282
37444
|
serverVersion: SERVER_VERSION,
|
|
37283
37445
|
addonIsStale: greeted ? stale : undefined,
|
|
37284
37446
|
greeting: status.connected && !greeted ? "The editor has connected and has not yet said who it is: its project, pid, addon version and ports arrive with its greeting a moment after the socket opens, later while it imports. Ask again for them." : undefined,
|
|
37285
|
-
bridgeAvailable: this.bridgeStartupError === null,
|
|
37447
|
+
bridgeAvailable: this.bridgeStartupError === null && status.port !== null,
|
|
37286
37448
|
mayYetConnect: status.connected ? undefined : arriving.length > 0 ? true : unfinished !== null ? unfinished.underway : anEditorIsStillComing(status.listeningSince, launchedIsUp),
|
|
37287
37449
|
editorsNotYetConnected: arriving.length > 0 ? arriving : undefined,
|
|
37288
37450
|
restartInterrupted: unfinished === null || unfinished.underway || arriving.length > 0 ? undefined : {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gdharness",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.53",
|
|
4
4
|
"mcpName": "io.github.Aureliolo/gdharness",
|
|
5
5
|
"description": "A harness for driving a Godot 4 project from an agent: editor addons, a runtime bridge into the running game, an MCP server in front of them, and a CLI that installs and diagnoses the Godot side.",
|
|
6
6
|
"type": "module",
|