gdharness 1.2.22 → 1.2.23
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
|
@@ -11422,7 +11422,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
11422
11422
|
name: "scene_tree",
|
|
11423
11423
|
title: "Scene tree",
|
|
11424
11424
|
effect: "read",
|
|
11425
|
-
description: "The nodes of a scene file: names, classes and hierarchy, with properties when asked. Needs the editor connected.",
|
|
11425
|
+
description: "The nodes of a scene file: names, classes and hierarchy, with properties when asked. Needs the editor connected. For the nodes of a game that is running, runtime_inspect tree, which needs no editor.",
|
|
11426
11426
|
parameters: {
|
|
11427
11427
|
projectPath: PROJECT_PATH,
|
|
11428
11428
|
scenePath: SCENE_PATH,
|
|
@@ -44893,7 +44893,7 @@ class GodotServer {
|
|
|
44893
44893
|
const { op: _op, ...arguments_ } = args;
|
|
44894
44894
|
const bridge = async (command, extra = {}) => {
|
|
44895
44895
|
const contained = this.containProjectFiles({ ...arguments_, ...extra });
|
|
44896
|
-
return contained.ok ? await this.handleViaBridge(command, contained.value) : contained.response;
|
|
44896
|
+
return contained.ok ? await this.handleViaBridge(command, contained.value, tool) : contained.response;
|
|
44897
44897
|
};
|
|
44898
44898
|
switch (tool) {
|
|
44899
44899
|
case "project_info":
|
|
@@ -49101,9 +49101,11 @@ class GodotServer {
|
|
|
49101
49101
|
const holds = held["classes"].map(String);
|
|
49102
49102
|
return { unseen: unseenByEditor(projectPath, holds), stillHeld: heldButGone(projectPath, holds) };
|
|
49103
49103
|
}
|
|
49104
|
-
async handleViaBridge(toolName, args) {
|
|
49104
|
+
async handleViaBridge(toolName, args, askedAs) {
|
|
49105
49105
|
if (!this.godotBridge.isConnected()) {
|
|
49106
|
+
const instead = askedAs !== undefined && Object.hasOwn(EDITOR_FREE_INSTEAD, askedAs) ? EDITOR_FREE_INSTEAD[askedAs] : undefined;
|
|
49106
49107
|
return this.createErrorResponse("Editor not connected: no Godot editor with the gdharness editor addon has reached this server.", [
|
|
49108
|
+
...instead === undefined ? [] : [instead],
|
|
49107
49109
|
"Open the project in the editor with editor_launch, or by hand",
|
|
49108
49110
|
"Enable the addon under Project > Project Settings > Plugins",
|
|
49109
49111
|
"editor_status says whether the bridge is up and what it is listening on"
|
|
@@ -49416,7 +49418,7 @@ async function runGodotServer() {
|
|
|
49416
49418
|
const server = new GodotServer;
|
|
49417
49419
|
await server.run();
|
|
49418
49420
|
}
|
|
49419
|
-
var UPDATE_NOTICE_EVERY = 500, FEEDBACK_NOTICE_EVERY = 250, run4, __dirname2, EDITOR_SERVERS_START_MS = 20000, HOLD_PING_MS = 2000, SLOWEST_FRAME_RATE = 20, PLAY_STARTS_WITHIN_MS = 30000, KEEPER_ANSWER_MS = 15000, EXIT_REPORTED_WITHIN_MS = 2000, CONSOLE_WRAPPER, LONGEST_BOOT_NOTED_MS, PROFILE_SEND_MS = 5000, MOST_GROUPS = 20, RUNTIME_LISTENING_LINE = "[gdharness] runtime listening on ", 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, STREAMS_GRACE_MS = 2000, PLAYING_STATUS_MS = 1000, SCAN_START_MS = 2000, SCAN_SETTLED_MS = 500, REIMPORT_TIMEOUT_MS = 300000, OPS_ELSEWHERE, 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/", PROFILE_FUNCTIONS = 30, PROFILER_ON_MS = 3000, PROPERTY_PLACES = "It is a property, so each place says whether it writes it (access write), which runs its set block, or reads it (access read), which runs its get block: withinCalls on a read says nothing of the set block, nor on a write of the get block unless the write is a compound one such as +=. Inside the property's own blocks its name is the value stored, which the engine reads and writes without running them, so those lines are not listed.", CALLERS_FOUND_BY_NAME = "Found by name in the project's scripts, since the engine's profiler counts calls and time per function and records nothing about who made them: a method of the same name on another class is listed too, unless one class was asked about and it is called through another class by name or through a variable declared as one. Each place carries the profile's numbers for the function it is written in.", MOST_ENGINE_ENTRIES = 200, ERROR_ASSERT = "GdUnitGodotErrorAssertImpl.gd", EXTERNAL_MEMBER = 'Could not resolve external class member "', STATIC_LIST_HINT;
|
|
49421
|
+
var UPDATE_NOTICE_EVERY = 500, FEEDBACK_NOTICE_EVERY = 250, run4, __dirname2, EDITOR_SERVERS_START_MS = 20000, HOLD_PING_MS = 2000, SLOWEST_FRAME_RATE = 20, PLAY_STARTS_WITHIN_MS = 30000, KEEPER_ANSWER_MS = 15000, EXIT_REPORTED_WITHIN_MS = 2000, CONSOLE_WRAPPER, LONGEST_BOOT_NOTED_MS, PROFILE_SEND_MS = 5000, MOST_GROUPS = 20, RUNTIME_LISTENING_LINE = "[gdharness] runtime listening on ", 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, STREAMS_GRACE_MS = 2000, PLAYING_STATUS_MS = 1000, SCAN_START_MS = 2000, SCAN_SETTLED_MS = 500, REIMPORT_TIMEOUT_MS = 300000, EDITOR_FREE_INSTEAD, OPS_ELSEWHERE, 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/", PROFILE_FUNCTIONS = 30, PROFILER_ON_MS = 3000, PROPERTY_PLACES = "It is a property, so each place says whether it writes it (access write), which runs its set block, or reads it (access read), which runs its get block: withinCalls on a read says nothing of the set block, nor on a write of the get block unless the write is a compound one such as +=. Inside the property's own blocks its name is the value stored, which the engine reads and writes without running them, so those lines are not listed.", CALLERS_FOUND_BY_NAME = "Found by name in the project's scripts, since the engine's profiler counts calls and time per function and records nothing about who made them: a method of the same name on another class is listed too, unless one class was asked about and it is called through another class by name or through a variable declared as one. Each place carries the profile's numbers for the function it is written in.", MOST_ENGINE_ENTRIES = 200, ERROR_ASSERT = "GdUnitGodotErrorAssertImpl.gd", EXTERNAL_MEMBER = 'Could not resolve external class member "', STATIC_LIST_HINT;
|
|
49420
49422
|
var init_server = __esm(() => {
|
|
49421
49423
|
init_dist();
|
|
49422
49424
|
init_stdio();
|
|
@@ -49480,6 +49482,9 @@ var init_server = __esm(() => {
|
|
|
49480
49482
|
object: "an object"
|
|
49481
49483
|
};
|
|
49482
49484
|
HANDOVER_MS = SUCCESSOR_CHECK_MS + 5000;
|
|
49485
|
+
EDITOR_FREE_INSTEAD = {
|
|
49486
|
+
scene_tree: "runtime_inspect tree reads the nodes of a game that is running, and needs no editor; scene_tree reads a scene file through the editor"
|
|
49487
|
+
};
|
|
49483
49488
|
OPS_ELSEWHERE = {
|
|
49484
49489
|
runtime_invoke: { get: "runtime_inspect property", property: "runtime_inspect property" }
|
|
49485
49490
|
};
|
|
@@ -284,6 +284,15 @@ static func _code_of(line: String) -> String:
|
|
|
284
284
|
return bare if mark == -1 else bare.substr(0, mark)
|
|
285
285
|
|
|
286
286
|
|
|
287
|
+
# [param content] with each line's comment taken off and its strings kept, which is where the paths
|
|
288
|
+
# a script loads are written.
|
|
289
|
+
static func _code_lines(content: String) -> String:
|
|
290
|
+
var kept: Array[String] = []
|
|
291
|
+
for line: String in content.split("\n"):
|
|
292
|
+
kept.append(Patterns.without_comment(line))
|
|
293
|
+
return "\n".join(kept)
|
|
294
|
+
|
|
295
|
+
|
|
287
296
|
# The comment on [param line], strings emptied first so a # inside one does not start it.
|
|
288
297
|
static func _comment_of(line: String) -> String:
|
|
289
298
|
var bare: String = Patterns.compiled(STRING_LITERAL).sub(line, '""', true)
|
|
@@ -361,6 +370,11 @@ func _analyze_resource(path: String, current_depth: int, walk: DependencyWalk) -
|
|
|
361
370
|
if file:
|
|
362
371
|
var content: String = file.get_as_text()
|
|
363
372
|
file.close()
|
|
373
|
+
# A script's comments name paths as examples, and read as references they were answered as
|
|
374
|
+
# dependencies, one of a file that does not exist: the shipped serialisation module's comment
|
|
375
|
+
# about `["res://addons/x/plugin.cfg"]` was a dependency of every project with the addon.
|
|
376
|
+
if path.get_extension() == "gd":
|
|
377
|
+
content = _code_lines(content)
|
|
364
378
|
|
|
365
379
|
for pattern: String in REFERENCE_PATTERNS:
|
|
366
380
|
var regex: RegEx = Patterns.compiled(pattern)
|
|
@@ -59,7 +59,7 @@ func get_gdscript_info(params: Dictionary) -> Dictionary:
|
|
|
59
59
|
if _triple_quotes(raw) % 2 == 1:
|
|
60
60
|
in_multiline_string = false
|
|
61
61
|
continue
|
|
62
|
-
var stripped: String =
|
|
62
|
+
var stripped: String = Patterns.without_comment(raw)
|
|
63
63
|
|
|
64
64
|
if stripped.is_empty():
|
|
65
65
|
continue
|
|
@@ -101,7 +101,7 @@ func get_gdscript_info(params: Dictionary) -> Dictionary:
|
|
|
101
101
|
if _can_wrap(header):
|
|
102
102
|
while _bracket_depth(stripped) > 0 and i + 1 < lines.size():
|
|
103
103
|
i += 1
|
|
104
|
-
stripped = (stripped + " " +
|
|
104
|
+
stripped = (stripped + " " + Patterns.without_comment(lines[i])).strip_edges()
|
|
105
105
|
header = Patterns.without_annotations(stripped)
|
|
106
106
|
var declaration: RegExMatch = Patterns.declared_class(header)
|
|
107
107
|
if declaration != null:
|
|
@@ -508,28 +508,6 @@ func _up_to(text: String, needle: String) -> String:
|
|
|
508
508
|
return text
|
|
509
509
|
|
|
510
510
|
|
|
511
|
-
# The line with any trailing comment taken off, quote-aware so a `#` inside a string stays.
|
|
512
|
-
#
|
|
513
|
-
# The only one of these scanners that does not count brackets, on purpose: a `#` starts a comment
|
|
514
|
-
# wherever it is, and there is no depth at which it does not. Giving this one the depth its
|
|
515
|
-
# neighbours have puts the comment into the value of `var table := { # keyed by name`, because the
|
|
516
|
-
# brace opens before the `#` and closes on a later line. Held by a case, since the argument for the
|
|
517
|
-
# difference is not visible from the code and reads like an oversight beside its neighbours.
|
|
518
|
-
func _without_comment(line: String) -> String:
|
|
519
|
-
var quote: String = ""
|
|
520
|
-
for i: int in range(line.length()):
|
|
521
|
-
var ch: String = line[i]
|
|
522
|
-
if not quote.is_empty():
|
|
523
|
-
if ch == quote and line[i - 1] != "\\":
|
|
524
|
-
quote = ""
|
|
525
|
-
continue
|
|
526
|
-
if ch == "#":
|
|
527
|
-
return line.substr(0, i).strip_edges()
|
|
528
|
-
if ch == '"' or ch == "'":
|
|
529
|
-
quote = ch
|
|
530
|
-
return line.strip_edges()
|
|
531
|
-
|
|
532
|
-
|
|
533
511
|
# Where the bracket opened at [from] closes, or -1 if it does not.
|
|
534
512
|
#
|
|
535
513
|
# `rfind(")")` took the last one on the line, which is the right answer until something after the
|
|
@@ -84,3 +84,25 @@ static func without_annotations(line: String) -> String:
|
|
|
84
84
|
rest = rest.substr(found.get_end()).strip_edges()
|
|
85
85
|
found = annotation.search(rest)
|
|
86
86
|
return rest
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
# The line with any trailing comment taken off, quote-aware so a `#` inside a string stays.
|
|
90
|
+
#
|
|
91
|
+
# The only one of the scanners here that does not count brackets, on purpose: a `#` starts a comment
|
|
92
|
+
# wherever it is, and there is no depth at which it does not. Giving this one the depth its
|
|
93
|
+
# neighbours have puts the comment into the value of `var table := { # keyed by name`, because the
|
|
94
|
+
# brace opens before the `#` and closes on a later line. Held by a case, since the argument for the
|
|
95
|
+
# difference is not visible from the code and reads like an oversight beside its neighbours.
|
|
96
|
+
static func without_comment(line: String) -> String:
|
|
97
|
+
var quote: String = ""
|
|
98
|
+
for i: int in range(line.length()):
|
|
99
|
+
var ch: String = line[i]
|
|
100
|
+
if not quote.is_empty():
|
|
101
|
+
if ch == quote and line[i - 1] != "\\":
|
|
102
|
+
quote = ""
|
|
103
|
+
continue
|
|
104
|
+
if ch == "#":
|
|
105
|
+
return line.substr(0, i).strip_edges()
|
|
106
|
+
if ch == '"' or ch == "'":
|
|
107
|
+
quote = ch
|
|
108
|
+
return line.strip_edges()
|
package/build/index.js
CHANGED
|
@@ -34454,7 +34454,7 @@ var TOOL_SPECS = [
|
|
|
34454
34454
|
name: "scene_tree",
|
|
34455
34455
|
title: "Scene tree",
|
|
34456
34456
|
effect: "read",
|
|
34457
|
-
description: "The nodes of a scene file: names, classes and hierarchy, with properties when asked. Needs the editor connected.",
|
|
34457
|
+
description: "The nodes of a scene file: names, classes and hierarchy, with properties when asked. Needs the editor connected. For the nodes of a game that is running, runtime_inspect tree, which needs no editor.",
|
|
34458
34458
|
parameters: {
|
|
34459
34459
|
projectPath: PROJECT_PATH2,
|
|
34460
34460
|
scenePath: SCENE_PATH,
|
|
@@ -36448,6 +36448,9 @@ function engineFlagsNote(args) {
|
|
|
36448
36448
|
const named = among.join(", ");
|
|
36449
36449
|
return `${named} ${among.length === 1 ? "is a flag" : "are flags"} the engine reads, and args goes to the game after --, where the engine does not read ${among.length === 1 ? "it" : "them"}. engineArgs hands the engine its own flags; ${among.length === 1 ? "leave it" : "leave them"} in args only for a game that reads ${among.length === 1 ? "that flag" : "those flags"} itself.`;
|
|
36450
36450
|
}
|
|
36451
|
+
var EDITOR_FREE_INSTEAD = {
|
|
36452
|
+
scene_tree: "runtime_inspect tree reads the nodes of a game that is running, and needs no editor; scene_tree reads a scene file through the editor"
|
|
36453
|
+
};
|
|
36451
36454
|
var OPS_ELSEWHERE = {
|
|
36452
36455
|
runtime_invoke: { get: "runtime_inspect property", property: "runtime_inspect property" }
|
|
36453
36456
|
};
|
|
@@ -37638,7 +37641,7 @@ class GodotServer {
|
|
|
37638
37641
|
const { op: _op, ...arguments_ } = args;
|
|
37639
37642
|
const bridge = async (command, extra = {}) => {
|
|
37640
37643
|
const contained = this.containProjectFiles({ ...arguments_, ...extra });
|
|
37641
|
-
return contained.ok ? await this.handleViaBridge(command, contained.value) : contained.response;
|
|
37644
|
+
return contained.ok ? await this.handleViaBridge(command, contained.value, tool) : contained.response;
|
|
37642
37645
|
};
|
|
37643
37646
|
switch (tool) {
|
|
37644
37647
|
case "project_info":
|
|
@@ -41846,9 +41849,11 @@ class GodotServer {
|
|
|
41846
41849
|
const holds = held["classes"].map(String);
|
|
41847
41850
|
return { unseen: unseenByEditor(projectPath, holds), stillHeld: heldButGone(projectPath, holds) };
|
|
41848
41851
|
}
|
|
41849
|
-
async handleViaBridge(toolName, args) {
|
|
41852
|
+
async handleViaBridge(toolName, args, askedAs) {
|
|
41850
41853
|
if (!this.godotBridge.isConnected()) {
|
|
41854
|
+
const instead = askedAs !== undefined && Object.hasOwn(EDITOR_FREE_INSTEAD, askedAs) ? EDITOR_FREE_INSTEAD[askedAs] : undefined;
|
|
41851
41855
|
return this.createErrorResponse("Editor not connected: no Godot editor with the gdharness editor addon has reached this server.", [
|
|
41856
|
+
...instead === undefined ? [] : [instead],
|
|
41852
41857
|
"Open the project in the editor with editor_launch, or by hand",
|
|
41853
41858
|
"Enable the addon under Project > Project Settings > Plugins",
|
|
41854
41859
|
"editor_status says whether the bridge is up and what it is listening on"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gdharness",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.23",
|
|
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",
|