gdharness 0.5.2 → 0.5.4

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.
Files changed (3) hide show
  1. package/build/cli.js +228 -193
  2. package/build/index.js +53 -34
  3. package/package.json +1 -1
package/build/index.js CHANGED
@@ -9804,7 +9804,7 @@ var require_websocket_server = __commonJS(function(exports, module) {
9804
9804
 
9805
9805
  // src/issues.ts
9806
9806
  import { createHash } from "node:crypto";
9807
- import process2 from "node:process";
9807
+ import process3 from "node:process";
9808
9808
 
9809
9809
  // src/errors.ts
9810
9810
  class Refusal extends Error {
@@ -9834,8 +9834,9 @@ import { homedir, tmpdir } from "node:os";
9834
9834
  import { join } from "node:path";
9835
9835
 
9836
9836
  // src/runner.ts
9837
+ import process2 from "node:process";
9837
9838
  function currentRunner() {
9838
- const versions = process.versions;
9839
+ const versions = process2.versions;
9839
9840
  return versions["bun"] === undefined ? "npx" : "bunx";
9840
9841
  }
9841
9842
  function runLine(runner, version, rest = "") {
@@ -10038,9 +10039,9 @@ function addonMismatch(addonVersion, serverVersion) {
10038
10039
  var NEW_ISSUE = "https://github.com/Aureliolo/gdharness/issues/new";
10039
10040
  var ENHANCEMENT_URL = `${NEW_ISSUE}?template=feature_request.md`;
10040
10041
  function runtime() {
10041
- const versions = process2.versions;
10042
+ const versions = process3.versions;
10042
10043
  const bun = versions["bun"];
10043
- return bun === undefined ? `node ${process2.versions.node}` : `bun ${bun}`;
10044
+ return bun === undefined ? `node ${process3.versions.node}` : `bun ${bun}`;
10044
10045
  }
10045
10046
  function defectSignature(where, message) {
10046
10047
  const normalised = `${where}
@@ -10052,7 +10053,7 @@ function facts(where, message, godotVersion) {
10052
10053
  ["Where", where],
10053
10054
  ["Error", message],
10054
10055
  ["Version", `gdharness ${SERVER_VERSION}`],
10055
- ["Runtime", `${runtime()}, ${process2.platform} ${process2.arch}`],
10056
+ ["Runtime", `${runtime()}, ${process3.platform} ${process3.arch}`],
10056
10057
  ["Godot", godotVersion ?? "not known at the point this failed"],
10057
10058
  ["Signature", defectSignature(where, message)]
10058
10059
  ];
@@ -10062,7 +10063,7 @@ function filledTemplate(where, message, godotVersion) {
10062
10063
  `**gdharness version**: ${SERVER_VERSION}`,
10063
10064
  `**Godot version**: ${godotVersion ?? ""}`,
10064
10065
  `**Bun version**: ${runtime()}`,
10065
- `**OS**: ${process2.platform} ${process2.arch}`,
10066
+ `**OS**: ${process3.platform} ${process3.arch}`,
10066
10067
  "**MCP client**:",
10067
10068
  "",
10068
10069
  "## What you did",
@@ -24590,7 +24591,7 @@ var EMPTY_COMPLETION_RESULT = {
24590
24591
  };
24591
24592
 
24592
24593
  // node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
24593
- import process3 from "node:process";
24594
+ import process4 from "node:process";
24594
24595
 
24595
24596
  // node_modules/@modelcontextprotocol/sdk/dist/esm/shared/stdio.js
24596
24597
  var STDIO_DEFAULT_MAX_BUFFER_SIZE = 10 * 1024 * 1024;
@@ -24634,7 +24635,7 @@ function serializeMessage(message) {
24634
24635
 
24635
24636
  // node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
24636
24637
  class StdioServerTransport {
24637
- constructor(_stdin = process3.stdin, _stdout = process3.stdout, options) {
24638
+ constructor(_stdin = process4.stdin, _stdout = process4.stdout, options) {
24638
24639
  this._stdin = _stdin;
24639
24640
  this._stdout = _stdout;
24640
24641
  this._started = false;
@@ -26090,7 +26091,7 @@ async function runOperation(engine, operation, params, projectPath) {
26090
26091
 
26091
26092
  // src/issues.ts
26092
26093
  import { createHash as createHash2 } from "node:crypto";
26093
- import process4 from "node:process";
26094
+ import process5 from "node:process";
26094
26095
  var NEW_ISSUE2 = "https://github.com/Aureliolo/gdharness/issues/new";
26095
26096
  var ENHANCEMENT_URL2 = `${NEW_ISSUE2}?template=feature_request.md`;
26096
26097
  function feedbackNotice() {
@@ -26102,9 +26103,9 @@ function feedbackNotice() {
26102
26103
  }, null, 2);
26103
26104
  }
26104
26105
  function runtime2() {
26105
- const versions = process4.versions;
26106
+ const versions = process5.versions;
26106
26107
  const bun = versions["bun"];
26107
- return bun === undefined ? `node ${process4.versions.node}` : `bun ${bun}`;
26108
+ return bun === undefined ? `node ${process5.versions.node}` : `bun ${bun}`;
26108
26109
  }
26109
26110
  function defectSignature2(where, message) {
26110
26111
  const normalised = `${where}
@@ -26116,7 +26117,7 @@ function facts2(where, message, godotVersion) {
26116
26117
  ["Where", where],
26117
26118
  ["Error", message],
26118
26119
  ["Version", `gdharness ${SERVER_VERSION}`],
26119
- ["Runtime", `${runtime2()}, ${process4.platform} ${process4.arch}`],
26120
+ ["Runtime", `${runtime2()}, ${process5.platform} ${process5.arch}`],
26120
26121
  ["Godot", godotVersion ?? "not known at the point this failed"],
26121
26122
  ["Signature", defectSignature2(where, message)]
26122
26123
  ];
@@ -26126,7 +26127,7 @@ function filledTemplate2(where, message, godotVersion) {
26126
26127
  `**gdharness version**: ${SERVER_VERSION}`,
26127
26128
  `**Godot version**: ${godotVersion ?? ""}`,
26128
26129
  `**Bun version**: ${runtime2()}`,
26129
- `**OS**: ${process4.platform} ${process4.arch}`,
26130
+ `**OS**: ${process5.platform} ${process5.arch}`,
26130
26131
  "**MCP client**:",
26131
26132
  "",
26132
26133
  "## What you did",
@@ -27308,32 +27309,41 @@ function parseAnnouncement(file, pid) {
27308
27309
  try {
27309
27310
  fields = asParams(JSON.parse(readFileSync6(file, "utf8")));
27310
27311
  } catch {
27311
- return null;
27312
+ return { kind: "rubbish" };
27312
27313
  }
27313
27314
  const project = readParams(fields, "project");
27314
27315
  const port = readNumber(fields, "port");
27315
27316
  const address = readString(fields, "address");
27316
- if (readNumber(fields, "protocol") !== RUNTIME_PROTOCOL || readNumber(fields, "pid") !== pid || port === undefined || !Number.isInteger(port) || port < 1 || port > 65535 || address === undefined || project === undefined) {
27317
- return null;
27317
+ const protocol = readNumber(fields, "protocol");
27318
+ if (readNumber(fields, "pid") !== pid || port === undefined || !Number.isInteger(port) || port < 1 || port > 65535 || address === undefined || project === undefined) {
27319
+ return { kind: "rubbish" };
27318
27320
  }
27319
- return {
27320
- pid,
27321
- port,
27322
- address,
27323
- project: { name: readString(project, "name") ?? "", path: readString(project, "path") ?? "" },
27324
- file
27321
+ const named = {
27322
+ name: readString(project, "name") ?? "",
27323
+ path: readString(project, "path") ?? ""
27325
27324
  };
27325
+ if (protocol !== RUNTIME_PROTOCOL) {
27326
+ return { kind: "unspoken", unspoken: { pid, protocol: protocol ?? 0, project: named } };
27327
+ }
27328
+ return { kind: "runtime", endpoint: { pid, port, address, project: named, file } };
27326
27329
  }
27327
- function discoverRuntimes(directories = runtimeDirectories()) {
27328
- const found = new Map;
27330
+ function runtimesAnnounced(directories = runtimeDirectories()) {
27331
+ const running = new Map;
27332
+ const unspoken = new Map;
27329
27333
  for (const directory of directories) {
27330
- for (const endpoint of announcedIn(directory)) {
27331
- if (!found.has(endpoint.pid)) {
27332
- found.set(endpoint.pid, endpoint);
27334
+ for (const found of announcedIn(directory)) {
27335
+ if (found.kind === "runtime" && !running.has(found.endpoint.pid)) {
27336
+ running.set(found.endpoint.pid, found.endpoint);
27337
+ } else if (found.kind === "unspoken" && !unspoken.has(found.unspoken.pid)) {
27338
+ unspoken.set(found.unspoken.pid, found.unspoken);
27333
27339
  }
27334
27340
  }
27335
27341
  }
27336
- return [...found.values()].sort((a, b) => b.pid - a.pid);
27342
+ const newest = (a, b) => b.pid - a.pid;
27343
+ return { running: [...running.values()].sort(newest), unspoken: [...unspoken.values()].sort(newest) };
27344
+ }
27345
+ function discoverRuntimes(directories = runtimeDirectories()) {
27346
+ return runtimesAnnounced(directories).running;
27337
27347
  }
27338
27348
  function announcedIn(directory) {
27339
27349
  if (!existsSync5(directory)) {
@@ -27347,22 +27357,30 @@ function announcedIn(directory) {
27347
27357
  }
27348
27358
  const file = join6(directory, entry);
27349
27359
  const pid = Number.parseInt(match[1] ?? "", 10);
27350
- const endpoint = processAlive(pid) ? parseAnnouncement(file, pid) : null;
27351
- if (endpoint) {
27352
- found.push(endpoint);
27353
- } else {
27360
+ const announced = processAlive(pid) ? parseAnnouncement(file, pid) : { kind: "rubbish" };
27361
+ if (announced.kind === "rubbish") {
27354
27362
  try {
27355
27363
  unlinkSync(file);
27356
27364
  } catch {}
27365
+ continue;
27357
27366
  }
27367
+ found.push(announced);
27358
27368
  }
27359
27369
  return found;
27360
27370
  }
27361
27371
  function describe2(endpoint) {
27362
27372
  return `pid ${endpoint.pid} on ${endpoint.address}:${endpoint.port} (${endpoint.project.name || "unnamed"} at ${endpoint.project.path})`;
27363
27373
  }
27364
- function chooseRuntime(endpoints, projectPath) {
27374
+ function tooNew(unspoken) {
27375
+ const named = unspoken.map((game) => `pid ${game.pid} speaks ${game.protocol} (${game.project.name || "unnamed"} at ${game.project.path})`).join("; ");
27376
+ const half = unspoken.some((game) => game.protocol > RUNTIME_PROTOCOL) ? "this server is the older half: reconnect it so it spawns the installed version" : "the addon is the older half: reinstall it and restart the game";
27377
+ return `A game is running, in a protocol this server does not speak ${named}. This server speaks ${RUNTIME_PROTOCOL}, so ${half}.`;
27378
+ }
27379
+ function chooseRuntime(endpoints, projectPath, unspoken = []) {
27365
27380
  if (endpoints.length === 0) {
27381
+ if (unspoken.length > 0) {
27382
+ return { problem: tooNew(unspoken) };
27383
+ }
27366
27384
  return {
27367
27385
  problem: "No game with the runtime addon is running. Start one with editor_run, or play the project from the editor with the addon enabled."
27368
27386
  };
@@ -29764,7 +29782,8 @@ class GodotServer {
29764
29782
  }
29765
29783
  async handleRuntimeCommand(command, args, timeoutMs = this.runtimeTimeoutMs()) {
29766
29784
  const { op: _op, projectPath, ...params } = asParams(args);
29767
- const choice = chooseRuntime(discoverRuntimes(), typeof projectPath === "string" ? projectPath : undefined);
29785
+ const announced = runtimesAnnounced();
29786
+ const choice = chooseRuntime(announced.running, typeof projectPath === "string" ? projectPath : undefined, announced.unspoken);
29768
29787
  if ("problem" in choice) {
29769
29788
  return this.createErrorResponse(choice.problem);
29770
29789
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gdharness",
3
- "version": "0.5.2",
3
+ "version": "0.5.4",
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",