gdharness 0.6.7 → 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
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
|
},
|
|
@@ -15785,7 +15804,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
15785
15804
|
operations: {
|
|
15786
15805
|
tree: { summary: "the live scene tree", requires: [] },
|
|
15787
15806
|
text: {
|
|
15788
|
-
summary: "every line of text under nodePath, in the order somebody reads the screen, leaving out what is hidden and everything under it",
|
|
15807
|
+
summary: "every line of text under nodePath, the values in its fields included, in the order somebody reads the screen, leaving out what is hidden and everything under it",
|
|
15789
15808
|
requires: []
|
|
15790
15809
|
},
|
|
15791
15810
|
find: {
|
|
@@ -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: {
|
|
@@ -15893,7 +15943,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
15893
15943
|
action: { summary: "press an action, or hold it", requires: ["action"] },
|
|
15894
15944
|
key: { summary: "press a key, or hold it", requires: ["keycode"] },
|
|
15895
15945
|
text: {
|
|
15896
|
-
summary: "type a string
|
|
15946
|
+
summary: "type a string into the field being edited, a character at a time, and say what it landed in",
|
|
15897
15947
|
requires: ["text"]
|
|
15898
15948
|
},
|
|
15899
15949
|
mouse_click: { summary: "one mouse button event at a position", requires: ["x", "y"] },
|
|
@@ -15905,14 +15955,19 @@ var init_tool_definitions = __esm(() => {
|
|
|
15905
15955
|
description: "Lets the running game get on with it and answers when something has happened: a number of frames, a signal, or a property reaching a value. Needs the game running with the runtime addon.",
|
|
15906
15956
|
parameters: {
|
|
15907
15957
|
projectPath: RUNNING_PROJECT_PATH,
|
|
15908
|
-
frames: {
|
|
15909
|
-
|
|
15910
|
-
|
|
15911
|
-
|
|
15912
|
-
|
|
15958
|
+
frames: {
|
|
15959
|
+
type: "number",
|
|
15960
|
+
ops: ["frames"],
|
|
15961
|
+
description: "frames: how many to let pass, 1 to 600. More than that is refused."
|
|
15962
|
+
},
|
|
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." },
|
|
15913
15967
|
timeoutMs: {
|
|
15914
15968
|
type: "number",
|
|
15915
|
-
|
|
15969
|
+
ops: ["signal", "until"],
|
|
15970
|
+
description: "signal, until: how long to wait before answering anyway, 1 to 120000. Default 5000."
|
|
15916
15971
|
}
|
|
15917
15972
|
},
|
|
15918
15973
|
requires: [],
|
|
@@ -15959,6 +16014,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
15959
16014
|
parameters: {
|
|
15960
16015
|
frameId: {
|
|
15961
16016
|
type: "number",
|
|
16017
|
+
ops: ["variables"],
|
|
15962
16018
|
description: "variables: which frame, from a stack answer. Default the innermost."
|
|
15963
16019
|
}
|
|
15964
16020
|
},
|
|
@@ -36262,6 +36318,15 @@ class GodotServer {
|
|
|
36262
36318
|
};
|
|
36263
36319
|
}
|
|
36264
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
|
+
}
|
|
36265
36330
|
const required = [...spec.requires, ...op !== null ? spec.operations?.[op]?.requires ?? [] : []];
|
|
36266
36331
|
const missing = required.filter((field) => {
|
|
36267
36332
|
const value = Object.hasOwn(args, field) ? args[field] : undefined;
|
|
@@ -36386,7 +36451,8 @@ class GodotServer {
|
|
|
36386
36451
|
return await this.handleRuntimeCommand("read_text", {
|
|
36387
36452
|
projectPath: args["projectPath"],
|
|
36388
36453
|
root: readNonEmptyString(args, "nodePath") ?? "/root",
|
|
36389
|
-
include_hidden: readBoolean(args, "includeHidden") ?? false
|
|
36454
|
+
include_hidden: readBoolean(args, "includeHidden") ?? false,
|
|
36455
|
+
limit: readPositiveNumber(args, "limit") ?? 500
|
|
36390
36456
|
});
|
|
36391
36457
|
case "rect":
|
|
36392
36458
|
return await this.handleRuntimeCommand("get_rect", {
|
|
@@ -37428,31 +37494,26 @@ class GodotServer {
|
|
|
37428
37494
|
});
|
|
37429
37495
|
}
|
|
37430
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
|
+
}
|
|
37431
37502
|
const timeoutMs = readPositiveNumber(args, "timeoutMs") ?? 5000;
|
|
37432
37503
|
const nodePath = readNonEmptyString(args, "nodePath") ?? "";
|
|
37433
37504
|
const patience = Math.max(this.runtimeTimeoutMs(), timeoutMs + 5000);
|
|
37434
|
-
|
|
37435
|
-
|
|
37436
|
-
|
|
37437
|
-
|
|
37438
|
-
|
|
37439
|
-
|
|
37440
|
-
|
|
37441
|
-
|
|
37442
|
-
|
|
37443
|
-
|
|
37444
|
-
|
|
37445
|
-
|
|
37446
|
-
}, patience);
|
|
37447
|
-
default:
|
|
37448
|
-
return await this.handleRuntimeCommand("wait_until", {
|
|
37449
|
-
projectPath: args["projectPath"],
|
|
37450
|
-
path: nodePath,
|
|
37451
|
-
property: readString(args, "property") ?? "",
|
|
37452
|
-
value: args["value"],
|
|
37453
|
-
timeout_ms: timeoutMs
|
|
37454
|
-
}, patience);
|
|
37455
|
-
}
|
|
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);
|
|
37456
37517
|
}
|
|
37457
37518
|
}
|
|
37458
37519
|
async function runGodotServer() {
|
|
@@ -38763,7 +38824,9 @@ function renderToolsMarkdown() {
|
|
|
38763
38824
|
const needs = [...tool.requires, ...spec.requires];
|
|
38764
38825
|
const isDefault = tool.defaultOperation === op ? " (default)" : "";
|
|
38765
38826
|
const wants = needs.length > 0 ? ` Needs: ${needs.map((name) => `\`${name}\``).join(", ")}.` : "";
|
|
38766
|
-
|
|
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}`);
|
|
38767
38830
|
}
|
|
38768
38831
|
lines.push("");
|
|
38769
38832
|
} else if (tool.requires.length > 0) {
|
|
@@ -38827,7 +38890,7 @@ that is running, and the project on disk. ${TOOL_SPECS.length} tools, named \`do
|
|
|
38827
38890
|
| Where a control is, and whether it is visible | \`runtime_inspect\` \`find\`, \`rect\` |
|
|
38828
38891
|
| What a property reads right now | \`runtime_inspect\` \`property\`, \`runtime_invoke\` |
|
|
38829
38892
|
| Press a button | \`runtime_input click\`, which says what was under the pointer |
|
|
38830
|
-
| Fill in a field | \`runtime_input click\` on it, then \`runtime_input text
|
|
38893
|
+
| Fill in a field | \`runtime_input click\` on it, then \`runtime_input text\`; a submitted field has to be clicked again |
|
|
38831
38894
|
| Click somebody standing in a 3D room | \`runtime_input click\` on the Node3D, which aims at what it draws |
|
|
38832
38895
|
| Pick something out of a dropdown | \`runtime_input choose\` on it, by what the item says |
|
|
38833
38896
|
| Answer a dialog, or press a bound key | \`runtime_input action\` or \`key\`, a whole press unless you hold it |
|
|
@@ -161,6 +161,11 @@ func inject_text(params: Dictionary) -> Dictionary:
|
|
|
161
161
|
return {"type": "error", "message": "text required"}
|
|
162
162
|
|
|
163
163
|
var viewport: Viewport = _host.get_tree().root
|
|
164
|
+
var focused: Control = viewport.gui_get_focus_owner()
|
|
165
|
+
var shut: String = _shut_to_typing(focused)
|
|
166
|
+
if not shut.is_empty():
|
|
167
|
+
return {"type": "error", "message": shut}
|
|
168
|
+
|
|
164
169
|
for index: int in text.length():
|
|
165
170
|
var down: InputEventKey = _typed(text.unicode_at(index))
|
|
166
171
|
viewport.push_input(down)
|
|
@@ -169,7 +174,41 @@ func inject_text(params: Dictionary) -> Dictionary:
|
|
|
169
174
|
up.pressed = false
|
|
170
175
|
viewport.push_input(up)
|
|
171
176
|
|
|
172
|
-
|
|
177
|
+
# Where it went, which is the one thing a caller cannot see from here. Null is a game reading
|
|
178
|
+
# keys for itself with nothing focused, which is a real thing to be typing at.
|
|
179
|
+
var into: Variant = null
|
|
180
|
+
if focused != null:
|
|
181
|
+
into = str(focused.get_path())
|
|
182
|
+
|
|
183
|
+
return {
|
|
184
|
+
"type": "input_injected",
|
|
185
|
+
"input_type": "text",
|
|
186
|
+
"text": text,
|
|
187
|
+
"characters": text.length(),
|
|
188
|
+
"into": into,
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
## Why nothing typed would reach [param focused], or "" when it would.
|
|
193
|
+
##
|
|
194
|
+
## Having the focus is not the same as being edited. Since Godot 4.4 a field is focused and shut
|
|
195
|
+
## until something opens it, which is what a click does and what submitting undoes: press Enter in
|
|
196
|
+
## a box and it keeps the focus and drops every key that arrives afterwards. Typing into one
|
|
197
|
+
## answered that the characters had gone in and put nothing anywhere, which is the shape a refusal
|
|
198
|
+
## exists to prevent. Measured on a spin box in a real game: `has_focus` true, `is_editing` false,
|
|
199
|
+
## three characters reported and the field unchanged.
|
|
200
|
+
##
|
|
201
|
+
## Only a [LineEdit] is refused: it is the one control the engine will say this about, since
|
|
202
|
+
## [TextEdit] has no editing state of its own. A game reading keys for itself is typed at with
|
|
203
|
+
## nothing focused at all, and that is not this tool's business to judge.
|
|
204
|
+
static func _shut_to_typing(focused: Control) -> String:
|
|
205
|
+
var field: LineEdit = focused as LineEdit
|
|
206
|
+
if field == null or field.is_editing():
|
|
207
|
+
return ""
|
|
208
|
+
return (
|
|
209
|
+
"%s has the focus and is not being edited, so nothing typed lands in it. Click it first."
|
|
210
|
+
% focused.get_path()
|
|
211
|
+
)
|
|
173
212
|
|
|
174
213
|
|
|
175
214
|
## The key press that produces [param glyph], as a keyboard would send it.
|
|
@@ -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,38 +153,62 @@ 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
|
|
|
175
185
|
## Walks [param node] depth first, which is the order the screen is laid out in and the order a
|
|
176
186
|
## person reads it.
|
|
177
|
-
|
|
178
|
-
|
|
187
|
+
##
|
|
188
|
+
## Internal children as well, because the number in a SpinBox is one: the field a player reads it
|
|
189
|
+
## in is a LineEdit the engine builds inside the box and leaves out of `get_children()`, so a
|
|
190
|
+
## screen full of forms answered with every label on it and none of the values in it. The hidden
|
|
191
|
+
## check covers a [Window] for the same walk: a dropdown's popup is a child that is not drawn
|
|
192
|
+
## until it is opened, and reading a closed menu would put every item on the screen.
|
|
193
|
+
func _read_into(node: Node, include_hidden: bool, most: int, into: PackedStringArray) -> void:
|
|
194
|
+
if into.size() >= most:
|
|
179
195
|
return
|
|
180
|
-
|
|
181
|
-
if not include_hidden and control != null and not control.visible:
|
|
196
|
+
if not include_hidden and not _drawn(node):
|
|
182
197
|
return
|
|
183
198
|
var said: String = _said_by(node)
|
|
184
199
|
if not said.is_empty():
|
|
185
200
|
into.append(said)
|
|
186
|
-
for child: Node in node.get_children():
|
|
187
|
-
_read_into(child, include_hidden, into)
|
|
201
|
+
for child: Node in node.get_children(true):
|
|
202
|
+
_read_into(child, include_hidden, most, into)
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
## Whether [param node] is on the screen at all, for the two kinds of thing that can be hidden.
|
|
206
|
+
static func _drawn(node: Node) -> bool:
|
|
207
|
+
var control: CanvasItem = node as CanvasItem
|
|
208
|
+
if control != null:
|
|
209
|
+
return control.visible
|
|
210
|
+
var window: Window = node as Window
|
|
211
|
+
return window == null or window.visible
|
|
188
212
|
|
|
189
213
|
|
|
190
214
|
## What one node says, or "" for a node that says nothing. Anything with a `text` property, which
|
|
@@ -9,6 +9,9 @@ const Values = preload("runtime_values.gd")
|
|
|
9
9
|
## since given up on the reply.
|
|
10
10
|
const CEILING_MSEC: int = 120000
|
|
11
11
|
|
|
12
|
+
## The most frames one wait may cover, which is about half a minute of a game drawing slowly.
|
|
13
|
+
const MOST_FRAMES: int = 600
|
|
14
|
+
|
|
12
15
|
var _host: Node
|
|
13
16
|
var _values: Values
|
|
14
17
|
|
|
@@ -18,8 +21,21 @@ func _init(host: Node, values: Values) -> void:
|
|
|
18
21
|
_values = values
|
|
19
22
|
|
|
20
23
|
|
|
24
|
+
## Refuses [param asked] rather than bringing it inside [param least] to [param most].
|
|
25
|
+
##
|
|
26
|
+
## A number quietly brought inside the range is a wait that did not last as long as the caller
|
|
27
|
+
## believes and a timeout that gave up sooner: asking for 900 frames and waiting 600 reads as 900
|
|
28
|
+
## frames of the game having passed, and everything measured off it is out by that much.
|
|
29
|
+
static func _out_of_range(named: String, asked: int, least: int, most: int) -> Dictionary:
|
|
30
|
+
return {
|
|
31
|
+
"type": "error", "message": "%s is %d, and %s takes %d to %d." % [named, asked, named, least, most]
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
|
|
21
35
|
func wait_frames(params: Dictionary) -> Dictionary:
|
|
22
|
-
var frames: int =
|
|
36
|
+
var frames: int = int(params.get("frames", 1))
|
|
37
|
+
if frames < 1 or frames > MOST_FRAMES:
|
|
38
|
+
return _out_of_range("frames", frames, 1, MOST_FRAMES)
|
|
23
39
|
var started: int = Time.get_ticks_msec()
|
|
24
40
|
for _frame: int in frames:
|
|
25
41
|
await _host.get_tree().process_frame
|
|
@@ -30,7 +46,9 @@ func wait_frames(params: Dictionary) -> Dictionary:
|
|
|
30
46
|
func wait_signal(params: Dictionary) -> Dictionary:
|
|
31
47
|
var node_path: String = str(params.get("path", ""))
|
|
32
48
|
var signal_name: String = str(params.get("signal", ""))
|
|
33
|
-
var timeout_ms: int =
|
|
49
|
+
var timeout_ms: int = int(params.get("timeout_ms", 5000))
|
|
50
|
+
if timeout_ms < 1 or timeout_ms > CEILING_MSEC:
|
|
51
|
+
return _out_of_range("timeout_ms", timeout_ms, 1, CEILING_MSEC)
|
|
34
52
|
if node_path.is_empty() or signal_name.is_empty():
|
|
35
53
|
return {"type": "error", "message": "Node path and signal name required"}
|
|
36
54
|
|
|
@@ -65,7 +83,9 @@ func wait_signal(params: Dictionary) -> Dictionary:
|
|
|
65
83
|
func wait_until(params: Dictionary) -> Dictionary:
|
|
66
84
|
var node_path: String = str(params.get("path", ""))
|
|
67
85
|
var property: String = str(params.get("property", ""))
|
|
68
|
-
var timeout_ms: int =
|
|
86
|
+
var timeout_ms: int = int(params.get("timeout_ms", 5000))
|
|
87
|
+
if timeout_ms < 1 or timeout_ms > CEILING_MSEC:
|
|
88
|
+
return _out_of_range("timeout_ms", timeout_ms, 1, CEILING_MSEC)
|
|
69
89
|
if node_path.is_empty() or property.is_empty():
|
|
70
90
|
return {"type": "error", "message": "Node path and property required"}
|
|
71
91
|
if not params.has("value"):
|
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
|
},
|
|
@@ -28339,7 +28357,7 @@ var TOOL_SPECS = [
|
|
|
28339
28357
|
operations: {
|
|
28340
28358
|
tree: { summary: "the live scene tree", requires: [] },
|
|
28341
28359
|
text: {
|
|
28342
|
-
summary: "every line of text under nodePath, in the order somebody reads the screen, leaving out what is hidden and everything under it",
|
|
28360
|
+
summary: "every line of text under nodePath, the values in its fields included, in the order somebody reads the screen, leaving out what is hidden and everything under it",
|
|
28343
28361
|
requires: []
|
|
28344
28362
|
},
|
|
28345
28363
|
find: {
|
|
@@ -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: {
|
|
@@ -28447,7 +28496,7 @@ var TOOL_SPECS = [
|
|
|
28447
28496
|
action: { summary: "press an action, or hold it", requires: ["action"] },
|
|
28448
28497
|
key: { summary: "press a key, or hold it", requires: ["keycode"] },
|
|
28449
28498
|
text: {
|
|
28450
|
-
summary: "type a string
|
|
28499
|
+
summary: "type a string into the field being edited, a character at a time, and say what it landed in",
|
|
28451
28500
|
requires: ["text"]
|
|
28452
28501
|
},
|
|
28453
28502
|
mouse_click: { summary: "one mouse button event at a position", requires: ["x", "y"] },
|
|
@@ -28459,14 +28508,19 @@ var TOOL_SPECS = [
|
|
|
28459
28508
|
description: "Lets the running game get on with it and answers when something has happened: a number of frames, a signal, or a property reaching a value. Needs the game running with the runtime addon.",
|
|
28460
28509
|
parameters: {
|
|
28461
28510
|
projectPath: RUNNING_PROJECT_PATH,
|
|
28462
|
-
frames: {
|
|
28463
|
-
|
|
28464
|
-
|
|
28465
|
-
|
|
28466
|
-
|
|
28511
|
+
frames: {
|
|
28512
|
+
type: "number",
|
|
28513
|
+
ops: ["frames"],
|
|
28514
|
+
description: "frames: how many to let pass, 1 to 600. More than that is refused."
|
|
28515
|
+
},
|
|
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." },
|
|
28467
28520
|
timeoutMs: {
|
|
28468
28521
|
type: "number",
|
|
28469
|
-
|
|
28522
|
+
ops: ["signal", "until"],
|
|
28523
|
+
description: "signal, until: how long to wait before answering anyway, 1 to 120000. Default 5000."
|
|
28470
28524
|
}
|
|
28471
28525
|
},
|
|
28472
28526
|
requires: [],
|
|
@@ -28513,6 +28567,7 @@ var TOOL_SPECS = [
|
|
|
28513
28567
|
parameters: {
|
|
28514
28568
|
frameId: {
|
|
28515
28569
|
type: "number",
|
|
28570
|
+
ops: ["variables"],
|
|
28516
28571
|
description: "variables: which frame, from a stack answer. Default the innermost."
|
|
28517
28572
|
}
|
|
28518
28573
|
},
|
|
@@ -28551,7 +28606,8 @@ function buildToolDefinitions() {
|
|
|
28551
28606
|
};
|
|
28552
28607
|
}
|
|
28553
28608
|
for (const [name, schema] of Object.entries(spec.parameters)) {
|
|
28554
|
-
|
|
28609
|
+
const { ops: _ops, ...carried } = schema;
|
|
28610
|
+
properties[name] = carried;
|
|
28555
28611
|
}
|
|
28556
28612
|
const required = [...spec.requires];
|
|
28557
28613
|
if (spec.operations && !spec.defaultOperation) {
|
|
@@ -29042,6 +29098,15 @@ class GodotServer {
|
|
|
29042
29098
|
};
|
|
29043
29099
|
}
|
|
29044
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
|
+
}
|
|
29045
29110
|
const required = [...spec.requires, ...op !== null ? spec.operations?.[op]?.requires ?? [] : []];
|
|
29046
29111
|
const missing = required.filter((field) => {
|
|
29047
29112
|
const value = Object.hasOwn(args, field) ? args[field] : undefined;
|
|
@@ -29166,7 +29231,8 @@ class GodotServer {
|
|
|
29166
29231
|
return await this.handleRuntimeCommand("read_text", {
|
|
29167
29232
|
projectPath: args["projectPath"],
|
|
29168
29233
|
root: readNonEmptyString(args, "nodePath") ?? "/root",
|
|
29169
|
-
include_hidden: readBoolean(args, "includeHidden") ?? false
|
|
29234
|
+
include_hidden: readBoolean(args, "includeHidden") ?? false,
|
|
29235
|
+
limit: readPositiveNumber(args, "limit") ?? 500
|
|
29170
29236
|
});
|
|
29171
29237
|
case "rect":
|
|
29172
29238
|
return await this.handleRuntimeCommand("get_rect", {
|
|
@@ -30208,31 +30274,26 @@ class GodotServer {
|
|
|
30208
30274
|
});
|
|
30209
30275
|
}
|
|
30210
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
|
+
}
|
|
30211
30282
|
const timeoutMs = readPositiveNumber(args, "timeoutMs") ?? 5000;
|
|
30212
30283
|
const nodePath = readNonEmptyString(args, "nodePath") ?? "";
|
|
30213
30284
|
const patience = Math.max(this.runtimeTimeoutMs(), timeoutMs + 5000);
|
|
30214
|
-
|
|
30215
|
-
|
|
30216
|
-
|
|
30217
|
-
|
|
30218
|
-
|
|
30219
|
-
|
|
30220
|
-
|
|
30221
|
-
|
|
30222
|
-
|
|
30223
|
-
|
|
30224
|
-
|
|
30225
|
-
|
|
30226
|
-
}, patience);
|
|
30227
|
-
default:
|
|
30228
|
-
return await this.handleRuntimeCommand("wait_until", {
|
|
30229
|
-
projectPath: args["projectPath"],
|
|
30230
|
-
path: nodePath,
|
|
30231
|
-
property: readString(args, "property") ?? "",
|
|
30232
|
-
value: args["value"],
|
|
30233
|
-
timeout_ms: timeoutMs
|
|
30234
|
-
}, patience);
|
|
30235
|
-
}
|
|
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);
|
|
30236
30297
|
}
|
|
30237
30298
|
}
|
|
30238
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",
|