gdharness 1.2.37 → 1.2.38
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 +607 -374
- package/build/godot/addons/gdharness_editor/headless_check.gd +41 -4
- package/build/godot/addons/gdharness_editor/scene_file.gd +86 -23
- package/build/godot/addons/gdharness_editor/tools/resource_tools.gd +20 -7
- package/build/godot/addons/gdharness_editor/tools/scene_tools.gd +44 -6
- package/build/godot/operations/godot_operations.gd +10 -3
- package/build/index.js +537 -311
- package/package.json +1 -1
package/build/cli.js
CHANGED
|
@@ -8181,6 +8181,9 @@ class GameLog {
|
|
|
8181
8181
|
count(severity) {
|
|
8182
8182
|
return this.entries.filter((entry) => entry.severity === severity).length;
|
|
8183
8183
|
}
|
|
8184
|
+
crashReport() {
|
|
8185
|
+
return this.entries.find((entry) => CRASH.test(entry.text));
|
|
8186
|
+
}
|
|
8184
8187
|
everything() {
|
|
8185
8188
|
return this.entries;
|
|
8186
8189
|
}
|
|
@@ -8260,21 +8263,98 @@ function runEngine(executable, args, options = {}) {
|
|
|
8260
8263
|
});
|
|
8261
8264
|
});
|
|
8262
8265
|
}
|
|
8266
|
+
function exitCodeText(code) {
|
|
8267
|
+
if (code === null) {
|
|
8268
|
+
return "exit code unknown";
|
|
8269
|
+
}
|
|
8270
|
+
if (code < 3221225472) {
|
|
8271
|
+
return `exit code ${code}`;
|
|
8272
|
+
}
|
|
8273
|
+
const hex = `0x${code.toString(16).toUpperCase()}`;
|
|
8274
|
+
return `exit code ${hex}, ${WINDOWS_STATUSES.get(code) ?? "a Windows error status"}`;
|
|
8275
|
+
}
|
|
8263
8276
|
function howItEnded(run) {
|
|
8264
8277
|
if (run.failure !== null) {
|
|
8265
8278
|
return run.exitSignal === null ? run.failure : `ended by ${run.exitSignal}, because ${run.failure}`;
|
|
8266
8279
|
}
|
|
8267
|
-
return run.exitSignal === null ?
|
|
8280
|
+
return run.exitSignal === null ? exitCodeText(run.exitCode) : `ended by ${run.exitSignal}`;
|
|
8268
8281
|
}
|
|
8282
|
+
var WINDOWS_STATUSES;
|
|
8269
8283
|
var init_engine_run = __esm(() => {
|
|
8270
8284
|
init_call_signal();
|
|
8271
8285
|
init_game_log();
|
|
8272
8286
|
init_spawn();
|
|
8287
|
+
WINDOWS_STATUSES = new Map([
|
|
8288
|
+
[3221225477, "an access violation, so it crashed"],
|
|
8289
|
+
[3221225501, "an illegal instruction, so it crashed"],
|
|
8290
|
+
[3221225620, "an integer division by zero, so it crashed"],
|
|
8291
|
+
[3221225725, "a stack overflow, so it crashed"],
|
|
8292
|
+
[3221225781, "a library it needs was not found, so it never started"],
|
|
8293
|
+
[3221225786, "its console was closed or sent Ctrl+C"],
|
|
8294
|
+
[3221226356, "a corrupted heap, so it crashed"],
|
|
8295
|
+
[3221226505, "a stack buffer overrun or a fast fail, so it crashed"]
|
|
8296
|
+
]);
|
|
8273
8297
|
});
|
|
8274
8298
|
|
|
8275
|
-
// src/
|
|
8276
|
-
import { existsSync as existsSync5, readdirSync as readdirSync3,
|
|
8299
|
+
// src/doc-cache.ts
|
|
8300
|
+
import { existsSync as existsSync5, readdirSync as readdirSync3, statSync as statSync4 } from "node:fs";
|
|
8301
|
+
import { homedir as homedir3 } from "node:os";
|
|
8277
8302
|
import { basename as basename3, dirname as dirname4, isAbsolute as isAbsolute2, join as join5 } from "node:path";
|
|
8303
|
+
import process5 from "node:process";
|
|
8304
|
+
function editorCacheDirectory(godotPath, platform = process5.platform, env = process5.env, home = homedir3()) {
|
|
8305
|
+
const beside = dirname4(godotPath);
|
|
8306
|
+
const bundle = platform === "darwin" && basename3(beside) === "MacOS" && basename3(dirname4(beside)) === "Contents" ? dirname4(dirname4(dirname4(beside))) : null;
|
|
8307
|
+
for (const folder of bundle === null ? [beside] : [beside, bundle]) {
|
|
8308
|
+
if (existsSync5(join5(folder, "._sc_")) || existsSync5(join5(folder, "_sc_"))) {
|
|
8309
|
+
return join5(folder, "editor_data", "cache");
|
|
8310
|
+
}
|
|
8311
|
+
}
|
|
8312
|
+
if (platform === "win32") {
|
|
8313
|
+
const set = (name) => env[name] === "" ? undefined : env[name];
|
|
8314
|
+
return join5(set("LOCALAPPDATA") ?? set("TEMP") ?? join5(home, "AppData", "Local"), "Godot");
|
|
8315
|
+
}
|
|
8316
|
+
if (platform === "darwin") {
|
|
8317
|
+
return join5(home, "Library", "Caches", "Godot");
|
|
8318
|
+
}
|
|
8319
|
+
const xdg = env["XDG_CACHE_HOME"];
|
|
8320
|
+
return join5(xdg !== undefined && isAbsolute2(xdg) ? xdg : join5(home, ".cache"), "godot");
|
|
8321
|
+
}
|
|
8322
|
+
function docCachesWritten(directory) {
|
|
8323
|
+
const written = new Map;
|
|
8324
|
+
let names;
|
|
8325
|
+
try {
|
|
8326
|
+
names = readdirSync3(directory);
|
|
8327
|
+
} catch {
|
|
8328
|
+
return written;
|
|
8329
|
+
}
|
|
8330
|
+
for (const name of names.filter((one) => CACHE_FILE.test(one))) {
|
|
8331
|
+
try {
|
|
8332
|
+
written.set(join5(directory, name), statSync4(join5(directory, name)).mtimeMs);
|
|
8333
|
+
} catch {}
|
|
8334
|
+
}
|
|
8335
|
+
return written;
|
|
8336
|
+
}
|
|
8337
|
+
function docCacheCrash(ran, before, after, what) {
|
|
8338
|
+
const report = ran.log.crashReport();
|
|
8339
|
+
if (ran.exitCode === 0 && ran.exitSignal === null && report === undefined || ran.failure !== null) {
|
|
8340
|
+
return null;
|
|
8341
|
+
}
|
|
8342
|
+
const rewritten = [...after].find(([path, at]) => before.get(path) !== at)?.[0];
|
|
8343
|
+
if (rewritten === undefined) {
|
|
8344
|
+
return null;
|
|
8345
|
+
}
|
|
8346
|
+
const said = report === undefined ? "" : `, and its crash handler printed "${report.text}"`;
|
|
8347
|
+
return `The first ${what} ended badly (${howItEnded(ran)}${said}) after rebuilding the editor's class reference ` + `cache, ${rewritten}. Godot crashes as an import or an export exits when it finds that cache ` + "stale, which it is whenever another build of the same minor version wrote it last, such as 4.7.1 " + `beside 4.7.2, and the crashing run writes a fresh one. So the ${what} was run again, and this answer ` + "is from that second run.";
|
|
8348
|
+
}
|
|
8349
|
+
var CACHE_FILE;
|
|
8350
|
+
var init_doc_cache = __esm(() => {
|
|
8351
|
+
init_engine_run();
|
|
8352
|
+
CACHE_FILE = /^editor_doc_cache-\d+\.\d+\.res$/;
|
|
8353
|
+
});
|
|
8354
|
+
|
|
8355
|
+
// src/library-copies.ts
|
|
8356
|
+
import { existsSync as existsSync6, readdirSync as readdirSync4, readFileSync as readFileSync3, renameSync, unlinkSync } from "node:fs";
|
|
8357
|
+
import { basename as basename4, dirname as dirname5, isAbsolute as isAbsolute3, join as join6 } from "node:path";
|
|
8278
8358
|
function freeLibraryCopies(projectPath, platform = process.platform) {
|
|
8279
8359
|
const setAside = [];
|
|
8280
8360
|
const swept = [];
|
|
@@ -8282,26 +8362,26 @@ function freeLibraryCopies(projectPath, platform = process.platform) {
|
|
|
8282
8362
|
return { setAside, swept };
|
|
8283
8363
|
}
|
|
8284
8364
|
for (const library of windowsLibraries(projectPath)) {
|
|
8285
|
-
const folder =
|
|
8286
|
-
const copy = `~${
|
|
8287
|
-
if (
|
|
8288
|
-
const aside =
|
|
8365
|
+
const folder = dirname5(library);
|
|
8366
|
+
const copy = `~${basename4(library)}`;
|
|
8367
|
+
if (existsSync6(join6(folder, copy))) {
|
|
8368
|
+
const aside = join6(folder, `${copy}.aside-${process.pid}-${Date.now()}`);
|
|
8289
8369
|
try {
|
|
8290
|
-
renameSync(
|
|
8370
|
+
renameSync(join6(folder, copy), aside);
|
|
8291
8371
|
setAside.push(aside);
|
|
8292
8372
|
} catch {}
|
|
8293
8373
|
}
|
|
8294
8374
|
let names;
|
|
8295
8375
|
try {
|
|
8296
|
-
names =
|
|
8376
|
+
names = readdirSync4(folder);
|
|
8297
8377
|
} catch {
|
|
8298
8378
|
continue;
|
|
8299
8379
|
}
|
|
8300
8380
|
for (const name of names) {
|
|
8301
8381
|
if (name.length > copy.length && name.toLowerCase().startsWith(copy.toLowerCase())) {
|
|
8302
8382
|
try {
|
|
8303
|
-
unlinkSync(
|
|
8304
|
-
swept.push(
|
|
8383
|
+
unlinkSync(join6(folder, name));
|
|
8384
|
+
swept.push(join6(folder, name));
|
|
8305
8385
|
} catch {}
|
|
8306
8386
|
}
|
|
8307
8387
|
}
|
|
@@ -8309,7 +8389,7 @@ function freeLibraryCopies(projectPath, platform = process.platform) {
|
|
|
8309
8389
|
return { setAside, swept };
|
|
8310
8390
|
}
|
|
8311
8391
|
function windowsLibraries(projectPath) {
|
|
8312
|
-
const listed = readOrNull(
|
|
8392
|
+
const listed = readOrNull(join6(projectPath, ".godot", "extension_list.cfg"));
|
|
8313
8393
|
if (listed === null) {
|
|
8314
8394
|
return [];
|
|
8315
8395
|
}
|
|
@@ -8328,7 +8408,7 @@ function windowsLibraries(projectPath) {
|
|
|
8328
8408
|
continue;
|
|
8329
8409
|
}
|
|
8330
8410
|
const named = inLibraries ? /^\s*windows[\w.]*\s*=\s*"([^"]+)"/.exec(row)?.[1] : undefined;
|
|
8331
|
-
const library = named === undefined ? null : resolveResource(projectPath,
|
|
8411
|
+
const library = named === undefined ? null : resolveResource(projectPath, dirname5(manifest), named);
|
|
8332
8412
|
if (library !== null) {
|
|
8333
8413
|
libraries.add(library);
|
|
8334
8414
|
}
|
|
@@ -8341,9 +8421,9 @@ function resolveResource(projectPath, relativeTo, path) {
|
|
|
8341
8421
|
return null;
|
|
8342
8422
|
}
|
|
8343
8423
|
if (path.startsWith("res://")) {
|
|
8344
|
-
return
|
|
8424
|
+
return join6(projectPath, path.slice("res://".length));
|
|
8345
8425
|
}
|
|
8346
|
-
return
|
|
8426
|
+
return isAbsolute3(path) ? path : join6(relativeTo, path);
|
|
8347
8427
|
}
|
|
8348
8428
|
function readOrNull(path) {
|
|
8349
8429
|
try {
|
|
@@ -8356,33 +8436,33 @@ var init_library_copies = () => {};
|
|
|
8356
8436
|
|
|
8357
8437
|
// src/reimport.ts
|
|
8358
8438
|
import { createHash } from "node:crypto";
|
|
8359
|
-
import { existsSync as
|
|
8360
|
-
import { basename as
|
|
8439
|
+
import { existsSync as existsSync7, readFileSync as readFileSync4, rmSync as rmSync3, statSync as statSync5, utimesSync, writeFileSync as writeFileSync3 } from "node:fs";
|
|
8440
|
+
import { basename as basename5, join as join7 } from "node:path";
|
|
8361
8441
|
function forceNextImport(projectPath, resourcePath) {
|
|
8362
8442
|
const relative = resourcePath.replace(/^res:\/\//, "");
|
|
8363
|
-
const sidecar =
|
|
8364
|
-
if (!
|
|
8443
|
+
const sidecar = join7(projectPath, `${relative}.import`);
|
|
8444
|
+
if (!existsSync7(sidecar)) {
|
|
8365
8445
|
return;
|
|
8366
8446
|
}
|
|
8367
|
-
const { atime, mtimeMs } =
|
|
8447
|
+
const { atime, mtimeMs } = statSync5(sidecar);
|
|
8368
8448
|
writeFileSync3(sidecar, readFileSync4(sidecar, "utf8").replace(/^valid=false\r?\n/m, ""));
|
|
8369
8449
|
utimesSync(sidecar, atime, new Date(mtimeMs + 2000));
|
|
8370
8450
|
rmSync3(importRecord(projectPath, resourcePath), { force: true });
|
|
8371
8451
|
}
|
|
8372
8452
|
function importRecord(projectPath, resourcePath) {
|
|
8373
8453
|
const digest = createHash("md5").update(resourcePath).digest("hex");
|
|
8374
|
-
return
|
|
8454
|
+
return join7(projectPath, ".godot", "imported", `${basename5(resourcePath)}-${digest}.md5`);
|
|
8375
8455
|
}
|
|
8376
8456
|
var init_reimport = () => {};
|
|
8377
8457
|
|
|
8378
8458
|
// src/alive.ts
|
|
8379
|
-
import
|
|
8459
|
+
import process6 from "node:process";
|
|
8380
8460
|
function alive(pid) {
|
|
8381
8461
|
if (pid === undefined || pid === null) {
|
|
8382
8462
|
return false;
|
|
8383
8463
|
}
|
|
8384
8464
|
try {
|
|
8385
|
-
|
|
8465
|
+
process6.kill(pid, 0);
|
|
8386
8466
|
return true;
|
|
8387
8467
|
} catch (error) {
|
|
8388
8468
|
return error.code === "EPERM";
|
|
@@ -8391,17 +8471,17 @@ function alive(pid) {
|
|
|
8391
8471
|
var init_alive = () => {};
|
|
8392
8472
|
|
|
8393
8473
|
// src/scratch.ts
|
|
8394
|
-
import { existsSync as
|
|
8474
|
+
import { existsSync as existsSync8, mkdtempSync, readdirSync as readdirSync5, rmSync as rmSync4 } from "node:fs";
|
|
8395
8475
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
8396
|
-
import { join as
|
|
8397
|
-
import
|
|
8476
|
+
import { join as join8 } from "node:path";
|
|
8477
|
+
import process7 from "node:process";
|
|
8398
8478
|
function scratchDirectory(kind) {
|
|
8399
|
-
return mkdtempSync(
|
|
8479
|
+
return mkdtempSync(join8(tmpdir2(), `gdharness-${kind}-${process7.pid}-`));
|
|
8400
8480
|
}
|
|
8401
8481
|
function sweepAbandonedScratch(directory = tmpdir2()) {
|
|
8402
8482
|
let entries;
|
|
8403
8483
|
try {
|
|
8404
|
-
entries =
|
|
8484
|
+
entries = readdirSync5(directory);
|
|
8405
8485
|
} catch {
|
|
8406
8486
|
return;
|
|
8407
8487
|
}
|
|
@@ -8412,7 +8492,7 @@ function sweepAbandonedScratch(directory = tmpdir2()) {
|
|
|
8412
8492
|
continue;
|
|
8413
8493
|
}
|
|
8414
8494
|
try {
|
|
8415
|
-
remove(
|
|
8495
|
+
remove(join8(directory, entry));
|
|
8416
8496
|
} catch {}
|
|
8417
8497
|
}
|
|
8418
8498
|
}
|
|
@@ -8450,7 +8530,7 @@ function tryAgain(path, attempt) {
|
|
|
8450
8530
|
lastTryOnExit();
|
|
8451
8531
|
}
|
|
8452
8532
|
const after = AGAIN_AFTER_MS[attempt];
|
|
8453
|
-
if (after === undefined || !
|
|
8533
|
+
if (after === undefined || !existsSync8(path)) {
|
|
8454
8534
|
waiting.delete(path);
|
|
8455
8535
|
if (after === undefined) {
|
|
8456
8536
|
atExit.add(path);
|
|
@@ -8471,7 +8551,7 @@ function lastTryOnExit() {
|
|
|
8471
8551
|
return;
|
|
8472
8552
|
}
|
|
8473
8553
|
lastTryWatched = true;
|
|
8474
|
-
|
|
8554
|
+
process7.once("exit", () => {
|
|
8475
8555
|
for (const path of [...waiting, ...atExit]) {
|
|
8476
8556
|
try {
|
|
8477
8557
|
remove(path);
|
|
@@ -8498,8 +8578,8 @@ var init_scratch = __esm(() => {
|
|
|
8498
8578
|
});
|
|
8499
8579
|
|
|
8500
8580
|
// src/headless.ts
|
|
8501
|
-
import { existsSync as
|
|
8502
|
-
import { dirname as
|
|
8581
|
+
import { existsSync as existsSync9, readFileSync as readFileSync5, realpathSync as realpathSync2, writeFileSync as writeFileSync4 } from "node:fs";
|
|
8582
|
+
import { dirname as dirname6, join as join9 } from "node:path";
|
|
8503
8583
|
function snakeCased(params) {
|
|
8504
8584
|
const result = emptyRecord();
|
|
8505
8585
|
for (const [key, value] of Object.entries(params)) {
|
|
@@ -8560,7 +8640,7 @@ function refusalsIn(log) {
|
|
|
8560
8640
|
}
|
|
8561
8641
|
function scriptFault(problems, script) {
|
|
8562
8642
|
const spelled = (path) => `(${path.replaceAll("\\", "/").toLowerCase()}/`;
|
|
8563
|
-
const directories = [...new Set([spelled(
|
|
8643
|
+
const directories = [...new Set([spelled(dirname6(script)), spelled(resolvedOr(dirname6(script)))])];
|
|
8564
8644
|
return problems.find((entry) => {
|
|
8565
8645
|
const at = entry.detail.find((line) => line.startsWith("at: "))?.replaceAll("\\", "/").toLowerCase();
|
|
8566
8646
|
return entry.severity === "error" && at !== undefined && directories.some((directory) => at.includes(directory));
|
|
@@ -8598,26 +8678,33 @@ async function runImport(godotPath, projectPath, options = {}) {
|
|
|
8598
8678
|
};
|
|
8599
8679
|
const first = await pass();
|
|
8600
8680
|
const collided = librariesNotCopied(first.messages);
|
|
8601
|
-
|
|
8681
|
+
const crash = first.docCacheNote;
|
|
8682
|
+
if (collided.length === 0 && crash === undefined) {
|
|
8602
8683
|
return first;
|
|
8603
8684
|
}
|
|
8604
|
-
const
|
|
8685
|
+
const { docCacheNote: _, ...again } = await pass();
|
|
8686
|
+
const second = again.ok || crash === undefined ? again : { ...again, message: `${again.message.replace(/[.\s]+$/, "")}. ${crash}` };
|
|
8605
8687
|
const still = librariesNotCopied(second.messages);
|
|
8606
8688
|
return {
|
|
8607
8689
|
...second,
|
|
8608
|
-
|
|
8609
|
-
|
|
8610
|
-
|
|
8690
|
+
...collided.length === 0 ? {} : {
|
|
8691
|
+
librariesRetried: collided,
|
|
8692
|
+
...still.length === 0 ? {} : { librariesNotLoaded: still },
|
|
8693
|
+
extensionNote: extensionRetryNote(collided, still)
|
|
8694
|
+
},
|
|
8695
|
+
...crash === undefined ? {} : { docCacheNote: crash }
|
|
8611
8696
|
};
|
|
8612
8697
|
}
|
|
8613
8698
|
async function importOnce(godotPath, projectPath) {
|
|
8614
8699
|
const logDir = scratchDirectory("import");
|
|
8700
|
+
const caches = editorCacheDirectory(godotPath);
|
|
8701
|
+
const cachesBefore = docCachesWritten(caches);
|
|
8615
8702
|
let ran;
|
|
8616
8703
|
try {
|
|
8617
8704
|
ran = await runEngine(godotPath, [
|
|
8618
8705
|
"--headless",
|
|
8619
8706
|
"--log-file",
|
|
8620
|
-
|
|
8707
|
+
join9(logDir, "engine.log"),
|
|
8621
8708
|
"--path",
|
|
8622
8709
|
projectPath,
|
|
8623
8710
|
"--import"
|
|
@@ -8626,24 +8713,26 @@ async function importOnce(godotPath, projectPath) {
|
|
|
8626
8713
|
discard(logDir);
|
|
8627
8714
|
}
|
|
8628
8715
|
const problems = problemsIn(ran.log);
|
|
8716
|
+
const crash = docCacheCrash(ran, cachesBefore, docCachesWritten(caches), "import");
|
|
8629
8717
|
if (ran.exitCode === 0 && ran.failure === null) {
|
|
8630
|
-
return { ok: true, ...reported2(problems) };
|
|
8718
|
+
return { ok: true, ...reported2(problems), ...crash === null ? {} : { docCacheNote: crash } };
|
|
8631
8719
|
}
|
|
8632
8720
|
return {
|
|
8633
8721
|
ok: false,
|
|
8634
8722
|
message: `the import pass failed (${howItEnded(ran)}): ${reason(ran.log, problems)}`,
|
|
8635
|
-
...reported2(problems)
|
|
8723
|
+
...reported2(problems),
|
|
8724
|
+
...crash === null ? {} : { docCacheNote: crash }
|
|
8636
8725
|
};
|
|
8637
8726
|
}
|
|
8638
8727
|
async function runOperation(engine, operation, params, projectPath) {
|
|
8639
8728
|
const paramsDir = scratchDirectory("params");
|
|
8640
|
-
const paramsFile =
|
|
8641
|
-
const answerFile =
|
|
8729
|
+
const paramsFile = join9(paramsDir, `${operation}.json`);
|
|
8730
|
+
const answerFile = join9(paramsDir, "answer.json");
|
|
8642
8731
|
writeFileSync4(paramsFile, JSON.stringify(snakeCased(params)), "utf8");
|
|
8643
8732
|
const args = [
|
|
8644
8733
|
"--headless",
|
|
8645
8734
|
"--log-file",
|
|
8646
|
-
|
|
8735
|
+
join9(paramsDir, "engine.log"),
|
|
8647
8736
|
"--path",
|
|
8648
8737
|
projectPath,
|
|
8649
8738
|
"--script",
|
|
@@ -8731,7 +8820,7 @@ async function runOperation(engine, operation, params, projectPath) {
|
|
|
8731
8820
|
};
|
|
8732
8821
|
}
|
|
8733
8822
|
function answerIn(file) {
|
|
8734
|
-
if (!
|
|
8823
|
+
if (!existsSync9(file)) {
|
|
8735
8824
|
return { kind: "none" };
|
|
8736
8825
|
}
|
|
8737
8826
|
const text = readFileSync5(file, "utf8");
|
|
@@ -8747,6 +8836,7 @@ function answerIn(file) {
|
|
|
8747
8836
|
}
|
|
8748
8837
|
var AFTER_ANSWER_NOTE = "Printed after this operation had written its answer, while the engine ran the project's autoloads and shut down: Godot starts a project's autoloads in every script run and cannot be told not to. An error raised in a project script here is that autoload's own, and none of these changes the answer.", BEFORE_OPERATION_NOTE = "Printed while the engine started the project's autoloads, before this operation ran: an operation that compiles the project's scripts waits for them, because the engine knows an autoload's name only once it is in the tree. An error raised in a project script here is that autoload's own.", OWN_ERROR = "[ERROR] ", ANSWERED = "[gdharness] answered", WAITING = "[gdharness] waiting for the autoloads", BEGUN = "[gdharness] begun", MESSAGES_KEPT = 200, ERRORS_NAMED = 3;
|
|
8749
8838
|
var init_headless = __esm(() => {
|
|
8839
|
+
init_doc_cache();
|
|
8750
8840
|
init_engine_run();
|
|
8751
8841
|
init_library_copies();
|
|
8752
8842
|
init_reimport();
|
|
@@ -8754,9 +8844,9 @@ var init_headless = __esm(() => {
|
|
|
8754
8844
|
});
|
|
8755
8845
|
|
|
8756
8846
|
// src/update-check.ts
|
|
8757
|
-
import { existsSync as
|
|
8758
|
-
import { homedir as
|
|
8759
|
-
import { join as
|
|
8847
|
+
import { existsSync as existsSync10, mkdirSync as mkdirSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "node:fs";
|
|
8848
|
+
import { homedir as homedir4, tmpdir as tmpdir3 } from "node:os";
|
|
8849
|
+
import { join as join10 } from "node:path";
|
|
8760
8850
|
function packageUrl(root) {
|
|
8761
8851
|
return `${root.replace(/\/+$/, "")}/gdharness/latest`;
|
|
8762
8852
|
}
|
|
@@ -8776,27 +8866,27 @@ function cacheDirectory(environment) {
|
|
|
8776
8866
|
const value = environment[name];
|
|
8777
8867
|
return value !== undefined && value !== "" ? value : null;
|
|
8778
8868
|
};
|
|
8779
|
-
const home = set("HOME") ??
|
|
8869
|
+
const home = set("HOME") ?? homedir4();
|
|
8780
8870
|
if (process.platform === "win32") {
|
|
8781
|
-
return
|
|
8871
|
+
return join10(set("LOCALAPPDATA") ?? home, "gdharness");
|
|
8782
8872
|
}
|
|
8783
8873
|
if (process.platform === "darwin") {
|
|
8784
|
-
return
|
|
8874
|
+
return join10(home, "Library", "Caches", "gdharness");
|
|
8785
8875
|
}
|
|
8786
|
-
return
|
|
8876
|
+
return join10(set("XDG_CACHE_HOME") ?? join10(home, ".cache"), "gdharness");
|
|
8787
8877
|
}
|
|
8788
8878
|
function cacheFile(environment = process.env) {
|
|
8789
8879
|
try {
|
|
8790
8880
|
const directory = cacheDirectory(environment);
|
|
8791
8881
|
mkdirSync3(directory, { recursive: true, mode: 448 });
|
|
8792
|
-
return
|
|
8882
|
+
return join10(directory, "update-check.json");
|
|
8793
8883
|
} catch {
|
|
8794
|
-
return
|
|
8884
|
+
return join10(tmpdir3(), "gdharness-update-check.json");
|
|
8795
8885
|
}
|
|
8796
8886
|
}
|
|
8797
8887
|
function readCache(path) {
|
|
8798
8888
|
try {
|
|
8799
|
-
if (!
|
|
8889
|
+
if (!existsSync10(path)) {
|
|
8800
8890
|
return null;
|
|
8801
8891
|
}
|
|
8802
8892
|
const parsed = JSON.parse(readFileSync6(path, "utf8"));
|
|
@@ -9031,7 +9121,7 @@ var init_server_version = __esm(() => {
|
|
|
9031
9121
|
|
|
9032
9122
|
// src/issues.ts
|
|
9033
9123
|
import { createHash as createHash2 } from "node:crypto";
|
|
9034
|
-
import
|
|
9124
|
+
import process8 from "node:process";
|
|
9035
9125
|
function feedbackNotice(every) {
|
|
9036
9126
|
return JSON.stringify({
|
|
9037
9127
|
gdharness_feedback: {
|
|
@@ -9042,9 +9132,9 @@ function feedbackNotice(every) {
|
|
|
9042
9132
|
}, null, 2);
|
|
9043
9133
|
}
|
|
9044
9134
|
function runtime() {
|
|
9045
|
-
const versions =
|
|
9135
|
+
const versions = process8.versions;
|
|
9046
9136
|
const bun = versions["bun"];
|
|
9047
|
-
return bun === undefined ? `node ${
|
|
9137
|
+
return bun === undefined ? `node ${process8.versions.node}` : `bun ${bun}`;
|
|
9048
9138
|
}
|
|
9049
9139
|
function defectSignature(where, message) {
|
|
9050
9140
|
const normalised = `${where}
|
|
@@ -9056,7 +9146,7 @@ function facts(where, message, godotVersion) {
|
|
|
9056
9146
|
["Where", where],
|
|
9057
9147
|
["Error", message],
|
|
9058
9148
|
["Version", `gdharness ${SERVER_VERSION}`],
|
|
9059
|
-
["Runtime", `${runtime()}, ${
|
|
9149
|
+
["Runtime", `${runtime()}, ${process8.platform} ${process8.arch}`],
|
|
9060
9150
|
["Godot", godotVersion ?? "not known at the point this failed"],
|
|
9061
9151
|
["Signature", defectSignature(where, message)]
|
|
9062
9152
|
];
|
|
@@ -9066,7 +9156,7 @@ function filledTemplate(where, message, godotVersion) {
|
|
|
9066
9156
|
`**gdharness version**: ${SERVER_VERSION}`,
|
|
9067
9157
|
`**Godot version**: ${godotVersion ?? ""}`,
|
|
9068
9158
|
`**Bun version**: ${runtime()}`,
|
|
9069
|
-
`**OS**: ${
|
|
9159
|
+
`**OS**: ${process8.platform} ${process8.arch}`,
|
|
9070
9160
|
"**MCP client**:",
|
|
9071
9161
|
"",
|
|
9072
9162
|
"## What you did",
|
|
@@ -9806,8 +9896,8 @@ var init_gdscript_source = __esm(() => {
|
|
|
9806
9896
|
});
|
|
9807
9897
|
|
|
9808
9898
|
// src/class-cache.ts
|
|
9809
|
-
import { existsSync as
|
|
9810
|
-
import { join as
|
|
9899
|
+
import { existsSync as existsSync11, readdirSync as readdirSync6, readFileSync as readFileSync8, statSync as statSync6 } from "node:fs";
|
|
9900
|
+
import { join as join11, posix } from "node:path";
|
|
9811
9901
|
function isToolScript(source) {
|
|
9812
9902
|
return TOOL.test(codeOf(source));
|
|
9813
9903
|
}
|
|
@@ -9823,14 +9913,14 @@ function declaredClasses(projectPath) {
|
|
|
9823
9913
|
}
|
|
9824
9914
|
function eachScript(projectPath, each) {
|
|
9825
9915
|
const visit = (directory, prefix) => {
|
|
9826
|
-
if (
|
|
9916
|
+
if (existsSync11(join11(directory, ".gdignore"))) {
|
|
9827
9917
|
return;
|
|
9828
9918
|
}
|
|
9829
|
-
for (const entry of
|
|
9919
|
+
for (const entry of readdirSync6(directory, { withFileTypes: true })) {
|
|
9830
9920
|
if (entry.name.startsWith(".")) {
|
|
9831
9921
|
continue;
|
|
9832
9922
|
}
|
|
9833
|
-
const path =
|
|
9923
|
+
const path = join11(directory, entry.name);
|
|
9834
9924
|
if (entry.isDirectory()) {
|
|
9835
9925
|
visit(path, `${prefix}${entry.name}/`);
|
|
9836
9926
|
} else if (entry.isFile() && entry.name.endsWith(".gd")) {
|
|
@@ -9856,8 +9946,8 @@ function scriptsNaming(projectPath, classes) {
|
|
|
9856
9946
|
return naming.sort((a, b) => a.script.localeCompare(b.script));
|
|
9857
9947
|
}
|
|
9858
9948
|
function cachedClasses(projectPath) {
|
|
9859
|
-
const cache =
|
|
9860
|
-
if (!
|
|
9949
|
+
const cache = join11(projectPath, ".godot", "global_script_class_cache.cfg");
|
|
9950
|
+
if (!existsSync11(cache)) {
|
|
9861
9951
|
return null;
|
|
9862
9952
|
}
|
|
9863
9953
|
const listed = new Map;
|
|
@@ -10127,9 +10217,9 @@ function uncachedClassNote(types) {
|
|
|
10127
10217
|
return `${types.join(", ")} ${one ? "is declared" : "are declared"} in this project and missing from ` + `.godot/global_script_class_cache.cfg, so a game launched now would not resolve ${one ? "it" : "them"} ` + "either. Try editor_rescan first: it asks the editor already running and starts nothing, and it " + "cleared this in 314ms in a second project with a 31-worker fan-out still importing. It is not " + "always enough, because a file another engine has already imported reads as settled and the " + "walk does not look inside it. project_import refresh_classes rewrites the cache from the " + "declarations on disk and does reach that case, at the cost of a short headless engine, which " + "is worth knowing when something else is already running against this project.";
|
|
10128
10218
|
}
|
|
10129
10219
|
function cacheWrittenAt(projectPath) {
|
|
10130
|
-
const cache =
|
|
10220
|
+
const cache = join11(projectPath, ".godot", "global_script_class_cache.cfg");
|
|
10131
10221
|
try {
|
|
10132
|
-
return
|
|
10222
|
+
return statSync6(cache).mtimeMs;
|
|
10133
10223
|
} catch {
|
|
10134
10224
|
return null;
|
|
10135
10225
|
}
|
|
@@ -10141,14 +10231,14 @@ function declaredSince(projectPath, paths, names, since) {
|
|
|
10141
10231
|
return false;
|
|
10142
10232
|
}
|
|
10143
10233
|
try {
|
|
10144
|
-
return
|
|
10234
|
+
return statSync6(join11(projectPath, path.slice("res://".length))).mtimeMs > since;
|
|
10145
10235
|
} catch {
|
|
10146
10236
|
return false;
|
|
10147
10237
|
}
|
|
10148
10238
|
});
|
|
10149
10239
|
}
|
|
10150
10240
|
function cachedAtMissingPaths(cached, projectPath) {
|
|
10151
|
-
return [...cached].filter(([, path]) => path.startsWith("res://") && !
|
|
10241
|
+
return [...cached].filter(([, path]) => path.startsWith("res://") && !existsSync11(join11(projectPath, path.slice("res://".length)))).map(([name]) => name).sort();
|
|
10152
10242
|
}
|
|
10153
10243
|
function staleAgainst(cached, projectPath) {
|
|
10154
10244
|
return [...declaredClasses(projectPath)].filter(([name, path]) => cached.get(name) !== path).map(([name]) => name);
|
|
@@ -10173,8 +10263,8 @@ var init_class_cache = __esm(() => {
|
|
|
10173
10263
|
});
|
|
10174
10264
|
|
|
10175
10265
|
// src/project-scan.ts
|
|
10176
|
-
import { existsSync as
|
|
10177
|
-
import { extname, join as
|
|
10266
|
+
import { existsSync as existsSync12, readdirSync as readdirSync7, readFileSync as readFileSync9 } from "node:fs";
|
|
10267
|
+
import { extname, join as join12 } from "node:path";
|
|
10178
10268
|
function skipped(name) {
|
|
10179
10269
|
return name.startsWith(".") || SKIPPED.has(name);
|
|
10180
10270
|
}
|
|
@@ -10182,10 +10272,10 @@ function steppedOver(directory) {
|
|
|
10182
10272
|
return gdignored(directory) || installedByUs(directory);
|
|
10183
10273
|
}
|
|
10184
10274
|
function gdignored(directory) {
|
|
10185
|
-
return
|
|
10275
|
+
return existsSync12(join12(directory, ".gdignore"));
|
|
10186
10276
|
}
|
|
10187
10277
|
function installedByUs(directory) {
|
|
10188
|
-
return
|
|
10278
|
+
return existsSync12(join12(directory, VERSION_MARKER));
|
|
10189
10279
|
}
|
|
10190
10280
|
function projectStructure(projectPath) {
|
|
10191
10281
|
const structure = { scenes: 0, scripts: 0, assets: 0, other: 0 };
|
|
@@ -10193,12 +10283,12 @@ function projectStructure(projectPath) {
|
|
|
10193
10283
|
if (steppedOver(directory)) {
|
|
10194
10284
|
return;
|
|
10195
10285
|
}
|
|
10196
|
-
for (const entry of
|
|
10286
|
+
for (const entry of readdirSync7(directory, { withFileTypes: true })) {
|
|
10197
10287
|
if (skipped(entry.name)) {
|
|
10198
10288
|
continue;
|
|
10199
10289
|
}
|
|
10200
10290
|
if (entry.isDirectory()) {
|
|
10201
|
-
visit(
|
|
10291
|
+
visit(join12(directory, entry.name));
|
|
10202
10292
|
} else if (entry.isFile()) {
|
|
10203
10293
|
const extension = entry.name.split(".").pop()?.toLowerCase() ?? "";
|
|
10204
10294
|
if (SCENE_EXTENSIONS.has(extension)) {
|
|
@@ -10222,13 +10312,13 @@ function projectFiles(projectPath, extensions) {
|
|
|
10222
10312
|
if (steppedOver(directory)) {
|
|
10223
10313
|
return;
|
|
10224
10314
|
}
|
|
10225
|
-
for (const entry of
|
|
10315
|
+
for (const entry of readdirSync7(directory, { withFileTypes: true })) {
|
|
10226
10316
|
if (skipped(entry.name)) {
|
|
10227
10317
|
continue;
|
|
10228
10318
|
}
|
|
10229
10319
|
const spelled = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
|
|
10230
10320
|
if (entry.isDirectory()) {
|
|
10231
|
-
visit(
|
|
10321
|
+
visit(join12(directory, entry.name), spelled);
|
|
10232
10322
|
} else if (entry.isFile() && extensions.includes(extname(entry.name).toLowerCase())) {
|
|
10233
10323
|
found.push(spelled);
|
|
10234
10324
|
}
|
|
@@ -10243,20 +10333,20 @@ function scriptsWithoutUid(projectPath) {
|
|
|
10243
10333
|
if (steppedOver(directory)) {
|
|
10244
10334
|
return;
|
|
10245
10335
|
}
|
|
10246
|
-
for (const entry of
|
|
10336
|
+
for (const entry of readdirSync7(directory, { withFileTypes: true })) {
|
|
10247
10337
|
if (skipped(entry.name)) {
|
|
10248
10338
|
continue;
|
|
10249
10339
|
}
|
|
10250
10340
|
const spelled = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
|
|
10251
10341
|
if (entry.isDirectory()) {
|
|
10252
|
-
visit(
|
|
10342
|
+
visit(join12(directory, entry.name), spelled);
|
|
10253
10343
|
continue;
|
|
10254
10344
|
}
|
|
10255
10345
|
if (!entry.isFile()) {
|
|
10256
10346
|
continue;
|
|
10257
10347
|
}
|
|
10258
10348
|
const extension = entry.name.split(".").pop()?.toLowerCase() ?? "";
|
|
10259
|
-
if (CARRIES_A_UID.has(extension) && !
|
|
10349
|
+
if (CARRIES_A_UID.has(extension) && !existsSync12(join12(directory, `${entry.name}.uid`))) {
|
|
10260
10350
|
found.push(spelled);
|
|
10261
10351
|
}
|
|
10262
10352
|
}
|
|
@@ -10273,11 +10363,11 @@ function projectTexts(projectPath) {
|
|
|
10273
10363
|
if (steppedOver(directory)) {
|
|
10274
10364
|
return;
|
|
10275
10365
|
}
|
|
10276
|
-
for (const entry of
|
|
10366
|
+
for (const entry of readdirSync7(directory, { withFileTypes: true })) {
|
|
10277
10367
|
if (skipped(entry.name)) {
|
|
10278
10368
|
continue;
|
|
10279
10369
|
}
|
|
10280
|
-
const absolute =
|
|
10370
|
+
const absolute = join12(directory, entry.name);
|
|
10281
10371
|
const spelled = `${prefix}${entry.name}`;
|
|
10282
10372
|
if (entry.isDirectory()) {
|
|
10283
10373
|
visit(absolute, `${spelled}/`);
|
|
@@ -10303,11 +10393,11 @@ function ignoredTexts(projectPath) {
|
|
|
10303
10393
|
return;
|
|
10304
10394
|
}
|
|
10305
10395
|
const under = ignored || gdignored(directory);
|
|
10306
|
-
for (const entry of
|
|
10396
|
+
for (const entry of readdirSync7(directory, { withFileTypes: true })) {
|
|
10307
10397
|
if (skipped(entry.name)) {
|
|
10308
10398
|
continue;
|
|
10309
10399
|
}
|
|
10310
|
-
const absolute =
|
|
10400
|
+
const absolute = join12(directory, entry.name);
|
|
10311
10401
|
const spelled = `${prefix}${entry.name}`;
|
|
10312
10402
|
if (entry.isDirectory()) {
|
|
10313
10403
|
visit(absolute, `${spelled}/`, under);
|
|
@@ -10340,14 +10430,14 @@ function searchProject(projectPath, options) {
|
|
|
10340
10430
|
if (steppedOver(directory)) {
|
|
10341
10431
|
return;
|
|
10342
10432
|
}
|
|
10343
|
-
for (const entry of
|
|
10433
|
+
for (const entry of readdirSync7(directory, { withFileTypes: true })) {
|
|
10344
10434
|
if (full()) {
|
|
10345
10435
|
return;
|
|
10346
10436
|
}
|
|
10347
10437
|
if (skipped(entry.name)) {
|
|
10348
10438
|
continue;
|
|
10349
10439
|
}
|
|
10350
|
-
const entryPath =
|
|
10440
|
+
const entryPath = join12(directory, entry.name);
|
|
10351
10441
|
if (entry.isDirectory()) {
|
|
10352
10442
|
visit(entryPath);
|
|
10353
10443
|
continue;
|
|
@@ -10792,16 +10882,16 @@ function readParams(params, key) {
|
|
|
10792
10882
|
import { createHash as createHash3 } from "node:crypto";
|
|
10793
10883
|
import {
|
|
10794
10884
|
cpSync,
|
|
10795
|
-
existsSync as
|
|
10885
|
+
existsSync as existsSync13,
|
|
10796
10886
|
mkdirSync as mkdirSync4,
|
|
10797
|
-
readdirSync as
|
|
10887
|
+
readdirSync as readdirSync8,
|
|
10798
10888
|
readFileSync as readFileSync11,
|
|
10799
10889
|
rmdirSync,
|
|
10800
10890
|
rmSync as rmSync5,
|
|
10801
|
-
statSync as
|
|
10891
|
+
statSync as statSync7,
|
|
10802
10892
|
writeFileSync as writeFileSync6
|
|
10803
10893
|
} from "node:fs";
|
|
10804
|
-
import { dirname as
|
|
10894
|
+
import { dirname as dirname7, join as join13 } from "node:path";
|
|
10805
10895
|
import { fileURLToPath } from "node:url";
|
|
10806
10896
|
function editorAddonDigest(from = shippedAddonsDirectory()) {
|
|
10807
10897
|
return addonsDigest(EDITOR_PLUGINS, from);
|
|
@@ -10812,13 +10902,13 @@ function runtimeAddonDigest(from = shippedAddonsDirectory()) {
|
|
|
10812
10902
|
function addonsDigest(names, from) {
|
|
10813
10903
|
const hash = createHash3("sha256");
|
|
10814
10904
|
const walk = (directory, relative) => {
|
|
10815
|
-
for (const name of
|
|
10905
|
+
for (const name of readdirSync8(directory).sort()) {
|
|
10816
10906
|
if (name === VERSION_MARKER || name === DIGEST_MARKER) {
|
|
10817
10907
|
continue;
|
|
10818
10908
|
}
|
|
10819
|
-
const path =
|
|
10909
|
+
const path = join13(directory, name);
|
|
10820
10910
|
const inside = `${relative}/${name}`;
|
|
10821
|
-
if (
|
|
10911
|
+
if (statSync7(path).isDirectory()) {
|
|
10822
10912
|
walk(path, inside);
|
|
10823
10913
|
} else {
|
|
10824
10914
|
hash.update(`${inside}
|
|
@@ -10830,7 +10920,7 @@ function addonsDigest(names, from) {
|
|
|
10830
10920
|
}
|
|
10831
10921
|
};
|
|
10832
10922
|
for (const name of names) {
|
|
10833
|
-
walk(
|
|
10923
|
+
walk(join13(from, name), name);
|
|
10834
10924
|
}
|
|
10835
10925
|
return hash.digest("hex");
|
|
10836
10926
|
}
|
|
@@ -10861,44 +10951,44 @@ function installedRuntimeDigest(projectPath) {
|
|
|
10861
10951
|
return digestMarker(projectPath, RUNTIME_ADDON);
|
|
10862
10952
|
}
|
|
10863
10953
|
function digestMarker(projectPath, addon) {
|
|
10864
|
-
const marker =
|
|
10865
|
-
if (!
|
|
10954
|
+
const marker = join13(projectPath, "addons", addon, DIGEST_MARKER);
|
|
10955
|
+
if (!existsSync13(marker)) {
|
|
10866
10956
|
return null;
|
|
10867
10957
|
}
|
|
10868
10958
|
const said = readFileSync11(marker, "utf8").trim();
|
|
10869
10959
|
return said === "" ? null : said;
|
|
10870
10960
|
}
|
|
10871
10961
|
function shippedAddonsDirectory() {
|
|
10872
|
-
return
|
|
10962
|
+
return join13(dirname7(fileURLToPath(import.meta.url)), "godot", "addons");
|
|
10873
10963
|
}
|
|
10874
10964
|
function shippedOperationsScript() {
|
|
10875
|
-
return
|
|
10965
|
+
return join13(dirname7(fileURLToPath(import.meta.url)), "godot", "operations", "godot_operations.gd");
|
|
10876
10966
|
}
|
|
10877
10967
|
function installAddons(projectPath, from = shippedAddonsDirectory()) {
|
|
10878
10968
|
const installed = [];
|
|
10879
10969
|
for (const name of ADDONS) {
|
|
10880
|
-
const source =
|
|
10881
|
-
if (!
|
|
10970
|
+
const source = join13(from, name);
|
|
10971
|
+
if (!existsSync13(join13(source, name === RUNTIME_ADDON ? "runtime_autoload.gd" : "plugin.cfg"))) {
|
|
10882
10972
|
throw new Error(`The package holds no ${name} addon at ${source}.`);
|
|
10883
10973
|
}
|
|
10884
|
-
const target =
|
|
10885
|
-
const replaced =
|
|
10974
|
+
const target = join13(projectPath, "addons", name);
|
|
10975
|
+
const replaced = existsSync13(target);
|
|
10886
10976
|
rmSync5(target, { recursive: true, force: true });
|
|
10887
|
-
mkdirSync4(
|
|
10977
|
+
mkdirSync4(dirname7(target), { recursive: true });
|
|
10888
10978
|
cpSync(source, target, { recursive: true });
|
|
10889
|
-
writeFileSync6(
|
|
10979
|
+
writeFileSync6(join13(target, VERSION_MARKER), `${SERVER_VERSION}
|
|
10890
10980
|
`);
|
|
10891
10981
|
installed.push({ name, path: target, replaced });
|
|
10892
10982
|
}
|
|
10893
|
-
writeFileSync6(
|
|
10983
|
+
writeFileSync6(join13(projectPath, "addons", ADDONS[0], DIGEST_MARKER), `${editorAddonDigest(from)}
|
|
10894
10984
|
`);
|
|
10895
|
-
writeFileSync6(
|
|
10985
|
+
writeFileSync6(join13(projectPath, "addons", RUNTIME_ADDON, DIGEST_MARKER), `${runtimeAddonDigest(from)}
|
|
10896
10986
|
`);
|
|
10897
10987
|
return installed;
|
|
10898
10988
|
}
|
|
10899
10989
|
function installedAddonVersion(projectPath) {
|
|
10900
|
-
const marker =
|
|
10901
|
-
if (!
|
|
10990
|
+
const marker = join13(projectPath, "addons", ADDONS[0], VERSION_MARKER);
|
|
10991
|
+
if (!existsSync13(marker)) {
|
|
10902
10992
|
return null;
|
|
10903
10993
|
}
|
|
10904
10994
|
const said = readFileSync11(marker, "utf8").trim();
|
|
@@ -10907,14 +10997,14 @@ function installedAddonVersion(projectPath) {
|
|
|
10907
10997
|
function removeAddons(projectPath) {
|
|
10908
10998
|
const removed = [];
|
|
10909
10999
|
for (const name of ADDONS) {
|
|
10910
|
-
const target =
|
|
10911
|
-
if (
|
|
11000
|
+
const target = join13(projectPath, "addons", name);
|
|
11001
|
+
if (existsSync13(target)) {
|
|
10912
11002
|
rmSync5(target, { recursive: true, force: true });
|
|
10913
11003
|
removed.push(target);
|
|
10914
11004
|
}
|
|
10915
11005
|
}
|
|
10916
|
-
const addons =
|
|
10917
|
-
if (removed.length > 0 &&
|
|
11006
|
+
const addons = join13(projectPath, "addons");
|
|
11007
|
+
if (removed.length > 0 && existsSync13(addons) && readdirSync8(addons).length === 0) {
|
|
10918
11008
|
rmdirSync(addons);
|
|
10919
11009
|
removed.push(addons);
|
|
10920
11010
|
}
|
|
@@ -10948,7 +11038,7 @@ function loaderAmong(projectPath, named) {
|
|
|
10948
11038
|
}
|
|
10949
11039
|
let source = null;
|
|
10950
11040
|
try {
|
|
10951
|
-
source = readFileSync11(
|
|
11041
|
+
source = readFileSync11(join13(projectPath, path), "utf8");
|
|
10952
11042
|
} catch {}
|
|
10953
11043
|
if (source?.includes(RUNTIME_AUTOLOAD.path)) {
|
|
10954
11044
|
return { name: entry, path, exists: true };
|
|
@@ -10990,10 +11080,10 @@ function enabledPlugins(settings) {
|
|
|
10990
11080
|
function inspectProject(projectPath) {
|
|
10991
11081
|
const problems = [];
|
|
10992
11082
|
const addons = ADDONS.map((name) => {
|
|
10993
|
-
const directory =
|
|
10994
|
-
const marker =
|
|
10995
|
-
const installed =
|
|
10996
|
-
const version =
|
|
11083
|
+
const directory = join13(projectPath, "addons", name);
|
|
11084
|
+
const marker = join13(directory, VERSION_MARKER);
|
|
11085
|
+
const installed = existsSync13(directory);
|
|
11086
|
+
const version = existsSync13(marker) ? readFileSync11(marker, "utf8").trim() : null;
|
|
10997
11087
|
const current = version === SERVER_VERSION;
|
|
10998
11088
|
if (!installed) {
|
|
10999
11089
|
problems.push(`addons/${name} is not installed; run gdharness setup`);
|
|
@@ -11002,7 +11092,7 @@ function inspectProject(projectPath) {
|
|
|
11002
11092
|
}
|
|
11003
11093
|
return { name, installed, version, current };
|
|
11004
11094
|
});
|
|
11005
|
-
const settings = parseProjectGodot(readFileSync11(
|
|
11095
|
+
const settings = parseProjectGodot(readFileSync11(join13(projectPath, "project.godot"), "utf8"));
|
|
11006
11096
|
const pluginsEnabled = enabledPlugins(settings);
|
|
11007
11097
|
for (const name of EDITOR_PLUGINS) {
|
|
11008
11098
|
if (!pluginsEnabled.includes(name)) {
|
|
@@ -11018,7 +11108,7 @@ function inspectProject(projectPath) {
|
|
|
11018
11108
|
const tracked = trackedByGit(projectPath, [...named.values()]);
|
|
11019
11109
|
const addonMissing = (path) => addons.some((addon) => !addon.installed && path.startsWith(`addons/${addon.name}/`));
|
|
11020
11110
|
for (const [name, path] of named) {
|
|
11021
|
-
if (!
|
|
11111
|
+
if (!existsSync13(join13(projectPath, path))) {
|
|
11022
11112
|
if (!addonMissing(path)) {
|
|
11023
11113
|
problems.push(`the ${name} autoload names res://${path}, which is not in this project, so it boots with a missing script; restore that file, or point the autoload at one that is here`);
|
|
11024
11114
|
}
|
|
@@ -11679,7 +11769,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
11679
11769
|
rootNodeType: {
|
|
11680
11770
|
type: "string",
|
|
11681
11771
|
ops: ["create"],
|
|
11682
|
-
description: "create: the root node class. Default Node."
|
|
11772
|
+
description: "create: the root node class, an engine class or a class_name a project script declares, which the root is made as with that script attached. Default Node."
|
|
11683
11773
|
},
|
|
11684
11774
|
rootNodeName: {
|
|
11685
11775
|
type: "string",
|
|
@@ -11717,7 +11807,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
11717
11807
|
{
|
|
11718
11808
|
name: "scene_node",
|
|
11719
11809
|
title: "Scene nodes",
|
|
11720
|
-
description: `One node in a scene file: add, read, set, duplicate, reparent or delete it, or paint TileMap cells. Any ClassDB node type can be added, so a NavigationRegion2D, an AnimationTree or a Camera3D is an add with that nodeType and its properties, and another scene is instanced by an add with instancePath. A set with properties {"name": "Boss"} renames a node. A property holding a Resource takes the res:// path of one, so a texture, a material or a theme is a set like any other. A write is refused while the editor holds unsaved changes to the scene, and for a node that belongs to a scene this one instances or inherits. ${WITHOUT_AN_EDITOR}`,
|
|
11810
|
+
description: `One node in a scene file: add, read, set, duplicate, reparent or delete it, or paint TileMap cells. Any ClassDB node type or script class can be added, so a NavigationRegion2D, an AnimationTree or a Camera3D is an add with that nodeType and its properties, and another scene is instanced by an add with instancePath. A set with properties {"name": "Boss"} renames a node. A property holding a Resource takes the res:// path of one, so a texture, a material or a theme is a set like any other. A write is refused while the editor holds unsaved changes to the scene, and for a node that belongs to a scene this one instances or inherits. ${WITHOUT_AN_EDITOR}`,
|
|
11721
11811
|
parameters: {
|
|
11722
11812
|
projectPath: PROJECT_PATH,
|
|
11723
11813
|
scenePath: SCENE_PATH,
|
|
@@ -11730,7 +11820,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
11730
11820
|
nodeType: {
|
|
11731
11821
|
type: "string",
|
|
11732
11822
|
ops: ["add"],
|
|
11733
|
-
description: 'add: the node class, such as "CharacterBody2D". Give this or instancePath.'
|
|
11823
|
+
description: 'add: the node class, such as "CharacterBody2D", or a class_name a project script declares, which the node is made as with that script attached. Give this or instancePath.'
|
|
11734
11824
|
},
|
|
11735
11825
|
instancePath: {
|
|
11736
11826
|
type: "string",
|
|
@@ -12075,7 +12165,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
12075
12165
|
resourceType: {
|
|
12076
12166
|
type: "string",
|
|
12077
12167
|
ops: ["create"],
|
|
12078
|
-
description: 'create: the resource class, such as "PhysicsMaterial" or "StandardMaterial3D".'
|
|
12168
|
+
description: 'create: the resource class, such as "PhysicsMaterial" or "StandardMaterial3D", or a class_name a project script declares, which the resource is made as with that script attached.'
|
|
12079
12169
|
},
|
|
12080
12170
|
properties: { ...PROPERTIES, ops: ["create", "modify"] },
|
|
12081
12171
|
script: {
|
|
@@ -12269,6 +12359,17 @@ var init_tool_definitions = __esm(() => {
|
|
|
12269
12359
|
ops: ["profile"],
|
|
12270
12360
|
description: "profile: also answer, under callers, where the project's scripts name this function in code: its name, or Class.name for the functions of one class. Each place has its script, line, the named function it is written in (within), whether the function is called there or passed on as a Callable (called), and the profile's calls, self and total milliseconds for the function it is in, those taking the most time first, forty at most. Found by name in the source, since the engine's profiler records nothing about who made a call, so a method of the same name on another class is listed too, except, when Class.name was asked, one called through another class of the project by name or through a parameter, local or class variable declared as one, and a call made through a string is not. A parameter, local or class variable of the same name is not the function and is not listed. A property's get or set block, which the profile names Class.@name_getter or Class.@name_setter, is asked about by that name or by the property's: the places are where the property is named, each saying instead of called whether it writes the property (access write), which runs the set block, or reads it (access read), which runs the get block, and the rows are both blocks'. Inside the property's own blocks its name is the value stored, which the engine reads and writes without running them, so those lines are not places."
|
|
12271
12361
|
},
|
|
12362
|
+
mark: {
|
|
12363
|
+
type: "boolean",
|
|
12364
|
+
ops: ["profile"],
|
|
12365
|
+
description: `profile: answer as usual and set the run's mark at these totals, so a later profile with since: "mark" answers for one action, a press or a commit, apart from the boot and everything before it. The mark is kept beside the run's profile, so a server that replaces this one reads it too, and setting it again moves it. Given with since, the answer counts from the mark before it moves, which times one span after another. Default false.`
|
|
12366
|
+
},
|
|
12367
|
+
since: {
|
|
12368
|
+
type: "string",
|
|
12369
|
+
enum: ["mark"],
|
|
12370
|
+
ops: ["profile"],
|
|
12371
|
+
description: 'profile: "mark" answers only what was added after the mark that mark: true set in this run: per function the calls and self and total milliseconds since it, with the functions that added no call left out, frames and coveredMs since it, and callersOf read off the same difference. Refused when no mark was set in this run, or when the profile holds less than it did at the mark, which is a profiler started again since.'
|
|
12372
|
+
},
|
|
12272
12373
|
limit: {
|
|
12273
12374
|
type: "integer",
|
|
12274
12375
|
minimum: 1,
|
|
@@ -12320,7 +12421,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
12320
12421
|
requires: []
|
|
12321
12422
|
}
|
|
12322
12423
|
},
|
|
12323
|
-
answer: "stop: endedPid is, for a run the editor plays, the number its game announced under, read at the stop rather than remembered, and the operating system hands a freed number to the next process, so it can equal the game before it. stopped is false, with the number under notSignalled, when that number no longer answers as the process the run was started as and nothing was signalled, the note naming which property it failed on, and false with the number under stillGoing when what was told to end was still running ten seconds later. endedThrough says how the stop ended a run that was going: close (the game was asked to close and exited, so exitCode is its own), keeper (the process that started the game ended it through the handle it holds), signal (its pid was signalled once shown to still be the run), or editor (the editor playing it was asked). A start that ends the run before it says how under endedPreviousRunThrough, with the same values, and one that refused to signal that run names the property it failed on under previousRunLeftBecause. check: hung is a run killed at timeoutMs, and errors and warnings count what it printed, which comes back under entries with repeats folded together. entries holds the errors and warnings and not the ordinary lines the run printed, which printedLinesNotShown counts; a check given --verbose holds those too, and editor_run start then editor_output reads everything. An engine crash is an error whatever it prints after: the crash handler's report is the entry, and the engine version and the native and GDScript backtraces are its detail, so a crashed run is never booted or clean. arguments is the engine command line the check ran, as a start's is. profile: per function its script, the function (Class.name when the script declares a class_name), the line its body's first statement is on, for a lambda the named function it is written in (within), calls, and self and total milliseconds, with functionsCounted, scriptMs (the self time of them all) and coveredMs. complete is true when the engine's own totals arrived, which it sends only when the profiler is switched off: the runtime addon does that as a game quits, so a scene that does its work in _ready and quits is covered whole; otherwise the answer is the frames summed so far and the note says what that misses. Self time counts a call into the engine's own methods as the caller's, and a call through super too, while the overridden function's row counts it again: such a row carries selfIncludesSuper, so self times along an override chain overlap. The profiler's own cost of timing a call lands in the self time of the function called; with the runtime addon it is measured each run on an empty function, as perCallOverheadUs, and a row with at least half its selfMs in it carries overheadMs, since a small function called often costs that much less without the profiler. A function the profiler started inside is left out, since its time would be counted from the engine's start. The milliseconds are time passed while the function ran, not processor time given to it, so they grow when other processes compete for the processor: compare two profiles taken under the same load. They are also summed over every thread: a function run by WorkerThreadPool or a Thread on several threads at once counts each thread's time, the engine does not say which thread a call ran on, and a function waiting for them has the wait in its own self time, so selfMs is not time taken from frames, and scriptMs can exceed coveredMs, which the note then says. A property's get and set blocks are named Class.@name_getter and Class.@name_setter, as the engine names them, and their rows carry property, the property's name. A game the editor plays that was not started with profile: true says, in the answer, that it was profiled from the moment it was asked.",
|
|
12424
|
+
answer: "stop: endedPid is, for a run the editor plays, the number its game announced under, read at the stop rather than remembered, and the operating system hands a freed number to the next process, so it can equal the game before it. stopped is false, with the number under notSignalled, when that number no longer answers as the process the run was started as and nothing was signalled, the note naming which property it failed on, and false with the number under stillGoing when what was told to end was still running ten seconds later. endedThrough says how the stop ended a run that was going: close (the game was asked to close and exited, so exitCode is its own), keeper (the process that started the game ended it through the handle it holds), signal (its pid was signalled once shown to still be the run), or editor (the editor playing it was asked). A start that ends the run before it says how under endedPreviousRunThrough, with the same values, and one that refused to signal that run names the property it failed on under previousRunLeftBecause. check: hung is a run killed at timeoutMs, and errors and warnings count what it printed, which comes back under entries with repeats folded together. entries holds the errors and warnings and not the ordinary lines the run printed, which printedLinesNotShown counts; a check given --verbose holds those too, and editor_run start then editor_output reads everything. An engine crash is an error whatever it prints after: the crash handler's report is the entry, and the engine version and the native and GDScript backtraces are its detail, so a crashed run is never booted or clean. arguments is the engine command line the check ran, as a start's is. profile: per function its script, the function (Class.name when the script declares a class_name), the line its body's first statement is on, for a lambda the named function it is written in (within), calls, and self and total milliseconds, with functionsCounted, scriptMs (the self time of them all) and coveredMs. complete is true when the engine's own totals arrived, which it sends only when the profiler is switched off: the runtime addon does that as a game quits, so a scene that does its work in _ready and quits is covered whole; otherwise the answer is the frames summed so far and the note says what that misses. Self time counts a call into the engine's own methods as the caller's, and a call through super too, while the overridden function's row counts it again: such a row carries selfIncludesSuper, so self times along an override chain overlap. The profiler's own cost of timing a call lands in the self time of the function called; with the runtime addon it is measured each run on an empty function, as perCallOverheadUs, and a row with at least half its selfMs in it carries overheadMs, since a small function called often costs that much less without the profiler. A function the profiler started inside is left out, since its time would be counted from the engine's start. The milliseconds are time passed while the function ran, not processor time given to it, so they grow when other processes compete for the processor: compare two profiles taken under the same load. They are also summed over every thread: a function run by WorkerThreadPool or a Thread on several threads at once counts each thread's time, the engine does not say which thread a call ran on, and a function waiting for them has the wait in its own self time, so selfMs is not time taken from frames, and scriptMs can exceed coveredMs, which the note then says. A property's get and set blocks are named Class.@name_getter and Class.@name_setter, as the engine names them, and their rows carry property, the property's name. A game the editor plays that was not started with profile: true says, in the answer, that it was profiled from the moment it was asked. A profile with mark answers marked: true; one with since answers since: \"mark\" and markSetMsAgo, and lessThanAtMark counts the functions left out for reading less than at the mark, which the engine's own totals, capped, can do when they replace the frames' sum.",
|
|
12324
12425
|
defaultOperation: "start"
|
|
12325
12426
|
},
|
|
12326
12427
|
{
|
|
@@ -32796,7 +32897,7 @@ var init_ajvProvider_CEoC__sr = __esm(() => {
|
|
|
32796
32897
|
});
|
|
32797
32898
|
|
|
32798
32899
|
// node_modules/@modelcontextprotocol/server/dist/shimsNode.mjs
|
|
32799
|
-
import
|
|
32900
|
+
import process10 from "node:process";
|
|
32800
32901
|
var init_shimsNode = __esm(() => {
|
|
32801
32902
|
init_ajvProvider_CEoC__sr();
|
|
32802
32903
|
});
|
|
@@ -34610,7 +34711,7 @@ var swallowsErrorsAfterClose, StdioServerTransport = class {
|
|
|
34610
34711
|
_readBuffer;
|
|
34611
34712
|
_started = false;
|
|
34612
34713
|
_closed = false;
|
|
34613
|
-
constructor(_stdin =
|
|
34714
|
+
constructor(_stdin = process10.stdin, _stdout = process10.stdout, options) {
|
|
34614
34715
|
this._stdin = _stdin;
|
|
34615
34716
|
this._stdout = _stdout;
|
|
34616
34717
|
this._readBuffer = new ReadBuffer({ maxBufferSize: options?.maxBufferSize });
|
|
@@ -34799,10 +34900,10 @@ var init_stdio = __esm(() => {
|
|
|
34799
34900
|
});
|
|
34800
34901
|
|
|
34801
34902
|
// src/runtime-client.ts
|
|
34802
|
-
import { existsSync as
|
|
34903
|
+
import { existsSync as existsSync15, readdirSync as readdirSync10, readFileSync as readFileSync13, statSync as statSync8, unlinkSync as unlinkSync2 } from "node:fs";
|
|
34803
34904
|
import { createConnection } from "node:net";
|
|
34804
34905
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
34805
|
-
import { dirname as
|
|
34906
|
+
import { dirname as dirname10, join as join16, resolve as resolve4 } from "node:path";
|
|
34806
34907
|
import { setTimeout as delay } from "node:timers/promises";
|
|
34807
34908
|
function runtimeDirectory(variables = process.env) {
|
|
34808
34909
|
const explicit = envValue("GDHARNESS_RUNTIME_DIR", variables);
|
|
@@ -34810,20 +34911,20 @@ function runtimeDirectory(variables = process.env) {
|
|
|
34810
34911
|
return explicit;
|
|
34811
34912
|
}
|
|
34812
34913
|
const perUser = envValue("XDG_RUNTIME_DIR", variables);
|
|
34813
|
-
return
|
|
34914
|
+
return join16(perUser ?? tmpdir4(), "gdharness");
|
|
34814
34915
|
}
|
|
34815
34916
|
function runtimeDirectories(variables = process.env) {
|
|
34816
34917
|
const candidates = [runtimeDirectory(variables)];
|
|
34817
34918
|
const fallbacks = process.platform === "win32" ? [envValue("SystemRoot", variables) ?? envValue("windir", variables) ?? "C:\\Windows"] : ["/tmp", "/var/tmp"];
|
|
34818
34919
|
for (const base of fallbacks) {
|
|
34819
|
-
candidates.push(
|
|
34920
|
+
candidates.push(join16(base, process.platform === "win32" ? "Temp" : "", "gdharness"));
|
|
34820
34921
|
}
|
|
34821
34922
|
return [...new Set(candidates.map((path) => resolve4(path)))];
|
|
34822
34923
|
}
|
|
34823
34924
|
function errorReportOf(pid, directories = runtimeDirectories()) {
|
|
34824
34925
|
for (const directory of directories) {
|
|
34825
|
-
const path =
|
|
34826
|
-
if (
|
|
34926
|
+
const path = join16(directory, `runtime-${pid}.log`);
|
|
34927
|
+
if (existsSync15(path)) {
|
|
34827
34928
|
return path;
|
|
34828
34929
|
}
|
|
34829
34930
|
}
|
|
@@ -34871,7 +34972,7 @@ function parseAnnouncement(file, pid) {
|
|
|
34871
34972
|
};
|
|
34872
34973
|
let announcedAt;
|
|
34873
34974
|
try {
|
|
34874
|
-
announcedAt =
|
|
34975
|
+
announcedAt = statSync8(file).mtimeMs;
|
|
34875
34976
|
} catch {}
|
|
34876
34977
|
const when = announcedAt === undefined ? {} : { announcedAt };
|
|
34877
34978
|
if (protocol !== RUNTIME_PROTOCOL) {
|
|
@@ -34963,29 +35064,29 @@ function strangersAmong(candidates, now, startTimes = startTimesOf) {
|
|
|
34963
35064
|
}).map((one) => one.file));
|
|
34964
35065
|
}
|
|
34965
35066
|
function announcedIn(directory) {
|
|
34966
|
-
if (!
|
|
35067
|
+
if (!existsSync15(directory)) {
|
|
34967
35068
|
return [];
|
|
34968
35069
|
}
|
|
34969
35070
|
const found = [];
|
|
34970
35071
|
const candidates = [];
|
|
34971
35072
|
const entries = [];
|
|
34972
|
-
for (const entry of
|
|
35073
|
+
for (const entry of readdirSync10(directory)) {
|
|
34973
35074
|
const report = ERROR_REPORT_PATTERN.exec(entry);
|
|
34974
35075
|
if (report) {
|
|
34975
|
-
sweepErrorReport(
|
|
35076
|
+
sweepErrorReport(join16(directory, entry), Number.parseInt(report[1] ?? "", 10));
|
|
34976
35077
|
continue;
|
|
34977
35078
|
}
|
|
34978
35079
|
const match = ANNOUNCEMENT_PATTERN.exec(entry);
|
|
34979
35080
|
if (!match) {
|
|
34980
35081
|
continue;
|
|
34981
35082
|
}
|
|
34982
|
-
const file =
|
|
35083
|
+
const file = join16(directory, entry);
|
|
34983
35084
|
const pid = Number.parseInt(match[1] ?? "", 10);
|
|
34984
35085
|
const running = alive(pid);
|
|
34985
35086
|
entries.push({ file, pid, running });
|
|
34986
35087
|
if (running) {
|
|
34987
35088
|
try {
|
|
34988
|
-
candidates.push({ file, pid, writtenAt:
|
|
35089
|
+
candidates.push({ file, pid, writtenAt: statSync8(file).mtimeMs });
|
|
34989
35090
|
} catch {}
|
|
34990
35091
|
}
|
|
34991
35092
|
}
|
|
@@ -35007,16 +35108,16 @@ function announcedIn(directory) {
|
|
|
35007
35108
|
found.push(announced);
|
|
35008
35109
|
}
|
|
35009
35110
|
const held = new Set(entries.map((one) => one.file));
|
|
35010
|
-
const here =
|
|
35111
|
+
const here = join16(directory, ".");
|
|
35011
35112
|
for (const file of verdicts.keys()) {
|
|
35012
|
-
if (
|
|
35113
|
+
if (dirname10(file) === here && !held.has(file)) {
|
|
35013
35114
|
verdicts.delete(file);
|
|
35014
35115
|
}
|
|
35015
35116
|
}
|
|
35016
35117
|
return found;
|
|
35017
35118
|
}
|
|
35018
35119
|
async function announcementEnded(pid, directories = runtimeDirectories()) {
|
|
35019
|
-
const files = directories.map((directory) =>
|
|
35120
|
+
const files = directories.map((directory) => join16(directory, `runtime-${pid}.json`)).filter((file) => existsSync15(file));
|
|
35020
35121
|
const first = files[0];
|
|
35021
35122
|
if (first === undefined) {
|
|
35022
35123
|
return;
|
|
@@ -35042,7 +35143,7 @@ function sweepErrorReport(path, pid, now = Date.now()) {
|
|
|
35042
35143
|
return;
|
|
35043
35144
|
}
|
|
35044
35145
|
try {
|
|
35045
|
-
if (now -
|
|
35146
|
+
if (now - statSync8(path).mtimeMs >= ERROR_REPORT_KEEP_MS) {
|
|
35046
35147
|
unlinkSync2(path);
|
|
35047
35148
|
}
|
|
35048
35149
|
} catch {}
|
|
@@ -35318,9 +35419,9 @@ var init_runtime_client = __esm(() => {
|
|
|
35318
35419
|
|
|
35319
35420
|
// src/boot-note.ts
|
|
35320
35421
|
import { mkdirSync as mkdirSync6, readFileSync as readFileSync14, writeFileSync as writeFileSync8 } from "node:fs";
|
|
35321
|
-
import { dirname as
|
|
35422
|
+
import { dirname as dirname11, join as join17 } from "node:path";
|
|
35322
35423
|
function bootNotePath(projectPath) {
|
|
35323
|
-
return
|
|
35424
|
+
return join17(projectPath, ".godot", "gdharness-boot.json");
|
|
35324
35425
|
}
|
|
35325
35426
|
function readScenes(projectPath) {
|
|
35326
35427
|
try {
|
|
@@ -35340,7 +35441,7 @@ function readBootNote(projectPath, scene) {
|
|
|
35340
35441
|
function writeBootNote(projectPath, scene, announcedAfterMs) {
|
|
35341
35442
|
try {
|
|
35342
35443
|
const path = bootNotePath(projectPath);
|
|
35343
|
-
mkdirSync6(
|
|
35444
|
+
mkdirSync6(dirname11(path), { recursive: true });
|
|
35344
35445
|
const scenes = {
|
|
35345
35446
|
...readScenes(projectPath),
|
|
35346
35447
|
[scene]: {
|
|
@@ -35367,9 +35468,9 @@ var init_boot_note = __esm(() => {
|
|
|
35367
35468
|
|
|
35368
35469
|
// src/breakpoint-note.ts
|
|
35369
35470
|
import { mkdirSync as mkdirSync7, readFileSync as readFileSync15, rmSync as rmSync7, writeFileSync as writeFileSync9 } from "node:fs";
|
|
35370
|
-
import { dirname as
|
|
35471
|
+
import { dirname as dirname12, isAbsolute as isAbsolute4, join as join18, relative as relative3 } from "node:path";
|
|
35371
35472
|
function breakpointNotePath(projectPath) {
|
|
35372
|
-
return
|
|
35473
|
+
return join18(projectPath, ".godot", "gdharness-breakpoints.json");
|
|
35373
35474
|
}
|
|
35374
35475
|
function writeBreakpointNote(projectPath, held) {
|
|
35375
35476
|
const path = breakpointNotePath(projectPath);
|
|
@@ -35386,7 +35487,7 @@ function writeBreakpointNote(projectPath, held) {
|
|
|
35386
35487
|
lines: [...one.lines]
|
|
35387
35488
|
}))
|
|
35388
35489
|
};
|
|
35389
|
-
mkdirSync7(
|
|
35490
|
+
mkdirSync7(dirname12(path), { recursive: true });
|
|
35390
35491
|
writeFileSync9(path, `${JSON.stringify(note, null, 2)}
|
|
35391
35492
|
`, "utf8");
|
|
35392
35493
|
} catch (error) {
|
|
@@ -35405,12 +35506,12 @@ function readBreakpointNote(projectPath) {
|
|
|
35405
35506
|
continue;
|
|
35406
35507
|
}
|
|
35407
35508
|
const { script, lines: listed } = entry;
|
|
35408
|
-
if (typeof script !== "string" ||
|
|
35509
|
+
if (typeof script !== "string" || isAbsolute4(script) || script.split("/").includes("..")) {
|
|
35409
35510
|
continue;
|
|
35410
35511
|
}
|
|
35411
35512
|
const lines = Array.isArray(listed) ? listed.filter((line) => Number.isInteger(line) && line > 0) : [];
|
|
35412
35513
|
if (lines.length > 0) {
|
|
35413
|
-
held.push({ scriptPath: realPathOr(
|
|
35514
|
+
held.push({ scriptPath: realPathOr(join18(projectPath, script)), lines });
|
|
35414
35515
|
}
|
|
35415
35516
|
}
|
|
35416
35517
|
return held;
|
|
@@ -35420,7 +35521,7 @@ function readBreakpointNote(projectPath) {
|
|
|
35420
35521
|
}
|
|
35421
35522
|
function withinProject(projectPath, scriptPath) {
|
|
35422
35523
|
const rel = relative3(projectPath, scriptPath);
|
|
35423
|
-
return rel !== "" && !rel.startsWith("..") && !
|
|
35524
|
+
return rel !== "" && !rel.startsWith("..") && !isAbsolute4(rel);
|
|
35424
35525
|
}
|
|
35425
35526
|
var init_breakpoint_note = __esm(() => {
|
|
35426
35527
|
init_errors();
|
|
@@ -35460,9 +35561,9 @@ var init_whole_file = __esm(() => {
|
|
|
35460
35561
|
|
|
35461
35562
|
// src/class-note.ts
|
|
35462
35563
|
import { mkdirSync as mkdirSync8, readFileSync as readFileSync16 } from "node:fs";
|
|
35463
|
-
import { dirname as
|
|
35564
|
+
import { dirname as dirname13, join as join19 } from "node:path";
|
|
35464
35565
|
function classNotePath(projectPath) {
|
|
35465
|
-
return
|
|
35566
|
+
return join19(projectPath, ".godot", "gdharness-classes.json");
|
|
35466
35567
|
}
|
|
35467
35568
|
function readClassNote(projectPath) {
|
|
35468
35569
|
try {
|
|
@@ -35496,7 +35597,7 @@ function unfinishedScanIn(value) {
|
|
|
35496
35597
|
function writeClassNote(projectPath, note) {
|
|
35497
35598
|
const path = classNotePath(projectPath);
|
|
35498
35599
|
try {
|
|
35499
|
-
mkdirSync8(
|
|
35600
|
+
mkdirSync8(dirname13(path), { recursive: true });
|
|
35500
35601
|
} catch {
|
|
35501
35602
|
return;
|
|
35502
35603
|
}
|
|
@@ -35508,11 +35609,11 @@ var init_class_note = __esm(() => {
|
|
|
35508
35609
|
});
|
|
35509
35610
|
|
|
35510
35611
|
// src/config-pin.ts
|
|
35511
|
-
import { existsSync as
|
|
35512
|
-
import { join as
|
|
35612
|
+
import { existsSync as existsSync16, readFileSync as readFileSync17 } from "node:fs";
|
|
35613
|
+
import { join as join20 } from "node:path";
|
|
35513
35614
|
function versionsNamedInConfig(projectPath) {
|
|
35514
|
-
const config =
|
|
35515
|
-
if (!
|
|
35615
|
+
const config = join20(projectPath, ".mcp.json");
|
|
35616
|
+
if (!existsSync16(config)) {
|
|
35516
35617
|
return [];
|
|
35517
35618
|
}
|
|
35518
35619
|
let text;
|
|
@@ -35538,22 +35639,22 @@ var init_config_pin = __esm(() => {
|
|
|
35538
35639
|
});
|
|
35539
35640
|
|
|
35540
35641
|
// src/cut-test-run.ts
|
|
35541
|
-
import { existsSync as
|
|
35542
|
-
import { dirname as
|
|
35642
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync9, readFileSync as readFileSync18, rmSync as rmSync9, writeFileSync as writeFileSync11 } from "node:fs";
|
|
35643
|
+
import { dirname as dirname14, join as join21 } from "node:path";
|
|
35543
35644
|
function keptAt(projectPath) {
|
|
35544
|
-
return
|
|
35645
|
+
return join21(projectPath, ".godot", "gdharness-reports", "cut-run.json");
|
|
35545
35646
|
}
|
|
35546
35647
|
function keepCutTestRun(projectPath, run) {
|
|
35547
35648
|
const path = keptAt(projectPath);
|
|
35548
35649
|
try {
|
|
35549
|
-
mkdirSync9(
|
|
35650
|
+
mkdirSync9(dirname14(path), { recursive: true });
|
|
35550
35651
|
writeFileSync11(path, `${JSON.stringify(run, null, 2)}
|
|
35551
35652
|
`, "utf8");
|
|
35552
35653
|
} catch {}
|
|
35553
35654
|
}
|
|
35554
35655
|
function takeCutTestRun(projectPath) {
|
|
35555
35656
|
const path = keptAt(projectPath);
|
|
35556
|
-
if (!
|
|
35657
|
+
if (!existsSync17(path)) {
|
|
35557
35658
|
return null;
|
|
35558
35659
|
}
|
|
35559
35660
|
try {
|
|
@@ -36370,27 +36471,27 @@ var init_dap_client = __esm(() => {
|
|
|
36370
36471
|
import { createHash as createHash4 } from "node:crypto";
|
|
36371
36472
|
import {
|
|
36372
36473
|
closeSync,
|
|
36373
|
-
existsSync as
|
|
36474
|
+
existsSync as existsSync18,
|
|
36374
36475
|
mkdtempSync as mkdtempSync2,
|
|
36375
36476
|
openSync,
|
|
36376
36477
|
readFileSync as readFileSync19,
|
|
36377
36478
|
rmSync as rmSync10,
|
|
36378
|
-
statSync as
|
|
36479
|
+
statSync as statSync9,
|
|
36379
36480
|
writeFileSync as writeFileSync12,
|
|
36380
36481
|
writeSync
|
|
36381
36482
|
} from "node:fs";
|
|
36382
36483
|
import { tmpdir as tmpdir5 } from "node:os";
|
|
36383
|
-
import { join as
|
|
36484
|
+
import { join as join22 } from "node:path";
|
|
36384
36485
|
import { setTimeout as delay3 } from "node:timers/promises";
|
|
36385
36486
|
function helperAssembly(directory = tmpdir5()) {
|
|
36386
|
-
return
|
|
36487
|
+
return join22(directory, `gdharness-desktop-${DIGEST}.dll`);
|
|
36387
36488
|
}
|
|
36388
36489
|
async function ensureHelperCompiled(directory = tmpdir5(), budgetMs = HELPER_COMPILE_MS, wait = true) {
|
|
36389
36490
|
const assembly = helperAssembly(directory);
|
|
36390
36491
|
const lock = `${assembly}.compiling`;
|
|
36391
36492
|
const until = Date.now() + budgetMs;
|
|
36392
36493
|
while (Date.now() < until) {
|
|
36393
|
-
if (
|
|
36494
|
+
if (existsSync18(assembly)) {
|
|
36394
36495
|
return null;
|
|
36395
36496
|
}
|
|
36396
36497
|
let mine = false;
|
|
@@ -36409,7 +36510,7 @@ async function ensureHelperCompiled(directory = tmpdir5(), budgetMs = HELPER_COM
|
|
|
36409
36510
|
}
|
|
36410
36511
|
try {
|
|
36411
36512
|
const holder = Number(readFileSync19(lock, "utf8").trim());
|
|
36412
|
-
const old = Date.now() -
|
|
36513
|
+
const old = Date.now() - statSync9(lock).mtimeMs > budgetMs;
|
|
36413
36514
|
if (old || Number.isInteger(holder) && holder > 0 && !alive(holder)) {
|
|
36414
36515
|
rmSync10(lock, { force: true });
|
|
36415
36516
|
continue;
|
|
@@ -36423,8 +36524,8 @@ async function ensureHelperCompiled(directory = tmpdir5(), budgetMs = HELPER_COM
|
|
|
36423
36524
|
return `the desktop helper was not compiled within ${String(budgetMs)} ms`;
|
|
36424
36525
|
}
|
|
36425
36526
|
async function compileHelper(directory, assembly, budgetMs) {
|
|
36426
|
-
const scratch = mkdtempSync2(
|
|
36427
|
-
const source =
|
|
36527
|
+
const scratch = mkdtempSync2(join22(directory, "gdharness-desktop-source-"));
|
|
36528
|
+
const source = join22(scratch, "helper.cs");
|
|
36428
36529
|
writeFileSync12(source, SOURCE, "utf8");
|
|
36429
36530
|
const literal = (path) => `'${path.replaceAll("'", "''")}'`;
|
|
36430
36531
|
const script = [
|
|
@@ -36462,7 +36563,7 @@ async function compileHelper(directory, assembly, budgetMs) {
|
|
|
36462
36563
|
});
|
|
36463
36564
|
compiler.once("close", (code) => {
|
|
36464
36565
|
clearTimeout(timer);
|
|
36465
|
-
resolve(
|
|
36566
|
+
resolve(existsSync18(assembly) ? null : `Windows PowerShell could not compile it${code === null ? " in time" : ""}: ${complained.trim() || `exit code ${String(code)}`}`);
|
|
36466
36567
|
});
|
|
36467
36568
|
});
|
|
36468
36569
|
} finally {
|
|
@@ -36629,21 +36730,21 @@ var init_desktop = __esm(() => {
|
|
|
36629
36730
|
// src/editor-log.ts
|
|
36630
36731
|
import {
|
|
36631
36732
|
closeSync as closeSync2,
|
|
36632
|
-
existsSync as
|
|
36733
|
+
existsSync as existsSync19,
|
|
36633
36734
|
mkdirSync as mkdirSync10,
|
|
36634
36735
|
openSync as openSync2,
|
|
36635
36736
|
readFileSync as readFileSync20,
|
|
36636
36737
|
readSync,
|
|
36637
36738
|
rmSync as rmSync11,
|
|
36638
|
-
statSync as
|
|
36739
|
+
statSync as statSync10,
|
|
36639
36740
|
writeFileSync as writeFileSync13
|
|
36640
36741
|
} from "node:fs";
|
|
36641
|
-
import { dirname as
|
|
36742
|
+
import { dirname as dirname15, join as join23 } from "node:path";
|
|
36642
36743
|
function editorLogPath(projectPath) {
|
|
36643
|
-
return
|
|
36744
|
+
return join23(projectPath, ".godot", "gdharness-editor.log");
|
|
36644
36745
|
}
|
|
36645
36746
|
function editorLogNotePath(projectPath) {
|
|
36646
|
-
return
|
|
36747
|
+
return join23(projectPath, ".godot", "gdharness-editor.json");
|
|
36647
36748
|
}
|
|
36648
36749
|
function readEditorLogNote(projectPath) {
|
|
36649
36750
|
try {
|
|
@@ -36658,7 +36759,7 @@ function readEditorLogNote(projectPath) {
|
|
|
36658
36759
|
function clearEditorLog(projectPath) {
|
|
36659
36760
|
try {
|
|
36660
36761
|
const path = editorLogPath(projectPath);
|
|
36661
|
-
mkdirSync10(
|
|
36762
|
+
mkdirSync10(dirname15(path), { recursive: true });
|
|
36662
36763
|
rmSync11(path, { force: true });
|
|
36663
36764
|
rmSync11(editorLogNotePath(projectPath), { force: true });
|
|
36664
36765
|
} catch {}
|
|
@@ -36666,7 +36767,7 @@ function clearEditorLog(projectPath) {
|
|
|
36666
36767
|
function writeEditorLogNote(projectPath, pid) {
|
|
36667
36768
|
try {
|
|
36668
36769
|
const path = editorLogNotePath(projectPath);
|
|
36669
|
-
mkdirSync10(
|
|
36770
|
+
mkdirSync10(dirname15(path), { recursive: true });
|
|
36670
36771
|
writeFileSync13(path, JSON.stringify({ pid, startedAt: new Date().toISOString() }, null, 2), "utf8");
|
|
36671
36772
|
} catch {}
|
|
36672
36773
|
}
|
|
@@ -36688,7 +36789,7 @@ function editorConsole(projectPath, editorPid, openedByAServer) {
|
|
|
36688
36789
|
return { kind: "older server", notedPid: noted?.pid ?? null };
|
|
36689
36790
|
}
|
|
36690
36791
|
const path = editorLogPath(projectPath);
|
|
36691
|
-
if (!
|
|
36792
|
+
if (!existsSync19(path)) {
|
|
36692
36793
|
return { kind: "not written" };
|
|
36693
36794
|
}
|
|
36694
36795
|
const log = new GameLog;
|
|
@@ -36744,7 +36845,7 @@ function foldProgress(text) {
|
|
|
36744
36845
|
`);
|
|
36745
36846
|
}
|
|
36746
36847
|
function theFirstOf(path) {
|
|
36747
|
-
const whole =
|
|
36848
|
+
const whole = statSync10(path).size;
|
|
36748
36849
|
if (whole <= MOST_CONSOLE_BYTES) {
|
|
36749
36850
|
return { text: readFileSync20(path, "utf8") };
|
|
36750
36851
|
}
|
|
@@ -36907,8 +37008,8 @@ var init_editor_log = __esm(() => {
|
|
|
36907
37008
|
});
|
|
36908
37009
|
|
|
36909
37010
|
// src/rename.ts
|
|
36910
|
-
import { existsSync as
|
|
36911
|
-
import { dirname as
|
|
37011
|
+
import { existsSync as existsSync20, mkdirSync as mkdirSync11, readFileSync as readFileSync21, renameSync as renameSync3, unlinkSync as unlinkSync3, writeFileSync as writeFileSync14 } from "node:fs";
|
|
37012
|
+
import { dirname as dirname16, posix as posix2 } from "node:path";
|
|
36912
37013
|
|
|
36913
37014
|
class Script {
|
|
36914
37015
|
path;
|
|
@@ -37616,21 +37717,21 @@ function applyRewrites(rewrites) {
|
|
|
37616
37717
|
if (now !== rewrite.before) {
|
|
37617
37718
|
throw new Error(`${rewrite.path} changed on disk while the rename was being worked out; nothing was written`);
|
|
37618
37719
|
}
|
|
37619
|
-
if (rewrite.writeTo !== rewrite.absolute &&
|
|
37720
|
+
if (rewrite.writeTo !== rewrite.absolute && existsSync20(rewrite.writeTo)) {
|
|
37620
37721
|
throw new Error(`${rewrite.writeTo} already exists; nothing was written`);
|
|
37621
37722
|
}
|
|
37622
37723
|
}
|
|
37623
37724
|
const done = [];
|
|
37624
37725
|
try {
|
|
37625
37726
|
for (const rewrite of rewrites) {
|
|
37626
|
-
mkdirSync11(
|
|
37727
|
+
mkdirSync11(dirname16(rewrite.writeTo), { recursive: true });
|
|
37627
37728
|
const staging = `${rewrite.writeTo}.gdharness-rename`;
|
|
37628
37729
|
writeFileSync14(staging, rewrite.after, "utf8");
|
|
37629
37730
|
renameSync3(staging, rewrite.writeTo);
|
|
37630
37731
|
let movedUid = false;
|
|
37631
37732
|
if (rewrite.writeTo !== rewrite.absolute) {
|
|
37632
37733
|
unlinkSync3(rewrite.absolute);
|
|
37633
|
-
if (
|
|
37734
|
+
if (existsSync20(`${rewrite.absolute}.uid`)) {
|
|
37634
37735
|
renameSync3(`${rewrite.absolute}.uid`, `${rewrite.writeTo}.uid`);
|
|
37635
37736
|
movedUid = true;
|
|
37636
37737
|
}
|
|
@@ -37676,10 +37777,10 @@ var init_rename = __esm(() => {
|
|
|
37676
37777
|
});
|
|
37677
37778
|
|
|
37678
37779
|
// src/lsp_client.ts
|
|
37679
|
-
import { existsSync as
|
|
37780
|
+
import { existsSync as existsSync21, realpathSync as realpathSync4 } from "node:fs";
|
|
37680
37781
|
import { readFile, realpath } from "node:fs/promises";
|
|
37681
37782
|
import { createConnection as createConnection3 } from "node:net";
|
|
37682
|
-
import { dirname as
|
|
37783
|
+
import { dirname as dirname17, join as join24, resolve as resolve5 } from "node:path";
|
|
37683
37784
|
import { setTimeout as delay4 } from "node:timers/promises";
|
|
37684
37785
|
import { fileURLToPath as fileURLToPath3, pathToFileURL } from "node:url";
|
|
37685
37786
|
function initializeTimeoutMs() {
|
|
@@ -37841,7 +37942,7 @@ class GodotLSPClient {
|
|
|
37841
37942
|
if (this.initialized) {
|
|
37842
37943
|
return;
|
|
37843
37944
|
}
|
|
37844
|
-
const rootPath = this.rootPath ??
|
|
37945
|
+
const rootPath = this.rootPath ?? dirname17(resolve5(filePath));
|
|
37845
37946
|
await this.initialize(rootPath);
|
|
37846
37947
|
}
|
|
37847
37948
|
async sendRequest(method, params, timeoutMs = this.timeouts.requestMs) {
|
|
@@ -38357,7 +38458,7 @@ async function resolveLSPPaths(projectPathValue, scriptPathValue) {
|
|
|
38357
38458
|
} catch {
|
|
38358
38459
|
throw new ArgumentRefusal(`Project path does not exist: ${requestedProjectPath}`);
|
|
38359
38460
|
}
|
|
38360
|
-
if (!
|
|
38461
|
+
if (!existsSync21(join24(projectPath, "project.godot"))) {
|
|
38361
38462
|
throw new ArgumentRefusal(`Not a Godot project: ${projectPath}. Point projectPath at the directory holding project.godot.`);
|
|
38362
38463
|
}
|
|
38363
38464
|
const contained = resolveWithinProject(projectPath, scriptPathValue);
|
|
@@ -38517,8 +38618,8 @@ var init_lsp_client = __esm(() => {
|
|
|
38517
38618
|
});
|
|
38518
38619
|
|
|
38519
38620
|
// src/rename-symbol.ts
|
|
38520
|
-
import { existsSync as
|
|
38521
|
-
import { basename as
|
|
38621
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync12, readdirSync as readdirSync11, realpathSync as realpathSync5, rmSync as rmSync12, writeFileSync as writeFileSync15 } from "node:fs";
|
|
38622
|
+
import { basename as basename7, dirname as dirname18, join as join25, relative as relative4, resolve as resolve6 } from "node:path";
|
|
38522
38623
|
function resourcePathOf(projectPath, absolute) {
|
|
38523
38624
|
const inside = relative4(onDiskSpelling(projectPath), onDiskSpelling(absolute));
|
|
38524
38625
|
if (inside === "" || inside.startsWith("..") || /^[A-Za-z]:/.test(inside)) {
|
|
@@ -38531,13 +38632,13 @@ function onDiskSpelling(path) {
|
|
|
38531
38632
|
let at = resolve6(path);
|
|
38532
38633
|
for (;; ) {
|
|
38533
38634
|
try {
|
|
38534
|
-
return
|
|
38635
|
+
return join25(realpathSync5.native(at), ...missing.reverse());
|
|
38535
38636
|
} catch {
|
|
38536
|
-
const parent =
|
|
38637
|
+
const parent = dirname18(at);
|
|
38537
38638
|
if (parent === at) {
|
|
38538
38639
|
return resolve6(path);
|
|
38539
38640
|
}
|
|
38540
|
-
missing.push(
|
|
38641
|
+
missing.push(basename7(at));
|
|
38541
38642
|
at = parent;
|
|
38542
38643
|
}
|
|
38543
38644
|
}
|
|
@@ -38584,7 +38685,7 @@ async function renameSymbol(request, services) {
|
|
|
38584
38685
|
reason: `newScriptPath must be a .gd path inside the project, and ${request.newScriptPath} is not.`
|
|
38585
38686
|
};
|
|
38586
38687
|
}
|
|
38587
|
-
if (
|
|
38688
|
+
if (existsSync22(request.newScriptPath)) {
|
|
38588
38689
|
return { ok: false, reason: `${moveTo} already exists, so the script cannot be moved there.` };
|
|
38589
38690
|
}
|
|
38590
38691
|
}
|
|
@@ -38760,7 +38861,7 @@ async function renameSymbol(request, services) {
|
|
|
38760
38861
|
scripts,
|
|
38761
38862
|
moves: new Map([[declaringPath, moveTo]]),
|
|
38762
38863
|
named: [declaringPath],
|
|
38763
|
-
exists: (path) =>
|
|
38864
|
+
exists: (path) => existsSync22(`${projectPath}/${path.slice("res://".length)}`)
|
|
38764
38865
|
}, plan);
|
|
38765
38866
|
}
|
|
38766
38867
|
plan = mentionIgnored(plan, ignoredTexts(projectPath), symbol, moveTo === null ? [] : [declaringPath]);
|
|
@@ -38959,15 +39060,15 @@ ${mention.kind}`;
|
|
|
38959
39060
|
};
|
|
38960
39061
|
}
|
|
38961
39062
|
function writeLeftAloneDetail(projectPath, symbol, detail) {
|
|
38962
|
-
const directory =
|
|
39063
|
+
const directory = join25(projectPath, ".godot", "gdharness-renames");
|
|
38963
39064
|
mkdirSync12(directory, { recursive: true });
|
|
38964
39065
|
detailWrites += 1;
|
|
38965
|
-
const path =
|
|
39066
|
+
const path = join25(directory, `${String(Date.now())}-${String(detailWrites).padStart(6, "0")}-${String(process.pid)}-${symbol}.json`);
|
|
38966
39067
|
writeFileSync15(path, `${JSON.stringify(detail, null, 2)}
|
|
38967
39068
|
`, "utf8");
|
|
38968
|
-
const kept =
|
|
39069
|
+
const kept = readdirSync11(directory).filter((name) => name.endsWith(".json")).sort().reverse();
|
|
38969
39070
|
for (const old of kept.slice(10)) {
|
|
38970
|
-
rmSync12(
|
|
39071
|
+
rmSync12(join25(directory, old), { force: true });
|
|
38971
39072
|
}
|
|
38972
39073
|
return path;
|
|
38973
39074
|
}
|
|
@@ -39008,25 +39109,25 @@ var init_rename_symbol = __esm(() => {
|
|
|
39008
39109
|
|
|
39009
39110
|
// src/file-move.ts
|
|
39010
39111
|
import {
|
|
39011
|
-
existsSync as
|
|
39112
|
+
existsSync as existsSync23,
|
|
39012
39113
|
mkdirSync as mkdirSync13,
|
|
39013
|
-
readdirSync as
|
|
39114
|
+
readdirSync as readdirSync12,
|
|
39014
39115
|
readFileSync as readFileSync22,
|
|
39015
39116
|
renameSync as renameSync4,
|
|
39016
|
-
statSync as
|
|
39117
|
+
statSync as statSync11,
|
|
39017
39118
|
writeFileSync as writeFileSync16
|
|
39018
39119
|
} from "node:fs";
|
|
39019
|
-
import { basename as
|
|
39120
|
+
import { basename as basename8, dirname as dirname19, extname as extname3, join as join26, relative as relative5 } from "node:path";
|
|
39020
39121
|
function filesUnder(directory) {
|
|
39021
|
-
return
|
|
39022
|
-
const path =
|
|
39122
|
+
return readdirSync12(directory, { withFileTypes: true }).flatMap((entry) => {
|
|
39123
|
+
const path = join26(directory, entry.name);
|
|
39023
39124
|
return entry.isDirectory() ? filesUnder(path) : [path];
|
|
39024
39125
|
});
|
|
39025
39126
|
}
|
|
39026
39127
|
function sameFile(one, other) {
|
|
39027
39128
|
try {
|
|
39028
|
-
const a =
|
|
39029
|
-
const b =
|
|
39129
|
+
const a = statSync11(one);
|
|
39130
|
+
const b = statSync11(other);
|
|
39030
39131
|
return a.ino === b.ino && a.dev === b.dev;
|
|
39031
39132
|
} catch {
|
|
39032
39133
|
return false;
|
|
@@ -39040,8 +39141,8 @@ function unseenByTheEngine(projectPath, path) {
|
|
|
39040
39141
|
return `${path} is under ${step}, and the engine passes over every directory whose name starts with a dot`;
|
|
39041
39142
|
}
|
|
39042
39143
|
if (at < steps.length - 1) {
|
|
39043
|
-
directory =
|
|
39044
|
-
if (
|
|
39144
|
+
directory = join26(directory, step);
|
|
39145
|
+
if (existsSync23(join26(directory, ".gdignore"))) {
|
|
39045
39146
|
return `${path} is under a directory holding a .gdignore, which the engine does not load`;
|
|
39046
39147
|
}
|
|
39047
39148
|
}
|
|
@@ -39053,21 +39154,21 @@ function installedAddonOf(projectPath, path) {
|
|
|
39053
39154
|
if (steps[0] !== "addons" || steps[1] === undefined) {
|
|
39054
39155
|
return null;
|
|
39055
39156
|
}
|
|
39056
|
-
return
|
|
39157
|
+
return existsSync23(join26(projectPath, "addons", steps[1], VERSION_MARKER)) ? `res://addons/${steps[1]}` : null;
|
|
39057
39158
|
}
|
|
39058
39159
|
function binaryResources(projectPath) {
|
|
39059
39160
|
const found = [];
|
|
39060
39161
|
const visit = (directory, prefix) => {
|
|
39061
|
-
if (
|
|
39162
|
+
if (existsSync23(join26(directory, ".gdignore"))) {
|
|
39062
39163
|
return;
|
|
39063
39164
|
}
|
|
39064
|
-
for (const entry of
|
|
39165
|
+
for (const entry of readdirSync12(directory, { withFileTypes: true })) {
|
|
39065
39166
|
if (entry.name.startsWith(".")) {
|
|
39066
39167
|
continue;
|
|
39067
39168
|
}
|
|
39068
39169
|
const path = `${prefix}${entry.name}`;
|
|
39069
39170
|
if (entry.isDirectory()) {
|
|
39070
|
-
visit(
|
|
39171
|
+
visit(join26(directory, entry.name), `${path}/`);
|
|
39071
39172
|
} else if (/\.(scn|res)$/i.test(entry.name)) {
|
|
39072
39173
|
found.push(path);
|
|
39073
39174
|
}
|
|
@@ -39077,7 +39178,7 @@ function binaryResources(projectPath) {
|
|
|
39077
39178
|
return found;
|
|
39078
39179
|
}
|
|
39079
39180
|
function targetPathOf(projectPath, absolute) {
|
|
39080
|
-
const inside = relative5(onDiskSpelling(projectPath),
|
|
39181
|
+
const inside = relative5(onDiskSpelling(projectPath), join26(onDiskSpelling(dirname19(absolute)), basename8(absolute)));
|
|
39081
39182
|
if (inside === "" || inside.startsWith("..") || /^[A-Za-z]:/.test(inside)) {
|
|
39082
39183
|
return null;
|
|
39083
39184
|
}
|
|
@@ -39095,13 +39196,13 @@ function planFileMove(projectPath, absoluteFrom, absoluteTo) {
|
|
|
39095
39196
|
if (to === null) {
|
|
39096
39197
|
return { ok: false, reason: `newPath must be a path inside the project, and ${absoluteTo} is not.` };
|
|
39097
39198
|
}
|
|
39098
|
-
if (!
|
|
39199
|
+
if (!existsSync23(absoluteFrom)) {
|
|
39099
39200
|
return { ok: false, reason: `There is nothing at ${from} to move.` };
|
|
39100
39201
|
}
|
|
39101
39202
|
if (from === to) {
|
|
39102
39203
|
return { ok: false, reason: `${from} is already there; nothing to move.` };
|
|
39103
39204
|
}
|
|
39104
|
-
const isDirectory =
|
|
39205
|
+
const isDirectory = statSync11(absoluteFrom).isDirectory();
|
|
39105
39206
|
if (from === "res://project.godot") {
|
|
39106
39207
|
return {
|
|
39107
39208
|
ok: false,
|
|
@@ -39124,7 +39225,7 @@ function planFileMove(projectPath, absoluteFrom, absoluteTo) {
|
|
|
39124
39225
|
if (isDirectory && to.startsWith(`${from}/`)) {
|
|
39125
39226
|
return { ok: false, reason: `${from} cannot be moved into itself.` };
|
|
39126
39227
|
}
|
|
39127
|
-
if (
|
|
39228
|
+
if (existsSync23(absoluteTo) && !sameFile(absoluteFrom, absoluteTo)) {
|
|
39128
39229
|
return { ok: false, reason: `${to} already exists, so nothing was moved there.` };
|
|
39129
39230
|
}
|
|
39130
39231
|
const unseen = unseenByTheEngine(projectPath, to);
|
|
@@ -39132,7 +39233,7 @@ function planFileMove(projectPath, absoluteFrom, absoluteTo) {
|
|
|
39132
39233
|
return { ok: false, reason: `${unseen}, so nothing there could load what was moved.` };
|
|
39133
39234
|
}
|
|
39134
39235
|
if (!isDirectory) {
|
|
39135
|
-
const owner = SIDECARS.map((sidecar) => from.endsWith(sidecar) ? from.slice(0, -sidecar.length) : null).find((path) => path !== null &&
|
|
39236
|
+
const owner = SIDECARS.map((sidecar) => from.endsWith(sidecar) ? from.slice(0, -sidecar.length) : null).find((path) => path !== null && existsSync23(join26(projectPath, path.slice("res://".length))));
|
|
39136
39237
|
if (owner !== undefined && owner !== null) {
|
|
39137
39238
|
return { ok: false, reason: `${from} belongs to ${owner} and moves with it: move ${owner} instead.` };
|
|
39138
39239
|
}
|
|
@@ -39154,7 +39255,7 @@ function planFileMove(projectPath, absoluteFrom, absoluteTo) {
|
|
|
39154
39255
|
} else {
|
|
39155
39256
|
moves.set(from, to);
|
|
39156
39257
|
for (const sidecar of SIDECARS) {
|
|
39157
|
-
if (
|
|
39258
|
+
if (existsSync23(`${absoluteFrom}${sidecar}`)) {
|
|
39158
39259
|
moves.set(`${from}${sidecar}`, `${to}${sidecar}`);
|
|
39159
39260
|
}
|
|
39160
39261
|
}
|
|
@@ -39166,12 +39267,12 @@ function planFileMove(projectPath, absoluteFrom, absoluteTo) {
|
|
|
39166
39267
|
scripts,
|
|
39167
39268
|
moves,
|
|
39168
39269
|
named: [from],
|
|
39169
|
-
exists: (path) =>
|
|
39270
|
+
exists: (path) => existsSync23(join26(projectPath, path.slice("res://".length)))
|
|
39170
39271
|
}, { edits: [], mentions: [] });
|
|
39171
39272
|
plan = mentionIgnored(plan, ignoredTexts(projectPath), null, [from]);
|
|
39172
39273
|
const binaries = [];
|
|
39173
39274
|
for (const path of binaryResources(projectPath)) {
|
|
39174
|
-
const bytes = readFileSync22(
|
|
39275
|
+
const bytes = readFileSync22(join26(projectPath, path.slice("res://".length)));
|
|
39175
39276
|
const names = [...moves.keys()].find((moving) => bytes.includes(moving));
|
|
39176
39277
|
if (names !== undefined) {
|
|
39177
39278
|
binaries.push({
|
|
@@ -39190,7 +39291,7 @@ function planFileMove(projectPath, absoluteFrom, absoluteTo) {
|
|
|
39190
39291
|
}));
|
|
39191
39292
|
const left = leftAloneReport(mentions);
|
|
39192
39293
|
const rewrites = rewritesOf(plan, texts, null);
|
|
39193
|
-
const absoluteOf = (path) =>
|
|
39294
|
+
const absoluteOf = (path) => join26(projectPath, path.slice("res://".length));
|
|
39194
39295
|
const rewritten = new Map(rewrites.map((rewrite) => [rewrite.path, rewrite]));
|
|
39195
39296
|
const moved = [...moves].map(([source, target]) => {
|
|
39196
39297
|
const rewrite = rewritten.get(source);
|
|
@@ -39237,19 +39338,19 @@ function applyFileMove(outcome) {
|
|
|
39237
39338
|
}
|
|
39238
39339
|
}
|
|
39239
39340
|
for (const file of outcome.moved) {
|
|
39240
|
-
if (!
|
|
39341
|
+
if (!existsSync23(file.absoluteFrom)) {
|
|
39241
39342
|
throw new Error(`${file.from} is gone from disk; nothing was written`);
|
|
39242
39343
|
}
|
|
39243
39344
|
if (file.text !== null && readFileSync22(file.absoluteFrom, "utf8") !== file.text.before) {
|
|
39244
39345
|
throw new Error(`${file.from} changed on disk while the move was being worked out; nothing was written`);
|
|
39245
39346
|
}
|
|
39246
|
-
if (
|
|
39347
|
+
if (existsSync23(file.absoluteTo) && !sameFile(file.absoluteFrom, file.absoluteTo)) {
|
|
39247
39348
|
throw new Error(`${file.to} already exists; nothing was written`);
|
|
39248
39349
|
}
|
|
39249
39350
|
}
|
|
39250
39351
|
const undo = [];
|
|
39251
39352
|
const rename = (source, target) => {
|
|
39252
|
-
mkdirSync13(
|
|
39353
|
+
mkdirSync13(dirname19(target), { recursive: true });
|
|
39253
39354
|
renameSync4(source, target);
|
|
39254
39355
|
undo.push(() => {
|
|
39255
39356
|
renameSync4(target, source);
|
|
@@ -43435,25 +43536,25 @@ var DEBUG_PREFIX = "debug ";
|
|
|
43435
43536
|
var init_keeper_channel = () => {};
|
|
43436
43537
|
|
|
43437
43538
|
// src/lifecycle-log.ts
|
|
43438
|
-
import { appendFileSync, mkdirSync as mkdirSync14, renameSync as renameSync5, statSync as
|
|
43439
|
-
import { dirname as
|
|
43440
|
-
import
|
|
43539
|
+
import { appendFileSync, mkdirSync as mkdirSync14, renameSync as renameSync5, statSync as statSync12 } from "node:fs";
|
|
43540
|
+
import { dirname as dirname20, join as join27 } from "node:path";
|
|
43541
|
+
import process11 from "node:process";
|
|
43441
43542
|
function lifecycleLogPath() {
|
|
43442
|
-
return
|
|
43543
|
+
return join27(runtimeDirectory(), "servers.log");
|
|
43443
43544
|
}
|
|
43444
43545
|
function writeLifecycle(event, fields = {}) {
|
|
43445
43546
|
const path = lifecycleLogPath();
|
|
43446
43547
|
try {
|
|
43447
|
-
mkdirSync14(
|
|
43448
|
-
if ((
|
|
43548
|
+
mkdirSync14(dirname20(path), { recursive: true });
|
|
43549
|
+
if ((statSync12(path, { throwIfNoEntry: false })?.size ?? 0) > LIFECYCLE_LOG_LIMIT) {
|
|
43449
43550
|
try {
|
|
43450
43551
|
renameSync5(path, `${path}.1`);
|
|
43451
43552
|
} catch {}
|
|
43452
43553
|
}
|
|
43453
43554
|
const line = {
|
|
43454
43555
|
at: new Date().toISOString(),
|
|
43455
|
-
pid:
|
|
43456
|
-
ppid:
|
|
43556
|
+
pid: process11.pid,
|
|
43557
|
+
ppid: process11.ppid,
|
|
43457
43558
|
version: SERVER_VERSION,
|
|
43458
43559
|
event
|
|
43459
43560
|
};
|
|
@@ -43760,14 +43861,14 @@ var init_prompts = __esm(() => {
|
|
|
43760
43861
|
|
|
43761
43862
|
// src/restart-note.ts
|
|
43762
43863
|
import { mkdirSync as mkdirSync15, readFileSync as readFileSync23, rmSync as rmSync13, writeFileSync as writeFileSync17 } from "node:fs";
|
|
43763
|
-
import { dirname as
|
|
43864
|
+
import { dirname as dirname21, join as join28 } from "node:path";
|
|
43764
43865
|
function restartNotePath(projectPath) {
|
|
43765
|
-
return
|
|
43866
|
+
return join28(projectPath, ".godot", "gdharness-restart.json");
|
|
43766
43867
|
}
|
|
43767
43868
|
function noteRestartBegun(note) {
|
|
43768
43869
|
const path = restartNotePath(note.projectPath);
|
|
43769
43870
|
try {
|
|
43770
|
-
mkdirSync15(
|
|
43871
|
+
mkdirSync15(dirname21(path), { recursive: true });
|
|
43771
43872
|
writeFileSync17(path, `${JSON.stringify(note, null, 2)}
|
|
43772
43873
|
`, "utf8");
|
|
43773
43874
|
} catch (error) {
|
|
@@ -43802,23 +43903,23 @@ var init_restart_note = __esm(() => {
|
|
|
43802
43903
|
|
|
43803
43904
|
// src/run-record.ts
|
|
43804
43905
|
import { createHash as createHash5 } from "node:crypto";
|
|
43805
|
-
import { mkdirSync as mkdirSync16, openSync as openSync3, readdirSync as
|
|
43806
|
-
import { basename as
|
|
43906
|
+
import { mkdirSync as mkdirSync16, openSync as openSync3, readdirSync as readdirSync13, readFileSync as readFileSync24, realpathSync as realpathSync6, rmSync as rmSync14, statSync as statSync13 } from "node:fs";
|
|
43907
|
+
import { basename as basename9, dirname as dirname22, join as join29, resolve as resolve7 } from "node:path";
|
|
43807
43908
|
function runsDirectory() {
|
|
43808
|
-
return
|
|
43909
|
+
return join29(runtimeDirectory(), "runs");
|
|
43809
43910
|
}
|
|
43810
43911
|
function realAsFarAsItExists(path) {
|
|
43811
43912
|
const missing = [];
|
|
43812
43913
|
let at = path;
|
|
43813
43914
|
for (;; ) {
|
|
43814
43915
|
try {
|
|
43815
|
-
return
|
|
43916
|
+
return join29(realpathSync6.native(at), ...missing);
|
|
43816
43917
|
} catch {
|
|
43817
|
-
const parent =
|
|
43918
|
+
const parent = dirname22(at);
|
|
43818
43919
|
if (parent === at) {
|
|
43819
43920
|
return path;
|
|
43820
43921
|
}
|
|
43821
|
-
missing.unshift(
|
|
43922
|
+
missing.unshift(basename9(at));
|
|
43822
43923
|
at = parent;
|
|
43823
43924
|
}
|
|
43824
43925
|
}
|
|
@@ -43829,10 +43930,10 @@ function projectKey(projectPath) {
|
|
|
43829
43930
|
return createHash5("sha256").update(folded.replaceAll("\\", "/")).digest("hex").slice(0, 16);
|
|
43830
43931
|
}
|
|
43831
43932
|
function runRecordPath(projectPath, directory = runtimeDirectory()) {
|
|
43832
|
-
return
|
|
43933
|
+
return join29(directory, "runs", `note-${projectKey(projectPath)}.json`);
|
|
43833
43934
|
}
|
|
43834
43935
|
function latestRunPath(servedBy, directory = runtimeDirectory()) {
|
|
43835
|
-
return
|
|
43936
|
+
return join29(directory, "runs", `latest-${projectKey(servedBy)}.json`);
|
|
43836
43937
|
}
|
|
43837
43938
|
function writeLatestRun(servedBy, latest) {
|
|
43838
43939
|
try {
|
|
@@ -43857,17 +43958,20 @@ function readLatestRun(servedBy, directory = runtimeDirectory()) {
|
|
|
43857
43958
|
}
|
|
43858
43959
|
function openTranscript(startedAt) {
|
|
43859
43960
|
mkdirSync16(runsDirectory(), { recursive: true });
|
|
43860
|
-
const path =
|
|
43961
|
+
const path = join29(runsDirectory(), `run-${startedAt}.log`);
|
|
43861
43962
|
return { path, fd: openSync3(path, "a") };
|
|
43862
43963
|
}
|
|
43863
43964
|
function profilePath(startedAt) {
|
|
43864
|
-
return
|
|
43965
|
+
return join29(runsDirectory(), `run-${startedAt}.profile.json`);
|
|
43966
|
+
}
|
|
43967
|
+
function profileMarkPath(startedAt) {
|
|
43968
|
+
return join29(runsDirectory(), `run-${startedAt}.profile-mark.json`);
|
|
43865
43969
|
}
|
|
43866
43970
|
function noteIsAbout(note, playing) {
|
|
43867
43971
|
return note.play !== undefined && playing !== null && note.play.play === playing.play && note.play.editorPid === playing.editorPid;
|
|
43868
43972
|
}
|
|
43869
43973
|
function editorNotePath(projectPath, directory = runtimeDirectory()) {
|
|
43870
|
-
return
|
|
43974
|
+
return join29(directory, "runs", `editor-${projectKey(projectPath)}.json`);
|
|
43871
43975
|
}
|
|
43872
43976
|
function writeEditorRunNote(note) {
|
|
43873
43977
|
mkdirSync16(runsDirectory(), { recursive: true });
|
|
@@ -43878,7 +43982,7 @@ function writeEditorRunNote(note) {
|
|
|
43878
43982
|
}
|
|
43879
43983
|
function readEditorRunNote(projectPath) {
|
|
43880
43984
|
for (const directory of runtimeDirectories()) {
|
|
43881
|
-
const note = editorNoteAt(editorNotePath(projectPath, directory)) ?? editorNoteAt(
|
|
43985
|
+
const note = editorNoteAt(editorNotePath(projectPath, directory)) ?? editorNoteAt(join29(directory, "runs", "editor-run.json"));
|
|
43882
43986
|
if (note !== null && isSameDirectory(note.projectPath, projectPath)) {
|
|
43883
43987
|
return note;
|
|
43884
43988
|
}
|
|
@@ -43912,7 +44016,7 @@ function readRunRecord(projectPath) {
|
|
|
43912
44016
|
if (own !== null) {
|
|
43913
44017
|
return own;
|
|
43914
44018
|
}
|
|
43915
|
-
const shared = recordAt(
|
|
44019
|
+
const shared = recordAt(join29(directory, "runs", SHARED_NOTE));
|
|
43916
44020
|
if (shared !== null && shared.projectPath !== "" && isSameDirectory(shared.projectPath, projectPath)) {
|
|
43917
44021
|
return shared;
|
|
43918
44022
|
}
|
|
@@ -43924,13 +44028,13 @@ function everyRunRecord() {
|
|
|
43924
44028
|
for (const directory of runtimeDirectories()) {
|
|
43925
44029
|
let entries;
|
|
43926
44030
|
try {
|
|
43927
|
-
entries =
|
|
44031
|
+
entries = readdirSync13(join29(directory, "runs"));
|
|
43928
44032
|
} catch {
|
|
43929
44033
|
continue;
|
|
43930
44034
|
}
|
|
43931
44035
|
for (const entry of entries) {
|
|
43932
44036
|
if (entry === SHARED_NOTE || entry.startsWith("note-") && entry.endsWith(".json")) {
|
|
43933
|
-
const record = recordAt(
|
|
44037
|
+
const record = recordAt(join29(directory, "runs", entry));
|
|
43934
44038
|
if (record !== null) {
|
|
43935
44039
|
found.push(record);
|
|
43936
44040
|
}
|
|
@@ -44099,7 +44203,7 @@ function whyNotTheRun(record, running, needed) {
|
|
|
44099
44203
|
if (after > SAME_RUN_WINDOW_MS) {
|
|
44100
44204
|
return `that process started ${Math.round(after / 1000)} seconds after the run had`;
|
|
44101
44205
|
}
|
|
44102
|
-
const engine = record.command === undefined ? null :
|
|
44206
|
+
const engine = record.command === undefined ? null : basename9(record.command);
|
|
44103
44207
|
const said = process.platform === "win32" ? running.text.toLowerCase() : running.text;
|
|
44104
44208
|
const wanted = engine === null || process.platform !== "win32" ? engine : engine.toLowerCase();
|
|
44105
44209
|
if (wanted !== null && !said.includes(wanted)) {
|
|
@@ -44138,7 +44242,7 @@ function judgeAnnouncedGame(running, projectPath, announcedAt) {
|
|
|
44138
44242
|
}
|
|
44139
44243
|
function clearRunRecord(projectPath) {
|
|
44140
44244
|
for (const directory of runtimeDirectories()) {
|
|
44141
|
-
const shared =
|
|
44245
|
+
const shared = join29(directory, "runs", SHARED_NOTE);
|
|
44142
44246
|
const sharedRecord = recordAt(shared);
|
|
44143
44247
|
const ours = sharedRecord !== null && sharedRecord.projectPath !== "" && isSameDirectory(sharedRecord.projectPath, projectPath);
|
|
44144
44248
|
for (const path of [runRecordPath(projectPath, directory), ...ours ? [shared] : []]) {
|
|
@@ -44150,7 +44254,7 @@ function clearRunRecord(projectPath) {
|
|
|
44150
44254
|
}
|
|
44151
44255
|
function ageMs(path, now) {
|
|
44152
44256
|
try {
|
|
44153
|
-
return now -
|
|
44257
|
+
return now - statSync13(path).mtimeMs;
|
|
44154
44258
|
} catch {
|
|
44155
44259
|
return 0;
|
|
44156
44260
|
}
|
|
@@ -44159,20 +44263,21 @@ function sweepTranscripts(keepMs = 24 * 60 * 60 * 1000, now = Date.now()) {
|
|
|
44159
44263
|
const directory = runsDirectory();
|
|
44160
44264
|
const kept = new Set(everyRunRecord().flatMap((record) => [
|
|
44161
44265
|
record.transcript,
|
|
44266
|
+
profileMarkPath(record.startedAt),
|
|
44162
44267
|
...record.profile === undefined ? [] : [record.profile]
|
|
44163
44268
|
]));
|
|
44164
44269
|
let entries;
|
|
44165
44270
|
try {
|
|
44166
|
-
entries =
|
|
44271
|
+
entries = readdirSync13(directory);
|
|
44167
44272
|
} catch {
|
|
44168
44273
|
return;
|
|
44169
44274
|
}
|
|
44170
44275
|
for (const entry of entries) {
|
|
44171
|
-
const transcript = entry.startsWith("run-") && (entry.endsWith(".log") || entry.endsWith(".profile.json"));
|
|
44276
|
+
const transcript = entry.startsWith("run-") && (entry.endsWith(".log") || entry.endsWith(".profile.json") || entry.endsWith(".profile-mark.json"));
|
|
44172
44277
|
if (!transcript && !entry.endsWith(".partial")) {
|
|
44173
44278
|
continue;
|
|
44174
44279
|
}
|
|
44175
|
-
const path =
|
|
44280
|
+
const path = join29(directory, entry);
|
|
44176
44281
|
if (kept.has(path) || ageMs(path, now) < keepMs) {
|
|
44177
44282
|
continue;
|
|
44178
44283
|
}
|
|
@@ -44193,7 +44298,7 @@ var init_run_record = __esm(() => {
|
|
|
44193
44298
|
});
|
|
44194
44299
|
|
|
44195
44300
|
// src/script-origins.ts
|
|
44196
|
-
import { join as
|
|
44301
|
+
import { join as join30 } from "node:path";
|
|
44197
44302
|
function unresolvedClassIn(message) {
|
|
44198
44303
|
return UNRESOLVED.exec(message)?.[1] ?? null;
|
|
44199
44304
|
}
|
|
@@ -44246,7 +44351,7 @@ function checkWithEngine(godotPath, projectPath) {
|
|
|
44246
44351
|
const ran = await runEngine(godotPath, [
|
|
44247
44352
|
"--headless",
|
|
44248
44353
|
"--log-file",
|
|
44249
|
-
|
|
44354
|
+
join30(logDir, "engine.log"),
|
|
44250
44355
|
"--path",
|
|
44251
44356
|
projectPath,
|
|
44252
44357
|
"--check-only",
|
|
@@ -44339,6 +44444,52 @@ function isCalibration(signature) {
|
|
|
44339
44444
|
const parts = signatureParts(signature);
|
|
44340
44445
|
return parts.script.endsWith(RUNTIME_SCRIPT) && (parts.function === PROFILER_PROBE || parts.function === PROFILER_CALIBRATOR);
|
|
44341
44446
|
}
|
|
44447
|
+
function writeProfileMark(path, mark) {
|
|
44448
|
+
return writeWhole(path, JSON.stringify(mark));
|
|
44449
|
+
}
|
|
44450
|
+
function readProfileMark(path) {
|
|
44451
|
+
try {
|
|
44452
|
+
const parsed = JSON.parse(readFileSync25(path, "utf8"));
|
|
44453
|
+
return typeof parsed === "object" && typeof parsed.totals === "object" ? parsed : null;
|
|
44454
|
+
} catch {
|
|
44455
|
+
return null;
|
|
44456
|
+
}
|
|
44457
|
+
}
|
|
44458
|
+
function totalsSince(now, mark) {
|
|
44459
|
+
if (now.frames < mark.frames || now.startedAt !== mark.startedAt) {
|
|
44460
|
+
return null;
|
|
44461
|
+
}
|
|
44462
|
+
let shrunk = 0;
|
|
44463
|
+
const functions = {};
|
|
44464
|
+
for (const [signature, sum] of Object.entries(now.functions)) {
|
|
44465
|
+
const before = mark.functions[signature];
|
|
44466
|
+
if (isCalibration(signature) || before === undefined) {
|
|
44467
|
+
functions[signature] = sum;
|
|
44468
|
+
continue;
|
|
44469
|
+
}
|
|
44470
|
+
const added = {
|
|
44471
|
+
calls: sum.calls - before.calls,
|
|
44472
|
+
selfSeconds: sum.selfSeconds - before.selfSeconds,
|
|
44473
|
+
totalSeconds: sum.totalSeconds - before.totalSeconds
|
|
44474
|
+
};
|
|
44475
|
+
if (added.calls < 0 || added.selfSeconds < 0 || added.totalSeconds < 0) {
|
|
44476
|
+
shrunk += 1;
|
|
44477
|
+
} else if (added.calls > 0) {
|
|
44478
|
+
functions[signature] = added;
|
|
44479
|
+
}
|
|
44480
|
+
}
|
|
44481
|
+
shrunk += Object.keys(mark.functions).filter((signature) => now.functions[signature] === undefined && (mark.functions[signature]?.calls ?? 0) > 0).length;
|
|
44482
|
+
return {
|
|
44483
|
+
totals: {
|
|
44484
|
+
...now,
|
|
44485
|
+
frames: now.frames - mark.frames,
|
|
44486
|
+
unreadable: Math.max(0, now.unreadable - mark.unreadable),
|
|
44487
|
+
...now.cappedFrames === undefined ? {} : { cappedFrames: Math.max(0, now.cappedFrames - (mark.cappedFrames ?? 0)) },
|
|
44488
|
+
functions
|
|
44489
|
+
},
|
|
44490
|
+
shrunk
|
|
44491
|
+
};
|
|
44492
|
+
}
|
|
44342
44493
|
function perCallCostOf(totals) {
|
|
44343
44494
|
for (const [signature, sum] of Object.entries(totals.functions)) {
|
|
44344
44495
|
const parts = signatureParts(signature);
|
|
@@ -44471,18 +44622,18 @@ import {
|
|
|
44471
44622
|
closeSync as closeSync3,
|
|
44472
44623
|
constants,
|
|
44473
44624
|
copyFileSync,
|
|
44474
|
-
existsSync as
|
|
44625
|
+
existsSync as existsSync24,
|
|
44475
44626
|
fstatSync,
|
|
44476
44627
|
mkdirSync as mkdirSync17,
|
|
44477
44628
|
openSync as openSync4,
|
|
44478
|
-
readdirSync as
|
|
44629
|
+
readdirSync as readdirSync14,
|
|
44479
44630
|
readFileSync as readFileSync26,
|
|
44480
44631
|
readSync as readSync2,
|
|
44481
44632
|
rmdirSync as rmdirSync2,
|
|
44482
|
-
statSync as
|
|
44633
|
+
statSync as statSync14,
|
|
44483
44634
|
writeFileSync as writeFileSync18
|
|
44484
44635
|
} from "node:fs";
|
|
44485
|
-
import { basename as
|
|
44636
|
+
import { basename as basename10, dirname as dirname23, extname as extname4, isAbsolute as isAbsolute5, join as join31, normalize as normalize2, relative as relative6 } from "node:path";
|
|
44486
44637
|
import { setTimeout as delay5 } from "node:timers/promises";
|
|
44487
44638
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
44488
44639
|
function editorRestartTimeoutMs() {
|
|
@@ -44512,22 +44663,22 @@ function writtenFileRefusal(path, rule) {
|
|
|
44512
44663
|
if (/^(res|user):\/\//.test(path)) {
|
|
44513
44664
|
return `${named} ${path} is a Godot path; ${what} is saved outside the project, at an absolute path on this machine.`;
|
|
44514
44665
|
}
|
|
44515
|
-
if (!
|
|
44666
|
+
if (!isAbsolute5(path)) {
|
|
44516
44667
|
return `${named} ${path} is not absolute; name the whole path, so where it is written does not depend on this server's directory.`;
|
|
44517
44668
|
}
|
|
44518
44669
|
if (!rule.extensions.includes(extname4(path).toLowerCase())) {
|
|
44519
44670
|
return `${named} ${path} does not end in ${rule.extensions.join(" or ")}, which is what ${what} is.`;
|
|
44520
44671
|
}
|
|
44521
|
-
const directory = realPathOr(
|
|
44522
|
-
if (!
|
|
44523
|
-
return `${named}'s directory ${
|
|
44672
|
+
const directory = realPathOr(dirname23(path));
|
|
44673
|
+
if (!existsSync24(directory)) {
|
|
44674
|
+
return `${named}'s directory ${dirname23(path)} does not exist; ${what} creates no directories.`;
|
|
44524
44675
|
}
|
|
44525
|
-
const where =
|
|
44676
|
+
const where = join31(directory, basename10(path));
|
|
44526
44677
|
const inside = rule.projects.find((project) => project !== "" && isWithinRoot(realPathOr(project), where));
|
|
44527
44678
|
if (inside !== undefined) {
|
|
44528
44679
|
return `${named} ${path} is inside the project at ${inside}; ${what} is saved outside it, so it never becomes part of the game.`;
|
|
44529
44680
|
}
|
|
44530
|
-
const taken = rule.writes(where).find((file) =>
|
|
44681
|
+
const taken = rule.writes(where).find((file) => existsSync24(file));
|
|
44531
44682
|
if (taken !== undefined) {
|
|
44532
44683
|
return taken === where ? `${named} ${path} already exists; ${what} is not written over a file. Name a new one.` : `${named} ${path} would write ${taken}, which already exists; ${what} is not written over a file. Name a new one.`;
|
|
44533
44684
|
}
|
|
@@ -44636,7 +44787,7 @@ function exited(run) {
|
|
|
44636
44787
|
return run.exitCode !== null || run.exitSignal !== null;
|
|
44637
44788
|
}
|
|
44638
44789
|
function howItExited(run) {
|
|
44639
|
-
return run.exitSignal === null ?
|
|
44790
|
+
return run.exitSignal === null ? exitCodeText(run.exitCode) : `ended by ${run.exitSignal}, with no exit code`;
|
|
44640
44791
|
}
|
|
44641
44792
|
function errorsUnread(run) {
|
|
44642
44793
|
return run.throughEditor && typeof run.errorReport !== "string";
|
|
@@ -44744,7 +44895,7 @@ function endedWithoutACode(run) {
|
|
|
44744
44895
|
function ourRunEnded(run, now) {
|
|
44745
44896
|
const which = `${run.throughEditor ? "played in the editor" : "started"}${run.pid === null ? "" : ` (pid ${run.pid})`}`;
|
|
44746
44897
|
const when = run.endedAt === undefined ? "" : ` ${Math.max(0, Math.round((now - run.endedAt) / 1000))}s ago`;
|
|
44747
|
-
const how = run.endedThrough === "close" ? "this server asked it to close" : run.endedThrough === "keeper" ? "this server ended it through the keeper holding it" : run.endedThrough === "signal" ? "this server signalled it" : run.endedThrough === "editor" ? "this server asked the editor to stop it" : run.exitCode !== null ? `it exited by itself with
|
|
44898
|
+
const how = run.endedThrough === "close" ? "this server asked it to close" : run.endedThrough === "keeper" ? "this server ended it through the keeper holding it" : run.endedThrough === "signal" ? "this server signalled it" : run.endedThrough === "editor" ? "this server asked the editor to stop it" : run.exitCode !== null ? `it exited by itself with ${exitCodeText(run.exitCode)}` : run.exitSignal === null ? "it is no longer running" : `it was ended by ${run.exitSignal}`;
|
|
44748
44899
|
return `No game with the runtime addon is running: the game this server ${which} ended${when}, and ${how}. editor_output has what it printed, and editor_run start runs it again.`;
|
|
44749
44900
|
}
|
|
44750
44901
|
function stopVerdict(stop) {
|
|
@@ -45063,7 +45214,7 @@ function notRunNote(notRun, failFast) {
|
|
|
45063
45214
|
function exportPresetRefusal(projectPath, preset) {
|
|
45064
45215
|
let text;
|
|
45065
45216
|
try {
|
|
45066
|
-
text = readFileSync26(
|
|
45217
|
+
text = readFileSync26(join31(projectPath, "export_presets.cfg"), "utf8");
|
|
45067
45218
|
} catch {
|
|
45068
45219
|
return `This project has no export_presets.cfg, so it has no preset '${preset}' to export with. project_export add_preset writes one, as the editor's Project > Export does.`;
|
|
45069
45220
|
}
|
|
@@ -45085,7 +45236,7 @@ function exportPresetRefusal(projectPath, preset) {
|
|
|
45085
45236
|
}
|
|
45086
45237
|
return names.length === 0 ? `export_presets.cfg names no preset, so there is no '${preset}' to export with. project_export add_preset adds one.` : `This project has no export preset named '${preset}'. Its presets are ${names.map((name) => `'${name}'`).join(", ")}.`;
|
|
45087
45238
|
}
|
|
45088
|
-
function exportAnswer(ending, asked, before, after, beside = []) {
|
|
45239
|
+
function exportAnswer(ending, asked, before, after, beside = [], docCacheNote = null) {
|
|
45089
45240
|
const { log } = ending;
|
|
45090
45241
|
const written = after !== null && after !== before;
|
|
45091
45242
|
const notCopied = librariesNotCopied(log.all);
|
|
@@ -45102,6 +45253,7 @@ function exportAnswer(ending, asked, before, after, beside = []) {
|
|
|
45102
45253
|
librariesNotLoaded: notCopied,
|
|
45103
45254
|
extensionNote: `The export could not load ${notCopied.join(" and ")}: on Windows an engine running as the editor, which an export is, loads a GDExtension from a ~ copy beside the library, and another engine on this project held that copy: it could not be moved aside before the export, or another engine made a new one in between. Export again once that engine has loaded or exited.`
|
|
45104
45255
|
},
|
|
45256
|
+
...docCacheNote === null ? {} : { docCacheNote },
|
|
45105
45257
|
...foldedEntriesAnswer(log)
|
|
45106
45258
|
};
|
|
45107
45259
|
if (verdict.exported) {
|
|
@@ -45126,9 +45278,9 @@ function writtenBeside(before, after, output, within) {
|
|
|
45126
45278
|
function filesIn(folder) {
|
|
45127
45279
|
const found = new Map;
|
|
45128
45280
|
try {
|
|
45129
|
-
for (const entry of
|
|
45281
|
+
for (const entry of readdirSync14(folder, { withFileTypes: true })) {
|
|
45130
45282
|
if (entry.isFile()) {
|
|
45131
|
-
found.set(entry.name,
|
|
45283
|
+
found.set(entry.name, statSync14(join31(folder, entry.name)).mtimeMs);
|
|
45132
45284
|
}
|
|
45133
45285
|
}
|
|
45134
45286
|
} catch {}
|
|
@@ -45158,7 +45310,7 @@ function unsavedInEditorNote(scripts) {
|
|
|
45158
45310
|
return `${named} ${one ? "is" : "are"} open in the editor's script ${one ? "tab" : "tabs"} with unsaved edits, and saving ${one ? "that tab" : "them"} there writes ${one ? "its" : "their"} text over what was written here: close ${one ? "it" : "them"} without saving, or save first and edit again.`;
|
|
45159
45311
|
}
|
|
45160
45312
|
function editorAddonMissing(projectPath) {
|
|
45161
|
-
if (!
|
|
45313
|
+
if (!existsSync24(join31(projectPath, "project.godot"))) {
|
|
45162
45314
|
return null;
|
|
45163
45315
|
}
|
|
45164
45316
|
const report = inspectProject(projectPath);
|
|
@@ -45396,7 +45548,7 @@ function profileFrameFunctions(projectPath) {
|
|
|
45396
45548
|
let projectGodot = null;
|
|
45397
45549
|
if (projectPath !== null) {
|
|
45398
45550
|
try {
|
|
45399
|
-
projectGodot = readFileSync26(
|
|
45551
|
+
projectGodot = readFileSync26(join31(projectPath, "project.godot"), "utf8");
|
|
45400
45552
|
} catch {}
|
|
45401
45553
|
}
|
|
45402
45554
|
return frameFunctionsFor(queuedMessagesOf(projectGodot));
|
|
@@ -45570,7 +45722,7 @@ function foldedEntriesAnswer(log, floor = "warning") {
|
|
|
45570
45722
|
}
|
|
45571
45723
|
function gdUnitFailsOnScriptErrors(projectPath) {
|
|
45572
45724
|
try {
|
|
45573
|
-
return settingKeys(readFileSync26(
|
|
45725
|
+
return settingKeys(readFileSync26(join31(projectPath, "project.godot"), "utf8")).get("gdunit4/report/godot/script_error") !== false;
|
|
45574
45726
|
} catch {
|
|
45575
45727
|
return true;
|
|
45576
45728
|
}
|
|
@@ -45585,13 +45737,13 @@ function uncaughtNote(uncaught) {
|
|
|
45585
45737
|
}
|
|
45586
45738
|
function elsewhereIn(projectPath, asked) {
|
|
45587
45739
|
const wanted = asked.replace(/^res:\/\//, "").replace(/\/+$/, "");
|
|
45588
|
-
if (wanted === "" ||
|
|
45740
|
+
if (wanted === "" || existsSync24(join31(projectPath, wanted))) {
|
|
45589
45741
|
return "";
|
|
45590
45742
|
}
|
|
45591
45743
|
const root = (wanted.split("/")[0] ?? "").toLowerCase();
|
|
45592
45744
|
let near = [];
|
|
45593
45745
|
try {
|
|
45594
|
-
near =
|
|
45746
|
+
near = readdirSync14(projectPath, { withFileTypes: true }).filter((entry) => entry.isDirectory() && entry.name.toLowerCase() !== root).map((entry) => entry.name).filter((name) => name.toLowerCase().startsWith("test") || root.startsWith(name.toLowerCase()));
|
|
45595
45747
|
} catch {
|
|
45596
45748
|
return "";
|
|
45597
45749
|
}
|
|
@@ -45682,23 +45834,23 @@ function engineChecked(why) {
|
|
|
45682
45834
|
}
|
|
45683
45835
|
function isDirectory(path) {
|
|
45684
45836
|
try {
|
|
45685
|
-
return
|
|
45837
|
+
return statSync14(path).isDirectory();
|
|
45686
45838
|
} catch {
|
|
45687
45839
|
return false;
|
|
45688
45840
|
}
|
|
45689
45841
|
}
|
|
45690
45842
|
function scriptsUnder(directory, prefix) {
|
|
45691
|
-
if (
|
|
45843
|
+
if (existsSync24(join31(directory, ".gdignore"))) {
|
|
45692
45844
|
return [];
|
|
45693
45845
|
}
|
|
45694
45846
|
const found = [];
|
|
45695
|
-
for (const entry of
|
|
45847
|
+
for (const entry of readdirSync14(directory, { withFileTypes: true }).sort((a, b) => a.name < b.name ? -1 : 1)) {
|
|
45696
45848
|
if (entry.name.startsWith(".")) {
|
|
45697
45849
|
continue;
|
|
45698
45850
|
}
|
|
45699
45851
|
const path = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
|
|
45700
45852
|
if (entry.isDirectory()) {
|
|
45701
|
-
found.push(...scriptsUnder(
|
|
45853
|
+
found.push(...scriptsUnder(join31(directory, entry.name), path));
|
|
45702
45854
|
} else if (entry.isFile() && entry.name.endsWith(".gd")) {
|
|
45703
45855
|
found.push(path);
|
|
45704
45856
|
}
|
|
@@ -45714,7 +45866,7 @@ function runtimeAddonAnnouncesEditors(projectPath) {
|
|
|
45714
45866
|
return false;
|
|
45715
45867
|
}
|
|
45716
45868
|
try {
|
|
45717
|
-
return readFileSync26(
|
|
45869
|
+
return readFileSync26(join31(projectPath, RUNTIME_AUTOLOAD.path), "utf8").includes('"editor_pid"');
|
|
45718
45870
|
} catch {
|
|
45719
45871
|
return false;
|
|
45720
45872
|
}
|
|
@@ -45739,7 +45891,7 @@ function needsItsOwnProcess(asked) {
|
|
|
45739
45891
|
class GodotServer {
|
|
45740
45892
|
stdio = null;
|
|
45741
45893
|
locator = new GodotLocator;
|
|
45742
|
-
operationsScript =
|
|
45894
|
+
operationsScript = join31(__dirname2, "godot", "operations", "godot_operations.gd");
|
|
45743
45895
|
godotBridge;
|
|
45744
45896
|
tools = buildToolDefinitions();
|
|
45745
45897
|
activeProcess = null;
|
|
@@ -46000,14 +46152,14 @@ class GodotServer {
|
|
|
46000
46152
|
}
|
|
46001
46153
|
noteTheStart() {
|
|
46002
46154
|
const cwd = process.cwd();
|
|
46003
|
-
const mcpJson =
|
|
46155
|
+
const mcpJson = join31(cwd, ".mcp.json");
|
|
46004
46156
|
const announced = this.ownProject === null ? null : readAnnouncement(announcementPath(this.ownProject));
|
|
46005
46157
|
const predecessor = announced !== null && announced.pid !== process.pid && alive(announced.pid) ? announced.pid : null;
|
|
46006
46158
|
writeLifecycle("started", {
|
|
46007
46159
|
argv: process.argv.slice(1),
|
|
46008
46160
|
cwd,
|
|
46009
46161
|
project: this.ownProject,
|
|
46010
|
-
mcpJson:
|
|
46162
|
+
mcpJson: existsSync24(mcpJson) ? mcpJson : null
|
|
46011
46163
|
});
|
|
46012
46164
|
if (predecessor !== null) {
|
|
46013
46165
|
writeLifecycle("replaced", { project: this.ownProject, predecessor });
|
|
@@ -46148,7 +46300,7 @@ class GodotServer {
|
|
|
46148
46300
|
return checked.response;
|
|
46149
46301
|
}
|
|
46150
46302
|
const named = args["projectPath"];
|
|
46151
|
-
if (typeof named === "string" &&
|
|
46303
|
+
if (typeof named === "string" && existsSync24(join31(named, "project.godot"))) {
|
|
46152
46304
|
this.lastProjectPath = named;
|
|
46153
46305
|
}
|
|
46154
46306
|
this.updates.refresh();
|
|
@@ -46184,7 +46336,7 @@ class GodotServer {
|
|
|
46184
46336
|
let notSaved;
|
|
46185
46337
|
try {
|
|
46186
46338
|
savedAt = answer.content.map((item, index) => {
|
|
46187
|
-
const file =
|
|
46339
|
+
const file = join31(directory, item.type === "text" ? `${stem}-${index}.txt` : `${stem}-${index}.${item.mimeType.split("/")[1] ?? "bin"}`);
|
|
46188
46340
|
writeFileSync18(file, item.type === "text" ? item.text : Buffer.from(item.data, "base64"));
|
|
46189
46341
|
return file;
|
|
46190
46342
|
});
|
|
@@ -46802,8 +46954,8 @@ class GodotServer {
|
|
|
46802
46954
|
if (path === undefined) {
|
|
46803
46955
|
return { ok: false, response: this.createErrorResponse("projectPath is required.") };
|
|
46804
46956
|
}
|
|
46805
|
-
const file =
|
|
46806
|
-
if (!
|
|
46957
|
+
const file = join31(path, "project.godot");
|
|
46958
|
+
if (!existsSync24(file)) {
|
|
46807
46959
|
return {
|
|
46808
46960
|
ok: false,
|
|
46809
46961
|
response: this.createErrorResponse(`Not a Godot project: ${path}`, [
|
|
@@ -46849,7 +47001,7 @@ class GodotServer {
|
|
|
46849
47001
|
}
|
|
46850
47002
|
runEnvironment(args) {
|
|
46851
47003
|
const savesIn = readNonEmptyString(args, "savesIn");
|
|
46852
|
-
if (savesIn !== undefined && !
|
|
47004
|
+
if (savesIn !== undefined && !isAbsolute5(savesIn)) {
|
|
46853
47005
|
return {
|
|
46854
47006
|
ok: false,
|
|
46855
47007
|
response: this.createErrorResponse(`savesIn must be an absolute path, not "${savesIn}".`, [
|
|
@@ -47108,7 +47260,8 @@ class GodotServer {
|
|
|
47108
47260
|
uidsCreated: given,
|
|
47109
47261
|
stillWithoutUid: after,
|
|
47110
47262
|
note: uidsLeftNote(after.length),
|
|
47111
|
-
...imported.extensionNote === undefined ? {} : { extensionNote: imported.extensionNote }
|
|
47263
|
+
...imported.extensionNote === undefined ? {} : { extensionNote: imported.extensionNote },
|
|
47264
|
+
...imported.docCacheNote === undefined ? {} : { docCacheNote: imported.docCacheNote }
|
|
47112
47265
|
}
|
|
47113
47266
|
}, scanned));
|
|
47114
47267
|
}
|
|
@@ -47347,6 +47500,9 @@ class GodotServer {
|
|
|
47347
47500
|
if (imported.extensionNote !== undefined) {
|
|
47348
47501
|
extra["extensionNote"] = imported.extensionNote;
|
|
47349
47502
|
}
|
|
47503
|
+
if (imported.docCacheNote !== undefined) {
|
|
47504
|
+
extra["docCacheNote"] = imported.docCacheNote;
|
|
47505
|
+
}
|
|
47350
47506
|
}
|
|
47351
47507
|
const after = await this.operation("get_import_status", statusAsk, projectPath);
|
|
47352
47508
|
if (!after.ok) {
|
|
@@ -47463,7 +47619,7 @@ class GodotServer {
|
|
|
47463
47619
|
const name = application["config/name"];
|
|
47464
47620
|
const mainScene = application["run/main_scene"];
|
|
47465
47621
|
const info = {
|
|
47466
|
-
name: typeof name === "string" && name !== "" ? name :
|
|
47622
|
+
name: typeof name === "string" && name !== "" ? name : basename10(project.value.path),
|
|
47467
47623
|
path: project.value.path,
|
|
47468
47624
|
mainScene: typeof mainScene === "string" && mainScene !== "" ? mainScene : null,
|
|
47469
47625
|
godotVersion: await this.godotVersion(engine.value),
|
|
@@ -47546,18 +47702,18 @@ class GodotServer {
|
|
|
47546
47702
|
if (!engine.ok) {
|
|
47547
47703
|
return engine.response;
|
|
47548
47704
|
}
|
|
47549
|
-
const folder =
|
|
47705
|
+
const folder = dirname23(output.absolutePath);
|
|
47550
47706
|
try {
|
|
47551
47707
|
mkdirSync17(folder, { recursive: true });
|
|
47552
47708
|
} catch (error) {
|
|
47553
|
-
return this.createErrorResponse(`Cannot create ${
|
|
47709
|
+
return this.createErrorResponse(`Cannot create ${dirname23(output.relativePath)} to export into: ${errorMessage(error)}`);
|
|
47554
47710
|
}
|
|
47555
47711
|
const debug = readBoolean(args, "debug") ?? false;
|
|
47556
47712
|
const exportLogs = scratchDirectory("export");
|
|
47557
47713
|
const exportArgs = [
|
|
47558
47714
|
"--headless",
|
|
47559
47715
|
"--log-file",
|
|
47560
|
-
|
|
47716
|
+
join31(exportLogs, "engine.log"),
|
|
47561
47717
|
"--path",
|
|
47562
47718
|
project.value.path,
|
|
47563
47719
|
debug ? "--export-debug" : "--export-release",
|
|
@@ -47565,24 +47721,37 @@ class GodotServer {
|
|
|
47565
47721
|
output.absolutePath
|
|
47566
47722
|
];
|
|
47567
47723
|
this.logDebug(`Exporting: ${engine.value} ${exportArgs.join(" ")}`);
|
|
47568
|
-
const writtenAt = () =>
|
|
47569
|
-
const
|
|
47570
|
-
const
|
|
47571
|
-
|
|
47572
|
-
|
|
47573
|
-
|
|
47574
|
-
|
|
47575
|
-
|
|
47724
|
+
const writtenAt = () => existsSync24(output.absolutePath) ? statSync14(output.absolutePath).mtimeMs : null;
|
|
47725
|
+
const caches = editorCacheDirectory(engine.value);
|
|
47726
|
+
const exportOnce = async () => {
|
|
47727
|
+
const before = writtenAt();
|
|
47728
|
+
const listedBefore = filesIn(folder);
|
|
47729
|
+
const cachesBefore = docCachesWritten(caches);
|
|
47730
|
+
const freed = freeLibraryCopies(project.value.path);
|
|
47731
|
+
if (freed.setAside.length > 0 || freed.swept.length > 0) {
|
|
47732
|
+
this.logDebug(`Before the export: set aside ${freed.setAside.join(", ")}; swept ${freed.swept.join(", ")}`);
|
|
47733
|
+
}
|
|
47734
|
+
const ending = await runEngine(engine.value, exportArgs, {
|
|
47735
|
+
timeout: { ms: 300000, said: "five minutes" }
|
|
47736
|
+
});
|
|
47737
|
+
const crash = docCacheCrash(ending, cachesBefore, docCachesWritten(caches), "export");
|
|
47738
|
+
return { ending, before, listedBefore, crash };
|
|
47739
|
+
};
|
|
47740
|
+
let run;
|
|
47741
|
+
let docCacheNote;
|
|
47576
47742
|
try {
|
|
47577
|
-
|
|
47743
|
+
const first = await exportOnce();
|
|
47744
|
+
docCacheNote = first.crash;
|
|
47745
|
+
run = first.crash === null ? first : await exportOnce();
|
|
47578
47746
|
} finally {
|
|
47579
47747
|
discard(exportLogs);
|
|
47580
47748
|
}
|
|
47749
|
+
const { ending, before, listedBefore } = run;
|
|
47581
47750
|
if (ending.exitSignal === null && ending.exitCode === null) {
|
|
47582
47751
|
return this.createErrorResponse(`Export could not be run: ${ending.failure ?? "the engine gave no exit status"}`);
|
|
47583
47752
|
}
|
|
47584
|
-
const beside = writtenBeside(listedBefore, filesIn(folder),
|
|
47585
|
-
return exportAnswer(ending, { preset, outputPath: output.relativePath, debug }, before, writtenAt(), beside);
|
|
47753
|
+
const beside = writtenBeside(listedBefore, filesIn(folder), basename10(output.absolutePath), dirname23(output.relativePath).replaceAll("\\", "/"));
|
|
47754
|
+
return exportAnswer(ending, { preset, outputPath: output.relativePath, debug }, before, writtenAt(), beside, docCacheNote);
|
|
47586
47755
|
}
|
|
47587
47756
|
reportTestProgress(log) {
|
|
47588
47757
|
const progress = callProgress();
|
|
@@ -47635,10 +47804,10 @@ class GodotServer {
|
|
|
47635
47804
|
if (movie !== null) {
|
|
47636
47805
|
return this.createErrorResponse(movie);
|
|
47637
47806
|
}
|
|
47638
|
-
const missing = paths.value.filter((path) => !
|
|
47807
|
+
const missing = paths.value.filter((path) => !existsSync24(join31(project.value.path, path.slice("res://".length))));
|
|
47639
47808
|
const asked = paths.value.filter((path) => !missing.includes(path));
|
|
47640
47809
|
const runner = "addons/gdUnit4/bin/GdUnitCmdTool.gd";
|
|
47641
|
-
const noRunner = !
|
|
47810
|
+
const noRunner = !existsSync24(join31(project.value.path, runner));
|
|
47642
47811
|
if (asked.length === 0) {
|
|
47643
47812
|
const verdict = `nothing at ${missing.join(" and ")}`;
|
|
47644
47813
|
const andNoRunner = noRunner ? `, and gdUnit4 is not installed in this project: no ${runner}` : "";
|
|
@@ -47729,21 +47898,21 @@ class GodotServer {
|
|
|
47729
47898
|
await untilGone([run.process.pid]);
|
|
47730
47899
|
}
|
|
47731
47900
|
await run.settled;
|
|
47732
|
-
const reportsDir =
|
|
47901
|
+
const reportsDir = join31(project.value.path, ".godot", ...ours.split("/"));
|
|
47733
47902
|
let report = null;
|
|
47734
47903
|
let reportProblem = null;
|
|
47735
47904
|
try {
|
|
47736
|
-
const written =
|
|
47905
|
+
const written = existsSync24(reportsDir) ? readdirSync14(reportsDir).filter((name) => name.startsWith("report_")).sort((a, b) => Number(a.slice("report_".length)) - Number(b.slice("report_".length))) : [];
|
|
47737
47906
|
const newest = written.at(-1);
|
|
47738
47907
|
if (newest !== undefined) {
|
|
47739
|
-
report = parseJUnit(readFileSync26(
|
|
47908
|
+
report = parseJUnit(readFileSync26(join31(reportsDir, newest, "results.xml"), "utf8"));
|
|
47740
47909
|
}
|
|
47741
47910
|
} catch (error) {
|
|
47742
47911
|
reportProblem = errorMessage(error);
|
|
47743
47912
|
} finally {
|
|
47744
47913
|
discard(reportsDir, userData);
|
|
47745
47914
|
try {
|
|
47746
|
-
rmdirSync2(
|
|
47915
|
+
rmdirSync2(dirname23(reportsDir));
|
|
47747
47916
|
} catch {}
|
|
47748
47917
|
}
|
|
47749
47918
|
const engineEntries = foldedRepeats(run.log.select({
|
|
@@ -47775,7 +47944,7 @@ class GodotServer {
|
|
|
47775
47944
|
const declaring = printedErrors.some((error) => unresolvedClassIn(error.message) !== null) ? await originsOf(printedErrors, declaredClasses(project.value.path), checkWithEngine(engine.value, project.value.path)) : { origins: [], unconfirmed: [] };
|
|
47776
47945
|
const scriptErrors = [...declaring.origins, ...printedErrors];
|
|
47777
47946
|
const brokenScripts = new Set(scriptErrors.map((error) => error.path)).size;
|
|
47778
|
-
const ranAs = cutShort !== null ? cutShort.verdict : nothingRan ?? (brokenScripts > 0 ? `script errors in ${brokenScripts === 1 ? "one script" : `${brokenScripts} scripts`}, so no suite ran` : undefined) ?? (uncaught.length > 0 ? `${uncaught.length === 1 ? "one script error" : `${uncaught.length} script errors`} that failed no case` : undefined) ?? (exitCode === null ? undefined : verdicts[exitCode]) ??
|
|
47947
|
+
const ranAs = cutShort !== null ? cutShort.verdict : nothingRan ?? (brokenScripts > 0 ? `script errors in ${brokenScripts === 1 ? "one script" : `${brokenScripts} scripts`}, so no suite ran` : undefined) ?? (uncaught.length > 0 ? `${uncaught.length === 1 ? "one script error" : `${uncaught.length} script errors`} that failed no case` : undefined) ?? (exitCode === null ? undefined : verdicts[exitCode]) ?? howItExited(run);
|
|
47779
47948
|
const nothingAt = missing.join(" and ");
|
|
47780
47949
|
const verdict = missing.length === 0 ? ranAs : `${ranAs}; nothing at ${nothingAt}`;
|
|
47781
47950
|
const missingNote = missing.length === 0 ? undefined : `Nothing at ${nothingAt}, so ${missing.length === 1 ? "it was" : "they were"} left out and only ${asked.join(", ")} ran; a run of fewer paths than were asked for is not called a pass.${elsewhereIn(project.value.path, missing[0] ?? "")}`;
|
|
@@ -48102,7 +48271,7 @@ class GodotServer {
|
|
|
48102
48271
|
const cached = cachedClasses(projectPath);
|
|
48103
48272
|
const sourceOf = (resourcePath) => {
|
|
48104
48273
|
const contained = resolveWithinProject(projectPath, resourcePath);
|
|
48105
|
-
if (!contained.ok || !
|
|
48274
|
+
if (!contained.ok || !existsSync24(contained.absolutePath)) {
|
|
48106
48275
|
return null;
|
|
48107
48276
|
}
|
|
48108
48277
|
try {
|
|
@@ -48196,7 +48365,7 @@ class GodotServer {
|
|
|
48196
48365
|
}
|
|
48197
48366
|
sourceOfClass(projectPath, resourcePath) {
|
|
48198
48367
|
const contained = resolveWithinProject(projectPath, resourcePath);
|
|
48199
|
-
if (!contained.ok || !
|
|
48368
|
+
if (!contained.ok || !existsSync24(contained.absolutePath)) {
|
|
48200
48369
|
return null;
|
|
48201
48370
|
}
|
|
48202
48371
|
try {
|
|
@@ -48358,7 +48527,7 @@ class GodotServer {
|
|
|
48358
48527
|
...Array.isArray(stack) ? {
|
|
48359
48528
|
stack: stack.map((frame) => ({
|
|
48360
48529
|
...frame,
|
|
48361
|
-
...projectPath !== "" && typeof frame["script"] === "string" && frame["script"].startsWith("res://") ? { path:
|
|
48530
|
+
...projectPath !== "" && typeof frame["script"] === "string" && frame["script"].startsWith("res://") ? { path: join31(projectPath, frame["script"].slice("res://".length)) } : {}
|
|
48362
48531
|
}))
|
|
48363
48532
|
} : {}
|
|
48364
48533
|
});
|
|
@@ -48637,7 +48806,7 @@ class GodotServer {
|
|
|
48637
48806
|
return null;
|
|
48638
48807
|
}
|
|
48639
48808
|
try {
|
|
48640
|
-
return settingKeys(readFileSync26(
|
|
48809
|
+
return settingKeys(readFileSync26(join31(projectPath, "project.godot"), "utf8"));
|
|
48641
48810
|
} catch {
|
|
48642
48811
|
return null;
|
|
48643
48812
|
}
|
|
@@ -49074,7 +49243,7 @@ class GodotServer {
|
|
|
49074
49243
|
}
|
|
49075
49244
|
async runtimeUp(projectPath, before, budget, withArgs) {
|
|
49076
49245
|
const budgetMs = budget.ms;
|
|
49077
|
-
if (!
|
|
49246
|
+
if (!existsSync24(join31(projectPath, RUNTIME_AUTOLOAD.path))) {
|
|
49078
49247
|
return runtimeVerdict(null, {
|
|
49079
49248
|
addon: false,
|
|
49080
49249
|
budgetMs,
|
|
@@ -49160,7 +49329,7 @@ class GodotServer {
|
|
|
49160
49329
|
}
|
|
49161
49330
|
let announcedAt;
|
|
49162
49331
|
try {
|
|
49163
|
-
announcedAt =
|
|
49332
|
+
announcedAt = statSync14(endpoint.file).mtimeMs;
|
|
49164
49333
|
} catch {
|
|
49165
49334
|
return;
|
|
49166
49335
|
}
|
|
@@ -50050,9 +50219,13 @@ class GodotServer {
|
|
|
50050
50219
|
if (callersName !== undefined && !callersName.replace(/@([^.@]+)_(?:getter|setter)$/, "$1").split(".").every(isIdentifier)) {
|
|
50051
50220
|
return this.createErrorResponse(`callersOf takes a function's name, or Class.name for the functions of one class, and ${JSON.stringify(callersName)} is neither.`, ["editor_run profile names each function as it answers; pass one of those"]);
|
|
50052
50221
|
}
|
|
50222
|
+
const asked = {
|
|
50223
|
+
mark: readBoolean(args, "mark") === true,
|
|
50224
|
+
since: readString(args, "since") === "mark"
|
|
50225
|
+
};
|
|
50053
50226
|
const going = await this.runStillGoing(run);
|
|
50054
50227
|
const projectPath = run.projectPath;
|
|
50055
|
-
const projectGone = projectPath !== null && !
|
|
50228
|
+
const projectGone = projectPath !== null && !existsSync24(join31(projectPath, "project.godot"));
|
|
50056
50229
|
if (projectGone && callersName !== undefined) {
|
|
50057
50230
|
return this.createErrorResponse(`callersOf reads the project's scripts, and the run's project at ${projectPath} has no project.godot any more.`, ["editor_run profile without callersOf answers with the profile itself"]);
|
|
50058
50231
|
}
|
|
@@ -50062,7 +50235,7 @@ class GodotServer {
|
|
|
50062
50235
|
...callersName === undefined ? {} : { callersOf: { name: callersName, scripts: () => projectScripts(projectPath) } }
|
|
50063
50236
|
};
|
|
50064
50237
|
if (run.throughEditor) {
|
|
50065
|
-
return await this.profileOfThePlayedGame(
|
|
50238
|
+
return await this.profileOfThePlayedGame(run, going, fromProject, limit, asked);
|
|
50066
50239
|
}
|
|
50067
50240
|
const path = run.profile;
|
|
50068
50241
|
if (path === undefined) {
|
|
@@ -50075,15 +50248,16 @@ class GodotServer {
|
|
|
50075
50248
|
]);
|
|
50076
50249
|
}
|
|
50077
50250
|
const totals = file.connected ? file : null;
|
|
50078
|
-
return this.
|
|
50251
|
+
return this.profileReply(run, {
|
|
50079
50252
|
through: "gdharness",
|
|
50080
50253
|
running: going,
|
|
50081
50254
|
totals,
|
|
50082
50255
|
coveredMs: totals?.lastFrameAt == null ? 0 : totals.lastFrameAt - totals.startedAt,
|
|
50083
50256
|
...fromProject
|
|
50084
|
-
}, limit)
|
|
50257
|
+
}, limit, asked);
|
|
50085
50258
|
}
|
|
50086
|
-
async profileOfThePlayedGame(
|
|
50259
|
+
async profileOfThePlayedGame(run, going, fromProject, limit, asked) {
|
|
50260
|
+
const { projectPath } = run;
|
|
50087
50261
|
const read = await this.handleViaBridge("profile_read", {});
|
|
50088
50262
|
if (read.isError === true) {
|
|
50089
50263
|
return read;
|
|
@@ -50105,12 +50279,13 @@ class GodotServer {
|
|
|
50105
50279
|
return this.createErrorResponse("The editor says it is playing and has no debugger session for the game, so there is nothing to switch the profiler on in yet.", ["Ask again in a moment, once the game is up"]);
|
|
50106
50280
|
}
|
|
50107
50281
|
const on = await this.firstProfiledFrame();
|
|
50282
|
+
const unmarked = asked.mark || asked.since ? " Nothing was marked, and nothing is counted from a mark: the profile begins now, so an answer without since covers the same span." : "";
|
|
50108
50283
|
return this.jsonTextResponse({
|
|
50109
50284
|
through: "editor",
|
|
50110
50285
|
running: true,
|
|
50111
50286
|
started: true,
|
|
50112
50287
|
functions: [],
|
|
50113
|
-
note: on ?
|
|
50288
|
+
note: on ? `The profiler was switched on now, for the game the editor is playing, and its first frame has arrived, so it covers what the game does from here on. Ask again for the functions; editor_run start with profile: true covers a game from its start.${unmarked}` : `The profiler was switched on for the game the editor is playing, and no frame of it had arrived ${PROFILER_ON_MS}ms later: what the game does before the first one is not counted. Ask again for the functions; editor_run start with profile: true covers a game from its start.${unmarked}`
|
|
50114
50289
|
});
|
|
50115
50290
|
}
|
|
50116
50291
|
const functions = asParams(held["functions"]);
|
|
@@ -50133,13 +50308,70 @@ class GodotServer {
|
|
|
50133
50308
|
];
|
|
50134
50309
|
}))
|
|
50135
50310
|
} : null;
|
|
50136
|
-
return this.
|
|
50311
|
+
return this.profileReply(run, {
|
|
50137
50312
|
through: "editor",
|
|
50138
50313
|
running: going,
|
|
50139
50314
|
totals,
|
|
50140
50315
|
coveredMs: readNonNegativeNumber(held, "coveredMs") ?? 0,
|
|
50141
50316
|
...fromProject
|
|
50142
|
-
}, limit)
|
|
50317
|
+
}, limit, asked);
|
|
50318
|
+
}
|
|
50319
|
+
profileReply(run, reading, limit, asked) {
|
|
50320
|
+
const markPath = profileMarkPath(run.startedAt);
|
|
50321
|
+
const now = Date.now();
|
|
50322
|
+
let shown = reading;
|
|
50323
|
+
const lead = [];
|
|
50324
|
+
let sinceFields = {};
|
|
50325
|
+
if (asked.since) {
|
|
50326
|
+
const mark = readProfileMark(markPath);
|
|
50327
|
+
if (mark === null || mark.runStartedAt !== run.startedAt || mark.projectPath !== run.projectPath) {
|
|
50328
|
+
return this.createErrorResponse("No mark has been set in this run, so there is nothing to count from.", ["editor_run profile with mark: true sets one at the totals it answers with"]);
|
|
50329
|
+
}
|
|
50330
|
+
const added = reading.totals === null ? null : totalsSince(reading.totals, mark.totals);
|
|
50331
|
+
if (added === null) {
|
|
50332
|
+
return this.createErrorResponse("The profile holds less than it did at the mark, so the profiler was started again since, and what it holds now cannot be counted from the mark.", ["editor_run profile with mark: true sets a mark in the profile as it is now"]);
|
|
50333
|
+
}
|
|
50334
|
+
shown = {
|
|
50335
|
+
...reading,
|
|
50336
|
+
totals: added.totals,
|
|
50337
|
+
coveredMs: Math.max(0, reading.coveredMs - mark.coveredMs)
|
|
50338
|
+
};
|
|
50339
|
+
const ago = now - mark.takenAt;
|
|
50340
|
+
sinceFields = {
|
|
50341
|
+
since: "mark",
|
|
50342
|
+
markSetMsAgo: ago,
|
|
50343
|
+
...added.shrunk > 0 ? { lessThanAtMark: added.shrunk } : {}
|
|
50344
|
+
};
|
|
50345
|
+
lead.push(`Counted from the mark set ${ago}ms ago: each function's calls and milliseconds are what it added after the mark, frames and coveredMs are those since it, and a function that added no call is left out.`);
|
|
50346
|
+
if (added.shrunk > 0) {
|
|
50347
|
+
const one = added.shrunk === 1;
|
|
50348
|
+
lead.push(`${one ? "One function reads" : `${added.shrunk} functions read`} less than at the mark and ${one ? "is" : "are"} left out, counted under lessThanAtMark: the engine's own totals arrived after the mark and replaced the frames' sum, and capped totals leave out the functions with the least time.`);
|
|
50349
|
+
}
|
|
50350
|
+
}
|
|
50351
|
+
if (asked.mark) {
|
|
50352
|
+
if (reading.totals === null) {
|
|
50353
|
+
return this.createErrorResponse("There is nothing to mark yet: the game has not connected to the profiler.", ["Ask again in a moment, once the game is up"]);
|
|
50354
|
+
}
|
|
50355
|
+
const mark = {
|
|
50356
|
+
takenAt: now,
|
|
50357
|
+
projectPath: run.projectPath,
|
|
50358
|
+
runStartedAt: run.startedAt,
|
|
50359
|
+
coveredMs: reading.coveredMs,
|
|
50360
|
+
totals: reading.totals
|
|
50361
|
+
};
|
|
50362
|
+
if (!writeProfileMark(markPath, mark)) {
|
|
50363
|
+
return this.createErrorResponse(`The mark could not be written to ${markPath}, so none is set.`);
|
|
50364
|
+
}
|
|
50365
|
+
lead.push(asked.since ? 'The mark is moved to the totals as they are now, so the next since: "mark" counts from here.' : 'The mark is set at these totals: editor_run profile with since: "mark" answers only what is added after it.');
|
|
50366
|
+
}
|
|
50367
|
+
const answer = profileAnswer(shown, limit);
|
|
50368
|
+
const note = [...lead, ...typeof answer["note"] === "string" ? [answer["note"]] : []].join(" ");
|
|
50369
|
+
return this.jsonTextResponse({
|
|
50370
|
+
...answer,
|
|
50371
|
+
...sinceFields,
|
|
50372
|
+
...asked.mark ? { marked: true } : {},
|
|
50373
|
+
note: note === "" ? undefined : note
|
|
50374
|
+
});
|
|
50143
50375
|
}
|
|
50144
50376
|
async firstProfiledFrame() {
|
|
50145
50377
|
const deadline = Date.now() + PROFILER_ON_MS;
|
|
@@ -50340,7 +50572,7 @@ class GodotServer {
|
|
|
50340
50572
|
notes.push(endedWithoutACode({
|
|
50341
50573
|
throughEditor: run.throughEditor,
|
|
50342
50574
|
announced: announced !== undefined || run.announcedPid !== undefined,
|
|
50343
|
-
couldAnnounce: run.projectPath !== null &&
|
|
50575
|
+
couldAnnounce: run.projectPath !== null && existsSync24(join31(run.projectPath, RUNTIME_AUTOLOAD.path)),
|
|
50344
50576
|
endedHere: typeof run.endedHere === "string",
|
|
50345
50577
|
pickedUp: run.pickedUp === true
|
|
50346
50578
|
}));
|
|
@@ -50970,7 +51202,7 @@ class GodotServer {
|
|
|
50970
51202
|
if (!scriptPath.ok) {
|
|
50971
51203
|
return this.createErrorResponse(scriptPath.reason);
|
|
50972
51204
|
}
|
|
50973
|
-
if (!
|
|
51205
|
+
if (!existsSync24(scriptPath.absolutePath)) {
|
|
50974
51206
|
return this.createErrorResponse(`Script file does not exist: ${scriptPath.absolutePath}`);
|
|
50975
51207
|
}
|
|
50976
51208
|
const asked = readNonEmptyString(args, "newScriptPath");
|
|
@@ -51552,7 +51784,7 @@ class GodotServer {
|
|
|
51552
51784
|
async ownRunOnItsWay(projectPath, budgetMs) {
|
|
51553
51785
|
const run = await this.currentRun();
|
|
51554
51786
|
const project = projectPath ?? run?.projectPath ?? null;
|
|
51555
|
-
if (run === null || project === null || run.announcedPid !== undefined || run.projectPath !== null && !isSameDirectory(run.projectPath, project) || !
|
|
51787
|
+
if (run === null || project === null || run.announcedPid !== undefined || run.projectPath !== null && !isSameDirectory(run.projectPath, project) || !existsSync24(join31(project, RUNTIME_AUTOLOAD.path)) || !await this.runStillGoing(run)) {
|
|
51556
51788
|
return null;
|
|
51557
51789
|
}
|
|
51558
51790
|
const already = runtimesAnnounced().running;
|
|
@@ -51561,7 +51793,7 @@ class GodotServer {
|
|
|
51561
51793
|
return !run.announcedBefore.has(one.pid);
|
|
51562
51794
|
}
|
|
51563
51795
|
try {
|
|
51564
|
-
return
|
|
51796
|
+
return statSync14(one.file).mtimeMs >= run.startedAt;
|
|
51565
51797
|
} catch {
|
|
51566
51798
|
return false;
|
|
51567
51799
|
}
|
|
@@ -51642,7 +51874,7 @@ class GodotServer {
|
|
|
51642
51874
|
}
|
|
51643
51875
|
}
|
|
51644
51876
|
const screenshotDir = expectsScreenshot ? scratchDirectory("runtime-screenshot") : null;
|
|
51645
|
-
const screenshotPath = screenshotDir ?
|
|
51877
|
+
const screenshotPath = screenshotDir ? join31(screenshotDir, "capture.png") : null;
|
|
51646
51878
|
const staleNote = this.runtimeStaleNote(choice.endpoint);
|
|
51647
51879
|
const stale = staleNote === undefined ? {} : { addonIsStale: true, staleNote };
|
|
51648
51880
|
const refused = (message) => this.createErrorResponse(staleNote === undefined ? message : `${message}
|
|
@@ -51839,6 +52071,7 @@ var init_server = __esm(() => {
|
|
|
51839
52071
|
init_cut_test_run();
|
|
51840
52072
|
init_dap_client();
|
|
51841
52073
|
init_desktop();
|
|
52074
|
+
init_doc_cache();
|
|
51842
52075
|
init_editor_log();
|
|
51843
52076
|
init_engine_run();
|
|
51844
52077
|
init_errors();
|
|
@@ -51881,7 +52114,7 @@ var init_server = __esm(() => {
|
|
|
51881
52114
|
init_tool_definitions();
|
|
51882
52115
|
init_tool_reference();
|
|
51883
52116
|
init_update_check();
|
|
51884
|
-
__dirname2 =
|
|
52117
|
+
__dirname2 = dirname23(fileURLToPath5(import.meta.url));
|
|
51885
52118
|
KEEPER_DEBUG_MS = PAUSE_MS + STEP_MS + 2 * READ_MS;
|
|
51886
52119
|
CONSOLE_WRAPPER = /_console(\.exe)?$/i;
|
|
51887
52120
|
LONGEST_BOOT_NOTED_MS = 5 * 60000;
|
|
@@ -51941,8 +52174,8 @@ var init_server = __esm(() => {
|
|
|
51941
52174
|
init_bridge_announce();
|
|
51942
52175
|
init_errors();
|
|
51943
52176
|
init_godot_path();
|
|
51944
|
-
import { existsSync as
|
|
51945
|
-
import { join as
|
|
52177
|
+
import { existsSync as existsSync25 } from "node:fs";
|
|
52178
|
+
import { join as join32, resolve as resolve8 } from "node:path";
|
|
51946
52179
|
|
|
51947
52180
|
// src/harnesses.ts
|
|
51948
52181
|
import { existsSync as existsSync4, mkdirSync as mkdirSync2, readdirSync as readdirSync2, readFileSync as readFileSync2, rmSync as rmSync2, statSync as statSync3, writeFileSync as writeFileSync2 } from "node:fs";
|
|
@@ -52877,10 +53110,10 @@ init_issues();
|
|
|
52877
53110
|
init_paths();
|
|
52878
53111
|
|
|
52879
53112
|
// src/prompt.ts
|
|
52880
|
-
import
|
|
53113
|
+
import process9 from "node:process";
|
|
52881
53114
|
import { createInterface } from "node:readline/promises";
|
|
52882
53115
|
function interactive() {
|
|
52883
|
-
return
|
|
53116
|
+
return process9.stdin.isTTY && process9.stdout.isTTY;
|
|
52884
53117
|
}
|
|
52885
53118
|
|
|
52886
53119
|
class Ask {
|
|
@@ -52889,7 +53122,7 @@ class Ask {
|
|
|
52889
53122
|
if (!interactive()) {
|
|
52890
53123
|
return fallback;
|
|
52891
53124
|
}
|
|
52892
|
-
this.line ??= createInterface({ input:
|
|
53125
|
+
this.line ??= createInterface({ input: process9.stdin, output: process9.stdout });
|
|
52893
53126
|
const answer = (await this.line.question(`${question} ${fallback ? "[Y/n]" : "[y/N]"} `)).trim();
|
|
52894
53127
|
return answer === "" ? fallback : answer.toLowerCase().startsWith("y");
|
|
52895
53128
|
}
|
|
@@ -52907,10 +53140,10 @@ init_setup();
|
|
|
52907
53140
|
init_setup();
|
|
52908
53141
|
init_tool_definitions();
|
|
52909
53142
|
init_tool_reference();
|
|
52910
|
-
import { existsSync as
|
|
52911
|
-
import { basename as
|
|
53143
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync5, readdirSync as readdirSync9, rmSync as rmSync6, writeFileSync as writeFileSync7 } from "node:fs";
|
|
53144
|
+
import { basename as basename6, dirname as dirname8, join as join14 } from "node:path";
|
|
52912
53145
|
var SKILL_NAME = "gdharness";
|
|
52913
|
-
var SHARED_SKILLS =
|
|
53146
|
+
var SHARED_SKILLS = join14(".agents", "skills");
|
|
52914
53147
|
function skillMarkdown(version) {
|
|
52915
53148
|
return `---
|
|
52916
53149
|
name: ${SKILL_NAME}
|
|
@@ -52997,7 +53230,7 @@ that is running, and the project on disk. ${TOOL_SPECS.length} tools, named \`do
|
|
|
52997
53230
|
| What every label on a screen says | \`runtime_inspect tree\` with \`properties\` naming \`text\`, which reads it off every node that has one; \`includeProperties\` is every stored property of every node and ran to seventy-six thousand characters for one row |
|
|
52998
53231
|
| Whether this engine has a method or property, and what it takes | \`editor_classes info\` with \`member\`, which answers that one member and which class declares it rather than the whole class |
|
|
52999
53232
|
| Wait for a run to finish | \`editor_run wait\`, never a sleep or a shell loop on the pid: it answers as \`editor_output\` does, once the run is over |
|
|
53000
|
-
| Find which GDScript makes a run slow | \`editor_run start\` with \`profile\` set, then \`editor_run profile\`: the engine's own script profiler, per function with calls and self and total milliseconds, by self time, never \`Time.get_ticks_usec()\` laps edited into the game. A scene that does its work in \`_ready\` and quits is covered whole when the runtime autoload is registered, and a game the editor is already playing is profiled from the moment it is asked. \`callersOf\` names where the project's scripts call one function, each place with its function's numbers, since the engine records no callers |
|
|
53233
|
+
| Find which GDScript makes a run slow | \`editor_run start\` with \`profile\` set, then \`editor_run profile\`: the engine's own script profiler, per function with calls and self and total milliseconds, by self time, never \`Time.get_ticks_usec()\` laps edited into the game. A scene that does its work in \`_ready\` and quits is covered whole when the runtime autoload is registered, and a game the editor is already playing is profiled from the moment it is asked. For one action, a press or a commit, set \`mark\` on a profile before it and pass \`since: "mark"\` after it, which answers only what the action added. \`callersOf\` names where the project's scripts call one function, each place with its function's numbers, since the engine records no callers |
|
|
53001
53234
|
| End a bench and the workers it opened | \`editor_run stop\` with \`andChildren\`, never a process listing by scene name: it ends every process under the run's, to any depth, that is a game of the project, announced as one or the project's engine run with \`--path\` on it, names them under \`endedChildren\` with what identified each, and leaves and names any other |
|
|
53002
53235
|
| End a game of the project that no server is holding | \`editor_run stop\` with its \`pid\` from \`editor_status\` \`runtimes\`, never a process kill by hand: it is ended only when it announced itself for the project and its command line is the project's engine run |
|
|
53003
53236
|
| Play a game without touching the player's saves | \`editor_run start\` with \`savesIn\`, a directory for this run's \`user://\`: the editor plays with its own environment, so a run given one is started by the server, and the answer says under \`savesNote\` when the platform ignores it |
|
|
@@ -53135,27 +53368,27 @@ what they report while starting comes back under \`engine_messages_before_operat
|
|
|
53135
53368
|
function skillFiles(version) {
|
|
53136
53369
|
return new Map([
|
|
53137
53370
|
["SKILL.md", skillMarkdown(version)],
|
|
53138
|
-
[
|
|
53371
|
+
[join14("references", "tools.md"), renderToolsMarkdown()]
|
|
53139
53372
|
]);
|
|
53140
53373
|
}
|
|
53141
53374
|
function skillDirectories(harnesses, projectPath, exists) {
|
|
53142
|
-
const directories = [
|
|
53375
|
+
const directories = [join14(projectPath, SHARED_SKILLS, SKILL_NAME)];
|
|
53143
53376
|
for (const harness of harnesses) {
|
|
53144
53377
|
if (harness.skills === undefined) {
|
|
53145
53378
|
continue;
|
|
53146
53379
|
}
|
|
53147
|
-
const own =
|
|
53148
|
-
if ((!harness.skills.shared || exists(own)) && !directories.includes(
|
|
53149
|
-
directories.push(
|
|
53380
|
+
const own = join14(projectPath, harness.skills.dir);
|
|
53381
|
+
if ((!harness.skills.shared || exists(own)) && !directories.includes(join14(own, SKILL_NAME))) {
|
|
53382
|
+
directories.push(join14(own, SKILL_NAME));
|
|
53150
53383
|
}
|
|
53151
53384
|
}
|
|
53152
53385
|
return directories;
|
|
53153
53386
|
}
|
|
53154
53387
|
function everySkillDirectory(projectPath, harnesses) {
|
|
53155
|
-
const directories = new Set([
|
|
53388
|
+
const directories = new Set([join14(projectPath, SHARED_SKILLS, SKILL_NAME)]);
|
|
53156
53389
|
for (const harness of harnesses) {
|
|
53157
53390
|
if (harness.skills !== undefined) {
|
|
53158
|
-
directories.add(
|
|
53391
|
+
directories.add(join14(projectPath, harness.skills.dir, SKILL_NAME));
|
|
53159
53392
|
}
|
|
53160
53393
|
}
|
|
53161
53394
|
return [...directories];
|
|
@@ -53163,33 +53396,33 @@ function everySkillDirectory(projectPath, harnesses) {
|
|
|
53163
53396
|
function removeSkill(directories, projectPath) {
|
|
53164
53397
|
const removed = [];
|
|
53165
53398
|
for (const directory of directories) {
|
|
53166
|
-
if (
|
|
53399
|
+
if (existsSync14(directory)) {
|
|
53167
53400
|
rmSync6(directory, { recursive: true, force: true });
|
|
53168
|
-
pruneEmpty(
|
|
53401
|
+
pruneEmpty(dirname8(directory), projectPath);
|
|
53169
53402
|
removed.push(directory);
|
|
53170
53403
|
}
|
|
53171
53404
|
}
|
|
53172
53405
|
return removed;
|
|
53173
53406
|
}
|
|
53174
53407
|
function pruneEmpty(directory, projectPath) {
|
|
53175
|
-
const shared =
|
|
53408
|
+
const shared = join14(projectPath, ".agents");
|
|
53176
53409
|
let path = directory;
|
|
53177
|
-
while (path !== projectPath && (
|
|
53178
|
-
if (
|
|
53410
|
+
while (path !== projectPath && (basename6(path) === "skills" || path === shared)) {
|
|
53411
|
+
if (readdirSync9(path).length > 0) {
|
|
53179
53412
|
return;
|
|
53180
53413
|
}
|
|
53181
53414
|
rmSync6(path, { recursive: true, force: true });
|
|
53182
|
-
path =
|
|
53415
|
+
path = dirname8(path);
|
|
53183
53416
|
}
|
|
53184
53417
|
}
|
|
53185
53418
|
function writeSkill(directories, version) {
|
|
53186
53419
|
const files = skillFiles(version);
|
|
53187
53420
|
return directories.map((directory) => {
|
|
53188
|
-
const replaced =
|
|
53421
|
+
const replaced = existsSync14(directory);
|
|
53189
53422
|
rmSync6(directory, { recursive: true, force: true });
|
|
53190
53423
|
for (const [name, contents] of files) {
|
|
53191
|
-
const path =
|
|
53192
|
-
mkdirSync5(
|
|
53424
|
+
const path = join14(directory, name);
|
|
53425
|
+
mkdirSync5(dirname8(path), { recursive: true });
|
|
53193
53426
|
writeFileSync7(path, contents, "utf8");
|
|
53194
53427
|
}
|
|
53195
53428
|
return { path: directory, replaced };
|
|
@@ -53198,13 +53431,13 @@ function writeSkill(directories, version) {
|
|
|
53198
53431
|
|
|
53199
53432
|
// src/version.ts
|
|
53200
53433
|
import { readFileSync as readFileSync12 } from "node:fs";
|
|
53201
|
-
import { dirname as
|
|
53434
|
+
import { dirname as dirname9, join as join15 } from "node:path";
|
|
53202
53435
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
53203
53436
|
function getLocalVersion() {
|
|
53204
|
-
const currentDirectory =
|
|
53437
|
+
const currentDirectory = dirname9(fileURLToPath2(import.meta.url));
|
|
53205
53438
|
const packageCandidates = [
|
|
53206
|
-
|
|
53207
|
-
|
|
53439
|
+
join15(currentDirectory, "..", "package.json"),
|
|
53440
|
+
join15(currentDirectory, "..", "..", "package.json")
|
|
53208
53441
|
];
|
|
53209
53442
|
for (const packagePath of packageCandidates) {
|
|
53210
53443
|
try {
|
|
@@ -53226,7 +53459,7 @@ class UsageError extends Refusal {
|
|
|
53226
53459
|
function projectArgument(at) {
|
|
53227
53460
|
const named = args.slice(at).find((value) => !value.startsWith("--"));
|
|
53228
53461
|
const projectPath = resolve8(named ?? ".");
|
|
53229
|
-
if (!
|
|
53462
|
+
if (!existsSync25(join32(projectPath, "project.godot"))) {
|
|
53230
53463
|
throw new UsageError(`Not a Godot project: ${projectPath} holds no project.godot.`);
|
|
53231
53464
|
}
|
|
53232
53465
|
return projectPath;
|
|
@@ -53271,7 +53504,7 @@ async function registerRuntime(godot, projectPath) {
|
|
|
53271
53504
|
return;
|
|
53272
53505
|
}
|
|
53273
53506
|
console.log(`${RUNTIME_AUTOLOAD.name} autoload left at ${named}, which this project registers itself`);
|
|
53274
|
-
if (!
|
|
53507
|
+
if (!existsSync25(join32(projectPath, (named ?? "").replace(/^res:\/\//, "")))) {
|
|
53275
53508
|
console.log(` that file is not in this project, so it boots with a missing script: restore it, or point the entry at res://${RUNTIME_AUTOLOAD.path}`);
|
|
53276
53509
|
}
|
|
53277
53510
|
}
|
|
@@ -53403,7 +53636,7 @@ async function setup() {
|
|
|
53403
53636
|
console.log("no harness configured; name one yourself, and gdharness harnesses lists them all");
|
|
53404
53637
|
}
|
|
53405
53638
|
if (!args.includes("--no-skill")) {
|
|
53406
|
-
const directories = skillDirectories(chosen.harnesses, projectPath,
|
|
53639
|
+
const directories = skillDirectories(chosen.harnesses, projectPath, existsSync25);
|
|
53407
53640
|
for (const written of writeSkill(directories, getLocalVersion())) {
|
|
53408
53641
|
console.log(`skill: ${written.replaced ? "replaced" : "written"} ${written.path}`);
|
|
53409
53642
|
}
|
|
@@ -53517,7 +53750,7 @@ async function upgrade() {
|
|
|
53517
53750
|
moved.push(took.from);
|
|
53518
53751
|
}
|
|
53519
53752
|
}
|
|
53520
|
-
for (const written of writeSkill(skillDirectories(already, projectPath,
|
|
53753
|
+
for (const written of writeSkill(skillDirectories(already, projectPath, existsSync25), version)) {
|
|
53521
53754
|
console.log(`skill: ${written.replaced ? "replaced" : "written"} ${written.path}`);
|
|
53522
53755
|
}
|
|
53523
53756
|
console.log(`
|