gdharness 1.0.4 → 1.0.6

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
@@ -31601,11 +31601,13 @@ function announcedIn(directory) {
31601
31601
  }
31602
31602
  const strangers = strangersAmong(candidates, Date.now());
31603
31603
  for (const { file, pid, alive } of entries) {
31604
- const announced = alive && !strangers.has(file) ? parseAnnouncement(file, pid) : { kind: "rubbish" };
31604
+ const gone = !alive || strangers.has(file);
31605
+ const announced = gone ? { kind: "rubbish" } : parseAnnouncement(file, pid);
31605
31606
  if (announced.kind === "rubbish") {
31606
- if (!alive || strangers.has(file)) {
31607
- wentAway(file, pid);
31607
+ if (!gone) {
31608
+ continue;
31608
31609
  }
31610
+ wentAway(file, pid);
31609
31611
  try {
31610
31612
  unlinkSync(file);
31611
31613
  } catch {}
@@ -31856,11 +31858,14 @@ function writeBootNote(projectPath, announcedAfterMs) {
31856
31858
  writeFileSync8(path, JSON.stringify({ announcedAfterMs: Math.round(announcedAfterMs), at: new Date().toISOString() }, null, 2), "utf8");
31857
31859
  } catch {}
31858
31860
  }
31861
+ function halfAsLongAgain(lastBootMs) {
31862
+ return Math.round(lastBootMs * 1.5);
31863
+ }
31859
31864
  function waitSizedTo(lastBootMs, usual = ANNOUNCE_BUDGET_MS) {
31860
31865
  if (lastBootMs === null) {
31861
31866
  return usual;
31862
31867
  }
31863
- return Math.min(LONGEST_SIZED_WAIT_MS, Math.max(usual, Math.round(lastBootMs * 1.5)));
31868
+ return Math.min(LONGEST_SIZED_WAIT_MS, Math.max(usual, halfAsLongAgain(lastBootMs)));
31864
31869
  }
31865
31870
  var LONGEST_SIZED_WAIT_MS = 60000;
31866
31871
  var init_boot_note = __esm(() => {
@@ -37707,6 +37712,13 @@ function aboutTheChildren(ended, throughEditor) {
37707
37712
  const leftToo = ended.left.length === 0 ? "" : ` ${counted(ended.left.length)} it had started ${ended.left.length === 1 ? "was" : "were"} left, named under childrenLeft: neither announced as a game of this project nor this project's engine run with --path on it, so what ${ended.left.length === 1 ? "it is" : "they are"} cannot be told from here.`;
37708
37713
  return `${endedToo}${leftToo}`;
37709
37714
  }
37715
+ function uidsLeftNote(left) {
37716
+ if (left === 0) {
37717
+ return "No scene was written: this reads the project and imports it, and does not resave anything.";
37718
+ }
37719
+ const them = left === 1 ? "it" : "them";
37720
+ return `${left === 1 ? "One file" : `${left} files`} still ${left === 1 ? "has" : "have"} no .uid, named under stillWithoutUid, which is the engine declining to import ${them} rather than this op skipping ${them}. No scene was written.`;
37721
+ }
37710
37722
  function endedToStartThis(ended) {
37711
37723
  if (ended === null) {
37712
37724
  return "";
@@ -37734,7 +37746,7 @@ function runtimeVerdict(endpoint, after) {
37734
37746
  };
37735
37747
  }
37736
37748
  const state = after.playingSeen === false ? "the editor has not yet reported the game as playing, which a restarted editor does not until its scan is over" : "the game is still running";
37737
- const sized = after.sizedToMs === undefined ? "" : `, half as long again as the ${after.sizedToMs}ms its last game took,`;
37749
+ const sized = after.sizedToMs === undefined ? "" : halfAsLongAgain(after.sizedToMs) > after.budgetMs ? `, the longest a start waits unasked though its last game took ${after.sizedToMs}ms to announce,` : `, half as long again as the ${after.sizedToMs}ms its last game took,`;
37738
37750
  return {
37739
37751
  listening: false,
37740
37752
  mayYetAnnounce: after.running,
@@ -37838,7 +37850,10 @@ function aboveTheRunner(entry) {
37838
37850
  const cut = entry.detail.length - runner;
37839
37851
  return {
37840
37852
  ...entry,
37841
- detail: [...entry.detail.slice(0, runner), `[and ${cut} frames inside ${RUNNER_DIRECTORY}]`]
37853
+ detail: [
37854
+ ...entry.detail.slice(0, runner),
37855
+ `[and ${cut === 1 ? "one frame" : `${cut} frames`} inside ${RUNNER_DIRECTORY}]`
37856
+ ]
37842
37857
  };
37843
37858
  }
37844
37859
  function elsewhereIn(projectPath, asked) {
@@ -38726,7 +38741,7 @@ class GodotServer {
38726
38741
  payload: {
38727
38742
  uidsCreated: given,
38728
38743
  stillWithoutUid: after,
38729
- note: after.length > 0 ? `${after.length} still have no .uid, which is the engine declining to import them rather than this op skipping them. No scene was written.` : "No scene was written: this reads the project and imports it, and does not resave anything."
38744
+ note: uidsLeftNote(after.length)
38730
38745
  }
38731
38746
  });
38732
38747
  }
@@ -39642,11 +39657,11 @@ class GodotServer {
39642
39657
  return !editorSaysGoing;
39643
39658
  }
39644
39659
  });
39645
- this.logDebug(`announce wait ended after ${Date.now() - waitBegan}ms: ${endpoint === null ? "nothing announced" : `pid ${endpoint.pid} announced`}, editor asked ${editorAsks} times, last said ${lastSaid()}`);
39660
+ const asked = editorAsks === 0 ? "editor not asked" : `editor asked ${editorAsks === 1 ? "once" : `${editorAsks} times`}, last said ${lastSaid()}`;
39661
+ this.logDebug(`announce wait ended after ${Date.now() - waitBegan}ms: ${endpoint === null ? "nothing announced" : `pid ${endpoint.pid} announced`}, ${asked}`);
39646
39662
  const going = this.currentRun();
39647
39663
  if (endpoint !== null && going !== null && going.announcedPid === undefined) {
39648
- going.announcedPid = endpoint.pid;
39649
- this.noteBootOf(going, endpoint);
39664
+ this.tie(going, endpoint);
39650
39665
  }
39651
39666
  return runtimeVerdict(endpoint, {
39652
39667
  addon: true,
@@ -39678,6 +39693,7 @@ class GodotServer {
39678
39693
  return;
39679
39694
  }
39680
39695
  const took = announcedAt - run.startedAt;
39696
+ this.logDebug(`boot of pid ${endpoint.pid} announced ${Math.round(took)}ms after the start of ${run.projectPath}`);
39681
39697
  if (took > 0 && took <= LONGEST_BOOT_NOTED_MS) {
39682
39698
  writeBootNote(run.projectPath, took);
39683
39699
  }
@@ -40002,16 +40018,16 @@ class GodotServer {
40002
40018
  return run.announcedPid;
40003
40019
  }
40004
40020
  const ours = this.announcedOfTheRunsProject(run, announced ?? runtimesAnnounced().running);
40005
- if (run.pid !== null && ours.some((one) => one.pid === run.pid)) {
40006
- run.announcedPid = run.pid;
40021
+ const own = ours.find((one) => one.pid === run.pid);
40022
+ if (own !== undefined) {
40023
+ this.tie(run, own);
40007
40024
  return run.announcedPid;
40008
40025
  }
40009
40026
  const before = run.announcedBefore ?? new Set;
40010
40027
  const candidates = ours.filter((one) => !before.has(one.pid) && this.isTheGameOf(run, one, run.projectPath));
40011
40028
  const theOne = candidates.length === 1 ? candidates[0] : undefined;
40012
40029
  if (theOne !== undefined) {
40013
- run.announcedPid = theOne.pid;
40014
- this.noteBootOf(run, theOne);
40030
+ this.tie(run, theOne);
40015
40031
  }
40016
40032
  return run.announcedPid;
40017
40033
  }
@@ -40049,7 +40065,7 @@ class GodotServer {
40049
40065
  if (tree === undefined) {
40050
40066
  return;
40051
40067
  }
40052
- return this.tieThroughTheTree(run, tree, new Set(ours.map((one) => one.pid)));
40068
+ return this.tieThroughTheTree(run, tree, ours);
40053
40069
  }
40054
40070
  async gameProcessOf(run) {
40055
40071
  if (run.gamePid !== undefined) {
@@ -40070,21 +40086,28 @@ class GodotServer {
40070
40086
  }
40071
40087
  return found;
40072
40088
  }
40073
- tieThroughTheTree(run, tree, games) {
40089
+ tieThroughTheTree(run, tree, announced) {
40074
40090
  const root = run.pid ?? (run.throughEditor ? this.godotBridge.getStatus().editorPid : undefined);
40075
40091
  if (root === undefined) {
40076
40092
  return;
40077
40093
  }
40078
- if (games.has(root)) {
40079
- run.announcedPid = root;
40080
- return root;
40094
+ const games = new Set(announced.map((one) => one.pid));
40095
+ const own = announced.find((one) => one.pid === root);
40096
+ if (own !== undefined) {
40097
+ this.tie(run, own);
40098
+ return own.pid;
40081
40099
  }
40082
- const nearest = descendantsIn(tree, root).filter((one) => games.has(one) && !gameBetween(tree, one, root, games));
40100
+ const under = new Set(descendantsIn(tree, root));
40101
+ const nearest = announced.filter((one) => under.has(one.pid) && !gameBetween(tree, one.pid, root, games));
40083
40102
  const theOne = nearest.length === 1 ? nearest[0] : undefined;
40084
40103
  if (theOne !== undefined) {
40085
- run.announcedPid = theOne;
40104
+ this.tie(run, theOne);
40086
40105
  }
40087
- return theOne;
40106
+ return theOne?.pid;
40107
+ }
40108
+ tie(run, endpoint) {
40109
+ run.announcedPid = endpoint.pid;
40110
+ this.noteBootOf(run, endpoint);
40088
40111
  }
40089
40112
  playedByOurEditor(endpoint, projectPath) {
40090
40113
  const editor = this.godotBridge.getStatus().editorPid;
@@ -40385,7 +40408,7 @@ class GodotServer {
40385
40408
  }
40386
40409
  const announced = this.announcedOfTheRunsProject(run, runtimesAnnounced().running);
40387
40410
  const games = new Set(announced.map((one) => one.pid));
40388
- const own = this.announcedPidOf(run, announced) ?? this.tieThroughTheTree(run, tree, games) ?? run.gamePid ?? this.gameByCommandLineIn(run, tree);
40411
+ const own = this.announcedPidOf(run, announced) ?? this.tieThroughTheTree(run, tree, announced) ?? run.gamePid ?? this.gameByCommandLineIn(run, tree);
40389
40412
  if (own !== undefined && run.announcedPid === undefined) {
40390
40413
  run.gamePid = own;
40391
40414
  }
package/build/index.js CHANGED
@@ -25233,11 +25233,13 @@ function announcedIn(directory) {
25233
25233
  }
25234
25234
  const strangers = strangersAmong(candidates, Date.now());
25235
25235
  for (const { file, pid, alive } of entries) {
25236
- const announced = alive && !strangers.has(file) ? parseAnnouncement(file, pid) : { kind: "rubbish" };
25236
+ const gone = !alive || strangers.has(file);
25237
+ const announced = gone ? { kind: "rubbish" } : parseAnnouncement(file, pid);
25237
25238
  if (announced.kind === "rubbish") {
25238
- if (!alive || strangers.has(file)) {
25239
- wentAway(file, pid);
25239
+ if (!gone) {
25240
+ continue;
25240
25241
  }
25242
+ wentAway(file, pid);
25241
25243
  try {
25242
25244
  unlinkSync(file);
25243
25245
  } catch {}
@@ -25482,11 +25484,14 @@ function writeBootNote(projectPath, announcedAfterMs) {
25482
25484
  } catch {}
25483
25485
  }
25484
25486
  var LONGEST_SIZED_WAIT_MS = 60000;
25487
+ function halfAsLongAgain(lastBootMs) {
25488
+ return Math.round(lastBootMs * 1.5);
25489
+ }
25485
25490
  function waitSizedTo(lastBootMs, usual = ANNOUNCE_BUDGET_MS) {
25486
25491
  if (lastBootMs === null) {
25487
25492
  return usual;
25488
25493
  }
25489
- return Math.min(LONGEST_SIZED_WAIT_MS, Math.max(usual, Math.round(lastBootMs * 1.5)));
25494
+ return Math.min(LONGEST_SIZED_WAIT_MS, Math.max(usual, halfAsLongAgain(lastBootMs)));
25490
25495
  }
25491
25496
 
25492
25497
  // src/breakpoint-note.ts
@@ -30402,6 +30407,13 @@ function aboutTheChildren(ended, throughEditor) {
30402
30407
  const leftToo = ended.left.length === 0 ? "" : ` ${counted(ended.left.length)} it had started ${ended.left.length === 1 ? "was" : "were"} left, named under childrenLeft: neither announced as a game of this project nor this project's engine run with --path on it, so what ${ended.left.length === 1 ? "it is" : "they are"} cannot be told from here.`;
30403
30408
  return `${endedToo}${leftToo}`;
30404
30409
  }
30410
+ function uidsLeftNote(left) {
30411
+ if (left === 0) {
30412
+ return "No scene was written: this reads the project and imports it, and does not resave anything.";
30413
+ }
30414
+ const them = left === 1 ? "it" : "them";
30415
+ return `${left === 1 ? "One file" : `${left} files`} still ${left === 1 ? "has" : "have"} no .uid, named under stillWithoutUid, which is the engine declining to import ${them} rather than this op skipping ${them}. No scene was written.`;
30416
+ }
30405
30417
  function endedToStartThis(ended) {
30406
30418
  if (ended === null) {
30407
30419
  return "";
@@ -30430,7 +30442,7 @@ function runtimeVerdict(endpoint, after) {
30430
30442
  };
30431
30443
  }
30432
30444
  const state = after.playingSeen === false ? "the editor has not yet reported the game as playing, which a restarted editor does not until its scan is over" : "the game is still running";
30433
- const sized = after.sizedToMs === undefined ? "" : `, half as long again as the ${after.sizedToMs}ms its last game took,`;
30445
+ const sized = after.sizedToMs === undefined ? "" : halfAsLongAgain(after.sizedToMs) > after.budgetMs ? `, the longest a start waits unasked though its last game took ${after.sizedToMs}ms to announce,` : `, half as long again as the ${after.sizedToMs}ms its last game took,`;
30434
30446
  return {
30435
30447
  listening: false,
30436
30448
  mayYetAnnounce: after.running,
@@ -30576,7 +30588,10 @@ function aboveTheRunner(entry) {
30576
30588
  const cut = entry.detail.length - runner;
30577
30589
  return {
30578
30590
  ...entry,
30579
- detail: [...entry.detail.slice(0, runner), `[and ${cut} frames inside ${RUNNER_DIRECTORY}]`]
30591
+ detail: [
30592
+ ...entry.detail.slice(0, runner),
30593
+ `[and ${cut === 1 ? "one frame" : `${cut} frames`} inside ${RUNNER_DIRECTORY}]`
30594
+ ]
30580
30595
  };
30581
30596
  }
30582
30597
  function elsewhereIn(projectPath, asked) {
@@ -31464,7 +31479,7 @@ class GodotServer {
31464
31479
  payload: {
31465
31480
  uidsCreated: given,
31466
31481
  stillWithoutUid: after,
31467
- note: after.length > 0 ? `${after.length} still have no .uid, which is the engine declining to import them rather than this op skipping them. No scene was written.` : "No scene was written: this reads the project and imports it, and does not resave anything."
31482
+ note: uidsLeftNote(after.length)
31468
31483
  }
31469
31484
  });
31470
31485
  }
@@ -32380,11 +32395,11 @@ class GodotServer {
32380
32395
  return !editorSaysGoing;
32381
32396
  }
32382
32397
  });
32383
- this.logDebug(`announce wait ended after ${Date.now() - waitBegan}ms: ${endpoint === null ? "nothing announced" : `pid ${endpoint.pid} announced`}, editor asked ${editorAsks} times, last said ${lastSaid()}`);
32398
+ const asked = editorAsks === 0 ? "editor not asked" : `editor asked ${editorAsks === 1 ? "once" : `${editorAsks} times`}, last said ${lastSaid()}`;
32399
+ this.logDebug(`announce wait ended after ${Date.now() - waitBegan}ms: ${endpoint === null ? "nothing announced" : `pid ${endpoint.pid} announced`}, ${asked}`);
32384
32400
  const going = this.currentRun();
32385
32401
  if (endpoint !== null && going !== null && going.announcedPid === undefined) {
32386
- going.announcedPid = endpoint.pid;
32387
- this.noteBootOf(going, endpoint);
32402
+ this.tie(going, endpoint);
32388
32403
  }
32389
32404
  return runtimeVerdict(endpoint, {
32390
32405
  addon: true,
@@ -32416,6 +32431,7 @@ class GodotServer {
32416
32431
  return;
32417
32432
  }
32418
32433
  const took = announcedAt - run.startedAt;
32434
+ this.logDebug(`boot of pid ${endpoint.pid} announced ${Math.round(took)}ms after the start of ${run.projectPath}`);
32419
32435
  if (took > 0 && took <= LONGEST_BOOT_NOTED_MS) {
32420
32436
  writeBootNote(run.projectPath, took);
32421
32437
  }
@@ -32740,16 +32756,16 @@ class GodotServer {
32740
32756
  return run.announcedPid;
32741
32757
  }
32742
32758
  const ours = this.announcedOfTheRunsProject(run, announced ?? runtimesAnnounced().running);
32743
- if (run.pid !== null && ours.some((one) => one.pid === run.pid)) {
32744
- run.announcedPid = run.pid;
32759
+ const own = ours.find((one) => one.pid === run.pid);
32760
+ if (own !== undefined) {
32761
+ this.tie(run, own);
32745
32762
  return run.announcedPid;
32746
32763
  }
32747
32764
  const before = run.announcedBefore ?? new Set;
32748
32765
  const candidates = ours.filter((one) => !before.has(one.pid) && this.isTheGameOf(run, one, run.projectPath));
32749
32766
  const theOne = candidates.length === 1 ? candidates[0] : undefined;
32750
32767
  if (theOne !== undefined) {
32751
- run.announcedPid = theOne.pid;
32752
- this.noteBootOf(run, theOne);
32768
+ this.tie(run, theOne);
32753
32769
  }
32754
32770
  return run.announcedPid;
32755
32771
  }
@@ -32787,7 +32803,7 @@ class GodotServer {
32787
32803
  if (tree === undefined) {
32788
32804
  return;
32789
32805
  }
32790
- return this.tieThroughTheTree(run, tree, new Set(ours.map((one) => one.pid)));
32806
+ return this.tieThroughTheTree(run, tree, ours);
32791
32807
  }
32792
32808
  async gameProcessOf(run) {
32793
32809
  if (run.gamePid !== undefined) {
@@ -32808,21 +32824,28 @@ class GodotServer {
32808
32824
  }
32809
32825
  return found;
32810
32826
  }
32811
- tieThroughTheTree(run, tree, games) {
32827
+ tieThroughTheTree(run, tree, announced) {
32812
32828
  const root = run.pid ?? (run.throughEditor ? this.godotBridge.getStatus().editorPid : undefined);
32813
32829
  if (root === undefined) {
32814
32830
  return;
32815
32831
  }
32816
- if (games.has(root)) {
32817
- run.announcedPid = root;
32818
- return root;
32832
+ const games = new Set(announced.map((one) => one.pid));
32833
+ const own = announced.find((one) => one.pid === root);
32834
+ if (own !== undefined) {
32835
+ this.tie(run, own);
32836
+ return own.pid;
32819
32837
  }
32820
- const nearest = descendantsIn(tree, root).filter((one) => games.has(one) && !gameBetween(tree, one, root, games));
32838
+ const under = new Set(descendantsIn(tree, root));
32839
+ const nearest = announced.filter((one) => under.has(one.pid) && !gameBetween(tree, one.pid, root, games));
32821
32840
  const theOne = nearest.length === 1 ? nearest[0] : undefined;
32822
32841
  if (theOne !== undefined) {
32823
- run.announcedPid = theOne;
32842
+ this.tie(run, theOne);
32824
32843
  }
32825
- return theOne;
32844
+ return theOne?.pid;
32845
+ }
32846
+ tie(run, endpoint) {
32847
+ run.announcedPid = endpoint.pid;
32848
+ this.noteBootOf(run, endpoint);
32826
32849
  }
32827
32850
  playedByOurEditor(endpoint, projectPath) {
32828
32851
  const editor = this.godotBridge.getStatus().editorPid;
@@ -33123,7 +33146,7 @@ class GodotServer {
33123
33146
  }
33124
33147
  const announced = this.announcedOfTheRunsProject(run, runtimesAnnounced().running);
33125
33148
  const games = new Set(announced.map((one) => one.pid));
33126
- const own = this.announcedPidOf(run, announced) ?? this.tieThroughTheTree(run, tree, games) ?? run.gamePid ?? this.gameByCommandLineIn(run, tree);
33149
+ const own = this.announcedPidOf(run, announced) ?? this.tieThroughTheTree(run, tree, announced) ?? run.gamePid ?? this.gameByCommandLineIn(run, tree);
33127
33150
  if (own !== undefined && run.announcedPid === undefined) {
33128
33151
  run.gamePid = own;
33129
33152
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gdharness",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
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",