gdharness 1.1.13 → 1.1.15

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
@@ -2,9 +2,8 @@
2
2
 
3
3
  // src/keeper.ts
4
4
  import { spawn } from "node:child_process";
5
- import { closeSync, existsSync, mkdtempSync, openSync as openSync2, readFileSync as readFileSync2, rmSync as rmSync2 } from "node:fs";
6
- import { tmpdir as tmpdir2 } from "node:os";
7
- import { join as join3 } from "node:path";
5
+ import { closeSync, existsSync as existsSync2, openSync as openSync2, readFileSync as readFileSync2 } from "node:fs";
6
+ import { join as join4 } from "node:path";
8
7
  import { setTimeout as delay } from "node:timers/promises";
9
8
 
10
9
  // src/desktop.ts
@@ -242,6 +241,7 @@ function readLaunched(printed) {
242
241
  }
243
242
 
244
243
  // src/run-record.ts
244
+ import { execFile as execFile2 } from "node:child_process";
245
245
  import { createHash as createHash2 } from "node:crypto";
246
246
  import {
247
247
  mkdirSync,
@@ -254,10 +254,33 @@ import {
254
254
  writeFileSync
255
255
  } from "node:fs";
256
256
  import { basename, dirname as dirname2, join as join2, resolve as resolve2 } from "node:path";
257
+ import { promisify as promisify2 } from "node:util";
257
258
 
258
259
  // src/process-children.ts
259
- import { execFile, execFileSync, spawnSync } from "node:child_process";
260
+ import { execFile } from "node:child_process";
260
261
  import { promisify } from "node:util";
262
+
263
+ // src/windows-ask.ts
264
+ var SCRIPT = [
265
+ "[Console]::OutputEncoding = [Text.Encoding]::UTF8",
266
+ "$in = [Console]::In",
267
+ "while ($null -ne ($line = $in.ReadLine())) {",
268
+ ' $parts = $line.Split(" ", 2)',
269
+ " try {",
270
+ " $asked = [Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($parts[1]))",
271
+ " $said = (& ([ScriptBlock]::Create($asked)) | Out-String -Width 1000000)",
272
+ ' $tag = "OK"',
273
+ " } catch {",
274
+ " $said = $_.Exception.Message",
275
+ ' $tag = "ERR"',
276
+ " }",
277
+ ' [Console]::Out.Write($parts[0] + " " + $tag + " " + [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($said)) + "`n")',
278
+ " [Console]::Out.Flush()",
279
+ "}"
280
+ ].join(`
281
+ `);
282
+
283
+ // src/process-children.ts
261
284
  var run = promisify(execFile);
262
285
 
263
286
  // src/runtime-client.ts
@@ -285,6 +308,7 @@ var LATE_ANSWER_MS = 30 * 60000;
285
308
  var lateAnswers = new Map;
286
309
 
287
310
  // src/run-record.ts
311
+ var execFileAsync = promisify2(execFile2);
288
312
  function runsDirectory() {
289
313
  return join2(runtimeDirectory(), "runs");
290
314
  }
@@ -361,6 +385,83 @@ function recordAt(path) {
361
385
  };
362
386
  }
363
387
 
