gdharness 1.3.4 → 1.3.5

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/keeper.js CHANGED
@@ -3,9 +3,33 @@
3
3
  // src/keeper.ts
4
4
  import { closeSync as closeSync2, existsSync as existsSync3, mkdtempSync as mkdtempSync4, openSync as openSync3, readFileSync as readFileSync3, rmSync as rmSync6, writeFileSync as writeFileSync3 } from "node:fs";
5
5
  import { tmpdir as tmpdir5 } from "node:os";
6
- import { join as join6 } from "node:path";
6
+ import { join as join7 } from "node:path";
7
7
  import { setTimeout as delay2 } from "node:timers/promises";
8
8
 
9
+ // src/alive.ts
10
+ import process2 from "node:process";
11
+ function exitOf(child) {
12
+ if (child.exitCode !== null || child.signalCode !== null) {
13
+ return Promise.resolve({ code: child.exitCode, signal: child.signalCode });
14
+ }
15
+ return new Promise((resolve) => {
16
+ child.once("exit", (code, signal) => {
17
+ resolve({ code, signal });
18
+ });
19
+ });
20
+ }
21
+ function alive(pid) {
22
+ if (pid === undefined || pid === null) {
23
+ return false;
24
+ }
25
+ try {
26
+ process2.kill(pid, 0);
27
+ return true;
28
+ } catch (error) {
29
+ return error.code === "EPERM";
30
+ }
31
+ }
32
+
9
33
  // src/desktop.ts
10
34
  import { createHash } from "node:crypto";
