gdharness 1.2.30 → 1.2.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/cli.js +7197 -6159
- package/build/godot/addons/gdharness_editor/bridge_client.gd +14 -0
- package/build/godot/addons/gdharness_editor/file_reads.gd +237 -0
- package/build/godot/addons/gdharness_editor/file_reads.gd.uid +1 -0
- package/build/godot/addons/gdharness_editor/plugin.gd +27 -0
- package/build/godot/addons/gdharness_editor/property_values.gd +80 -54
- package/build/godot/addons/gdharness_editor/scene_file.gd +38 -1
- package/build/godot/addons/gdharness_editor/serialisation.gd +46 -0
- package/build/godot/addons/gdharness_editor/tool_executor.gd +1 -0
- package/build/godot/addons/gdharness_editor/tools/class_tools.gd +22 -0
- package/build/godot/addons/gdharness_editor/tools/filesystem_tools.gd +64 -1
- package/build/godot/addons/gdharness_editor/tools/resource_tools.gd +82 -44
- package/build/godot/addons/gdharness_editor/tools/scene_placing.gd +178 -0
- package/build/godot/addons/gdharness_editor/tools/scene_placing.gd.uid +1 -0
- package/build/godot/addons/gdharness_editor/tools/scene_tools.gd +73 -124
- package/build/godot/addons/gdharness_runtime/runtime_autoload.gd +39 -0
- package/build/godot/addons/gdharness_runtime/runtime_hover.gd +285 -0
- package/build/godot/addons/gdharness_runtime/runtime_hover.gd.uid +1 -0
- package/build/godot/addons/gdharness_runtime/runtime_input.gd +151 -106
- package/build/godot/addons/gdharness_runtime/runtime_mouse.gd +112 -0
- package/build/godot/addons/gdharness_runtime/runtime_mouse.gd.uid +1 -0
- package/build/godot/addons/gdharness_runtime/runtime_paths.gd +5 -4
- package/build/godot/addons/gdharness_runtime/runtime_values.gd +1 -47
- package/build/godot/addons/gdharness_runtime/serialisation.gd +46 -0
- package/build/godot/operations/audio_buses.gd +14 -1
- package/build/godot/operations/class_cache.gd +146 -35
- package/build/godot/operations/classdb_queries.gd +19 -12
- package/build/godot/operations/file_reads.gd +237 -0
- package/build/godot/operations/gdscript_authoring.gd +136 -19
- package/build/godot/operations/godot_operations.gd +24 -0
- package/build/godot/operations/project_config.gd +16 -6
- package/build/godot/operations/property_values.gd +767 -0
- package/build/godot/operations/rename_checks.gd +5 -2
- package/build/godot/operations/serialisation.gd +46 -0
- package/build/index.js +4896 -3872
- package/build/keeper.js +184 -71
- package/package.json +1 -1
package/build/keeper.js
CHANGED
|
@@ -1,14 +1,57 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/keeper.ts
|
|
4
|
-
import { closeSync, existsSync as
|
|
5
|
-
import { tmpdir as
|
|
6
|
-
import { join as
|
|
7
|
-
import { setTimeout as
|
|
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
|
+
import { tmpdir as tmpdir5 } from "node:os";
|
|
6
|
+
import { join as join6 } from "node:path";
|
|
7
|
+
import { setTimeout as delay2 } from "node:timers/promises";
|
|
8
8
|
|
|
9
9
|
// src/desktop.ts
|
|
10
10
|
import { createHash } from "node:crypto";
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
closeSync,
|
|
13
|
+
existsSync,
|
|
14
|
+
mkdtempSync,
|
|
15
|
+
openSync,
|
|
16
|
+
readFileSync,
|
|
17
|
+
rmSync,
|
|
18
|
+
statSync,
|
|
19
|
+
writeFileSync,
|
|
20
|
+
writeSync
|
|
21
|
+
} from "node:fs";
|
|
22
|
+
import { tmpdir } from "node:os";
|
|
23
|
+
import { join } from "node:path";
|
|
24
|
+
import { setTimeout as delay } from "node:timers/promises";
|
|
25
|
+
|
|
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
|
+
// src/spawn.ts
|
|
41
|
+
import {
|
|
42
|
+
execFile,
|
|
43
|
+
execFileSync,
|
|
44
|
+
spawn,
|
|
45
|
+
spawnSync
|
|
46
|
+
} from "node:child_process";
|
|
47
|
+
function spawnHidden(command, args, options = {}) {
|
|
48
|
+
return spawn(command, [...args], { ...options, windowsHide: true });
|
|
49
|
+
}
|
|
50
|
+
function spawnShown(command, args, options = {}) {
|
|
51
|
+
return spawn(command, [...args], { ...options, windowsHide: false });
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// src/desktop.ts
|
|
12
55
|
var DESKTOP_VARIABLE = "GDHARNESS_DESKTOP";
|
|
13
56
|
var SOURCE = `using System;
|
|
14
57
|
using System.ComponentModel;
|
|
@@ -161,6 +204,95 @@ public static class GdharnessDesktop {
|
|
|
161
204
|
}
|
|
162
205
|
}
|
|
163
206
|
`;
|
|
207
|
+
var DIGEST = createHash("sha256").update(SOURCE).digest("hex").slice(0, 16);
|
|
208
|
+
var HELPER_COMPILE_MS = 180000;
|
|
209
|
+
function helperAssembly(directory = tmpdir()) {
|
|
210
|
+
return join(directory, `gdharness-desktop-${DIGEST}.dll`);
|
|
211
|
+
}
|
|
212
|
+
async function ensureHelperCompiled(directory = tmpdir(), budgetMs = HELPER_COMPILE_MS, wait = true) {
|
|
213
|
+
const assembly = helperAssembly(directory);
|
|
214
|
+
const lock = `${assembly}.compiling`;
|
|
215
|
+
const until = Date.now() + budgetMs;
|
|
216
|
+
while (Date.now() < until) {
|
|
217
|
+
if (existsSync(assembly)) {
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
220
|
+
let mine = false;
|
|
221
|
+
try {
|
|
222
|
+
const held = openSync(lock, "wx");
|
|
223
|
+
writeSync(held, String(process.pid));
|
|
224
|
+
closeSync(held);
|
|
225
|
+
mine = true;
|
|
226
|
+
} catch {}
|
|
227
|
+
if (mine) {
|
|
228
|
+
try {
|
|
229
|
+
return await compileHelper(directory, assembly, until - Date.now());
|
|
230
|
+
} finally {
|
|
231
|
+
rmSync(lock, { force: true });
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
try {
|
|
235
|
+
const holder = Number(readFileSync(lock, "utf8").trim());
|
|
236
|
+
const old = Date.now() - statSync(lock).mtimeMs > budgetMs;
|
|
237
|
+
if (old || Number.isInteger(holder) && holder > 0 && !alive(holder)) {
|
|
238
|
+
rmSync(lock, { force: true });
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
} catch {}
|
|
242
|
+
if (!wait) {
|
|
243
|
+
return null;
|
|
244
|
+
}
|
|
245
|
+
await delay(200);
|
|
246
|
+
}
|
|
247
|
+
return `the desktop helper was not compiled within ${String(budgetMs)} ms`;
|
|
248
|
+
}
|
|
249
|
+
async function compileHelper(directory, assembly, budgetMs) {
|
|
250
|
+
const scratch = mkdtempSync(join(directory, "gdharness-desktop-source-"));
|
|
251
|
+
const source = join(scratch, "helper.cs");
|
|
252
|
+
writeFileSync(source, SOURCE, "utf8");
|
|
253
|
+
const literal = (path) => `'${path.replaceAll("'", "''")}'`;
|
|
254
|
+
const script = [
|
|
255
|
+
"$ErrorActionPreference = 'Stop'",
|
|
256
|
+
"try {",
|
|
257
|
+
` $partial = ${literal(assembly)} + '.' + $PID`,
|
|
258
|
+
` Add-Type -TypeDefinition ([IO.File]::ReadAllText(${literal(source)}, [Text.Encoding]::UTF8)) -OutputAssembly $partial`,
|
|
259
|
+
` Move-Item -LiteralPath $partial -Destination ${literal(assembly)}`,
|
|
260
|
+
"} catch {",
|
|
261
|
+
" [Console]::Error.WriteLine($_.Exception.Message)",
|
|
262
|
+
" exit 1",
|
|
263
|
+
"}"
|
|
264
|
+
].join(`
|
|
265
|
+
`);
|
|
266
|
+
try {
|
|
267
|
+
return await new Promise((resolve) => {
|
|
268
|
+
const compiler = spawnHidden("powershell.exe", [
|
|
269
|
+
"-NoProfile",
|
|
270
|
+
"-NonInteractive",
|
|
271
|
+
"-ExecutionPolicy",
|
|
272
|
+
"Bypass",
|
|
273
|
+
"-EncodedCommand",
|
|
274
|
+
Buffer.from(script, "utf16le").toString("base64")
|
|
275
|
+
], { stdio: ["ignore", "ignore", "pipe"] });
|
|
276
|
+
let complained = "";
|
|
277
|
+
compiler.stderr.on("data", (chunk) => {
|
|
278
|
+
complained += chunk.toString();
|
|
279
|
+
});
|
|
280
|
+
const timer = setTimeout(() => {
|
|
281
|
+
compiler.kill();
|
|
282
|
+
}, budgetMs);
|
|
283
|
+
compiler.once("error", (error) => {
|
|
284
|
+
clearTimeout(timer);
|
|
285
|
+
resolve(`Windows PowerShell did not start: ${error.message}`);
|
|
286
|
+
});
|
|
287
|
+
compiler.once("close", (code) => {
|
|
288
|
+
clearTimeout(timer);
|
|
289
|
+
resolve(existsSync(assembly) ? null : `Windows PowerShell could not compile it${code === null ? " in time" : ""}: ${complained.trim() || `exit code ${String(code)}`}`);
|
|
290
|
+
});
|
|
291
|
+
});
|
|
292
|
+
} finally {
|
|
293
|
+
rmSync(scratch, { recursive: true, force: true });
|
|
294
|
+
}
|
|
295
|
+
}
|
|
164
296
|
function quoted(argument) {
|
|
165
297
|
if (argument !== "" && !/[\s"]/.test(argument)) {
|
|
166
298
|
return argument;
|
|
@@ -180,7 +312,7 @@ function quoted(argument) {
|
|
|
180
312
|
function windowsCommandLine(command, args) {
|
|
181
313
|
return [command, ...args].map(quoted).join(" ");
|
|
182
314
|
}
|
|
183
|
-
function throughHelper(how, command, args, files) {
|
|
315
|
+
function throughHelper(how, command, args, files, assembly = helperAssembly()) {
|
|
184
316
|
const spec = {
|
|
185
317
|
desktop: how.desktop ?? "",
|
|
186
318
|
noActivate: how.noActivate === true,
|
|
@@ -188,8 +320,7 @@ function throughHelper(how, command, args, files) {
|
|
|
188
320
|
pidFile: files.pid,
|
|
189
321
|
stopFile: files.stop,
|
|
190
322
|
directory: how.directory,
|
|
191
|
-
|
|
192
|
-
digest: createHash("sha256").update(SOURCE).digest("hex").slice(0, 16)
|
|
323
|
+
assembly
|
|
193
324
|
};
|
|
194
325
|
writeFileSync(files.spec, JSON.stringify(spec), "utf8");
|
|
195
326
|
const literal = (path) => `'${path.replaceAll("'", "''")}'`;
|
|
@@ -197,13 +328,7 @@ function throughHelper(how, command, args, files) {
|
|
|
197
328
|
"$ErrorActionPreference = 'Stop'",
|
|
198
329
|
"try {",
|
|
199
330
|
` $spec = [IO.File]::ReadAllText(${literal(files.spec)}, [Text.Encoding]::UTF8) | ConvertFrom-Json`,
|
|
200
|
-
"
|
|
201
|
-
" if (-not (Test-Path -LiteralPath $assembly)) {",
|
|
202
|
-
' $partial = "$assembly.$PID"',
|
|
203
|
-
" Add-Type -TypeDefinition $spec.source -OutputAssembly $partial",
|
|
204
|
-
" try { Move-Item -LiteralPath $partial -Destination $assembly } catch { Remove-Item -LiteralPath $partial -ErrorAction SilentlyContinue }",
|
|
205
|
-
" }",
|
|
206
|
-
" Add-Type -LiteralPath $assembly",
|
|
331
|
+
" Add-Type -LiteralPath $spec.assembly",
|
|
207
332
|
" $code = [GdharnessDesktop]::Run($spec.desktop, $spec.commandLine, $spec.pidFile, [bool]$spec.noActivate, $spec.stopFile, $spec.directory)",
|
|
208
333
|
"} catch {",
|
|
209
334
|
` [IO.File]::WriteAllText(${literal(files.error)}, $_.Exception.Message)`,
|
|
@@ -1003,11 +1128,11 @@ class GameDebugger {
|
|
|
1003
1128
|
|
|
1004
1129
|
// src/keeper-channel.ts
|
|
1005
1130
|
import { randomUUID } from "node:crypto";
|
|
1006
|
-
import { mkdtempSync, rmSync } from "node:fs";
|
|
1131
|
+
import { mkdtempSync as mkdtempSync2, rmSync as rmSync2 } from "node:fs";
|
|
1007
1132
|
import { connect, createServer as createServer2 } from "node:net";
|
|
1008
|
-
import { tmpdir } from "node:os";
|
|
1009
|
-
import { join } from "node:path";
|
|
1010
|
-
import
|
|
1133
|
+
import { tmpdir as tmpdir2 } from "node:os";
|
|
1134
|
+
import { join as join2 } from "node:path";
|
|
1135
|
+
import process3 from "node:process";
|
|
1011
1136
|
var DEBUG_PREFIX = "debug ";
|
|
1012
1137
|
function endedOrWhyNot(end) {
|
|
1013
1138
|
try {
|
|
@@ -1034,8 +1159,8 @@ function errorText(error) {
|
|
|
1034
1159
|
async function listenForTheServer(end, debug) {
|
|
1035
1160
|
let directory = null;
|
|
1036
1161
|
try {
|
|
1037
|
-
directory =
|
|
1038
|
-
const address = directory === null ? `\\\\.\\pipe\\gdharness-keeper-${randomUUID()}` :
|
|
1162
|
+
directory = process3.platform === "win32" ? null : mkdtempSync2(join2(tmpdir2(), "gdharness-keeper-"));
|
|
1163
|
+
const address = directory === null ? `\\\\.\\pipe\\gdharness-keeper-${randomUUID()}` : join2(directory, "keeper.sock");
|
|
1039
1164
|
const server = createServer2((socket) => {
|
|
1040
1165
|
socket.setEncoding("utf8");
|
|
1041
1166
|
socket.on("error", () => {});
|
|
@@ -1071,13 +1196,13 @@ async function listenForTheServer(end, debug) {
|
|
|
1071
1196
|
close: () => {
|
|
1072
1197
|
server.close();
|
|
1073
1198
|
if (made !== null) {
|
|
1074
|
-
|
|
1199
|
+
rmSync2(made, { recursive: true, force: true });
|
|
1075
1200
|
}
|
|
1076
1201
|
}
|
|
1077
1202
|
};
|
|
1078
1203
|
} catch {
|
|
1079
1204
|
if (directory !== null) {
|
|
1080
|
-
|
|
1205
|
+
rmSync2(directory, { recursive: true, force: true });
|
|
1081
1206
|
}
|
|
1082
1207
|
return null;
|
|
1083
1208
|
}
|
|
@@ -1094,22 +1219,6 @@ function listening(server, address) {
|
|
|
1094
1219
|
|
|
1095
1220
|
// src/outside.ts
|
|
1096
1221
|
import { fileURLToPath } from "node:url";
|
|
1097
|
-
|
|
1098
|
-
// src/spawn.ts
|
|
1099
|
-
import {
|
|
1100
|
-
execFile,
|
|
1101
|
-
execFileSync,
|
|
1102
|
-
spawn,
|
|
1103
|
-
spawnSync
|
|
1104
|
-
} from "node:child_process";
|
|
1105
|
-
function spawnHidden(command, args, options = {}) {
|
|
1106
|
-
return spawn(command, [...args], { ...options, windowsHide: true });
|
|
1107
|
-
}
|
|
1108
|
-
function spawnShown(command, args, options = {}) {
|
|
1109
|
-
return spawn(command, [...args], { ...options, windowsHide: false });
|
|
1110
|
-
}
|
|
1111
|
-
|
|
1112
|
-
// src/outside.ts
|
|
1113
1222
|
function withChanges(base, changes) {
|
|
1114
1223
|
const env = { ...base };
|
|
1115
1224
|
for (const [name, value] of Object.entries(changes)) {
|
|
@@ -1144,8 +1253,8 @@ function readLaunched(printed) {
|
|
|
1144
1253
|
|
|
1145
1254
|
// src/run-record.ts
|
|
1146
1255
|
import { createHash as createHash2 } from "node:crypto";
|
|
1147
|
-
import { mkdirSync, openSync, readdirSync, readFileSync, realpathSync, rmSync as
|
|
1148
|
-
import { basename, dirname as dirname2, join as
|
|
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";
|
|
1149
1258
|
|
|
1150
1259
|
// src/windows-ask.ts
|
|
1151
1260
|
var SCRIPT = [
|
|
@@ -1168,8 +1277,8 @@ var SCRIPT = [
|
|
|
1168
1277
|
`);
|
|
1169
1278
|
|
|
1170
1279
|
// src/runtime-client.ts
|
|
1171
|
-
import { tmpdir as
|
|
1172
|
-
import { dirname, join as
|
|
1280
|
+
import { tmpdir as tmpdir3 } from "node:os";
|
|
1281
|
+
import { dirname, join as join3, resolve } from "node:path";
|
|
1173
1282
|
|
|
1174
1283
|
// src/launch.ts
|
|
1175
1284
|
function envValue(name, variables = process.env) {
|
|
@@ -1214,7 +1323,7 @@ function runtimeDirectory(variables = process.env) {
|
|
|
1214
1323
|
return explicit;
|
|
1215
1324
|
}
|
|
1216
1325
|
const perUser = envValue("XDG_RUNTIME_DIR", variables);
|
|
1217
|
-
return
|
|
1326
|
+
return join3(perUser ?? tmpdir3(), "gdharness");
|
|
1218
1327
|
}
|
|
1219
1328
|
var ERROR_REPORT_KEEP_MS = 60 * 60 * 1000;
|
|
1220
1329
|
var verdicts = new Map;
|
|
@@ -1222,7 +1331,7 @@ var LATE_ANSWER_MS = 30 * 60000;
|
|
|
1222
1331
|
var lateAnswers = new Map;
|
|
1223
1332
|
|
|
1224
1333
|
// src/whole-file.ts
|
|
1225
|
-
import { renameSync, rmSync as
|
|
1334
|
+
import { renameSync, rmSync as rmSync3, writeFileSync as writeFileSync2 } from "node:fs";
|
|
1226
1335
|
var HELD_FILE_MS = 1000;
|
|
1227
1336
|
var HELD_FILE_RETRY_MS = 10;
|
|
1228
1337
|
var HELD_BY_A_READER = new Set(["EPERM", "EACCES", "EBUSY"]);
|
|
@@ -1242,7 +1351,7 @@ function writeWhole(path, text) {
|
|
|
1242
1351
|
const code = error.code ?? "";
|
|
1243
1352
|
if (!HELD_BY_A_READER.has(code) || Date.now() >= until) {
|
|
1244
1353
|
try {
|
|
1245
|
-
|
|
1354
|
+
rmSync3(partial, { force: true });
|
|
1246
1355
|
} catch {}
|
|
1247
1356
|
return false;
|
|
1248
1357
|
}
|
|
@@ -1253,14 +1362,14 @@ function writeWhole(path, text) {
|
|
|
1253
1362
|
|
|
1254
1363
|
// src/run-record.ts
|
|
1255
1364
|
function runsDirectory() {
|
|
1256
|
-
return
|
|
1365
|
+
return join4(runtimeDirectory(), "runs");
|
|
1257
1366
|
}
|
|
1258
1367
|
function realAsFarAsItExists(path) {
|
|
1259
1368
|
const missing = [];
|
|
1260
1369
|
let at = path;
|
|
1261
1370
|
for (;; ) {
|
|
1262
1371
|
try {
|
|
1263
|
-
return
|
|
1372
|
+
return join4(realpathSync.native(at), ...missing);
|
|
1264
1373
|
} catch {
|
|
1265
1374
|
const parent = dirname2(at);
|
|
1266
1375
|
if (parent === at) {
|
|
@@ -1277,7 +1386,7 @@ function projectKey(projectPath) {
|
|
|
1277
1386
|
return createHash2("sha256").update(folded.replaceAll("\\", "/")).digest("hex").slice(0, 16);
|
|
1278
1387
|
}
|
|
1279
1388
|
function runRecordPath(projectPath, directory = runtimeDirectory()) {
|
|
1280
|
-
return
|
|
1389
|
+
return join4(directory, "runs", `note-${projectKey(projectPath)}.json`);
|
|
1281
1390
|
}
|
|
1282
1391
|
function writeRunRecord(record) {
|
|
1283
1392
|
mkdirSync(runsDirectory(), { recursive: true });
|
|
@@ -1303,7 +1412,7 @@ function recordRunEnded(projectPath, pid, ending) {
|
|
|
1303
1412
|
function recordAt(path) {
|
|
1304
1413
|
let parsed;
|
|
1305
1414
|
try {
|
|
1306
|
-
parsed = JSON.parse(
|
|
1415
|
+
parsed = JSON.parse(readFileSync2(path, "utf8"));
|
|
1307
1416
|
} catch {
|
|
1308
1417
|
return null;
|
|
1309
1418
|
}
|
|
@@ -1336,16 +1445,16 @@ function recordAt(path) {
|
|
|
1336
1445
|
}
|
|
1337
1446
|
|
|
1338
1447
|
// src/scratch.ts
|
|
1339
|
-
import { existsSync, mkdtempSync as
|
|
1340
|
-
import { tmpdir as
|
|
1341
|
-
import { join as
|
|
1342
|
-
import
|
|
1448
|
+
import { existsSync as existsSync2, mkdtempSync as mkdtempSync3, readdirSync as readdirSync2, rmSync as rmSync5 } from "node:fs";
|
|
1449
|
+
import { tmpdir as tmpdir4 } from "node:os";
|
|
1450
|
+
import { join as join5 } from "node:path";
|
|
1451
|
+
import process4 from "node:process";
|
|
1343
1452
|
var SWEPT = { recursive: true, force: true };
|
|
1344
1453
|
var AGAIN_AFTER_MS = [250, 500, 1000, 2000, 4000, 8000, 16000];
|
|
1345
1454
|
var waiting = new Set;
|
|
1346
1455
|
var atExit = new Set;
|
|
1347
1456
|
function scratchDirectory(kind) {
|
|
1348
|
-
return
|
|
1457
|
+
return mkdtempSync3(join5(tmpdir4(), `gdharness-${kind}-${process4.pid}-`));
|
|
1349
1458
|
}
|
|
1350
1459
|
function discardWith(remove, paths) {
|
|
1351
1460
|
const left = [];
|
|
@@ -1369,7 +1478,7 @@ function discard(...paths) {
|
|
|
1369
1478
|
return left;
|
|
1370
1479
|
}
|
|
1371
1480
|
function remove(path) {
|
|
1372
|
-
|
|
1481
|
+
rmSync5(path, SWEPT);
|
|
1373
1482
|
}
|
|
1374
1483
|
function tryAgain(path, attempt) {
|
|
1375
1484
|
if (attempt === 0) {
|
|
@@ -1381,7 +1490,7 @@ function tryAgain(path, attempt) {
|
|
|
1381
1490
|
lastTryOnExit();
|
|
1382
1491
|
}
|
|
1383
1492
|
const after = AGAIN_AFTER_MS[attempt];
|
|
1384
|
-
if (after === undefined || !
|
|
1493
|
+
if (after === undefined || !existsSync2(path)) {
|
|
1385
1494
|
waiting.delete(path);
|
|
1386
1495
|
if (after === undefined) {
|
|
1387
1496
|
atExit.add(path);
|
|
@@ -1403,7 +1512,7 @@ function lastTryOnExit() {
|
|
|
1403
1512
|
return;
|
|
1404
1513
|
}
|
|
1405
1514
|
lastTryWatched = true;
|
|
1406
|
-
|
|
1515
|
+
process4.once("exit", () => {
|
|
1407
1516
|
for (const path of [...waiting, ...atExit]) {
|
|
1408
1517
|
try {
|
|
1409
1518
|
remove(path);
|
|
@@ -1805,18 +1914,22 @@ async function startDirectly(spec, output, hidden, detached = true, errors = out
|
|
|
1805
1914
|
}
|
|
1806
1915
|
async function startThroughHelper(spec, output, stopFile) {
|
|
1807
1916
|
const scratch = scratchDirectory("desktop");
|
|
1808
|
-
const pidFile =
|
|
1809
|
-
const errorFile =
|
|
1917
|
+
const pidFile = join6(scratch, "pid");
|
|
1918
|
+
const errorFile = join6(scratch, "error");
|
|
1810
1919
|
const where = spec.desktop === undefined ? "without activation" : `on the ${spec.desktop} desktop`;
|
|
1811
1920
|
try {
|
|
1921
|
+
const compiled = await ensureHelperCompiled();
|
|
1922
|
+
if (compiled !== null) {
|
|
1923
|
+
return { error: `it could not be started ${where}: ${compiled}` };
|
|
1924
|
+
}
|
|
1812
1925
|
const wrapper = throughHelper({ ...spec, directory: process.cwd() }, spec.command, spec.args, {
|
|
1813
|
-
spec:
|
|
1926
|
+
spec: join6(scratch, "spec.json"),
|
|
1814
1927
|
pid: pidFile,
|
|
1815
1928
|
error: errorFile,
|
|
1816
1929
|
stop: stopFile
|
|
1817
1930
|
});
|
|
1818
1931
|
const envChanges = spec.desktop === undefined ? spec.envChanges : { ...spec.envChanges, [DESKTOP_VARIABLE]: spec.desktop };
|
|
1819
|
-
const started = await startDirectly({ ...spec, command: wrapper.command, args: wrapper.args, envChanges }, output, true, false, "ignore",
|
|
1932
|
+
const started = await startDirectly({ ...spec, command: wrapper.command, args: wrapper.args, envChanges }, output, true, false, "ignore", tmpdir5());
|
|
1820
1933
|
if ("error" in started) {
|
|
1821
1934
|
return started;
|
|
1822
1935
|
}
|
|
@@ -1825,15 +1938,15 @@ async function startThroughHelper(spec, output, stopFile) {
|
|
|
1825
1938
|
exited.now = true;
|
|
1826
1939
|
});
|
|
1827
1940
|
for (let waited = 0;waited < DESKTOP_START_MS; waited += 50) {
|
|
1828
|
-
const pid =
|
|
1941
|
+
const pid = existsSync3(pidFile) ? Number(readFileSync3(pidFile, "utf8").trim()) : Number.NaN;
|
|
1829
1942
|
if (Number.isInteger(pid) && pid > 0) {
|
|
1830
1943
|
return { child: started.child, pid };
|
|
1831
1944
|
}
|
|
1832
1945
|
if (exited.now) {
|
|
1833
|
-
const why =
|
|
1946
|
+
const why = existsSync3(errorFile) ? readFileSync3(errorFile, "utf8").trim() : "it exited saying nothing";
|
|
1834
1947
|
return { error: `it could not be started ${where}: ${why}` };
|
|
1835
1948
|
}
|
|
1836
|
-
await
|
|
1949
|
+
await delay2(50);
|
|
1837
1950
|
}
|
|
1838
1951
|
started.child.kill();
|
|
1839
1952
|
return { error: `it was not started ${where} within ${DESKTOP_START_MS} ms` };
|
|
@@ -1854,7 +1967,7 @@ function profiledBy(path, projectPath) {
|
|
|
1854
1967
|
};
|
|
1855
1968
|
let projectGodot = null;
|
|
1856
1969
|
try {
|
|
1857
|
-
projectGodot =
|
|
1970
|
+
projectGodot = readFileSync3(join6(projectPath, "project.godot"), "utf8");
|
|
1858
1971
|
} catch {}
|
|
1859
1972
|
const observer = new ProfileObserver((totals) => {
|
|
1860
1973
|
const first = latest === null;
|
|
@@ -1956,15 +2069,15 @@ async function keep() {
|
|
|
1956
2069
|
const profiled = run?.profile === undefined ? null : profiledBy(run.profile, run.projectPath);
|
|
1957
2070
|
const debuggerPeer = run === undefined || run.debugged === false ? null : await GameDebugger.open(run.breakpoints ?? [], profiled === null ? [] : [profiled.observer]);
|
|
1958
2071
|
const toStart = debuggerPeer === null ? spec : { ...spec, args: withEngineArguments(spec.args, debuggerPeer.engineArguments) };
|
|
1959
|
-
const transcript = run === undefined ? "ignore" :
|
|
1960
|
-
const own = run !== undefined && throughTheHelper(spec) ?
|
|
1961
|
-
const stopFile = own === null ? "" :
|
|
2072
|
+
const transcript = run === undefined ? "ignore" : openSync3(run.transcript, "a");
|
|
2073
|
+
const own = run !== undefined && throughTheHelper(spec) ? mkdtempSync4(join6(tmpdir5(), "gdharness-stop-")) : null;
|
|
2074
|
+
const stopFile = own === null ? "" : join6(own, "stop");
|
|
1962
2075
|
let started;
|
|
1963
2076
|
try {
|
|
1964
2077
|
started = await startDetached(toStart, transcript, run === undefined, stopFile);
|
|
1965
2078
|
} finally {
|
|
1966
2079
|
if (transcript !== "ignore") {
|
|
1967
|
-
|
|
2080
|
+
closeSync2(transcript);
|
|
1968
2081
|
}
|
|
1969
2082
|
}
|
|
1970
2083
|
if ("error" in started) {
|
|
@@ -1998,7 +2111,7 @@ async function keep() {
|
|
|
1998
2111
|
debuggerPeer?.close();
|
|
1999
2112
|
listening?.close();
|
|
2000
2113
|
if (own !== null) {
|
|
2001
|
-
|
|
2114
|
+
rmSync6(own, { recursive: true, force: true });
|
|
2002
2115
|
}
|
|
2003
2116
|
process.exit(0);
|
|
2004
2117
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gdharness",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.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",
|