gdharness 1.0.30 → 1.0.32

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
@@ -6,10 +6,28 @@ import { closeSync, openSync as openSync2 } from "node:fs";
6
6
 
7
7
  // src/outside.ts
8
8
  import { fileURLToPath } from "node:url";
9
- var KEEPER_SCRIPT = fileURLToPath(new URL("./keeper.js", import.meta.url));
10
- function decodeSpec(encoded) {
11
- return JSON.parse(Buffer.from(encoded, "base64url").toString("utf8"));
9
+ function withChanges(base, changes) {
10
+ const env = { ...base };
11
+ for (const [name, value] of Object.entries(changes)) {
12
+ if (value === null) {
13
+ delete env[name];
14
+ } else {
15
+ env[name] = value;
16
+ }
17
+ }
18
+ return env;
19
+ }
20
+ function sendSpec(child, spec) {
21
+ child.stdin?.end(JSON.stringify(spec), "utf8");
22
+ }
23
+ async function receivedSpec() {
24
+ const chunks = [];
25
+ for await (const chunk of process.stdin) {
26
+ chunks.push(chunk);
27
+ }
28
+ return JSON.parse(Buffer.concat(chunks).toString("utf8"));
12
29
  }
30
+ var KEEPER_SCRIPT = fileURLToPath(new URL("./keeper.js", import.meta.url));
13
31
  function readLaunched(printed) {
14
32
  const line = printed.split(`
15
33
  `).find((one) => one.trim() !== "")?.trim() ?? "";
@@ -21,8 +39,18 @@ function readLaunched(printed) {
21
39
  }
22
40
 
23
41
  // src/run-record.ts
24
- import { mkdirSync, openSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs";
25
- import { basename, join as join2 } from "node:path";
42
+ import { createHash } from "node:crypto";
43
+ import {
44
+ mkdirSync,
45
+ openSync,
46
+ readdirSync,
47
+ readFileSync,
48
+ realpathSync,
49
+ rmSync,
50
+ statSync,
51
+ writeFileSync
52
+ } from "node:fs";
53
+ import { basename, dirname as dirname2, join as join2, resolve as resolve2 } from "node:path";
26
54
 
27
55
  // src/process-children.ts
28
56
  import { execFile, execFileSync, spawnSync } from "node:child_process";
@@ -57,15 +85,36 @@ var lateAnswers = new Map;
57
85
  function runsDirectory() {
58
86
  return join2(runtimeDirectory(), "runs");
59
87
  }
60
- function recordPath() {
61
- return join2(runsDirectory(), "run.json");
88
+ function realAsFarAsItExists(path) {
89
+ const missing = [];
90
+ let at = path;
91
+ for (;; ) {
92
+ try {
93
+ return join2(realpathSync.native(at), ...missing);
94
+ } catch {
95
+ const parent = dirname2(at);
96
+ if (parent === at) {
97
+ return path;
98
+ }
99
+ missing.unshift(basename(at));
100
+ at = parent;
101
+ }
102
+ }
103
+ }
104
+ function projectKey(projectPath) {
105
+ const real = realAsFarAsItExists(resolve2(projectPath)).replace(/[\\/]+$/, "");
106
+ const folded = process.platform === "win32" || process.platform === "darwin" ? real.toLowerCase() : real;
107
+ return createHash("sha256").update(folded.replaceAll("\\", "/")).digest("hex").slice(0, 16);
108
+ }
109
+ function runRecordPath(projectPath, directory = runtimeDirectory()) {
110
+ return join2(directory, "runs", `note-${projectKey(projectPath)}.json`);
62
111
  }
63
112
  function writeRunRecord(record) {
64
113
  mkdirSync(runsDirectory(), { recursive: true });
65
- writeFileSync(recordPath(), JSON.stringify(record, null, 2), "utf8");
114
+ writeFileSync(runRecordPath(record.projectPath), JSON.stringify(record, null, 2), "utf8");
66
115
  }
67
- function recordRunEnded(pid, ending) {
68
- const path = recordPath();
116
+ function recordRunEnded(projectPath, pid, ending) {
117
+ const path = runRecordPath(projectPath);
69
118
  const record = recordAt(path);
70
119
  if (record === null || record.pid !== pid) {
71
120
  return;
@@ -115,7 +164,7 @@ async function startDetached(spec, output) {
115
164
  const child = spawn(spec.command, [...spec.args], {
116
165
  stdio: ["ignore", output, output],
117
166
  detached: true,
118
- ...spec.env === undefined ? {} : { env: spec.env }
167
+ env: withChanges(process.env, spec.envChanges)
119
168
  });
120
169
  child.once("error", (error) => {
121
170
  resolve({ error: error.message });
@@ -125,8 +174,8 @@ async function startDetached(spec, output) {
125
174
  });
126
175
  });
127
176
  }
128
- async function launch(encoded) {
129
- const spec = decodeSpec(encoded);
177
+ async function launch() {
178
+ const spec = await receivedSpec();
130
179
  if (spec.run === undefined) {
131
180
  const started = await startDetached(spec, "ignore");
132
181
  if ("error" in started) {
@@ -139,8 +188,8 @@ async function launch(encoded) {
139
188
  }
140
189
  process.exit(0);
141
190
  }
142
- const keeper = spawn(process.execPath, [KEEPER_SCRIPT, "keep", encoded], {
143
- stdio: ["ignore", "pipe", "pipe"],
191
+ const keeper = spawn(process.execPath, [KEEPER_SCRIPT, "keep"], {
192
+ stdio: ["pipe", "pipe", "pipe"],
144
193
  detached: true,
145
194
  windowsHide: true
146
195
  });
@@ -151,6 +200,7 @@ async function launch(encoded) {
151
200
  `);
152
201
  process.exit(0);
153
202
  });
203
+ sendSpec(keeper, spec);
154
204
  keeper.stderr.on("data", (chunk) => {
155
205
  complained += chunk.toString();
156
206
  });
@@ -173,8 +223,8 @@ async function launch(encoded) {
173
223
  process.exit(0);
174
224
  });
175
225
  }
176
- async function keep(encoded) {
177
- const spec = decodeSpec(encoded);
226
+ async function keep() {
227
+ const spec = await receivedSpec();
178
228
  if (spec.run === undefined) {
179
229
  process.stdout.write(`error a keeper was started without a run
180
230
  `);
@@ -203,20 +253,20 @@ async function keep(encoded) {
203
253
  command: spec.command
204
254
  });
205
255
  child.once("exit", (code, signal) => {
206
- recordRunEnded(pid, { exitCode: code, exitSignal: code === null ? signal : null });
256
+ recordRunEnded(run.projectPath, pid, { exitCode: code, exitSignal: code === null ? signal : null });
207
257
  process.exit(0);
208
258
  });
209
259
  process.stdout.write(`pid ${pid}
210
260
  `);
211
261
  process.stdout.end();
212
262
  }
213
- var [mode, encoded] = process.argv.slice(2);
214
- if (mode === "launch" && encoded !== undefined) {
215
- await launch(encoded);
216
- } else if (mode === "keep" && encoded !== undefined) {
217
- await keep(encoded);
263
+ var mode = process.argv[2];
264
+ if (mode === "launch") {
265
+ await launch();
266
+ } else if (mode === "keep") {
267
+ await keep();
218
268
  } else {
219
- process.stderr.write(`keeper.js launch|keep <spec>
269
+ process.stderr.write(`keeper.js launch|keep, with the spec on stdin
220
270
  `);
221
271
  process.exit(2);
222
272
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gdharness",
3
- "version": "1.0.30",
3
+ "version": "1.0.32",
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",