gdharness 0.6.8 → 0.6.9
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 +108 -48
- package/build/godot/addons/gdharness_runtime/runtime_queries.gd +18 -8
- package/build/index.js +105 -47
- package/package.json +1 -1
package/build/cli.js
CHANGED
|
@@ -15034,6 +15034,13 @@ function readParams(params, key) {
|
|
|
15034
15034
|
}
|
|
15035
15035
|
|
|
15036
15036
|
// src/tool-definitions.ts
|
|
15037
|
+
function opTakes(spec, op, name) {
|
|
15038
|
+
const ops = spec.parameters[name]?.ops;
|
|
15039
|
+
return ops === undefined || ops.includes(op);
|
|
15040
|
+
}
|
|
15041
|
+
function argumentsOf(spec, op) {
|
|
15042
|
+
return Object.keys(spec.parameters).filter((name) => opTakes(spec, op, name));
|
|
15043
|
+
}
|
|
15037
15044
|
function toolSpec(name) {
|
|
15038
15045
|
return SPECS_BY_NAME[name];
|
|
15039
15046
|
}
|
|
@@ -15059,7 +15066,8 @@ function buildToolDefinitions() {
|
|
|
15059
15066
|
};
|
|
15060
15067
|
}
|
|
15061
15068
|
for (const [name, schema] of Object.entries(spec.parameters)) {
|
|
15062
|
-
|
|
15069
|
+
const { ops: _ops, ...carried } = schema;
|
|
15070
|
+
properties[name] = carried;
|
|
15063
15071
|
}
|
|
15064
15072
|
const required = [...spec.requires];
|
|
15065
15073
|
if (spec.operations && !spec.defaultOperation) {
|
|
@@ -15749,35 +15757,46 @@ var init_tool_definitions = __esm(() => {
|
|
|
15749
15757
|
projectPath: RUNNING_PROJECT_PATH,
|
|
15750
15758
|
nodePath: {
|
|
15751
15759
|
type: "string",
|
|
15760
|
+
ops: ["tree", "text", "find", "rect", "property"],
|
|
15752
15761
|
description: "tree, find, text: where to start, default /root. rect: the node to place. property: the node to read."
|
|
15753
15762
|
},
|
|
15754
15763
|
property: {
|
|
15755
15764
|
type: "string",
|
|
15765
|
+
ops: ["property", "find"],
|
|
15756
15766
|
description: "property: which one to read. find: read this one off every node matched, so a panel of labels is one call rather than one per label."
|
|
15757
15767
|
},
|
|
15758
|
-
depth: { type: "number", description: "tree: levels to descend. Default 3." },
|
|
15768
|
+
depth: { type: "number", ops: ["tree"], description: "tree: levels to descend. Default 3." },
|
|
15759
15769
|
includeProperties: {
|
|
15760
15770
|
type: "boolean",
|
|
15771
|
+
ops: ["tree"],
|
|
15761
15772
|
description: "tree: include each node's properties. Default false."
|
|
15762
15773
|
},
|
|
15763
15774
|
className: {
|
|
15764
15775
|
type: "string",
|
|
15776
|
+
ops: ["find"],
|
|
15765
15777
|
description: "find: a native class, matching its subclasses too, or a class_name."
|
|
15766
15778
|
},
|
|
15767
|
-
script: { type: "string", description: "find: the script file the node carries." },
|
|
15779
|
+
script: { type: "string", ops: ["find"], description: "find: the script file the node carries." },
|
|
15768
15780
|
namePattern: {
|
|
15769
15781
|
type: "string",
|
|
15782
|
+
ops: ["find"],
|
|
15770
15783
|
description: 'find: a case-insensitive glob on the node name, such as "Enemy*".'
|
|
15771
15784
|
},
|
|
15772
|
-
group: { type: "string", description: "find: a group the node is in." },
|
|
15773
|
-
limit: {
|
|
15785
|
+
group: { type: "string", ops: ["find"], description: "find: a group the node is in." },
|
|
15786
|
+
limit: {
|
|
15787
|
+
type: "number",
|
|
15788
|
+
ops: ["find", "text"],
|
|
15789
|
+
description: "find: the most nodes to answer with, default 100. text: the most lines, default 500, with truncated saying whether there were more."
|
|
15790
|
+
},
|
|
15774
15791
|
includeHidden: {
|
|
15775
15792
|
type: "boolean",
|
|
15793
|
+
ops: ["text"],
|
|
15776
15794
|
description: "text: read hidden nodes as well, for checking that something is not showing. Default false."
|
|
15777
15795
|
},
|
|
15778
15796
|
metrics: {
|
|
15779
15797
|
type: "array",
|
|
15780
15798
|
items: { type: "string" },
|
|
15799
|
+
ops: ["metrics"],
|
|
15781
15800
|
description: "metrics: which to read. Default all."
|
|
15782
15801
|
}
|
|
15783
15802
|
},
|
|
@@ -15810,10 +15829,14 @@ var init_tool_definitions = __esm(() => {
|
|
|
15810
15829
|
parameters: {
|
|
15811
15830
|
projectPath: RUNNING_PROJECT_PATH,
|
|
15812
15831
|
nodePath: { type: "string", description: 'Absolute node path, such as "/root/Main/Player".' },
|
|
15813
|
-
property: { type: "string" },
|
|
15814
|
-
value: { description: "set: the value, fitted to the property's type." },
|
|
15815
|
-
method: { type: "string" },
|
|
15816
|
-
args: {
|
|
15832
|
+
property: { type: "string", ops: ["set"] },
|
|
15833
|
+
value: { ops: ["set"], description: "set: the value, fitted to the property's type." },
|
|
15834
|
+
method: { type: "string", ops: ["call"] },
|
|
15835
|
+
args: {
|
|
15836
|
+
type: "array",
|
|
15837
|
+
ops: ["call"],
|
|
15838
|
+
description: "call: the arguments, fitted to the method's parameter types."
|
|
15839
|
+
}
|
|
15817
15840
|
},
|
|
15818
15841
|
requires: ["nodePath"],
|
|
15819
15842
|
operations: {
|
|
@@ -15828,6 +15851,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
15828
15851
|
projectPath: RUNNING_PROJECT_PATH,
|
|
15829
15852
|
viewportPath: {
|
|
15830
15853
|
type: "string",
|
|
15854
|
+
ops: ["viewport"],
|
|
15831
15855
|
description: "viewport: the Viewport node. Default the root viewport."
|
|
15832
15856
|
},
|
|
15833
15857
|
width: { type: "number", description: "Scale the image to this width." },
|
|
@@ -15847,38 +15871,64 @@ var init_tool_definitions = __esm(() => {
|
|
|
15847
15871
|
projectPath: RUNNING_PROJECT_PATH,
|
|
15848
15872
|
nodePath: {
|
|
15849
15873
|
type: "string",
|
|
15874
|
+
ops: ["click", "choose"],
|
|
15850
15875
|
description: "click: the Control to click, at its centre, or the 3D node to click, where it is drawn. choose: the PopupMenu, or the OptionButton or MenuButton in front of one."
|
|
15851
15876
|
},
|
|
15852
|
-
action: { type: "string", description: "action: the InputMap action name." },
|
|
15877
|
+
action: { type: "string", ops: ["action"], description: "action: the InputMap action name." },
|
|
15853
15878
|
pressed: {
|
|
15854
15879
|
type: "boolean",
|
|
15855
|
-
|
|
15880
|
+
ops: ["action", "key", "mouse_click"],
|
|
15881
|
+
description: "action, key: leave it out and the press is a whole one, down and up a frame apart. true holds it down, false lets go of one being held. mouse_click is one raw event, so it is down unless you say false."
|
|
15856
15882
|
},
|
|
15857
|
-
strength: { type: "number", description: "action: 0 to 1. Default 1." },
|
|
15883
|
+
strength: { type: "number", ops: ["action"], description: "action: 0 to 1. Default 1." },
|
|
15858
15884
|
keycode: {
|
|
15859
15885
|
type: ["string", "number"],
|
|
15886
|
+
ops: ["key"],
|
|
15860
15887
|
description: 'key: the key name, such as "Space" or "A", or its Godot keycode.'
|
|
15861
15888
|
},
|
|
15862
15889
|
text: {
|
|
15863
15890
|
type: "string",
|
|
15891
|
+
ops: ["text", "choose"],
|
|
15864
15892
|
description: "text: what to type. A newline is Enter and a tab is Tab. choose: the item to take, by what it says."
|
|
15865
15893
|
},
|
|
15866
15894
|
index: {
|
|
15867
15895
|
type: "number",
|
|
15896
|
+
ops: ["choose"],
|
|
15868
15897
|
description: "choose: the item to take, by where it is in the list, when text will not do."
|
|
15869
15898
|
},
|
|
15870
|
-
shift: { type: "boolean" },
|
|
15871
|
-
ctrl: { type: "boolean" },
|
|
15872
|
-
alt: { type: "boolean" },
|
|
15873
|
-
x: {
|
|
15874
|
-
|
|
15899
|
+
shift: { type: "boolean", ops: ["key"] },
|
|
15900
|
+
ctrl: { type: "boolean", ops: ["key"] },
|
|
15901
|
+
alt: { type: "boolean", ops: ["key"] },
|
|
15902
|
+
x: {
|
|
15903
|
+
type: "number",
|
|
15904
|
+
ops: ["mouse_click", "mouse_motion"],
|
|
15905
|
+
description: "mouse_click, mouse_motion: window pixels."
|
|
15906
|
+
},
|
|
15907
|
+
y: {
|
|
15908
|
+
type: "number",
|
|
15909
|
+
ops: ["mouse_click", "mouse_motion"],
|
|
15910
|
+
description: "mouse_click, mouse_motion: window pixels."
|
|
15911
|
+
},
|
|
15875
15912
|
button: {
|
|
15876
15913
|
type: ["string", "number"],
|
|
15914
|
+
ops: ["click", "mouse_click"],
|
|
15877
15915
|
description: "click, mouse_click: left, right, middle, wheel_up or wheel_down, or a button number. Default left."
|
|
15878
15916
|
},
|
|
15879
|
-
doubleClick: {
|
|
15880
|
-
|
|
15881
|
-
|
|
15917
|
+
doubleClick: {
|
|
15918
|
+
type: "boolean",
|
|
15919
|
+
ops: ["click", "mouse_click"],
|
|
15920
|
+
description: "click, mouse_click: default false."
|
|
15921
|
+
},
|
|
15922
|
+
relativeX: {
|
|
15923
|
+
type: "number",
|
|
15924
|
+
ops: ["mouse_motion"],
|
|
15925
|
+
description: "mouse_motion: movement since the last event."
|
|
15926
|
+
},
|
|
15927
|
+
relativeY: {
|
|
15928
|
+
type: "number",
|
|
15929
|
+
ops: ["mouse_motion"],
|
|
15930
|
+
description: "mouse_motion: movement since the last event."
|
|
15931
|
+
}
|
|
15882
15932
|
},
|
|
15883
15933
|
requires: [],
|
|
15884
15934
|
operations: {
|
|
@@ -15907,14 +15957,16 @@ var init_tool_definitions = __esm(() => {
|
|
|
15907
15957
|
projectPath: RUNNING_PROJECT_PATH,
|
|
15908
15958
|
frames: {
|
|
15909
15959
|
type: "number",
|
|
15960
|
+
ops: ["frames"],
|
|
15910
15961
|
description: "frames: how many to let pass, 1 to 600. More than that is refused."
|
|
15911
15962
|
},
|
|
15912
|
-
nodePath: { type: "string", description: "signal, until: the node." },
|
|
15913
|
-
signal: { type: "string", description: "signal: the signal name." },
|
|
15914
|
-
property: { type: "string", description: "until: the property name." },
|
|
15915
|
-
value: { description: "until: the value to wait for, fitted to the property's type." },
|
|
15963
|
+
nodePath: { type: "string", ops: ["signal", "until"], description: "signal, until: the node." },
|
|
15964
|
+
signal: { type: "string", ops: ["signal"], description: "signal: the signal name." },
|
|
15965
|
+
property: { type: "string", ops: ["until"], description: "until: the property name." },
|
|
15966
|
+
value: { ops: ["until"], description: "until: the value to wait for, fitted to the property's type." },
|
|
15916
15967
|
timeoutMs: {
|
|
15917
15968
|
type: "number",
|
|
15969
|
+
ops: ["signal", "until"],
|
|
15918
15970
|
description: "signal, until: how long to wait before answering anyway, 1 to 120000. Default 5000."
|
|
15919
15971
|
}
|
|
15920
15972
|
},
|
|
@@ -15962,6 +16014,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
15962
16014
|
parameters: {
|
|
15963
16015
|
frameId: {
|
|
15964
16016
|
type: "number",
|
|
16017
|
+
ops: ["variables"],
|
|
15965
16018
|
description: "variables: which frame, from a stack answer. Default the innermost."
|
|
15966
16019
|
}
|
|
15967
16020
|
},
|
|
@@ -36265,6 +36318,15 @@ class GodotServer {
|
|
|
36265
36318
|
};
|
|
36266
36319
|
}
|
|
36267
36320
|
}
|
|
36321
|
+
if (op !== null) {
|
|
36322
|
+
const elsewhere = Object.keys(args).filter((key) => key !== "op" && !opTakes(spec, op, key));
|
|
36323
|
+
if (elsewhere.length > 0) {
|
|
36324
|
+
return {
|
|
36325
|
+
ok: false,
|
|
36326
|
+
response: this.createErrorResponse(`${spec.name} ${op} does not take ${elsewhere.join(", ")}. ${op} takes: ${argumentsOf(spec, op).join(", ")}.`)
|
|
36327
|
+
};
|
|
36328
|
+
}
|
|
36329
|
+
}
|
|
36268
36330
|
const required = [...spec.requires, ...op !== null ? spec.operations?.[op]?.requires ?? [] : []];
|
|
36269
36331
|
const missing = required.filter((field) => {
|
|
36270
36332
|
const value = Object.hasOwn(args, field) ? args[field] : undefined;
|
|
@@ -36389,7 +36451,8 @@ class GodotServer {
|
|
|
36389
36451
|
return await this.handleRuntimeCommand("read_text", {
|
|
36390
36452
|
projectPath: args["projectPath"],
|
|
36391
36453
|
root: readNonEmptyString(args, "nodePath") ?? "/root",
|
|
36392
|
-
include_hidden: readBoolean(args, "includeHidden") ?? false
|
|
36454
|
+
include_hidden: readBoolean(args, "includeHidden") ?? false,
|
|
36455
|
+
limit: readPositiveNumber(args, "limit") ?? 500
|
|
36393
36456
|
});
|
|
36394
36457
|
case "rect":
|
|
36395
36458
|
return await this.handleRuntimeCommand("get_rect", {
|
|
@@ -37431,31 +37494,26 @@ class GodotServer {
|
|
|
37431
37494
|
});
|
|
37432
37495
|
}
|
|
37433
37496
|
async handleRuntimeWait(op, args) {
|
|
37497
|
+
if (op === "frames") {
|
|
37498
|
+
const frames = readPositiveNumber(args, "frames") ?? 1;
|
|
37499
|
+
const waited = patienceForFrames(frames, this.runtimeTimeoutMs());
|
|
37500
|
+
return await this.handleRuntimeCommand("wait_frames", { projectPath: args["projectPath"], frames }, waited);
|
|
37501
|
+
}
|
|
37434
37502
|
const timeoutMs = readPositiveNumber(args, "timeoutMs") ?? 5000;
|
|
37435
37503
|
const nodePath = readNonEmptyString(args, "nodePath") ?? "";
|
|
37436
37504
|
const patience = Math.max(this.runtimeTimeoutMs(), timeoutMs + 5000);
|
|
37437
|
-
|
|
37438
|
-
|
|
37439
|
-
|
|
37440
|
-
|
|
37441
|
-
|
|
37442
|
-
|
|
37443
|
-
|
|
37444
|
-
|
|
37445
|
-
|
|
37446
|
-
|
|
37447
|
-
|
|
37448
|
-
|
|
37449
|
-
}, patience);
|
|
37450
|
-
default:
|
|
37451
|
-
return await this.handleRuntimeCommand("wait_until", {
|
|
37452
|
-
projectPath: args["projectPath"],
|
|
37453
|
-
path: nodePath,
|
|
37454
|
-
property: readString(args, "property") ?? "",
|
|
37455
|
-
value: args["value"],
|
|
37456
|
-
timeout_ms: timeoutMs
|
|
37457
|
-
}, patience);
|
|
37458
|
-
}
|
|
37505
|
+
return op === "signal" ? await this.handleRuntimeCommand("wait_signal", {
|
|
37506
|
+
projectPath: args["projectPath"],
|
|
37507
|
+
path: nodePath,
|
|
37508
|
+
signal: readString(args, "signal") ?? "",
|
|
37509
|
+
timeout_ms: timeoutMs
|
|
37510
|
+
}, patience) : await this.handleRuntimeCommand("wait_until", {
|
|
37511
|
+
projectPath: args["projectPath"],
|
|
37512
|
+
path: nodePath,
|
|
37513
|
+
property: readString(args, "property") ?? "",
|
|
37514
|
+
value: args["value"],
|
|
37515
|
+
timeout_ms: timeoutMs
|
|
37516
|
+
}, patience);
|
|
37459
37517
|
}
|
|
37460
37518
|
}
|
|
37461
37519
|
async function runGodotServer() {
|
|
@@ -38766,7 +38824,9 @@ function renderToolsMarkdown() {
|
|
|
38766
38824
|
const needs = [...tool.requires, ...spec.requires];
|
|
38767
38825
|
const isDefault = tool.defaultOperation === op ? " (default)" : "";
|
|
38768
38826
|
const wants = needs.length > 0 ? ` Needs: ${needs.map((name) => `\`${name}\``).join(", ")}.` : "";
|
|
38769
|
-
|
|
38827
|
+
const spare = argumentsOf(tool, op).filter((name) => !needs.includes(name) && name !== "projectPath");
|
|
38828
|
+
const takes = spare.length > 0 ? ` Takes: ${spare.map((name) => `\`${name}\``).join(", ")}.` : "";
|
|
38829
|
+
lines.push(`- \`op: ${op}\`${isDefault}: ${spec.summary}.${wants}${takes}`);
|
|
38770
38830
|
}
|
|
38771
38831
|
lines.push("");
|
|
38772
38832
|
} else if (tool.requires.length > 0) {
|
|
@@ -10,8 +10,8 @@ const Values = preload("runtime_values.gd")
|
|
|
10
10
|
const FIND_LIMIT: int = 100
|
|
11
11
|
const FIND_LIMIT_CEILING: int = 1000
|
|
12
12
|
|
|
13
|
-
## The most lines one read answers with. A screen is a few dozen; a
|
|
14
|
-
## pointed this at by mistake.
|
|
13
|
+
## The most lines one read answers with, unless asked for fewer. A screen is a few dozen; a
|
|
14
|
+
## thousand is a tree somebody pointed this at by mistake.
|
|
15
15
|
const READ_LIMIT: int = 500
|
|
16
16
|
|
|
17
17
|
var _host: Node
|
|
@@ -153,22 +153,32 @@ func _matches(
|
|
|
153
153
|
## A hidden node is left out and so is everything under it, because what a player reads is what is
|
|
154
154
|
## drawn. [param include_hidden] asks for the lot instead, which is what a caller checking that
|
|
155
155
|
## something is not showing wants.
|
|
156
|
+
##
|
|
157
|
+
## [param limit] is the first few lines rather than all of them, which is how the top of a screen
|
|
158
|
+
## is read without the hall under it: the bar along the top of a guild is eleven lines and the
|
|
159
|
+
## panel it sits on is two hundred.
|
|
156
160
|
func read_text(params: Dictionary) -> Dictionary:
|
|
157
161
|
var root_path: String = str(params.get("root", "/root"))
|
|
158
162
|
var include_hidden: bool = bool(params.get("include_hidden", false))
|
|
163
|
+
var limit: int = clampi(int(params.get("limit", READ_LIMIT)), 1, READ_LIMIT)
|
|
159
164
|
|
|
160
165
|
var root: Node = _host.get_tree().root.get_node_or_null(root_path)
|
|
161
166
|
if root == null:
|
|
162
167
|
return {"type": "error", "message": "Node not found: " + root_path}
|
|
163
168
|
|
|
164
|
-
|
|
165
|
-
|
|
169
|
+
# One line further than asked for, so that whether anything was left behind is read off the
|
|
170
|
+
# walk rather than guessed at from the count: a panel of exactly as many lines as the caller
|
|
171
|
+
# asked for is one they have read all of, and saying otherwise sends them back for nothing.
|
|
172
|
+
var read: PackedStringArray = PackedStringArray()
|
|
173
|
+
_read_into(root, include_hidden, limit + 1, read)
|
|
174
|
+
var more: bool = read.size() > limit
|
|
175
|
+
var lines: PackedStringArray = read.slice(0, limit) if more else read
|
|
166
176
|
return {
|
|
167
177
|
"type": "text",
|
|
168
178
|
"root": root_path,
|
|
169
179
|
"lines": lines,
|
|
170
180
|
"count": lines.size(),
|
|
171
|
-
"truncated":
|
|
181
|
+
"truncated": more,
|
|
172
182
|
}
|
|
173
183
|
|
|
174
184
|
|
|
@@ -180,8 +190,8 @@ func read_text(params: Dictionary) -> Dictionary:
|
|
|
180
190
|
## screen full of forms answered with every label on it and none of the values in it. The hidden
|
|
181
191
|
## check covers a [Window] for the same walk: a dropdown's popup is a child that is not drawn
|
|
182
192
|
## until it is opened, and reading a closed menu would put every item on the screen.
|
|
183
|
-
func _read_into(node: Node, include_hidden: bool, into: PackedStringArray) -> void:
|
|
184
|
-
if into.size() >=
|
|
193
|
+
func _read_into(node: Node, include_hidden: bool, most: int, into: PackedStringArray) -> void:
|
|
194
|
+
if into.size() >= most:
|
|
185
195
|
return
|
|
186
196
|
if not include_hidden and not _drawn(node):
|
|
187
197
|
return
|
|
@@ -189,7 +199,7 @@ func _read_into(node: Node, include_hidden: bool, into: PackedStringArray) -> vo
|
|
|
189
199
|
if not said.is_empty():
|
|
190
200
|
into.append(said)
|
|
191
201
|
for child: Node in node.get_children(true):
|
|
192
|
-
_read_into(child, include_hidden, into)
|
|
202
|
+
_read_into(child, include_hidden, most, into)
|
|
193
203
|
|
|
194
204
|
|
|
195
205
|
## Whether [param node] is on the screen at all, for the two kinds of thing that can be hidden.
|
package/build/index.js
CHANGED
|
@@ -27633,6 +27633,13 @@ function installedAddonVersion(projectPath) {
|
|
|
27633
27633
|
}
|
|
27634
27634
|
|
|
27635
27635
|
// src/tool-definitions.ts
|
|
27636
|
+
function opTakes(spec, op, name) {
|
|
27637
|
+
const ops = spec.parameters[name]?.ops;
|
|
27638
|
+
return ops === undefined || ops.includes(op);
|
|
27639
|
+
}
|
|
27640
|
+
function argumentsOf(spec, op) {
|
|
27641
|
+
return Object.keys(spec.parameters).filter((name) => opTakes(spec, op, name));
|
|
27642
|
+
}
|
|
27636
27643
|
var PROJECT_PATH = {
|
|
27637
27644
|
type: "string",
|
|
27638
27645
|
description: "Absolute path to the project directory, the one holding project.godot."
|
|
@@ -28303,35 +28310,46 @@ var TOOL_SPECS = [
|
|
|
28303
28310
|
projectPath: RUNNING_PROJECT_PATH,
|
|
28304
28311
|
nodePath: {
|
|
28305
28312
|
type: "string",
|
|
28313
|
+
ops: ["tree", "text", "find", "rect", "property"],
|
|
28306
28314
|
description: "tree, find, text: where to start, default /root. rect: the node to place. property: the node to read."
|
|
28307
28315
|
},
|
|
28308
28316
|
property: {
|
|
28309
28317
|
type: "string",
|
|
28318
|
+
ops: ["property", "find"],
|
|
28310
28319
|
description: "property: which one to read. find: read this one off every node matched, so a panel of labels is one call rather than one per label."
|
|
28311
28320
|
},
|
|
28312
|
-
depth: { type: "number", description: "tree: levels to descend. Default 3." },
|
|
28321
|
+
depth: { type: "number", ops: ["tree"], description: "tree: levels to descend. Default 3." },
|
|
28313
28322
|
includeProperties: {
|
|
28314
28323
|
type: "boolean",
|
|
28324
|
+
ops: ["tree"],
|
|
28315
28325
|
description: "tree: include each node's properties. Default false."
|
|
28316
28326
|
},
|
|
28317
28327
|
className: {
|
|
28318
28328
|
type: "string",
|
|
28329
|
+
ops: ["find"],
|
|
28319
28330
|
description: "find: a native class, matching its subclasses too, or a class_name."
|
|
28320
28331
|
},
|
|
28321
|
-
script: { type: "string", description: "find: the script file the node carries." },
|
|
28332
|
+
script: { type: "string", ops: ["find"], description: "find: the script file the node carries." },
|
|
28322
28333
|
namePattern: {
|
|
28323
28334
|
type: "string",
|
|
28335
|
+
ops: ["find"],
|
|
28324
28336
|
description: 'find: a case-insensitive glob on the node name, such as "Enemy*".'
|
|
28325
28337
|
},
|
|
28326
|
-
group: { type: "string", description: "find: a group the node is in." },
|
|
28327
|
-
limit: {
|
|
28338
|
+
group: { type: "string", ops: ["find"], description: "find: a group the node is in." },
|
|
28339
|
+
limit: {
|
|
28340
|
+
type: "number",
|
|
28341
|
+
ops: ["find", "text"],
|
|
28342
|
+
description: "find: the most nodes to answer with, default 100. text: the most lines, default 500, with truncated saying whether there were more."
|
|
28343
|
+
},
|
|
28328
28344
|
includeHidden: {
|
|
28329
28345
|
type: "boolean",
|
|
28346
|
+
ops: ["text"],
|
|
28330
28347
|
description: "text: read hidden nodes as well, for checking that something is not showing. Default false."
|
|
28331
28348
|
},
|
|
28332
28349
|
metrics: {
|
|
28333
28350
|
type: "array",
|
|
28334
28351
|
items: { type: "string" },
|
|
28352
|
+
ops: ["metrics"],
|
|
28335
28353
|
description: "metrics: which to read. Default all."
|
|
28336
28354
|
}
|
|
28337
28355
|
},
|
|
@@ -28364,10 +28382,14 @@ var TOOL_SPECS = [
|
|
|
28364
28382
|
parameters: {
|
|
28365
28383
|
projectPath: RUNNING_PROJECT_PATH,
|
|
28366
28384
|
nodePath: { type: "string", description: 'Absolute node path, such as "/root/Main/Player".' },
|
|
28367
|
-
property: { type: "string" },
|
|
28368
|
-
value: { description: "set: the value, fitted to the property's type." },
|
|
28369
|
-
method: { type: "string" },
|
|
28370
|
-
args: {
|
|
28385
|
+
property: { type: "string", ops: ["set"] },
|
|
28386
|
+
value: { ops: ["set"], description: "set: the value, fitted to the property's type." },
|
|
28387
|
+
method: { type: "string", ops: ["call"] },
|
|
28388
|
+
args: {
|
|
28389
|
+
type: "array",
|
|
28390
|
+
ops: ["call"],
|
|
28391
|
+
description: "call: the arguments, fitted to the method's parameter types."
|
|
28392
|
+
}
|
|
28371
28393
|
},
|
|
28372
28394
|
requires: ["nodePath"],
|
|
28373
28395
|
operations: {
|
|
@@ -28382,6 +28404,7 @@ var TOOL_SPECS = [
|
|
|
28382
28404
|
projectPath: RUNNING_PROJECT_PATH,
|
|
28383
28405
|
viewportPath: {
|
|
28384
28406
|
type: "string",
|
|
28407
|
+
ops: ["viewport"],
|
|
28385
28408
|
description: "viewport: the Viewport node. Default the root viewport."
|
|
28386
28409
|
},
|
|
28387
28410
|
width: { type: "number", description: "Scale the image to this width." },
|
|
@@ -28401,38 +28424,64 @@ var TOOL_SPECS = [
|
|
|
28401
28424
|
projectPath: RUNNING_PROJECT_PATH,
|
|
28402
28425
|
nodePath: {
|
|
28403
28426
|
type: "string",
|
|
28427
|
+
ops: ["click", "choose"],
|
|
28404
28428
|
description: "click: the Control to click, at its centre, or the 3D node to click, where it is drawn. choose: the PopupMenu, or the OptionButton or MenuButton in front of one."
|
|
28405
28429
|
},
|
|
28406
|
-
action: { type: "string", description: "action: the InputMap action name." },
|
|
28430
|
+
action: { type: "string", ops: ["action"], description: "action: the InputMap action name." },
|
|
28407
28431
|
pressed: {
|
|
28408
28432
|
type: "boolean",
|
|
28409
|
-
|
|
28433
|
+
ops: ["action", "key", "mouse_click"],
|
|
28434
|
+
description: "action, key: leave it out and the press is a whole one, down and up a frame apart. true holds it down, false lets go of one being held. mouse_click is one raw event, so it is down unless you say false."
|
|
28410
28435
|
},
|
|
28411
|
-
strength: { type: "number", description: "action: 0 to 1. Default 1." },
|
|
28436
|
+
strength: { type: "number", ops: ["action"], description: "action: 0 to 1. Default 1." },
|
|
28412
28437
|
keycode: {
|
|
28413
28438
|
type: ["string", "number"],
|
|
28439
|
+
ops: ["key"],
|
|
28414
28440
|
description: 'key: the key name, such as "Space" or "A", or its Godot keycode.'
|
|
28415
28441
|
},
|
|
28416
28442
|
text: {
|
|
28417
28443
|
type: "string",
|
|
28444
|
+
ops: ["text", "choose"],
|
|
28418
28445
|
description: "text: what to type. A newline is Enter and a tab is Tab. choose: the item to take, by what it says."
|
|
28419
28446
|
},
|
|
28420
28447
|
index: {
|
|
28421
28448
|
type: "number",
|
|
28449
|
+
ops: ["choose"],
|
|
28422
28450
|
description: "choose: the item to take, by where it is in the list, when text will not do."
|
|
28423
28451
|
},
|
|
28424
|
-
shift: { type: "boolean" },
|
|
28425
|
-
ctrl: { type: "boolean" },
|
|
28426
|
-
alt: { type: "boolean" },
|
|
28427
|
-
x: {
|
|
28428
|
-
|
|
28452
|
+
shift: { type: "boolean", ops: ["key"] },
|
|
28453
|
+
ctrl: { type: "boolean", ops: ["key"] },
|
|
28454
|
+
alt: { type: "boolean", ops: ["key"] },
|
|
28455
|
+
x: {
|
|
28456
|
+
type: "number",
|
|
28457
|
+
ops: ["mouse_click", "mouse_motion"],
|
|
28458
|
+
description: "mouse_click, mouse_motion: window pixels."
|
|
28459
|
+
},
|
|
28460
|
+
y: {
|
|
28461
|
+
type: "number",
|
|
28462
|
+
ops: ["mouse_click", "mouse_motion"],
|
|
28463
|
+
description: "mouse_click, mouse_motion: window pixels."
|
|
28464
|
+
},
|
|
28429
28465
|
button: {
|
|
28430
28466
|
type: ["string", "number"],
|
|
28467
|
+
ops: ["click", "mouse_click"],
|
|
28431
28468
|
description: "click, mouse_click: left, right, middle, wheel_up or wheel_down, or a button number. Default left."
|
|
28432
28469
|
},
|
|
28433
|
-
doubleClick: {
|
|
28434
|
-
|
|
28435
|
-
|
|
28470
|
+
doubleClick: {
|
|
28471
|
+
type: "boolean",
|
|
28472
|
+
ops: ["click", "mouse_click"],
|
|
28473
|
+
description: "click, mouse_click: default false."
|
|
28474
|
+
},
|
|
28475
|
+
relativeX: {
|
|
28476
|
+
type: "number",
|
|
28477
|
+
ops: ["mouse_motion"],
|
|
28478
|
+
description: "mouse_motion: movement since the last event."
|
|
28479
|
+
},
|
|
28480
|
+
relativeY: {
|
|
28481
|
+
type: "number",
|
|
28482
|
+
ops: ["mouse_motion"],
|
|
28483
|
+
description: "mouse_motion: movement since the last event."
|
|
28484
|
+
}
|
|
28436
28485
|
},
|
|
28437
28486
|
requires: [],
|
|
28438
28487
|
operations: {
|
|
@@ -28461,14 +28510,16 @@ var TOOL_SPECS = [
|
|
|
28461
28510
|
projectPath: RUNNING_PROJECT_PATH,
|
|
28462
28511
|
frames: {
|
|
28463
28512
|
type: "number",
|
|
28513
|
+
ops: ["frames"],
|
|
28464
28514
|
description: "frames: how many to let pass, 1 to 600. More than that is refused."
|
|
28465
28515
|
},
|
|
28466
|
-
nodePath: { type: "string", description: "signal, until: the node." },
|
|
28467
|
-
signal: { type: "string", description: "signal: the signal name." },
|
|
28468
|
-
property: { type: "string", description: "until: the property name." },
|
|
28469
|
-
value: { description: "until: the value to wait for, fitted to the property's type." },
|
|
28516
|
+
nodePath: { type: "string", ops: ["signal", "until"], description: "signal, until: the node." },
|
|
28517
|
+
signal: { type: "string", ops: ["signal"], description: "signal: the signal name." },
|
|
28518
|
+
property: { type: "string", ops: ["until"], description: "until: the property name." },
|
|
28519
|
+
value: { ops: ["until"], description: "until: the value to wait for, fitted to the property's type." },
|
|
28470
28520
|
timeoutMs: {
|
|
28471
28521
|
type: "number",
|
|
28522
|
+
ops: ["signal", "until"],
|
|
28472
28523
|
description: "signal, until: how long to wait before answering anyway, 1 to 120000. Default 5000."
|
|
28473
28524
|
}
|
|
28474
28525
|
},
|
|
@@ -28516,6 +28567,7 @@ var TOOL_SPECS = [
|
|
|
28516
28567
|
parameters: {
|
|
28517
28568
|
frameId: {
|
|
28518
28569
|
type: "number",
|
|
28570
|
+
ops: ["variables"],
|
|
28519
28571
|
description: "variables: which frame, from a stack answer. Default the innermost."
|
|
28520
28572
|
}
|
|
28521
28573
|
},
|
|
@@ -28554,7 +28606,8 @@ function buildToolDefinitions() {
|
|
|
28554
28606
|
};
|
|
28555
28607
|
}
|
|
28556
28608
|
for (const [name, schema] of Object.entries(spec.parameters)) {
|
|
28557
|
-
|
|
28609
|
+
const { ops: _ops, ...carried } = schema;
|
|
28610
|
+
properties[name] = carried;
|
|
28558
28611
|
}
|
|
28559
28612
|
const required = [...spec.requires];
|
|
28560
28613
|
if (spec.operations && !spec.defaultOperation) {
|
|
@@ -29045,6 +29098,15 @@ class GodotServer {
|
|
|
29045
29098
|
};
|
|
29046
29099
|
}
|
|
29047
29100
|
}
|
|
29101
|
+
if (op !== null) {
|
|
29102
|
+
const elsewhere = Object.keys(args).filter((key) => key !== "op" && !opTakes(spec, op, key));
|
|
29103
|
+
if (elsewhere.length > 0) {
|
|
29104
|
+
return {
|
|
29105
|
+
ok: false,
|
|
29106
|
+
response: this.createErrorResponse(`${spec.name} ${op} does not take ${elsewhere.join(", ")}. ${op} takes: ${argumentsOf(spec, op).join(", ")}.`)
|
|
29107
|
+
};
|
|
29108
|
+
}
|
|
29109
|
+
}
|
|
29048
29110
|
const required = [...spec.requires, ...op !== null ? spec.operations?.[op]?.requires ?? [] : []];
|
|
29049
29111
|
const missing = required.filter((field) => {
|
|
29050
29112
|
const value = Object.hasOwn(args, field) ? args[field] : undefined;
|
|
@@ -29169,7 +29231,8 @@ class GodotServer {
|
|
|
29169
29231
|
return await this.handleRuntimeCommand("read_text", {
|
|
29170
29232
|
projectPath: args["projectPath"],
|
|
29171
29233
|
root: readNonEmptyString(args, "nodePath") ?? "/root",
|
|
29172
|
-
include_hidden: readBoolean(args, "includeHidden") ?? false
|
|
29234
|
+
include_hidden: readBoolean(args, "includeHidden") ?? false,
|
|
29235
|
+
limit: readPositiveNumber(args, "limit") ?? 500
|
|
29173
29236
|
});
|
|
29174
29237
|
case "rect":
|
|
29175
29238
|
return await this.handleRuntimeCommand("get_rect", {
|
|
@@ -30211,31 +30274,26 @@ class GodotServer {
|
|
|
30211
30274
|
});
|
|
30212
30275
|
}
|
|
30213
30276
|
async handleRuntimeWait(op, args) {
|
|
30277
|
+
if (op === "frames") {
|
|
30278
|
+
const frames = readPositiveNumber(args, "frames") ?? 1;
|
|
30279
|
+
const waited = patienceForFrames(frames, this.runtimeTimeoutMs());
|
|
30280
|
+
return await this.handleRuntimeCommand("wait_frames", { projectPath: args["projectPath"], frames }, waited);
|
|
30281
|
+
}
|
|
30214
30282
|
const timeoutMs = readPositiveNumber(args, "timeoutMs") ?? 5000;
|
|
30215
30283
|
const nodePath = readNonEmptyString(args, "nodePath") ?? "";
|
|
30216
30284
|
const patience = Math.max(this.runtimeTimeoutMs(), timeoutMs + 5000);
|
|
30217
|
-
|
|
30218
|
-
|
|
30219
|
-
|
|
30220
|
-
|
|
30221
|
-
|
|
30222
|
-
|
|
30223
|
-
|
|
30224
|
-
|
|
30225
|
-
|
|
30226
|
-
|
|
30227
|
-
|
|
30228
|
-
|
|
30229
|
-
}, patience);
|
|
30230
|
-
default:
|
|
30231
|
-
return await this.handleRuntimeCommand("wait_until", {
|
|
30232
|
-
projectPath: args["projectPath"],
|
|
30233
|
-
path: nodePath,
|
|
30234
|
-
property: readString(args, "property") ?? "",
|
|
30235
|
-
value: args["value"],
|
|
30236
|
-
timeout_ms: timeoutMs
|
|
30237
|
-
}, patience);
|
|
30238
|
-
}
|
|
30285
|
+
return op === "signal" ? await this.handleRuntimeCommand("wait_signal", {
|
|
30286
|
+
projectPath: args["projectPath"],
|
|
30287
|
+
path: nodePath,
|
|
30288
|
+
signal: readString(args, "signal") ?? "",
|
|
30289
|
+
timeout_ms: timeoutMs
|
|
30290
|
+
}, patience) : await this.handleRuntimeCommand("wait_until", {
|
|
30291
|
+
projectPath: args["projectPath"],
|
|
30292
|
+
path: nodePath,
|
|
30293
|
+
property: readString(args, "property") ?? "",
|
|
30294
|
+
value: args["value"],
|
|
30295
|
+
timeout_ms: timeoutMs
|
|
30296
|
+
}, patience);
|
|
30239
30297
|
}
|
|
30240
30298
|
}
|
|
30241
30299
|
async function runGodotServer() {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gdharness",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.9",
|
|
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",
|