388
+ // src/scratch.ts
389
+ import { existsSync, mkdtempSync, readdirSync as readdirSync2, rmSync as rmSync2 } from "node:fs";
390
+ import { tmpdir as tmpdir2 } from "node:os";
391
+ import { join as join3 } from "node:path";
392
+ import process2 from "node:process";
393
+ var SWEPT = { recursive: true, force: true };
394
+ var AGAIN_AFTER_MS = [250, 500, 1000, 2000, 4000, 8000, 16000];
395
+ var waiting = new Set;
396
+ var atExit = new Set;
397
+ function scratchDirectory(kind) {
398
+ return mkdtempSync(join3(tmpdir2(), `gdharness-${kind}-${process2.pid}-`));
399
+ }
400
+ function discardWith(remove, paths) {
401
+ const left = [];
402
+ for (const path of paths) {
403
+ if (!path) {
404
+ continue;
405
+ }
406
+ try {
407
+ remove(path);
408
+ } catch (error) {
409
+ left.push({ path, reason: error instanceof Error ? error.message : String(error) });
410
+ }
411
+ }
412
+ return left;
413
+ }
414
+ function discard(...paths) {
415
+ const left = discardWith(remove, paths);
416
+ for (const { path } of left) {
417
+ tryAgain(path, 0);
418
+ }
419
+ return left;
420
+ }
421
+ function remove(path) {
422
+ rmSync2(path, SWEPT);
423
+ }
424
+ function tryAgain(path, attempt) {
425
+ if (attempt === 0) {
426
+ if (waiting.has(path)) {
427
+ return;
428
+ }
429
+ waiting.add(path);
430
+ atExit.delete(path);
431
+ lastTryOnExit();
432
+ }
433
+ const after = AGAIN_AFTER_MS[attempt];
434
+ if (after === undefined || !existsSync(path)) {
435
+ waiting.delete(path);
436
+ if (after === undefined) {
437
+ atExit.add(path);
438
+ }
439
+ return;
440
+ }
441
+ setTimeout(() => {
442
+ try {
443
+ remove(path);
444
+ waiting.delete(path);
445
+ } catch {
446
+ tryAgain(path, attempt + 1);
447
+ }
448
+ }, after).unref();
449
+ }
450
+ var lastTryWatched = false;
451
+ function lastTryOnExit() {
452
+ if (lastTryWatched) {
453
+ return;
454
+ }
455
+ lastTryWatched = true;
456
+ process2.once("exit", () => {
457
+ for (const path of [...waiting, ...atExit]) {
458
+ try {
459
+ remove(path);
460
+ } catch {}
461
+ }
462
+ });
463
+ }
464
+
364
465
  // src/keeper.ts
365
466
  async function startDirectly(spec, output, hidden, detached = true, errors = output) {
366
467
  return await new Promise((resolve) => {
@@ -379,9 +480,9 @@ async function startDirectly(spec, output, hidden, detached = true, errors = out
379
480
  });
380
481
  }
381
482
  async function startThroughHelper(spec, output) {
382
- const scratch = mkdtempSync(join3(tmpdir2(), "gdharness-desktop-"));
383
- const pidFile = join3(scratch, "pid");
384
- const errorFile = join3(scratch, "error");
483
+ const scratch = scratchDirectory("desktop");
484
+ const pidFile = join4(scratch, "pid");
485
+ const errorFile = join4(scratch, "error");
385
486
  const where = spec.desktop === undefined ? "without activation" : `on the ${spec.desktop} desktop`;
386
487
  try {
387
488
  const wrapper = throughHelper(spec, spec.command, spec.args, pidFile, errorFile);
@@ -394,12 +495,12 @@ async function startThroughHelper(spec, output) {
394
495
  exited.now = true;
395
496
  });
396
497
  for (let waited = 0;waited < DESKTOP_START_MS; waited += 50) {
397
- const pid = existsSync(pidFile) ? Number(readFileSync2(pidFile, "utf8").trim()) : Number.NaN;
498
+ const pid = existsSync2(pidFile) ? Number(readFileSync2(pidFile, "utf8").trim()) : Number.NaN;
398
499
  if (Number.isInteger(pid) && pid > 0) {
399
500
  return { child: started.child, pid };
400
501
  }
401
502
  if (exited.now) {
402
- const why = existsSync(errorFile) ? readFileSync2(errorFile, "utf8").trim() : "it exited saying nothing";
503
+ const why = existsSync2(errorFile) ? readFileSync2(errorFile, "utf8").trim() : "it exited saying nothing";
403
504
  return { error: `it could not be started ${where}: ${why}` };
404
505
  }
405
506
  await delay(50);
@@ -407,7 +508,7 @@ async function startThroughHelper(spec, output) {
407
508
  started.child.kill();
408
509
  return { error: `it was not started ${where} within ${DESKTOP_START_MS} ms` };
409
510
  } finally {
410
- rmSync2(scratch, { recursive: true, force: true });
511
+ discard(scratch);
411
512
  }
412
513
  }
413
514
  var DESKTOP_START_MS = 60000;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gdharness",
3
- "version": "1.1.13",
3
+ "version": "1.1.15",
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",