gdharness 0.13.9 → 0.13.10

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
@@ -36452,6 +36452,34 @@ function runningAs(pid) {
36452
36452
  return null;
36453
36453
  }
36454
36454
  }
36455
+ function listeningPid(port) {
36456
+ try {
36457
+ if (process.platform === "win32") {
36458
+ const said = execFileSync("powershell", [
36459
+ "-NoProfile",
36460
+ "-NonInteractive",
36461
+ "-Command",
36462
+ `(Get-NetTCPConnection -State Listen -LocalPort ${port} -ErrorAction SilentlyContinue | Select-Object -First 1).OwningProcess`
36463
+ ], { encoding: "utf8", timeout: 15000 }).trim();
36464
+ return /^\d+$/.test(said) ? Number(said) : null;
36465
+ }
36466
+ for (const [command, args, pattern] of [
36467
+ ["lsof", ["-nP", `-iTCP:${port}`, "-sTCP:LISTEN", "-t"], /^(\d+)/],
36468
+ ["ss", ["-ltnpH", `sport = :${port}`], /pid=(\d+)/]
36469
+ ]) {
36470
+ try {
36471
+ const said = execFileSync(command, [...args], { encoding: "utf8", timeout: 15000 }).trim();
36472
+ const found = pattern.exec(said);
36473
+ if (found?.[1] !== undefined) {
36474
+ return Number(found[1]);
36475
+ }
36476
+ } catch {}
36477
+ }
36478
+ return null;
36479
+ } catch {
36480
+ return null;
36481
+ }
36482
+ }
36455
36483
  function windowsCommandLine(pid) {
36456
36484
  try {
36457
36485
  const answer = execFileSync("powershell", [
@@ -37886,6 +37914,19 @@ class GodotServer {
37886
37914
  async handleDAP(toolName, args) {
37887
37915
  return handleDAPTool(this.dap(), toolName, args);
37888
37916
  }
37917
+ adapterBelongsElsewhere() {
37918
+ const status = this.godotBridge.getStatus();
37919
+ const ours = status.editorPid;
37920
+ if (ours === undefined) {
37921
+ return null;
37922
+ }
37923
+ const port = this.editorServes("dapPort", "GDHARNESS_DAP_PORT", DEFAULT_DAP_PORT);
37924
+ const holder = listeningPid(port);
37925
+ if (holder === null || holder === ours) {
37926
+ return null;
37927
+ }
37928
+ return `The debug adapter on port ${port} belongs to process ${holder}, and the editor on this server's bridge is process ${ours}. Godot gives every editor the same debug adapter port by default, so the one this editor names in its settings is not necessarily the one it holds. Playing the game now would put another editor's console and another project's debugger under this project's name.`;
37929
+ }
37889
37930
  dap() {
37890
37931
  const port = this.editorServes("dapPort", "GDHARNESS_DAP_PORT", DEFAULT_DAP_PORT);
37891
37932
  if (this.dapClient !== null && this.dapClient.port !== port) {
@@ -38233,6 +38274,14 @@ class GodotServer {
38233
38274
  "GDHARNESS_DAP_PORT points this server at another one"
38234
38275
  ]);
38235
38276
  }
38277
+ const stranger = this.adapterBelongsElsewhere();
38278
+ if (stranger !== null) {
38279
+ return this.createErrorResponse(stranger, [
38280
+ "editor_status names the editor on the bridge and the port it says it serves",
38281
+ "GDHARNESS_DAP_PORT points this server at the right one",
38282
+ "Closing the other editor, or moving its debug adapter port in its editor settings, frees this one"
38283
+ ]);
38284
+ }
38236
38285
  const answer = await this.handleViaBridge("play_scene", scene === null ? {} : { scenePath: `res://${scene}` });
38237
38286
  if (answer.isError === true) {
38238
38287
  return answer;
package/build/index.js CHANGED
@@ -28099,6 +28099,34 @@ function runningAs(pid) {
28099
28099
  return null;
28100
28100
  }
28101
28101
  }
28102
+ function listeningPid(port) {
28103
+ try {
28104
+ if (process.platform === "win32") {
28105
+ const said = execFileSync("powershell", [
28106
+ "-NoProfile",
28107
+ "-NonInteractive",
28108
+ "-Command",
28109
+ `(Get-NetTCPConnection -State Listen -LocalPort ${port} -ErrorAction SilentlyContinue | Select-Object -First 1).OwningProcess`
28110
+ ], { encoding: "utf8", timeout: 15000 }).trim();
28111
+ return /^\d+$/.test(said) ? Number(said) : null;
28112
+ }
28113
+ for (const [command, args, pattern] of [
28114
+ ["lsof", ["-nP", `-iTCP:${port}`, "-sTCP:LISTEN", "-t"], /^(\d+)/],
28115
+ ["ss", ["-ltnpH", `sport = :${port}`], /pid=(\d+)/]
28116
+ ]) {
28117
+ try {
28118
+ const said = execFileSync(command, [...args], { encoding: "utf8", timeout: 15000 }).trim();
28119
+ const found = pattern.exec(said);
28120
+ if (found?.[1] !== undefined) {
28121
+ return Number(found[1]);
28122
+ }
28123
+ } catch {}
28124
+ }
28125
+ return null;
28126
+ } catch {
28127
+ return null;
28128
+ }
28129
+ }
28102
28130
  function windowsCommandLine(pid) {
28103
28131
  try {
28104
28132
  const answer = execFileSync("powershell", [
@@ -30640,6 +30668,19 @@ class GodotServer {
30640
30668
  async handleDAP(toolName, args) {
30641
30669
  return handleDAPTool(this.dap(), toolName, args);
30642
30670
  }
30671
+ adapterBelongsElsewhere() {
30672
+ const status = this.godotBridge.getStatus();
30673
+ const ours = status.editorPid;
30674
+ if (ours === undefined) {
30675
+ return null;
30676
+ }
30677
+ const port = this.editorServes("dapPort", "GDHARNESS_DAP_PORT", DEFAULT_DAP_PORT);
30678
+ const holder = listeningPid(port);
30679
+ if (holder === null || holder === ours) {
30680
+ return null;
30681
+ }
30682
+ return `The debug adapter on port ${port} belongs to process ${holder}, and the editor on this server's bridge is process ${ours}. Godot gives every editor the same debug adapter port by default, so the one this editor names in its settings is not necessarily the one it holds. Playing the game now would put another editor's console and another project's debugger under this project's name.`;
30683
+ }
30643
30684
  dap() {
30644
30685
  const port = this.editorServes("dapPort", "GDHARNESS_DAP_PORT", DEFAULT_DAP_PORT);
30645
30686
  if (this.dapClient !== null && this.dapClient.port !== port) {
@@ -30987,6 +31028,14 @@ class GodotServer {
30987
31028
  "GDHARNESS_DAP_PORT points this server at another one"
30988
31029
  ]);
30989
31030
  }
31031
+ const stranger = this.adapterBelongsElsewhere();
31032
+ if (stranger !== null) {
31033
+ return this.createErrorResponse(stranger, [
31034
+ "editor_status names the editor on the bridge and the port it says it serves",
31035
+ "GDHARNESS_DAP_PORT points this server at the right one",
31036
+ "Closing the other editor, or moving its debug adapter port in its editor settings, frees this one"
31037
+ ]);
31038
+ }
30990
31039
  const answer = await this.handleViaBridge("play_scene", scene === null ? {} : { scenePath: `res://${scene}` });
30991
31040
  if (answer.isError === true) {
30992
31041
  return answer;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gdharness",
3
- "version": "0.13.9",
3
+ "version": "0.13.10",
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",