gdharness 0.12.13 → 0.12.14

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
@@ -15759,7 +15759,7 @@ var init_tool_definitions = __esm(() => {
15759
15759
  },
15760
15760
  {
15761
15761
  name: "editor_status",
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.",
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.',
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 < 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 = Date.now();
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) {
@@ -37609,6 +37615,7 @@ class GodotServer {
37609
37615
  serverVersion: SERVER_VERSION,
37610
37616
  addonIsStale: status.connected ? stale : undefined,
37611
37617
  bridgeAvailable: this.bridgeStartupError === null,
37618
+ mayYetConnect: status.connected ? undefined : mayYetConnect(status.listeningSince),
37612
37619
  startupError: this.bridgeStartupError,
37613
37620
  staleNote: stale ? addonMismatch(status.addonVersion, SERVER_VERSION) : undefined,
37614
37621
  retryingBridge: this.bridgeRetry === null ? undefined : true,
@@ -39268,6 +39275,32 @@ function registered(harness, projectPath) {
39268
39275
  const held = existing[harness.container];
39269
39276
  return typeof held === "object" && held !== null && !Array.isArray(held) && SERVER_KEY in held;
39270
39277
  }
39278
+ function recordedEnginePath(projectPath) {
39279
+ for (const harness of HARNESSES) {
39280
+ if (harness.scope !== "project" || harness.toml || harness.yaml || harness.snippet) {
39281
+ continue;
39282
+ }
39283
+ const path = configPath(harness, projectPath);
39284
+ if (!existsSync4(path)) {
39285
+ continue;
39286
+ }
39287
+ let parsed;
39288
+ try {
39289
+ parsed = JSON.parse(readFileSync(path, "utf8"));
39290
+ } catch {
39291
+ continue;
39292
+ }
39293
+ const entry = reach(reach(parsed, harness.container), SERVER_KEY);
39294
+ const named = reach(reach(entry, harness.shape === "opencode" ? "environment" : "env"), "GODOT_PATH");
39295
+ if (typeof named === "string" && named !== "" && existsSync4(named)) {
39296
+ return named;
39297
+ }
39298
+ }
39299
+ return null;
39300
+ }
39301
+ function reach(held, key) {
39302
+ return typeof held === "object" && held !== null && !Array.isArray(held) ? held[key] : undefined;
39303
+ }
39271
39304
  function disconnect(harness, projectPath) {
39272
39305
  const path = configPath(harness, projectPath);
39273
39306
  if (!existsSync4(path)) {
@@ -40057,8 +40090,8 @@ function projectArgument(at) {
40057
40090
  }
40058
40091
  return projectPath;
40059
40092
  }
40060
- async function engine() {
40061
- const godotPath = await new GodotLocator().find();
40093
+ async function engine(projectPath) {
40094
+ const godotPath = await new GodotLocator().find() ?? (projectPath === undefined ? null : recordedEnginePath(projectPath));
40062
40095
  if (godotPath === null) {
40063
40096
  throw new UsageError(`No Godot executable found. ${GodotLocator.ADVICE.join(". ")}.`);
40064
40097
  }
@@ -40181,7 +40214,7 @@ export, so turn it off before you ship: gdharness runtime off${where}`);
40181
40214
  async function setup() {
40182
40215
  const projectPath = projectArgument(1);
40183
40216
  const runtime = !args.includes("--no-runtime");
40184
- const godot = await engine();
40217
+ const godot = await engine(projectPath);
40185
40218
  for (const addon of installAddons(projectPath)) {
40186
40219
  console.log(`${addon.replaced ? "replaced" : "installed"} ${addon.path}`);
40187
40220
  }
@@ -40231,7 +40264,7 @@ async function setup() {
40231
40264
  }
40232
40265
  async function uninstall() {
40233
40266
  const projectPath = projectArgument(1);
40234
- const godot = await engine();
40267
+ const godot = await engine(projectPath);
40235
40268
  const named = namedHarnesses();
40236
40269
  const from = named.length > 0 ? named : HARNESSES;
40237
40270
  for (const harness of from) {
@@ -40282,8 +40315,8 @@ async function upgrade() {
40282
40315
  const where = projectPath === process.cwd() ? "" : ` ${projectPath}`;
40283
40316
  throw new UsageError(`gdharness is not installed in ${projectPath}. Run gdharness setup${where} instead.`);
40284
40317
  }
40318
+ const godot = await engine(projectPath);
40285
40319
  console.log(installed === version ? `already ${version}; reinstalling` : `${installed} -> ${version}`);
40286
- const godot = await engine();
40287
40320
  for (const addon of installAddons(projectPath)) {
40288
40321
  console.log(`${addon.replaced ? "replaced" : "installed"} ${addon.path}`);
40289
40322
  }
@@ -40341,7 +40374,7 @@ async function runtime3() {
40341
40374
  throw new UsageError("gdharness runtime takes on or off, then the project directory.");
40342
40375
  }
40343
40376
  const projectPath = projectArgument(2);
40344
- const godot = await engine();
40377
+ const godot = await engine(projectPath);
40345
40378
  if (state === "on") {
40346
40379
  await registerRuntime(godot, projectPath);
40347
40380
  return;
@@ -40363,7 +40396,7 @@ async function runtime3() {
40363
40396
  }
40364
40397
  async function classes() {
40365
40398
  const projectPath = projectArgument(1);
40366
- const godot = await engine();
40399
+ const godot = await engine(projectPath);
40367
40400
  const outcome = await runOperation(godot, "refresh_class_cache", {}, projectPath);
40368
40401
  said2(outcome, "rebuilding the class list");
40369
40402
  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 < 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 = Date.now();
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: "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.",
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.',
28659
28666
  parameters: {},
28660
28667
  requires: []
28661
28668
  },
@@ -30391,6 +30398,7 @@ class GodotServer {
30391
30398
  serverVersion: SERVER_VERSION,
30392
30399
  addonIsStale: status.connected ? stale : undefined,
30393
30400
  bridgeAvailable: this.bridgeStartupError === null,
30401
+ mayYetConnect: status.connected ? undefined : mayYetConnect(status.listeningSince),
30394
30402
  startupError: this.bridgeStartupError,
30395
30403
  staleNote: stale ? addonMismatch(status.addonVersion, SERVER_VERSION) : undefined,
30396
30404
  retryingBridge: this.bridgeRetry === null ? undefined : true,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gdharness",
3
- "version": "0.12.13",
3
+ "version": "0.12.14",
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",