11
35
  import {
@@ -23,20 +47,6 @@ import { tmpdir } from "node:os";
23
47
  import { join } from "node:path";
24
48
  import { setTimeout as delay } from "node:timers/promises";
25
49
 
26
- // src/alive.ts
27
- import process2 from "node:process";
28
- function alive(pid) {
29
- if (pid === undefined || pid === null) {
30
- return false;
31
- }
32
- try {
33
- process2.kill(pid, 0);
34
- return true;
35
- } catch (error) {
36
- return error.code === "EPERM";
37
- }
38
- }
39
-
40
50
  // src/spawn.ts
41
51
  import {
42
52
  execFile,
@@ -1031,7 +1041,7 @@ class GameDebugger {
1031
1041
  }
1032
1042
  if (!held.hasStack) {
1033
1043
  this.forget("debug_enter");
1034
- this.send("next", [], held.thread);
1044
+ this.send("step", [], held.thread);
1035
1045
  const again = await this.next(GameDebugger.named("debug_enter"), STEP_MS);
1036
1046
  if (again === null) {
1037
1047
  return {
@@ -1253,8 +1263,28 @@ function readLaunched(printed) {
1253
1263
 
1254
1264
  // src/run-record.ts
1255
1265
  import { createHash as createHash2 } from "node:crypto";
1256
- import { mkdirSync, openSync as openSync2, readdirSync, readFileSync as readFileSync2, realpathSync, rmSync as rmSync4, statSync as statSync2 } from "node:fs";
1257
- import { basename, dirname as dirname2, join as join4, resolve as resolve2 } from "node:path";
1266
+ import { mkdirSync, openSync as openSync2, readdirSync, readFileSync as readFileSync2, rmSync as rmSync4, statSync as statSync3 } from "node:fs";
1267
+ import { basename as basename2, join as join5, resolve as resolve3 } from "node:path";
1268
+
1269
+ // src/paths.ts
1270
+ import { realpathSync, statSync as statSync2 } from "node:fs";
1271
+ import { basename, dirname, isAbsolute, join as join3, relative, resolve, sep } from "node:path";
1272
+ function realAsFarAsItExists(path) {
1273
+ const missing = [];
1274
+ let at = path;
1275
+ for (;; ) {
1276
+ try {
1277
+ return join3(realpathSync.native(at), ...missing);
1278
+ } catch {
1279
+ const parent = dirname(at);
1280
+ if (parent === at) {
1281
+ return path;
1282
+ }
1283
+ missing.unshift(basename(at));
1284
+ at = parent;
1285
+ }
1286
+ }
1287
+ }
1258
1288
 
1259
1289
  // src/windows-ask.ts
1260
1290
  var SCRIPT = [
@@ -1278,7 +1308,7 @@ var SCRIPT = [
1278
1308
 
1279
1309
  // src/runtime-client.ts
1280
1310
  import { tmpdir as tmpdir3 } from "node:os";
1281
- import { dirname, join as join3, resolve } from "node:path";
1311
+ import { dirname as dirname2, join as join4, resolve as resolve2 } from "node:path";
1282
1312
 
1283
1313
  // src/launch.ts
1284
1314
  function envValue(name, variables = process.env) {
@@ -1323,7 +1353,7 @@ function runtimeDirectory(variables = process.env) {
1323
1353
  return explicit;
1324
1354
  }
1325
1355
  const perUser = envValue("XDG_RUNTIME_DIR", variables);
1326
- return join3(perUser ?? tmpdir3(), "gdharness");
1356
+ return join4(perUser ?? tmpdir3(), "gdharness");
1327
1357
  }
1328
1358
  var ERROR_REPORT_KEEP_MS = 60 * 60 * 1000;
1329
1359
  var verdicts = new Map;
@@ -1363,31 +1393,15 @@ function writeWhole(path, text) {
1363
1393
 
1364
1394
  // src/run-record.ts
1365
1395
  function runsDirectory() {
1366
- return join4(runtimeDirectory(), "runs");
1367
- }
1368
- function realAsFarAsItExists(path) {
1369
- const missing = [];
1370
- let at = path;
1371
- for (;; ) {
1372
- try {
1373
- return join4(realpathSync.native(at), ...missing);
1374
- } catch {
1375
- const parent = dirname2(at);
1376
- if (parent === at) {
1377
- return path;
1378
- }
1379
- missing.unshift(basename(at));
1380
- at = parent;
1381
- }
1382
- }
1396
+ return join5(runtimeDirectory(), "runs");
1383
1397
  }
1384
1398
  function projectKey(projectPath) {
1385
- const real = realAsFarAsItExists(resolve2(projectPath)).replace(/[\\/]+$/, "");
1399
+ const real = realAsFarAsItExists(resolve3(projectPath)).replace(/[\\/]+$/, "");
1386
1400
  const folded = process.platform === "win32" || process.platform === "darwin" ? real.toLowerCase() : real;
1387
1401
  return createHash2("sha256").update(folded.replaceAll("\\", "/")).digest("hex").slice(0, 16);
1388
1402
  }
1389
1403
  function runRecordPath(projectPath, directory = runtimeDirectory()) {
1390
- return join4(directory, "runs", `note-${projectKey(projectPath)}.json`);
1404
+ return join5(directory, "runs", `note-${projectKey(projectPath)}.json`);
1391
1405
  }
1392
1406
  function writeRunRecord(record) {
1393
1407
  mkdirSync(runsDirectory(), { recursive: true });
@@ -1454,14 +1468,14 @@ function recordAt(path) {
1454
1468
  // src/scratch.ts
1455
1469
  import { existsSync as existsSync2, mkdtempSync as mkdtempSync3, readdirSync as readdirSync2, rmSync as rmSync5 } from "node:fs";
1456
1470
  import { tmpdir as tmpdir4 } from "node:os";
1457
- import { join as join5 } from "node:path";
1471
+ import { join as join6 } from "node:path";
1458
1472
  import process4 from "node:process";
1459
1473
  var SWEPT = { recursive: true, force: true };
1460
1474
  var AGAIN_AFTER_MS = [250, 500, 1000, 2000, 4000, 8000, 16000];
1461
1475
  var waiting = new Set;
1462
1476
  var atExit = new Set;
1463
1477
  function scratchDirectory(kind) {
1464
- return mkdtempSync3(join5(tmpdir4(), `gdharness-${kind}-${process4.pid}-`));
1478
+ return mkdtempSync3(join6(tmpdir4(), `gdharness-${kind}-${process4.pid}-`));
1465
1479
  }
1466
1480
  function discardWith(remove, paths) {
1467
1481
  const left = [];
@@ -1921,8 +1935,8 @@ async function startDirectly(spec, output, hidden, detached = true, errors = out
1921
1935
  }
1922
1936
  async function startThroughHelper(spec, output, stopFile) {
1923
1937
  const scratch = scratchDirectory("desktop");
1924
- const pidFile = join6(scratch, "pid");
1925
- const errorFile = join6(scratch, "error");
1938
+ const pidFile = join7(scratch, "pid");
1939
+ const errorFile = join7(scratch, "error");
1926
1940
  const where = spec.desktop === undefined ? "without activation" : `on the ${spec.desktop} desktop`;
1927
1941
  try {
1928
1942
  const compiled = await ensureHelperCompiled();
@@ -1930,7 +1944,7 @@ async function startThroughHelper(spec, output, stopFile) {
1930
1944
  return { error: `it could not be started ${where}: ${compiled}` };
1931
1945
  }
1932
1946
  const wrapper = throughHelper({ ...spec, directory: process.cwd() }, spec.command, spec.args, {
1933
- spec: join6(scratch, "spec.json"),
1947
+ spec: join7(scratch, "spec.json"),
1934
1948
  pid: pidFile,
1935
1949
  error: errorFile,
1936
1950
  stop: stopFile
@@ -1974,7 +1988,7 @@ function profiledBy(path, projectPath) {
1974
1988
  };
1975
1989
  let projectGodot = null;
1976
1990
  try {
1977
- projectGodot = readFileSync3(join6(projectPath, "project.godot"), "utf8");
1991
+ projectGodot = readFileSync3(join7(projectPath, "project.godot"), "utf8");
1978
1992
  } catch {}
1979
1993
  const observer = new ProfileObserver((totals) => {
1980
1994
  const first = latest === null;
@@ -2077,8 +2091,8 @@ async function keep() {
2077
2091
  const debuggerPeer = run === undefined || run.debugged === false ? null : await GameDebugger.open(run.breakpoints ?? [], profiled === null ? [] : [profiled.observer]);
2078
2092
  const toStart = debuggerPeer === null ? spec : { ...spec, args: withEngineArguments(spec.args, debuggerPeer.engineArguments) };
2079
2093
  const transcript = run === undefined ? "ignore" : openSync3(run.transcript, "a");
2080
- const own = run !== undefined && throughTheHelper(spec) ? mkdtempSync4(join6(tmpdir5(), "gdharness-stop-")) : null;
2081
- const stopFile = own === null ? "" : join6(own, "stop");
2094
+ const own = run !== undefined && throughTheHelper(spec) ? mkdtempSync4(join7(tmpdir5(), "gdharness-stop-")) : null;
2095
+ const stopFile = own === null ? "" : join7(own, "stop");
2082
2096
  let started;
2083
2097
  try {
2084
2098
  started = await startDetached(toStart, transcript, run === undefined, stopFile);
@@ -2108,26 +2122,8 @@ async function keep() {
2108
2122
  if (record !== null) {
2109
2123
  writeNoteIfItCan(record);
2110
2124
  }
2125
+ const exited = exitOf(child);
2111
2126
  let listening = null;
2112
- child.once("exit", (code, signal) => {
2113
- const finish = () => {
2114
- profiled?.finish();
2115
- if (run !== undefined) {
2116
- recordRunEnded(run.projectPath, pid, { exitCode: code, exitSignal: code === null ? signal : null });
2117
- }
2118
- debuggerPeer?.close();
2119
- listening?.close();
2120
- if (own !== null) {
2121
- rmSync6(own, { recursive: true, force: true });
2122
- }
2123
- process.exit(0);
2124
- };
2125
- if (debuggerPeer === null) {
2126
- finish();
2127
- } else {
2128
- debuggerPeer.drained(DRAIN_MS).then(finish);
2129
- }
2130
- });
2131
2127
  if (record !== null) {
2132
2128
  listening = await listenForTheServer(() => {
2133
2129
  if (stopFile !== "") {
@@ -2140,9 +2136,27 @@ async function keep() {
2140
2136
  writeNoteIfItCan({ ...record, keeper: listening.address });
2141
2137
  }
2142
2138
  }
2143
- process.stdout.write(`pid ${pid}
2144
- `);
2145
- process.stdout.end();
2139
+ await new Promise((resolve) => {
2140
+ process.stdout.once("error", () => {
2141
+ resolve();
2142
+ });
2143
+ process.stdout.end(`pid ${pid}
2144
+ `, resolve);
2145
+ });
2146
+ const { code, signal } = await exited;
2147
+ if (debuggerPeer !== null) {
2148
+ await debuggerPeer.drained(DRAIN_MS);
2149
+ }
2150
+ profiled?.finish();
2151
+ if (run !== undefined) {
2152
+ recordRunEnded(run.projectPath, pid, { exitCode: code, exitSignal: code === null ? signal : null });
2153
+ }
2154
+ debuggerPeer?.close();
2155
+ listening?.close();
2156
+ if (own !== null) {
2157
+ rmSync6(own, { recursive: true, force: true });
2158
+ }
2159
+ process.exit(0);
2146
2160
  }
2147
2161
  var mode = process.argv[2];
2148
2162
  if (mode === "launch") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gdharness",
3
- "version": "1.3.4",
3
+ "version": "1.3.5",
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",