gdharness 0.9.3 → 0.9.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 CHANGED
@@ -15668,7 +15668,7 @@ var init_tool_definitions = __esm(() => {
15668
15668
  },
15669
15669
  {
15670
15670
  name: "editor_run",
15671
- description: "The run: starting the project, stopping it, or booting it once to see whether it comes up clean. start keeps it running and collecting output until stop, windowed where there is a display and headless where there is not, unless headless says otherwise; only runtime_capture needs the window. check boots it headless for a few frames, waits for it to quit, and answers with the verdict: whether it came up, and every error and warning it printed on the way.",
15671
+ description: "The run: starting the project, stopping it, or booting it once to see whether it comes up clean. start keeps it running and collecting output until it quits or is stopped, windowed where there is a display and headless where there is not, unless headless says otherwise; only runtime_capture needs the window. A run that quits on its own is kept, so a scene that prints an answer and quits is start, then editor_output until running is false. check boots it headless for a few frames, waits for it to quit, and answers with the verdict: whether it came up, and every error and warning it printed on the way.",
15672
15672
  parameters: {
15673
15673
  projectPath: PROJECT_PATH,
15674
15674
  scene: { type: "string", description: "A scene to run instead of the main scene." },
@@ -15681,7 +15681,7 @@ var init_tool_definitions = __esm(() => {
15681
15681
  },
15682
15682
  requires: ["projectPath"],
15683
15683
  operations: {
15684
- start: { summary: "run the project until stop", requires: [] },
15684
+ start: { summary: "run the project until it quits or is stopped", requires: [] },
15685
15685
  stop: { summary: "end the run and answer with what it printed last", requires: [] },
15686
15686
  check: { summary: "boot headless, quit after a few frames, and report the verdict", requires: [] }
15687
15687
  },
@@ -15689,7 +15689,7 @@ var init_tool_definitions = __esm(() => {
15689
15689
  },
15690
15690
  {
15691
15691
  name: "editor_output",
15692
- 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.",
15692
+ 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.",
15693
15693
  parameters: {
15694
15694
  severity: {
15695
15695
  type: "string",
@@ -36708,6 +36708,15 @@ class GodotServer {
36708
36708
  }
36709
36709
  async debuggedGame() {
36710
36710
  const game = this.activeProcess;
36711
+ if (game !== null && game.exitCode !== null) {
36712
+ return {
36713
+ ok: false,
36714
+ response: this.createErrorResponse(`The last run has already finished (exit code ${game.exitCode}), so there is no debug session to answer for.`, [
36715
+ "editor_output still reads what it printed, until the next run starts",
36716
+ "editor_run start plays another, which the debugger can hold"
36717
+ ])
36718
+ };
36719
+ }
36711
36720
  if (game === null) {
36712
36721
  return {
36713
36722
  ok: false,
@@ -37200,7 +37209,7 @@ class GodotServer {
37200
37209
  version: godotPath === null ? null : await this.godotVersion(godotPath)
37201
37210
  },
37202
37211
  game: {
37203
- processActive: this.activeProcess !== null,
37212
+ processActive: this.activeProcess?.exitCode === null,
37204
37213
  playingInEditor: playing,
37205
37214
  runtimeConnected: games.some((game) => game.reachable),
37206
37215
  runtimes: games
@@ -37364,7 +37373,7 @@ class GodotServer {
37364
37373
  if (op === "check") {
37365
37374
  return await this.checkBoot(engine.value, project.value.path, sceneArgument, args);
37366
37375
  }
37367
- if (this.activeProcess) {
37376
+ if (this.activeProcess?.exitCode === null) {
37368
37377
  this.logDebug("Ending the running game before starting another");
37369
37378
  await this.endActiveGame();
37370
37379
  }
@@ -37383,11 +37392,6 @@ class GodotServer {
37383
37392
  this.logDebug(`Running Godot project: ${engine.value} ${cmdArgs.join(" ")}`);
37384
37393
  const started = this.spawnGame(engine.value, cmdArgs);
37385
37394
  this.activeProcess = started;
37386
- started.process.on("exit", () => {
37387
- if (this.activeProcess === started) {
37388
- this.activeProcess = null;
37389
- }
37390
- });
37391
37395
  return this.jsonTextResponse({
37392
37396
  started: true,
37393
37397
  through: "gdharness",
@@ -38603,6 +38607,17 @@ function disconnect(harness, projectPath) {
38603
38607
  `, "utf8");
38604
38608
  return { harness, path, action: "removed" };
38605
38609
  }
38610
+ function launchedBy(entry) {
38611
+ if (typeof entry !== "object" || entry === null || Array.isArray(entry)) {
38612
+ return;
38613
+ }
38614
+ const held = entry;
38615
+ const whole = held["command"];
38616
+ const args = Array.isArray(held["args"]) ? held["args"] : [];
38617
+ const parts = Array.isArray(whole) ? whole : [whole, ...args];
38618
+ const said = parts.filter((part) => typeof part === "string");
38619
+ return said.length === 0 ? undefined : said.join(" ");
38620
+ }
38606
38621
  function connect(harness, projectPath, launch) {
38607
38622
  const path = configPath(harness, projectPath);
38608
38623
  if (harness.snippet !== undefined) {
@@ -38627,8 +38642,11 @@ function connect(harness, projectPath, launch) {
38627
38642
  }
38628
38643
  const container = typeof existing === "object" && existing !== null && !Array.isArray(existing) ? existing[harness.container] : undefined;
38629
38644
  const already = typeof container === "object" && container !== null && SERVER_KEY in container;
38630
- return put(harness, path, `${JSON.stringify(merged(existing, harness, launch), null, 2)}
38645
+ const written = put(harness, path, `${JSON.stringify(merged(existing, harness, launch), null, 2)}
38631
38646
  `, already);
38647
+ const was = launchedBy(container?.[SERVER_KEY]);
38648
+ const now = launchedBy(entryFor(harness.shape, launch));
38649
+ return was === undefined || was === now ? written : { ...written, wasLaunchedBy: was };
38632
38650
  }
38633
38651
  function put(harness, path, contents, already) {
38634
38652
  mkdirSync(dirname2(path), { recursive: true });
@@ -39315,6 +39333,10 @@ function reportConnection(group, launch, projectPath) {
39315
39333
  return;
39316
39334
  }
39317
39335
  console.log(`${who}: ${written.action} ${written.path}`);
39336
+ if (written.wasLaunchedBy !== undefined) {
39337
+ console.log(` it was launched by ${written.wasLaunchedBy}`);
39338
+ console.log(` now by ${[launch.command, ...launch.args].join(" ")}`);
39339
+ }
39318
39340
  for (const harness of group.harnesses) {
39319
39341
  if (harness.manual !== undefined) {
39320
39342
  console.log(` ${harness.name}: ${harness.manual}`);
package/build/index.js CHANGED
@@ -28262,7 +28262,7 @@ var TOOL_SPECS = [
28262
28262
  },
28263
28263
  {
28264
28264
  name: "editor_run",
28265
- description: "The run: starting the project, stopping it, or booting it once to see whether it comes up clean. start keeps it running and collecting output until stop, windowed where there is a display and headless where there is not, unless headless says otherwise; only runtime_capture needs the window. check boots it headless for a few frames, waits for it to quit, and answers with the verdict: whether it came up, and every error and warning it printed on the way.",
28265
+ description: "The run: starting the project, stopping it, or booting it once to see whether it comes up clean. start keeps it running and collecting output until it quits or is stopped, windowed where there is a display and headless where there is not, unless headless says otherwise; only runtime_capture needs the window. A run that quits on its own is kept, so a scene that prints an answer and quits is start, then editor_output until running is false. check boots it headless for a few frames, waits for it to quit, and answers with the verdict: whether it came up, and every error and warning it printed on the way.",
28266
28266
  parameters: {
28267
28267
  projectPath: PROJECT_PATH,
28268
28268
  scene: { type: "string", description: "A scene to run instead of the main scene." },
@@ -28275,7 +28275,7 @@ var TOOL_SPECS = [
28275
28275
  },
28276
28276
  requires: ["projectPath"],
28277
28277
  operations: {
28278
- start: { summary: "run the project until stop", requires: [] },
28278
+ start: { summary: "run the project until it quits or is stopped", requires: [] },
28279
28279
  stop: { summary: "end the run and answer with what it printed last", requires: [] },
28280
28280
  check: { summary: "boot headless, quit after a few frames, and report the verdict", requires: [] }
28281
28281
  },
@@ -28283,7 +28283,7 @@ var TOOL_SPECS = [
28283
28283
  },
28284
28284
  {
28285
28285
  name: "editor_output",
28286
- 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.",
28286
+ 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.",
28287
28287
  parameters: {
28288
28288
  severity: {
28289
28289
  type: "string",
@@ -29488,6 +29488,15 @@ class GodotServer {
29488
29488
  }
29489
29489
  async debuggedGame() {
29490
29490
  const game = this.activeProcess;
29491
+ if (game !== null && game.exitCode !== null) {
29492
+ return {
29493
+ ok: false,
29494
+ response: this.createErrorResponse(`The last run has already finished (exit code ${game.exitCode}), so there is no debug session to answer for.`, [
29495
+ "editor_output still reads what it printed, until the next run starts",
29496
+ "editor_run start plays another, which the debugger can hold"
29497
+ ])
29498
+ };
29499
+ }
29491
29500
  if (game === null) {
29492
29501
  return {
29493
29502
  ok: false,
@@ -29980,7 +29989,7 @@ class GodotServer {
29980
29989
  version: godotPath === null ? null : await this.godotVersion(godotPath)
29981
29990
  },
29982
29991
  game: {
29983
- processActive: this.activeProcess !== null,
29992
+ processActive: this.activeProcess?.exitCode === null,
29984
29993
  playingInEditor: playing,
29985
29994
  runtimeConnected: games.some((game) => game.reachable),
29986
29995
  runtimes: games
@@ -30144,7 +30153,7 @@ class GodotServer {
30144
30153
  if (op === "check") {
30145
30154
  return await this.checkBoot(engine.value, project.value.path, sceneArgument, args);
30146
30155
  }
30147
- if (this.activeProcess) {
30156
+ if (this.activeProcess?.exitCode === null) {
30148
30157
  this.logDebug("Ending the running game before starting another");
30149
30158
  await this.endActiveGame();
30150
30159
  }
@@ -30163,11 +30172,6 @@ class GodotServer {
30163
30172
  this.logDebug(`Running Godot project: ${engine.value} ${cmdArgs.join(" ")}`);
30164
30173
  const started = this.spawnGame(engine.value, cmdArgs);
30165
30174
  this.activeProcess = started;
30166
- started.process.on("exit", () => {
30167
- if (this.activeProcess === started) {
30168
- this.activeProcess = null;
30169
- }
30170
- });
30171
30175
  return this.jsonTextResponse({
30172
30176
  started: true,
30173
30177
  through: "gdharness",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gdharness",
3
- "version": "0.9.3",
3
+ "version": "0.9.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",