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.
- package/build/cli.js +228 -193
- package/build/index.js +53 -34
- 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
|
|
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 =
|
|
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 =
|
|
10042
|
+
const versions = process3.versions;
|
|
10042
10043
|
const bun = versions["bun"];
|
|
10043
|
-
return bun === undefined ? `node ${
|
|
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()}, ${
|
|
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**: ${
|
|
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
|
|
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 =
|
|
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
|
|
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 =
|
|
26106
|
+
const versions = process5.versions;
|
|
26106
26107
|
const bun = versions["bun"];
|
|
26107
|
-
return bun === undefined ? `node ${
|
|
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()}, ${
|
|
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**: ${
|
|
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
|
|
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
|
-
|
|
27317
|
-
|
|
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
|
-
|
|
27320
|
-
|
|
27321
|
-
|
|
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
|
|
27328
|
-
const
|
|
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
|
|
27331
|
-
if (
|
|
27332
|
-
|
|
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
|
-
|
|
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
|
|
27351
|
-
if (
|
|
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
|
|
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
|
|
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.
|
|
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",
|