gdharness 0.12.1 → 0.12.3

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
@@ -15714,7 +15714,7 @@ var init_tool_definitions = __esm(() => {
15714
15714
  },
15715
15715
  {
15716
15716
  name: "editor_output",
15717
- description: "What the project started by editor_run has printed, as entries with a severity: the errors and warnings the engine reported, each with where it happened, and everything else as info. Answers with the counts and the verdict as well as the entries. A run that has quit still answers here, with running false and its exit code, until the next one starts.",
15717
+ description: "What the project started by editor_run has printed, as entries with a severity: the errors and warnings the engine reported, each with where it happened, and everything else as info. Answers with the counts and the verdict as well as the entries. A run that has quit still answers here, with running false and its exit code, until the next one starts. A run this server started also answers with transcript, the file both its streams are written to: uncapped, written while the run is going, and the thing to read or tail for a long run rather than the engine's own log, which every engine start rotates away.",
15718
15718
  parameters: {
15719
15719
  severity: {
15720
15720
  type: "string",
@@ -38002,6 +38002,13 @@ class GodotServer {
38002
38002
  limit: readPositiveNumber(args, "limit") ?? 200
38003
38003
  });
38004
38004
  const halt = run.throughEditor ? this.dapClient?.whereItStopped() ?? null : null;
38005
+ const notes = [];
38006
+ if (run.endedUnwatched === true) {
38007
+ notes.push("This run outlived the server that started it and is over now, so its exit code was never collected. Everything it printed is below, read back from its transcript.");
38008
+ }
38009
+ if (run.transcript !== null && selected.omitted > 0) {
38010
+ notes.push(`Everything this run has printed is in ${run.transcript}, uncapped and still being written.`);
38011
+ }
38005
38012
  return this.jsonTextResponse({
38006
38013
  running: stillRunning(run),
38007
38014
  exitCode: run.exitCode,
@@ -38013,7 +38020,8 @@ class GodotServer {
38013
38020
  heldAt: halt,
38014
38021
  endedUnwatched: run.endedUnwatched === true ? true : undefined,
38015
38022
  project: !run.throughEditor && run.process === null ? run.projectPath ?? undefined : undefined,
38016
- note: run.endedUnwatched === true ? "This run outlived the server that started it and is over now, so its exit code was never collected. Everything it printed is below, read back from its transcript." : undefined,
38023
+ transcript: run.transcript ?? undefined,
38024
+ note: notes.length > 0 ? notes.join(" ") : undefined,
38017
38025
  omitted: selected.omitted,
38018
38026
  entries: selected.entries
38019
38027
  });
@@ -39483,6 +39491,23 @@ function autoloadIsOurs(named) {
39483
39491
  async function setRuntime(engine, projectPath, on) {
39484
39492
  return on ? await runOperation2(engine, "add_autoload", { name: RUNTIME_AUTOLOAD.name, path: `res://${RUNTIME_AUTOLOAD.path}`, enabled: true }, projectPath) : await runOperation2(engine, "remove_autoload", { name: RUNTIME_AUTOLOAD.name }, projectPath);
39485
39493
  }
39494
+ function loaderAmong(projectPath, named) {
39495
+ let byName = null;
39496
+ for (const [entry, path] of named) {
39497
+ if (entry === RUNTIME_AUTOLOAD.name || path === RUNTIME_AUTOLOAD.path) {
39498
+ continue;
39499
+ }
39500
+ let source = "";
39501
+ try {
39502
+ source = readFileSync7(join8(projectPath, path), "utf8");
39503
+ } catch {}
39504
+ if (source.includes(RUNTIME_AUTOLOAD.path)) {
39505
+ return { name: entry, path };
39506
+ }
39507
+ byName ??= path.toLowerCase().includes("gdharness") ? { name: entry, path } : null;
39508
+ }
39509
+ return byName;
39510
+ }
39486
39511
  function autoloadPaths(settings) {
39487
39512
  const named = new Map;
39488
39513
  for (const [name, value] of Object.entries(settings["autoload"] ?? {})) {
@@ -39537,9 +39562,10 @@ function inspectProject(projectPath) {
39537
39562
  }
39538
39563
  const autoload = settings["autoload"];
39539
39564
  const registered = autoload === undefined ? undefined : readString(autoload, RUNTIME_AUTOLOAD.name);
39540
- const runtimeAutoload = typeof registered === "string";
39541
39565
  const runtimeAutoloadPath = typeof registered === "string" ? registered.replace(/^\*/, "") : null;
39542
39566
  const named = autoloadPaths(settings);
39567
+ const runtimeLoaderAutoload = loaderAmong(projectPath, named);
39568
+ const runtimeAutoload = runtimeAutoloadPath !== null || runtimeLoaderAutoload !== null;
39543
39569
  const tracked = trackedByGit(projectPath, [...named.values()]);
39544
39570
  for (const [name, path] of named) {
39545
39571
  if (tracked !== null && !tracked.has(path) && existsSync7(join8(projectPath, path))) {
@@ -39559,6 +39585,7 @@ function inspectProject(projectPath) {
39559
39585
  pluginsEnabled,
39560
39586
  runtimeAutoload,
39561
39587
  runtimeAutoloadPath,
39588
+ runtimeLoaderAutoload,
39562
39589
  staleClasses,
39563
39590
  classCacheExists: cached !== null,
39564
39591
  problems
@@ -39846,7 +39873,13 @@ function said2(outcome, what) {
39846
39873
  }
39847
39874
  }
39848
39875
  async function registerRuntime(godot, projectPath) {
39849
- const named = inspectProject(projectPath).runtimeAutoloadPath;
39876
+ const report = inspectProject(projectPath);
39877
+ const named = report.runtimeAutoloadPath;
39878
+ const loader = report.runtimeLoaderAutoload;
39879
+ if (named === null && loader !== null) {
39880
+ console.log(`${loader.name} autoload already names res://${loader.path}, which is this project bringing the runtime up its own way, so ${RUNTIME_AUTOLOAD.name} was not added: a second entry would come up beside it without whatever that file refuses. If it is not a loader, add ${RUNTIME_AUTOLOAD.name} yourself.`);
39881
+ return;
39882
+ }
39850
39883
  if (autoloadIsOurs(named)) {
39851
39884
  said2(await setRuntime(godot, projectPath, true), "registering the runtime autoload");
39852
39885
  console.log(`${RUNTIME_AUTOLOAD.name} autoload registered at res://${RUNTIME_AUTOLOAD.path}`);
@@ -40092,7 +40125,7 @@ function doctorReport(projectPath) {
40092
40125
  console.log(`addons/${addon.name}: ${addon.installed ? `installed, ${addon.version ?? "unknown version"}${addon.current ? "" : " (not this version)"}` : "missing"}`);
40093
40126
  }
40094
40127
  console.log(`editor plugins enabled: ${report.pluginsEnabled.join(", ") || "none"}`);
40095
- console.log(`runtime autoload: ${report.runtimeAutoload ? "registered" : "not registered"}`);
40128
+ console.log(`runtime autoload: ${report.runtimeLoaderAutoload === null ? report.runtimeAutoload ? "registered" : "not registered" : `registered through res://${report.runtimeLoaderAutoload.path}, as ${report.runtimeLoaderAutoload.name}`}`);
40096
40129
  console.log(report.classCacheExists ? `class cache: ${report.staleClasses.length === 0 ? "current" : `stale for ${report.staleClasses.join(", ")}`}` : "class cache: missing");
40097
40130
  for (const problem of report.problems) {
40098
40131
  console.log(`problem: ${problem}`);
@@ -40113,8 +40146,20 @@ async function runtime3() {
40113
40146
  await registerRuntime(godot, projectPath);
40114
40147
  return;
40115
40148
  }
40149
+ const before = inspectProject(projectPath);
40150
+ const loader = before.runtimeLoaderAutoload;
40151
+ if (before.runtimeAutoloadPath === null) {
40152
+ console.log(`${RUNTIME_AUTOLOAD.name} autoload is not registered, so there was nothing to remove`);
40153
+ if (loader !== null) {
40154
+ console.log(` ${loader.name} names res://${loader.path}, which brings the runtime up its own way. That line is this project's, not this tool's, so nothing here has changed it: whatever that file decides about a release build still decides it.`);
40155
+ }
40156
+ return;
40157
+ }
40116
40158
  said2(await setRuntime(godot, projectPath, false), "turning the runtime off");
40117
40159
  console.log(`${RUNTIME_AUTOLOAD.name} autoload removed`);
40160
+ if (loader !== null) {
40161
+ console.log(` ${loader.name} still names res://${loader.path}, which brings the runtime up its own way: this command has not touched it, so whatever that file decides about a release build still decides it`);
40162
+ }
40118
40163
  }
40119
40164
  async function classes() {
40120
40165
  const projectPath = projectArgument(1);
package/build/index.js CHANGED
@@ -28518,7 +28518,7 @@ var TOOL_SPECS = [
28518
28518
  },
28519
28519
  {
28520
28520
  name: "editor_output",
28521
- description: "What the project started by editor_run has printed, as entries with a severity: the errors and warnings the engine reported, each with where it happened, and everything else as info. Answers with the counts and the verdict as well as the entries. A run that has quit still answers here, with running false and its exit code, until the next one starts.",
28521
+ description: "What the project started by editor_run has printed, as entries with a severity: the errors and warnings the engine reported, each with where it happened, and everything else as info. Answers with the counts and the verdict as well as the entries. A run that has quit still answers here, with running false and its exit code, until the next one starts. A run this server started also answers with transcript, the file both its streams are written to: uncapped, written while the run is going, and the thing to read or tail for a long run rather than the engine's own log, which every engine start rotates away.",
28522
28522
  parameters: {
28523
28523
  severity: {
28524
28524
  type: "string",
@@ -30784,6 +30784,13 @@ class GodotServer {
30784
30784
  limit: readPositiveNumber(args, "limit") ?? 200
30785
30785
  });
30786
30786
  const halt = run.throughEditor ? this.dapClient?.whereItStopped() ?? null : null;
30787
+ const notes = [];
30788
+ if (run.endedUnwatched === true) {
30789
+ notes.push("This run outlived the server that started it and is over now, so its exit code was never collected. Everything it printed is below, read back from its transcript.");
30790
+ }
30791
+ if (run.transcript !== null && selected.omitted > 0) {
30792
+ notes.push(`Everything this run has printed is in ${run.transcript}, uncapped and still being written.`);
30793
+ }
30787
30794
  return this.jsonTextResponse({
30788
30795
  running: stillRunning(run),
30789
30796
  exitCode: run.exitCode,
@@ -30795,7 +30802,8 @@ class GodotServer {
30795
30802
  heldAt: halt,
30796
30803
  endedUnwatched: run.endedUnwatched === true ? true : undefined,
30797
30804
  project: !run.throughEditor && run.process === null ? run.projectPath ?? undefined : undefined,
30798
- note: run.endedUnwatched === true ? "This run outlived the server that started it and is over now, so its exit code was never collected. Everything it printed is below, read back from its transcript." : undefined,
30805
+ transcript: run.transcript ?? undefined,
30806
+ note: notes.length > 0 ? notes.join(" ") : undefined,
30799
30807
  omitted: selected.omitted,
30800
30808
  entries: selected.entries
30801
30809
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gdharness",
3
- "version": "0.12.1",
3
+ "version": "0.12.3",
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",