gdharness 0.12.13 → 0.12.15
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 +48 -10
- package/build/index.js +15 -2
- package/package.json +1 -1
package/build/cli.js
CHANGED
|
@@ -15759,7 +15759,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
15759
15759
|
},
|
|
15760
15760
|
{
|
|
15761
15761
|
name: "editor_status",
|
|
15762
|
-
description:
|
|
15762
|
+
description: 'Whether the editor addon is connected, which Godot answers, whether the editor is playing something, and whether a game with the runtime addon is reachable. When connected is false, mayYetConnect says whether that is final: the editor dials this server rather than the other way round and backs off between tries, so for the first half-minute after a server starts, false means "not reached yet" as often as it means "no editor". True is worth waiting out; false is an editor that is not there. listeningSince is when this server took the bridge port, which is what that window is measured from and what a caller wanting its own window should read.',
|
|
15763
15763
|
parameters: {},
|
|
15764
15764
|
requires: []
|
|
15765
15765
|
},
|
|
@@ -34405,17 +34405,21 @@ function resolveDefaultBridgeHost() {
|
|
|
34405
34405
|
function servedPort(said) {
|
|
34406
34406
|
return typeof said === "number" && Number.isInteger(said) && said > 0 && said <= 65535 ? said : undefined;
|
|
34407
34407
|
}
|
|
34408
|
+
function mayYetConnect(listeningSince, now = Date.now()) {
|
|
34409
|
+
return listeningSince === undefined || now - listeningSince.getTime() < CONNECT_WINDOW_MS;
|
|
34410
|
+
}
|
|
34408
34411
|
function getDefaultBridge(ownProject = null) {
|
|
34409
34412
|
defaultBridge ??= new GodotBridge(resolveDefaultBridgePort(), resolveDefaultBridgeHost(), DEFAULT_TIMEOUT_MS, ownProject);
|
|
34410
34413
|
return defaultBridge;
|
|
34411
34414
|
}
|
|
34412
|
-
var DEFAULT_PORT = 6505, DEFAULT_HOST = "127.0.0.1", DEFAULT_TIMEOUT_MS = 30000, KEEPALIVE_INTERVAL_MS = 1e4, SECOND_CONNECTION_CLOSE_CODE = 4000, OTHER_PROJECT_CLOSE_CODE = 4001, GodotBridge, defaultBridge = null;
|
|
34415
|
+
var DEFAULT_PORT = 6505, DEFAULT_HOST = "127.0.0.1", DEFAULT_TIMEOUT_MS = 30000, KEEPALIVE_INTERVAL_MS = 1e4, SECOND_CONNECTION_CLOSE_CODE = 4000, OTHER_PROJECT_CLOSE_CODE = 4001, CONNECT_WINDOW_MS = 35000, GodotBridge, defaultBridge = null;
|
|
34413
34416
|
var init_godot_bridge = __esm(() => {
|
|
34414
34417
|
init_wrapper();
|
|
34415
34418
|
init_errors();
|
|
34416
34419
|
init_paths();
|
|
34417
34420
|
init_ports();
|
|
34418
34421
|
GodotBridge = class GodotBridge extends EventEmitter {
|
|
34422
|
+
listeningSince = null;
|
|
34419
34423
|
httpServer = null;
|
|
34420
34424
|
godotWss = null;
|
|
34421
34425
|
socket = null;
|
|
@@ -34478,6 +34482,7 @@ var init_godot_bridge = __esm(() => {
|
|
|
34478
34482
|
});
|
|
34479
34483
|
server.once("listening", () => {
|
|
34480
34484
|
settled = true;
|
|
34485
|
+
this.listeningSince = new Date;
|
|
34481
34486
|
this.httpServer = server;
|
|
34482
34487
|
this.godotWss = godotWss;
|
|
34483
34488
|
const bound = server.address();
|
|
@@ -34554,7 +34559,8 @@ var init_godot_bridge = __esm(() => {
|
|
|
34554
34559
|
debugPort: this.connectionInfo?.debugPort,
|
|
34555
34560
|
openedByAServer: this.connectionInfo?.openedByAServer,
|
|
34556
34561
|
pendingRequests: this.pendingRequests.size,
|
|
34557
|
-
queuedResources: this.resourceQueues.size
|
|
34562
|
+
queuedResources: this.resourceQueues.size,
|
|
34563
|
+
listeningSince: this.listeningSince ?? undefined
|
|
34558
34564
|
};
|
|
34559
34565
|
}
|
|
34560
34566
|
invokeTool(toolName, args) {
|
|
@@ -37337,8 +37343,11 @@ class GodotServer {
|
|
|
37337
37343
|
return this.createErrorResponse(`Cannot create ${dirname9(output.relativePath)} to export into: ${errorMessage(error)}`);
|
|
37338
37344
|
}
|
|
37339
37345
|
const debug = readBoolean(args, "debug") ?? false;
|
|
37346
|
+
const exportLogs = mkdtempSync3(join16(tmpdir5(), "gdharness-export-"));
|
|
37340
37347
|
const exportArgs = [
|
|
37341
37348
|
"--headless",
|
|
37349
|
+
"--log-file",
|
|
37350
|
+
join16(exportLogs, "engine.log"),
|
|
37342
37351
|
"--path",
|
|
37343
37352
|
project.value.path,
|
|
37344
37353
|
debug ? "--export-debug" : "--export-release",
|
|
@@ -37360,6 +37369,8 @@ class GodotServer {
|
|
|
37360
37369
|
log.append("stdout", failed.stdout);
|
|
37361
37370
|
log.append("stderr", failed.stderr);
|
|
37362
37371
|
exitCode = typeof failed.code === "number" ? failed.code : -1;
|
|
37372
|
+
} finally {
|
|
37373
|
+
rmSync9(exportLogs, { recursive: true, force: true });
|
|
37363
37374
|
}
|
|
37364
37375
|
log.finish();
|
|
37365
37376
|
const problems = log.select({ severity: "warning", sinceLastCall: false, limit: 200 });
|
|
@@ -37609,6 +37620,7 @@ class GodotServer {
|
|
|
37609
37620
|
serverVersion: SERVER_VERSION,
|
|
37610
37621
|
addonIsStale: status.connected ? stale : undefined,
|
|
37611
37622
|
bridgeAvailable: this.bridgeStartupError === null,
|
|
37623
|
+
mayYetConnect: status.connected ? undefined : mayYetConnect(status.listeningSince),
|
|
37612
37624
|
startupError: this.bridgeStartupError,
|
|
37613
37625
|
staleNote: stale ? addonMismatch(status.addonVersion, SERVER_VERSION) : undefined,
|
|
37614
37626
|
retryingBridge: this.bridgeRetry === null ? undefined : true,
|
|
@@ -39268,6 +39280,32 @@ function registered(harness, projectPath) {
|
|
|
39268
39280
|
const held = existing[harness.container];
|
|
39269
39281
|
return typeof held === "object" && held !== null && !Array.isArray(held) && SERVER_KEY in held;
|
|
39270
39282
|
}
|
|
39283
|
+
function recordedEnginePath(projectPath) {
|
|
39284
|
+
for (const harness of HARNESSES) {
|
|
39285
|
+
if (harness.scope !== "project" || harness.toml || harness.yaml || harness.snippet) {
|
|
39286
|
+
continue;
|
|
39287
|
+
}
|
|
39288
|
+
const path = configPath(harness, projectPath);
|
|
39289
|
+
if (!existsSync4(path)) {
|
|
39290
|
+
continue;
|
|
39291
|
+
}
|
|
39292
|
+
let parsed;
|
|
39293
|
+
try {
|
|
39294
|
+
parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
39295
|
+
} catch {
|
|
39296
|
+
continue;
|
|
39297
|
+
}
|
|
39298
|
+
const entry = reach(reach(parsed, harness.container), SERVER_KEY);
|
|
39299
|
+
const named = reach(reach(entry, harness.shape === "opencode" ? "environment" : "env"), "GODOT_PATH");
|
|
39300
|
+
if (typeof named === "string" && named !== "" && existsSync4(named)) {
|
|
39301
|
+
return named;
|
|
39302
|
+
}
|
|
39303
|
+
}
|
|
39304
|
+
return null;
|
|
39305
|
+
}
|
|
39306
|
+
function reach(held, key) {
|
|
39307
|
+
return typeof held === "object" && held !== null && !Array.isArray(held) ? held[key] : undefined;
|
|
39308
|
+
}
|
|
39271
39309
|
function disconnect(harness, projectPath) {
|
|
39272
39310
|
const path = configPath(harness, projectPath);
|
|
39273
39311
|
if (!existsSync4(path)) {
|
|
@@ -40057,8 +40095,8 @@ function projectArgument(at) {
|
|
|
40057
40095
|
}
|
|
40058
40096
|
return projectPath;
|
|
40059
40097
|
}
|
|
40060
|
-
async function engine() {
|
|
40061
|
-
const godotPath = await new GodotLocator().find();
|
|
40098
|
+
async function engine(projectPath) {
|
|
40099
|
+
const godotPath = await new GodotLocator().find() ?? (projectPath === undefined ? null : recordedEnginePath(projectPath));
|
|
40062
40100
|
if (godotPath === null) {
|
|
40063
40101
|
throw new UsageError(`No Godot executable found. ${GodotLocator.ADVICE.join(". ")}.`);
|
|
40064
40102
|
}
|
|
@@ -40181,7 +40219,7 @@ export, so turn it off before you ship: gdharness runtime off${where}`);
|
|
|
40181
40219
|
async function setup() {
|
|
40182
40220
|
const projectPath = projectArgument(1);
|
|
40183
40221
|
const runtime = !args.includes("--no-runtime");
|
|
40184
|
-
const godot = await engine();
|
|
40222
|
+
const godot = await engine(projectPath);
|
|
40185
40223
|
for (const addon of installAddons(projectPath)) {
|
|
40186
40224
|
console.log(`${addon.replaced ? "replaced" : "installed"} ${addon.path}`);
|
|
40187
40225
|
}
|
|
@@ -40231,7 +40269,7 @@ async function setup() {
|
|
|
40231
40269
|
}
|
|
40232
40270
|
async function uninstall() {
|
|
40233
40271
|
const projectPath = projectArgument(1);
|
|
40234
|
-
const godot = await engine();
|
|
40272
|
+
const godot = await engine(projectPath);
|
|
40235
40273
|
const named = namedHarnesses();
|
|
40236
40274
|
const from = named.length > 0 ? named : HARNESSES;
|
|
40237
40275
|
for (const harness of from) {
|
|
@@ -40282,8 +40320,8 @@ async function upgrade() {
|
|
|
40282
40320
|
const where = projectPath === process.cwd() ? "" : ` ${projectPath}`;
|
|
40283
40321
|
throw new UsageError(`gdharness is not installed in ${projectPath}. Run gdharness setup${where} instead.`);
|
|
40284
40322
|
}
|
|
40323
|
+
const godot = await engine(projectPath);
|
|
40285
40324
|
console.log(installed === version ? `already ${version}; reinstalling` : `${installed} -> ${version}`);
|
|
40286
|
-
const godot = await engine();
|
|
40287
40325
|
for (const addon of installAddons(projectPath)) {
|
|
40288
40326
|
console.log(`${addon.replaced ? "replaced" : "installed"} ${addon.path}`);
|
|
40289
40327
|
}
|
|
@@ -40341,7 +40379,7 @@ async function runtime3() {
|
|
|
40341
40379
|
throw new UsageError("gdharness runtime takes on or off, then the project directory.");
|
|
40342
40380
|
}
|
|
40343
40381
|
const projectPath = projectArgument(2);
|
|
40344
|
-
const godot = await engine();
|
|
40382
|
+
const godot = await engine(projectPath);
|
|
40345
40383
|
if (state === "on") {
|
|
40346
40384
|
await registerRuntime(godot, projectPath);
|
|
40347
40385
|
return;
|
|
@@ -40363,7 +40401,7 @@ async function runtime3() {
|
|
|
40363
40401
|
}
|
|
40364
40402
|
async function classes() {
|
|
40365
40403
|
const projectPath = projectArgument(1);
|
|
40366
|
-
const godot = await engine();
|
|
40404
|
+
const godot = await engine(projectPath);
|
|
40367
40405
|
const outcome = await runOperation(godot, "refresh_class_cache", {}, projectPath);
|
|
40368
40406
|
said2(outcome, "rebuilding the class list");
|
|
40369
40407
|
if (outcome.ok) {
|
package/build/index.js
CHANGED
|
@@ -25611,8 +25611,13 @@ function resolveDefaultBridgeHost() {
|
|
|
25611
25611
|
function servedPort(said) {
|
|
25612
25612
|
return typeof said === "number" && Number.isInteger(said) && said > 0 && said <= 65535 ? said : undefined;
|
|
25613
25613
|
}
|
|
25614
|
+
var CONNECT_WINDOW_MS = 35000;
|
|
25615
|
+
function mayYetConnect(listeningSince, now = Date.now()) {
|
|
25616
|
+
return listeningSince === undefined || now - listeningSince.getTime() < CONNECT_WINDOW_MS;
|
|
25617
|
+
}
|
|
25614
25618
|
|
|
25615
25619
|
class GodotBridge extends EventEmitter {
|
|
25620
|
+
listeningSince = null;
|
|
25616
25621
|
httpServer = null;
|
|
25617
25622
|
godotWss = null;
|
|
25618
25623
|
socket = null;
|
|
@@ -25675,6 +25680,7 @@ class GodotBridge extends EventEmitter {
|
|
|
25675
25680
|
});
|
|
25676
25681
|
server.once("listening", () => {
|
|
25677
25682
|
settled = true;
|
|
25683
|
+
this.listeningSince = new Date;
|
|
25678
25684
|
this.httpServer = server;
|
|
25679
25685
|
this.godotWss = godotWss;
|
|
25680
25686
|
const bound = server.address();
|
|
@@ -25751,7 +25757,8 @@ class GodotBridge extends EventEmitter {
|
|
|
25751
25757
|
debugPort: this.connectionInfo?.debugPort,
|
|
25752
25758
|
openedByAServer: this.connectionInfo?.openedByAServer,
|
|
25753
25759
|
pendingRequests: this.pendingRequests.size,
|
|
25754
|
-
queuedResources: this.resourceQueues.size
|
|
25760
|
+
queuedResources: this.resourceQueues.size,
|
|
25761
|
+
listeningSince: this.listeningSince ?? undefined
|
|
25755
25762
|
};
|
|
25756
25763
|
}
|
|
25757
25764
|
invokeTool(toolName, args) {
|
|
@@ -28655,7 +28662,7 @@ var TOOL_SPECS = [
|
|
|
28655
28662
|
},
|
|
28656
28663
|
{
|
|
28657
28664
|
name: "editor_status",
|
|
28658
|
-
description:
|
|
28665
|
+
description: 'Whether the editor addon is connected, which Godot answers, whether the editor is playing something, and whether a game with the runtime addon is reachable. When connected is false, mayYetConnect says whether that is final: the editor dials this server rather than the other way round and backs off between tries, so for the first half-minute after a server starts, false means "not reached yet" as often as it means "no editor". True is worth waiting out; false is an editor that is not there. listeningSince is when this server took the bridge port, which is what that window is measured from and what a caller wanting its own window should read.',
|
|
28659
28666
|
parameters: {},
|
|
28660
28667
|
requires: []
|
|
28661
28668
|
},
|
|
@@ -30119,8 +30126,11 @@ class GodotServer {
|
|
|
30119
30126
|
return this.createErrorResponse(`Cannot create ${dirname4(output.relativePath)} to export into: ${errorMessage(error)}`);
|
|
30120
30127
|
}
|
|
30121
30128
|
const debug = readBoolean(args, "debug") ?? false;
|
|
30129
|
+
const exportLogs = mkdtempSync2(join10(tmpdir4(), "gdharness-export-"));
|
|
30122
30130
|
const exportArgs = [
|
|
30123
30131
|
"--headless",
|
|
30132
|
+
"--log-file",
|
|
30133
|
+
join10(exportLogs, "engine.log"),
|
|
30124
30134
|
"--path",
|
|
30125
30135
|
project.value.path,
|
|
30126
30136
|
debug ? "--export-debug" : "--export-release",
|
|
@@ -30142,6 +30152,8 @@ class GodotServer {
|
|
|
30142
30152
|
log.append("stdout", failed.stdout);
|
|
30143
30153
|
log.append("stderr", failed.stderr);
|
|
30144
30154
|
exitCode = typeof failed.code === "number" ? failed.code : -1;
|
|
30155
|
+
} finally {
|
|
30156
|
+
rmSync5(exportLogs, { recursive: true, force: true });
|
|
30145
30157
|
}
|
|
30146
30158
|
log.finish();
|
|
30147
30159
|
const problems = log.select({ severity: "warning", sinceLastCall: false, limit: 200 });
|
|
@@ -30391,6 +30403,7 @@ class GodotServer {
|
|
|
30391
30403
|
serverVersion: SERVER_VERSION,
|
|
30392
30404
|
addonIsStale: status.connected ? stale : undefined,
|
|
30393
30405
|
bridgeAvailable: this.bridgeStartupError === null,
|
|
30406
|
+
mayYetConnect: status.connected ? undefined : mayYetConnect(status.listeningSince),
|
|
30394
30407
|
startupError: this.bridgeStartupError,
|
|
30395
30408
|
staleNote: stale ? addonMismatch(status.addonVersion, SERVER_VERSION) : undefined,
|
|
30396
30409
|
retryingBridge: this.bridgeRetry === null ? undefined : true,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gdharness",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.15",
|
|
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",
|