gdharness 1.3.3 → 1.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/cli.js +532 -456
- package/build/godot/addons/gdharness_editor/animation_reads.gd +3 -2
- package/build/godot/addons/gdharness_editor/declared_values.gd +181 -4
- package/build/godot/addons/gdharness_editor/file_reads.gd +15 -3
- package/build/godot/addons/gdharness_editor/headless_check.gd +44 -23
- package/build/godot/addons/gdharness_editor/property_paths.gd +225 -0
- package/build/godot/addons/gdharness_editor/property_paths.gd.uid +1 -0
- package/build/godot/addons/gdharness_editor/property_values.gd +192 -231
- package/build/godot/addons/gdharness_editor/reading.gd +15 -0
- package/build/godot/addons/gdharness_editor/same_values.gd +177 -0
- package/build/godot/addons/gdharness_editor/same_values.gd.uid +1 -0
- package/build/godot/addons/gdharness_editor/scene_file.gd +97 -14
- package/build/godot/addons/gdharness_editor/serialisation.gd +75 -3
- package/build/godot/addons/gdharness_editor/tool_executor.gd +9 -3
- package/build/godot/addons/gdharness_editor/tools/animation_tools.gd +38 -35
- package/build/godot/addons/gdharness_editor/tools/class_tools.gd +20 -12
- package/build/godot/addons/gdharness_editor/tools/resource_tools.gd +3 -2
- package/build/godot/addons/gdharness_editor/tools/scene_groups.gd +91 -0
- package/build/godot/addons/gdharness_editor/tools/scene_groups.gd.uid +1 -0
- package/build/godot/addons/gdharness_editor/tools/scene_placing.gd +68 -2
- package/build/godot/addons/gdharness_editor/tools/scene_tools.gd +94 -333
- package/build/godot/addons/gdharness_editor/tools/signal_tools.gd +363 -0
- package/build/godot/addons/gdharness_editor/tools/signal_tools.gd.uid +1 -0
- package/build/godot/addons/gdharness_runtime/declared_values.gd +293 -0
- package/build/godot/addons/gdharness_runtime/declared_values.gd.uid +1 -0
- package/build/godot/addons/gdharness_runtime/reading.gd +15 -0
- package/build/godot/addons/gdharness_runtime/runtime_autoload.gd +4 -0
- package/build/godot/addons/gdharness_runtime/runtime_changes.gd +29 -1
- package/build/godot/addons/gdharness_runtime/runtime_input.gd +26 -39
- package/build/godot/addons/gdharness_runtime/runtime_mouse.gd +18 -0
- package/build/godot/addons/gdharness_runtime/runtime_pad.gd +3 -2
- package/build/godot/addons/gdharness_runtime/runtime_paths.gd +2 -1
- package/build/godot/addons/gdharness_runtime/runtime_values.gd +62 -5
- package/build/godot/addons/gdharness_runtime/runtime_waits.gd +48 -15
- package/build/godot/addons/gdharness_runtime/serialisation.gd +75 -3
- package/build/godot/operations/animation_reads.gd +3 -2
- package/build/godot/operations/classdb_queries.gd +112 -46
- package/build/godot/operations/declared_values.gd +181 -4
- package/build/godot/operations/dependencies.gd +14 -39
- package/build/godot/operations/editor_tools.gd +4 -3
- package/build/godot/operations/file_reads.gd +15 -3
- package/build/godot/operations/gdscript_analysis.gd +229 -75
- package/build/godot/operations/gdscript_authoring.gd +157 -36
- package/build/godot/operations/import_pipeline.gd +17 -6
- package/build/godot/operations/logger.gd +7 -0
- package/build/godot/operations/property_paths.gd +225 -0
- package/build/godot/operations/property_values.gd +192 -231
- package/build/godot/operations/reading.gd +15 -0
- package/build/godot/operations/same_values.gd +177 -0
- package/build/godot/operations/serialisation.gd +75 -3
- package/build/keeper.js +83 -69
- package/package.json +1 -1
package/build/cli.js
CHANGED
|
@@ -7836,16 +7836,25 @@ var require_public_api = __commonJS(function(exports) {
|
|
|
7836
7836
|
|
|
7837
7837
|
// src/paths.ts
|
|
7838
7838
|
import { realpathSync, statSync as statSync2 } from "node:fs";
|
|
7839
|
-
import { isAbsolute, relative, resolve, sep } from "node:path";
|
|
7840
|
-
function
|
|
7841
|
-
|
|
7842
|
-
|
|
7843
|
-
|
|
7844
|
-
|
|
7839
|
+
import { basename as basename2, dirname as dirname2, isAbsolute, join as join3, relative, resolve, sep } from "node:path";
|
|
7840
|
+
function realAsFarAsItExists(path) {
|
|
7841
|
+
const missing = [];
|
|
7842
|
+
let at = path;
|
|
7843
|
+
for (;; ) {
|
|
7844
|
+
try {
|
|
7845
|
+
return join3(realpathSync.native(at), ...missing);
|
|
7846
|
+
} catch {
|
|
7847
|
+
const parent = dirname2(at);
|
|
7848
|
+
if (parent === at) {
|
|
7849
|
+
return path;
|
|
7850
|
+
}
|
|
7851
|
+
missing.unshift(basename2(at));
|
|
7852
|
+
at = parent;
|
|
7853
|
+
}
|
|
7845
7854
|
}
|
|
7846
7855
|
}
|
|
7847
7856
|
function isSameDirectory(onePath, otherPath) {
|
|
7848
|
-
return relative(
|
|
7857
|
+
return relative(realAsFarAsItExists(resolve(onePath)), realAsFarAsItExists(resolve(otherPath))) === "";
|
|
7849
7858
|
}
|
|
7850
7859
|
function isWithinRoot(rootPath, candidatePath) {
|
|
7851
7860
|
const step = relative(resolve(rootPath), resolve(candidatePath));
|
|
@@ -7880,9 +7889,11 @@ function resolveWithinProject(projectPath, candidatePath, options = {}) {
|
|
|
7880
7889
|
};
|
|
7881
7890
|
}
|
|
7882
7891
|
if (withoutScheme.startsWith("/") || withoutScheme.startsWith("\\") || isAbsolute(withoutScheme)) {
|
|
7892
|
+
const tail = withoutScheme.split(/[\\/]+/).filter((step) => step !== "" && !step.endsWith(":"));
|
|
7893
|
+
const example = tail.slice(-2).join("/") || "scenes/main.tscn";
|
|
7883
7894
|
return {
|
|
7884
7895
|
ok: false,
|
|
7885
|
-
reason: `Path '${trimmed}' is absolute and outside the project; give a path inside it, such as '
|
|
7896
|
+
reason: `Path '${trimmed}' is absolute and outside the project; give a path inside it, such as '${example}'.`
|
|
7886
7897
|
};
|
|
7887
7898
|
}
|
|
7888
7899
|
const kept = keptInside(trimmed);
|
|
@@ -7922,7 +7933,7 @@ function exportedBuild(projectPath, candidatePath) {
|
|
|
7922
7933
|
reason: `There is no ${found.relativePath} in the project to start: export the build first, with project_export run.`
|
|
7923
7934
|
};
|
|
7924
7935
|
}
|
|
7925
|
-
if (!isWithinRoot(
|
|
7936
|
+
if (!isWithinRoot(realAsFarAsItExists(projectPath), real)) {
|
|
7926
7937
|
return {
|
|
7927
7938
|
ok: false,
|
|
7928
7939
|
reason: `${found.relativePath} leads outside the project, to ${real}, so it is not started.`
|
|
@@ -7941,7 +7952,7 @@ var init_paths = __esm(() => {
|
|
|
7941
7952
|
|
|
7942
7953
|
// src/runner.ts
|
|
7943
7954
|
import { existsSync as existsSync3 } from "node:fs";
|
|
7944
|
-
import { dirname as
|
|
7955
|
+
import { dirname as dirname3, join as join4 } from "node:path";
|
|
7945
7956
|
import process3 from "node:process";
|
|
7946
7957
|
function currentRunner() {
|
|
7947
7958
|
const versions = process3.versions;
|
|
@@ -7962,7 +7973,7 @@ function spawnFor(runner, version, execPath = process3.execPath) {
|
|
|
7962
7973
|
return { command: alongside(execPath, "npx") ?? "npx", args: ["-y", spec] };
|
|
7963
7974
|
}
|
|
7964
7975
|
function alongside(execPath, name) {
|
|
7965
|
-
const runner =
|
|
7976
|
+
const runner = join4(dirname3(execPath), process3.platform === "win32" ? `${name}.cmd` : name);
|
|
7966
7977
|
return existsSync3(runner) ? runner : undefined;
|
|
7967
7978
|
}
|
|
7968
7979
|
var init_runner = () => {};
|
|
@@ -8303,25 +8314,25 @@ var init_engine_run = __esm(() => {
|
|
|
8303
8314
|
// src/doc-cache.ts
|
|
8304
8315
|
import { existsSync as existsSync5, readdirSync as readdirSync3, statSync as statSync4 } from "node:fs";
|
|
8305
8316
|
import { homedir as homedir3 } from "node:os";
|
|
8306
|
-
import { basename as
|
|
8317
|
+
import { basename as basename4, dirname as dirname5, isAbsolute as isAbsolute2, join as join6 } from "node:path";
|
|
8307
8318
|
import process5 from "node:process";
|
|
8308
8319
|
function editorCacheDirectory(godotPath, platform = process5.platform, env = process5.env, home = homedir3()) {
|
|
8309
|
-
const beside =
|
|
8310
|
-
const bundle = platform === "darwin" &&
|
|
8320
|
+
const beside = dirname5(godotPath);
|
|
8321
|
+
const bundle = platform === "darwin" && basename4(beside) === "MacOS" && basename4(dirname5(beside)) === "Contents" ? dirname5(dirname5(dirname5(beside))) : null;
|
|
8311
8322
|
for (const folder of bundle === null ? [beside] : [beside, bundle]) {
|
|
8312
|
-
if (existsSync5(
|
|
8313
|
-
return
|
|
8323
|
+
if (existsSync5(join6(folder, "._sc_")) || existsSync5(join6(folder, "_sc_"))) {
|
|
8324
|
+
return join6(folder, "editor_data", "cache");
|
|
8314
8325
|
}
|
|
8315
8326
|
}
|
|
8316
8327
|
if (platform === "win32") {
|
|
8317
8328
|
const set = (name) => env[name] === "" ? undefined : env[name];
|
|
8318
|
-
return
|
|
8329
|
+
return join6(set("LOCALAPPDATA") ?? set("TEMP") ?? join6(home, "AppData", "Local"), "Godot");
|
|
8319
8330
|
}
|
|
8320
8331
|
if (platform === "darwin") {
|
|
8321
|
-
return
|
|
8332
|
+
return join6(home, "Library", "Caches", "Godot");
|
|
8322
8333
|
}
|
|
8323
8334
|
const xdg = env["XDG_CACHE_HOME"];
|
|
8324
|
-
return
|
|
8335
|
+
return join6(xdg !== undefined && isAbsolute2(xdg) ? xdg : join6(home, ".cache"), "godot");
|
|
8325
8336
|
}
|
|
8326
8337
|
function docCachesWritten(directory) {
|
|
8327
8338
|
const written = new Map;
|
|
@@ -8333,7 +8344,7 @@ function docCachesWritten(directory) {
|
|
|
8333
8344
|
}
|
|
8334
8345
|
for (const name of names.filter((one) => CACHE_FILE.test(one))) {
|
|
8335
8346
|
try {
|
|
8336
|
-
written.set(
|
|
8347
|
+
written.set(join6(directory, name), statSync4(join6(directory, name)).mtimeMs);
|
|
8337
8348
|
} catch {}
|
|
8338
8349
|
}
|
|
8339
8350
|
return written;
|
|
@@ -8358,7 +8369,7 @@ var init_doc_cache = __esm(() => {
|
|
|
8358
8369
|
|
|
8359
8370
|
// src/library-copies.ts
|
|
8360
8371
|
import { existsSync as existsSync6, readdirSync as readdirSync4, readFileSync as readFileSync3, renameSync, unlinkSync } from "node:fs";
|
|
8361
|
-
import { basename as
|
|
8372
|
+
import { basename as basename5, dirname as dirname6, isAbsolute as isAbsolute3, join as join7 } from "node:path";
|
|
8362
8373
|
function freeLibraryCopies(projectPath, platform = process.platform) {
|
|
8363
8374
|
const setAside = [];
|
|
8364
8375
|
const swept = [];
|
|
@@ -8366,12 +8377,12 @@ function freeLibraryCopies(projectPath, platform = process.platform) {
|
|
|
8366
8377
|
return { setAside, swept };
|
|
8367
8378
|
}
|
|
8368
8379
|
for (const library of windowsLibraries(projectPath)) {
|
|
8369
|
-
const folder =
|
|
8370
|
-
const copy = `~${
|
|
8371
|
-
if (existsSync6(
|
|
8372
|
-
const aside =
|
|
8380
|
+
const folder = dirname6(library);
|
|
8381
|
+
const copy = `~${basename5(library)}`;
|
|
8382
|
+
if (existsSync6(join7(folder, copy))) {
|
|
8383
|
+
const aside = join7(folder, `${copy}.aside-${process.pid}-${Date.now()}`);
|
|
8373
8384
|
try {
|
|
8374
|
-
renameSync(
|
|
8385
|
+
renameSync(join7(folder, copy), aside);
|
|
8375
8386
|
setAside.push(aside);
|
|
8376
8387
|
} catch {}
|
|
8377
8388
|
}
|
|
@@ -8384,8 +8395,8 @@ function freeLibraryCopies(projectPath, platform = process.platform) {
|
|
|
8384
8395
|
for (const name of names) {
|
|
8385
8396
|
if (name.length > copy.length && name.toLowerCase().startsWith(copy.toLowerCase())) {
|
|
8386
8397
|
try {
|
|
8387
|
-
unlinkSync(
|
|
8388
|
-
swept.push(
|
|
8398
|
+
unlinkSync(join7(folder, name));
|
|
8399
|
+
swept.push(join7(folder, name));
|
|
8389
8400
|
} catch {}
|
|
8390
8401
|
}
|
|
8391
8402
|
}
|
|
@@ -8393,7 +8404,7 @@ function freeLibraryCopies(projectPath, platform = process.platform) {
|
|
|
8393
8404
|
return { setAside, swept };
|
|
8394
8405
|
}
|
|
8395
8406
|
function windowsLibraries(projectPath) {
|
|
8396
|
-
const listed = readOrNull(
|
|
8407
|
+
const listed = readOrNull(join7(projectPath, ".godot", "extension_list.cfg"));
|
|
8397
8408
|
if (listed === null) {
|
|
8398
8409
|
return [];
|
|
8399
8410
|
}
|
|
@@ -8412,7 +8423,7 @@ function windowsLibraries(projectPath) {
|
|
|
8412
8423
|
continue;
|
|
8413
8424
|
}
|
|
8414
8425
|
const named = inLibraries ? /^\s*windows[\w.]*\s*=\s*"([^"]+)"/.exec(row)?.[1] : undefined;
|
|
8415
|
-
const library = named === undefined ? null : resolveResource(projectPath,
|
|
8426
|
+
const library = named === undefined ? null : resolveResource(projectPath, dirname6(manifest), named);
|
|
8416
8427
|
if (library !== null) {
|
|
8417
8428
|
libraries.add(library);
|
|
8418
8429
|
}
|
|
@@ -8425,9 +8436,9 @@ function resolveResource(projectPath, relativeTo, path) {
|
|
|
8425
8436
|
return null;
|
|
8426
8437
|
}
|
|
8427
8438
|
if (path.startsWith("res://")) {
|
|
8428
|
-
return
|
|
8439
|
+
return join7(projectPath, path.slice("res://".length));
|
|
8429
8440
|
}
|
|
8430
|
-
return isAbsolute3(path) ? path :
|
|
8441
|
+
return isAbsolute3(path) ? path : join7(relativeTo, path);
|
|
8431
8442
|
}
|
|
8432
8443
|
function readOrNull(path) {
|
|
8433
8444
|
try {
|
|
@@ -8441,10 +8452,10 @@ var init_library_copies = () => {};
|
|
|
8441
8452
|
// src/reimport.ts
|
|
8442
8453
|
import { createHash } from "node:crypto";
|
|
8443
8454
|
import { existsSync as existsSync7, readFileSync as readFileSync4, rmSync as rmSync3, statSync as statSync5, utimesSync, writeFileSync as writeFileSync3 } from "node:fs";
|
|
8444
|
-
import { basename as
|
|
8455
|
+
import { basename as basename6, join as join8 } from "node:path";
|
|
8445
8456
|
function forceNextImport(projectPath, resourcePath) {
|
|
8446
8457
|
const relative = resourcePath.replace(/^res:\/\//, "");
|
|
8447
|
-
const sidecar =
|
|
8458
|
+
const sidecar = join8(projectPath, `${relative}.import`);
|
|
8448
8459
|
if (!existsSync7(sidecar)) {
|
|
8449
8460
|
return;
|
|
8450
8461
|
}
|
|
@@ -8455,7 +8466,7 @@ function forceNextImport(projectPath, resourcePath) {
|
|
|
8455
8466
|
}
|
|
8456
8467
|
function importRecord(projectPath, resourcePath) {
|
|
8457
8468
|
const digest = createHash("md5").update(resourcePath).digest("hex");
|
|
8458
|
-
return
|
|
8469
|
+
return join8(projectPath, ".godot", "imported", `${basename6(resourcePath)}-${digest}.md5`);
|
|
8459
8470
|
}
|
|
8460
8471
|
var init_reimport = () => {};
|
|
8461
8472
|
|
|
@@ -8477,10 +8488,10 @@ var init_alive = () => {};
|
|
|
8477
8488
|
// src/scratch.ts
|
|
8478
8489
|
import { existsSync as existsSync8, mkdtempSync, readdirSync as readdirSync5, rmSync as rmSync4 } from "node:fs";
|
|
8479
8490
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
8480
|
-
import { join as
|
|
8491
|
+
import { join as join9 } from "node:path";
|
|
8481
8492
|
import process7 from "node:process";
|
|
8482
8493
|
function scratchDirectory(kind) {
|
|
8483
|
-
return mkdtempSync(
|
|
8494
|
+
return mkdtempSync(join9(tmpdir2(), `gdharness-${kind}-${process7.pid}-`));
|
|
8484
8495
|
}
|
|
8485
8496
|
function sweepAbandonedScratch(directory = tmpdir2()) {
|
|
8486
8497
|
let entries;
|
|
@@ -8496,7 +8507,7 @@ function sweepAbandonedScratch(directory = tmpdir2()) {
|
|
|
8496
8507
|
continue;
|
|
8497
8508
|
}
|
|
8498
8509
|
try {
|
|
8499
|
-
remove(
|
|
8510
|
+
remove(join9(directory, entry));
|
|
8500
8511
|
} catch {}
|
|
8501
8512
|
}
|
|
8502
8513
|
}
|
|
@@ -9081,7 +9092,7 @@ var init_junit = __esm(() => {
|
|
|
9081
9092
|
});
|
|
9082
9093
|
|
|
9083
9094
|
// src/script-origins.ts
|
|
9084
|
-
import { join as
|
|
9095
|
+
import { join as join10 } from "node:path";
|
|
9085
9096
|
function unresolvedClassIn(message) {
|
|
9086
9097
|
return UNRESOLVED.exec(message)?.[1] ?? null;
|
|
9087
9098
|
}
|
|
@@ -9143,7 +9154,7 @@ function checkWithEngine(godotPath, projectPath) {
|
|
|
9143
9154
|
const ran = await runEngine(godotPath, [
|
|
9144
9155
|
"--headless",
|
|
9145
9156
|
"--log-file",
|
|
9146
|
-
|
|
9157
|
+
join10(logDir, "engine.log"),
|
|
9147
9158
|
"--path",
|
|
9148
9159
|
projectPath,
|
|
9149
9160
|
"--check-only",
|
|
@@ -9189,8 +9200,8 @@ var init_script_origins = __esm(() => {
|
|
|
9189
9200
|
});
|
|
9190
9201
|
|
|
9191
9202
|
// src/headless.ts
|
|
9192
|
-
import { existsSync as existsSync9, readFileSync as readFileSync5,
|
|
9193
|
-
import { dirname as
|
|
9203
|
+
import { existsSync as existsSync9, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "node:fs";
|
|
9204
|
+
import { dirname as dirname7, join as join11 } from "node:path";
|
|
9194
9205
|
function snakeCased(params) {
|
|
9195
9206
|
const result = emptyRecord();
|
|
9196
9207
|
for (const [key, value] of Object.entries(params)) {
|
|
@@ -9222,6 +9233,19 @@ function withNotCompiled(note, entries) {
|
|
|
9222
9233
|
const path = entries.map(notCompiledIn).find((found) => found !== undefined);
|
|
9223
9234
|
return path === undefined ? note : `${note} ${path} did not compile, so every run of this project starts its autoloads without it until it is fixed; its errors are among these.`;
|
|
9224
9235
|
}
|
|
9236
|
+
function withoutAsides(entries) {
|
|
9237
|
+
const kept = [];
|
|
9238
|
+
let aside = false;
|
|
9239
|
+
for (const entry of entries) {
|
|
9240
|
+
const mark = entry.source === "stderr" ? entry.text : "";
|
|
9241
|
+
if (mark === ASIDE || mark === RESUMED) {
|
|
9242
|
+
aside = mark === ASIDE;
|
|
9243
|
+
} else if (!aside) {
|
|
9244
|
+
kept.push(entry);
|
|
9245
|
+
}
|
|
9246
|
+
}
|
|
9247
|
+
return kept;
|
|
9248
|
+
}
|
|
9225
9249
|
function problemsIn(log) {
|
|
9226
9250
|
return problemsAmong(log.everything());
|
|
9227
9251
|
}
|
|
@@ -9258,19 +9282,12 @@ function refusalsIn(log) {
|
|
|
9258
9282
|
}
|
|
9259
9283
|
function scriptFault(problems, script) {
|
|
9260
9284
|
const spelled = (path) => `(${path.replaceAll("\\", "/").toLowerCase()}/`;
|
|
9261
|
-
const directories = [...new Set([spelled(
|
|
9285
|
+
const directories = [...new Set([spelled(dirname7(script)), spelled(realAsFarAsItExists(dirname7(script)))])];
|
|
9262
9286
|
return problems.find((entry) => {
|
|
9263
9287
|
const at = entry.detail.find((line) => line.startsWith("at: "))?.replaceAll("\\", "/").toLowerCase();
|
|
9264
9288
|
return entry.severity === "error" && at !== undefined && directories.some((directory) => at.includes(directory));
|
|
9265
9289
|
});
|
|
9266
9290
|
}
|
|
9267
|
-
function resolvedOr(path) {
|
|
9268
|
-
try {
|
|
9269
|
-
return realpathSync2.native(path);
|
|
9270
|
-
} catch {
|
|
9271
|
-
return path;
|
|
9272
|
-
}
|
|
9273
|
-
}
|
|
9274
9291
|
function librariesNotCopied(messages) {
|
|
9275
9292
|
const found = new Set;
|
|
9276
9293
|
for (const entry of messages) {
|
|
@@ -9322,7 +9339,7 @@ async function importOnce(godotPath, projectPath) {
|
|
|
9322
9339
|
ran = await runEngine(godotPath, [
|
|
9323
9340
|
"--headless",
|
|
9324
9341
|
"--log-file",
|
|
9325
|
-
|
|
9342
|
+
join11(logDir, "engine.log"),
|
|
9326
9343
|
"--path",
|
|
9327
9344
|
projectPath,
|
|
9328
9345
|
"--import"
|
|
@@ -9344,13 +9361,13 @@ async function importOnce(godotPath, projectPath) {
|
|
|
9344
9361
|
}
|
|
9345
9362
|
async function runOperation(engine, operation, params, projectPath) {
|
|
9346
9363
|
const paramsDir = scratchDirectory("params");
|
|
9347
|
-
const paramsFile =
|
|
9348
|
-
const answerFile =
|
|
9364
|
+
const paramsFile = join11(paramsDir, `${operation}.json`);
|
|
9365
|
+
const answerFile = join11(paramsDir, "answer.json");
|
|
9349
9366
|
writeFileSync4(paramsFile, JSON.stringify(snakeCased(params)), "utf8");
|
|
9350
9367
|
const args = [
|
|
9351
9368
|
"--headless",
|
|
9352
9369
|
"--log-file",
|
|
9353
|
-
|
|
9370
|
+
join11(paramsDir, "engine.log"),
|
|
9354
9371
|
"--path",
|
|
9355
9372
|
projectPath,
|
|
9356
9373
|
"--script",
|
|
@@ -9368,7 +9385,7 @@ async function runOperation(engine, operation, params, projectPath) {
|
|
|
9368
9385
|
} finally {
|
|
9369
9386
|
discard(paramsDir);
|
|
9370
9387
|
}
|
|
9371
|
-
const everything = ran.log.everything();
|
|
9388
|
+
const everything = withoutAsides(ran.log.everything());
|
|
9372
9389
|
const marked = (text) => everything.findIndex((entry) => entry.source === "stderr" && entry.text === text);
|
|
9373
9390
|
const answered = marked(ANSWERED);
|
|
9374
9391
|
const waiting = marked(WAITING);
|
|
@@ -9452,11 +9469,12 @@ function answerIn(file) {
|
|
|
9452
9469
|
};
|
|
9453
9470
|
}
|
|
9454
9471
|
}
|
|
9455
|
-
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;
|
|
9472
|
+
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", ASIDE = "[gdharness] aside", RESUMED = "[gdharness] resumed", MESSAGES_KEPT = 200, ERRORS_NAMED = 3;
|
|
9456
9473
|
var init_headless = __esm(() => {
|
|
9457
9474
|
init_doc_cache();
|
|
9458
9475
|
init_engine_run();
|
|
9459
9476
|
init_library_copies();
|
|
9477
|
+
init_paths();
|
|
9460
9478
|
init_reimport();
|
|
9461
9479
|
init_scratch();
|
|
9462
9480
|
init_script_origins();
|
|
@@ -9465,7 +9483,7 @@ var init_headless = __esm(() => {
|
|
|
9465
9483
|
// src/update-check.ts
|
|
9466
9484
|
import { existsSync as existsSync10, mkdirSync as mkdirSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "node:fs";
|
|
9467
9485
|
import { homedir as homedir4, tmpdir as tmpdir3 } from "node:os";
|
|
9468
|
-
import { join as
|
|
9486
|
+
import { join as join12 } from "node:path";
|
|
9469
9487
|
function packageUrl(root) {
|
|
9470
9488
|
return `${root.replace(/\/+$/, "")}/gdharness/latest`;
|
|
9471
9489
|
}
|
|
@@ -9487,20 +9505,20 @@ function cacheDirectory(environment) {
|
|
|
9487
9505
|
};
|
|
9488
9506
|
const home = set("HOME") ?? homedir4();
|
|
9489
9507
|
if (process.platform === "win32") {
|
|
9490
|
-
return
|
|
9508
|
+
return join12(set("LOCALAPPDATA") ?? home, "gdharness");
|
|
9491
9509
|
}
|
|
9492
9510
|
if (process.platform === "darwin") {
|
|
9493
|
-
return
|
|
9511
|
+
return join12(home, "Library", "Caches", "gdharness");
|
|
9494
9512
|
}
|
|
9495
|
-
return
|
|
9513
|
+
return join12(set("XDG_CACHE_HOME") ?? join12(home, ".cache"), "gdharness");
|
|
9496
9514
|
}
|
|
9497
9515
|
function cacheFile(environment = process.env) {
|
|
9498
9516
|
try {
|
|
9499
9517
|
const directory = cacheDirectory(environment);
|
|
9500
9518
|
mkdirSync3(directory, { recursive: true, mode: 448 });
|
|
9501
|
-
return
|
|
9519
|
+
return join12(directory, "update-check.json");
|
|
9502
9520
|
} catch {
|
|
9503
|
-
return
|
|
9521
|
+
return join12(tmpdir3(), "gdharness-update-check.json");
|
|
9504
9522
|
}
|
|
9505
9523
|
}
|
|
9506
9524
|
function readCache(path) {
|
|
@@ -10501,7 +10519,7 @@ var init_gdscript_source = __esm(() => {
|
|
|
10501
10519
|
|
|
10502
10520
|
// src/class-cache.ts
|
|
10503
10521
|
import { existsSync as existsSync11, readdirSync as readdirSync6, readFileSync as readFileSync8, statSync as statSync6 } from "node:fs";
|
|
10504
|
-
import { join as
|
|
10522
|
+
import { join as join13, posix } from "node:path";
|
|
10505
10523
|
function isToolScript(source) {
|
|
10506
10524
|
return TOOL.test(codeOf(source));
|
|
10507
10525
|
}
|
|
@@ -10517,14 +10535,14 @@ function declaredClasses(projectPath) {
|
|
|
10517
10535
|
}
|
|
10518
10536
|
function eachScript(projectPath, each) {
|
|
10519
10537
|
const visit = (directory, prefix) => {
|
|
10520
|
-
if (existsSync11(
|
|
10538
|
+
if (existsSync11(join13(directory, ".gdignore"))) {
|
|
10521
10539
|
return;
|
|
10522
10540
|
}
|
|
10523
10541
|
for (const entry of readdirSync6(directory, { withFileTypes: true })) {
|
|
10524
10542
|
if (entry.name.startsWith(".")) {
|
|
10525
10543
|
continue;
|
|
10526
10544
|
}
|
|
10527
|
-
const path =
|
|
10545
|
+
const path = join13(directory, entry.name);
|
|
10528
10546
|
if (entry.isDirectory()) {
|
|
10529
10547
|
visit(path, `${prefix}${entry.name}/`);
|
|
10530
10548
|
} else if (entry.isFile() && entry.name.endsWith(".gd")) {
|
|
@@ -10550,7 +10568,7 @@ function scriptsNaming(projectPath, classes) {
|
|
|
10550
10568
|
return naming.sort((a, b) => a.script.localeCompare(b.script));
|
|
10551
10569
|
}
|
|
10552
10570
|
function cachedClasses(projectPath) {
|
|
10553
|
-
const cache =
|
|
10571
|
+
const cache = join13(projectPath, ".godot", "global_script_class_cache.cfg");
|
|
10554
10572
|
if (!existsSync11(cache)) {
|
|
10555
10573
|
return null;
|
|
10556
10574
|
}
|
|
@@ -10561,6 +10579,22 @@ function cachedClasses(projectPath) {
|
|
|
10561
10579
|
}
|
|
10562
10580
|
return listed;
|
|
10563
10581
|
}
|
|
10582
|
+
function addonOf(path) {
|
|
10583
|
+
return /^res:\/\/addons\/([^/]+)\//.exec(path)?.[1];
|
|
10584
|
+
}
|
|
10585
|
+
function withAddonPathsCounted(paths) {
|
|
10586
|
+
const own = [];
|
|
10587
|
+
const inAddons = {};
|
|
10588
|
+
for (const path of paths) {
|
|
10589
|
+
const addon = addonOf(path);
|
|
10590
|
+
if (addon === undefined) {
|
|
10591
|
+
own.push(path);
|
|
10592
|
+
} else {
|
|
10593
|
+
inAddons[addon] = (inAddons[addon] ?? 0) + 1;
|
|
10594
|
+
}
|
|
10595
|
+
}
|
|
10596
|
+
return { own, inAddons };
|
|
10597
|
+
}
|
|
10564
10598
|
function withAddonClassesCounted(rebuilt, listed) {
|
|
10565
10599
|
const counted = { ...rebuilt };
|
|
10566
10600
|
for (const key of ["added", "changed"]) {
|
|
@@ -10571,7 +10605,7 @@ function withAddonClassesCounted(rebuilt, listed) {
|
|
|
10571
10605
|
const own = [];
|
|
10572
10606
|
const perAddon = {};
|
|
10573
10607
|
for (const name of names) {
|
|
10574
|
-
const addon =
|
|
10608
|
+
const addon = addonOf(listed.get(String(name)) ?? "");
|
|
10575
10609
|
if (addon === undefined) {
|
|
10576
10610
|
own.push(name);
|
|
10577
10611
|
} else {
|
|
@@ -10832,12 +10866,12 @@ function uncachedForTheEngineNote(projectPath, types) {
|
|
|
10832
10866
|
}
|
|
10833
10867
|
const one = types.length === 1;
|
|
10834
10868
|
const named = types.map((type) => `${type.type} (${type.declaredIn})`).join(", ");
|
|
10835
|
-
const cacheExists = existsSync11(
|
|
10869
|
+
const cacheExists = existsSync11(join13(projectPath, ".godot", "global_script_class_cache.cfg"));
|
|
10836
10870
|
const state = cacheExists ? `missing from .godot/global_script_class_cache.cfg, which is where the engine that compiled these scripts resolves a class name from, so the error is the cache being behind the files rather than the script.` : `the engine that compiled these scripts resolves a class name from .godot/global_script_class_cache.cfg, which has not been written yet, as in a project no editor has opened, so the error is the missing cache rather than the script.`;
|
|
10837
10871
|
return `${named} ${one ? "is" : "are"} declared in this project${cacheExists ? " and" : ", but"} ${state} ` + "project_import refresh_classes writes the cache from the declarations on disk; editor_run start " + "writes it before a game, and an editor opening the project writes it when it scans.";
|
|
10838
10872
|
}
|
|
10839
10873
|
function cacheWrittenAt(projectPath) {
|
|
10840
|
-
const cache =
|
|
10874
|
+
const cache = join13(projectPath, ".godot", "global_script_class_cache.cfg");
|
|
10841
10875
|
try {
|
|
10842
10876
|
return statSync6(cache).mtimeMs;
|
|
10843
10877
|
} catch {
|
|
@@ -10851,14 +10885,14 @@ function declaredSince(projectPath, paths, names, since) {
|
|
|
10851
10885
|
return false;
|
|
10852
10886
|
}
|
|
10853
10887
|
try {
|
|
10854
|
-
return statSync6(
|
|
10888
|
+
return statSync6(join13(projectPath, path.slice("res://".length))).mtimeMs > since;
|
|
10855
10889
|
} catch {
|
|
10856
10890
|
return false;
|
|
10857
10891
|
}
|
|
10858
10892
|
});
|
|
10859
10893
|
}
|
|
10860
10894
|
function cachedAtMissingPaths(cached, projectPath) {
|
|
10861
|
-
return [...cached].filter(([, path]) => path.startsWith("res://") && !existsSync11(
|
|
10895
|
+
return [...cached].filter(([, path]) => path.startsWith("res://") && !existsSync11(join13(projectPath, path.slice("res://".length)))).map(([name]) => name).sort();
|
|
10862
10896
|
}
|
|
10863
10897
|
function staleAgainst(cached, projectPath) {
|
|
10864
10898
|
return [...declaredClasses(projectPath)].filter(([name, path]) => cached.get(name) !== path).map(([name]) => name);
|
|
@@ -10884,7 +10918,7 @@ var init_class_cache = __esm(() => {
|
|
|
10884
10918
|
|
|
10885
10919
|
// src/project-scan.ts
|
|
10886
10920
|
import { existsSync as existsSync12, readdirSync as readdirSync7, readFileSync as readFileSync9 } from "node:fs";
|
|
10887
|
-
import { extname, join as
|
|
10921
|
+
import { extname, join as join14 } from "node:path";
|
|
10888
10922
|
function skipped(name) {
|
|
10889
10923
|
return name.startsWith(".") || SKIPPED.has(name);
|
|
10890
10924
|
}
|
|
@@ -10892,10 +10926,10 @@ function steppedOver(directory) {
|
|
|
10892
10926
|
return gdignored(directory) || installedByUs(directory);
|
|
10893
10927
|
}
|
|
10894
10928
|
function gdignored(directory) {
|
|
10895
|
-
return existsSync12(
|
|
10929
|
+
return existsSync12(join14(directory, ".gdignore"));
|
|
10896
10930
|
}
|
|
10897
10931
|
function installedByUs(directory) {
|
|
10898
|
-
return existsSync12(
|
|
10932
|
+
return existsSync12(join14(directory, VERSION_MARKER));
|
|
10899
10933
|
}
|
|
10900
10934
|
function projectStructure(projectPath) {
|
|
10901
10935
|
const structure = { scenes: 0, scripts: 0, assets: 0, other: 0 };
|
|
@@ -10908,7 +10942,7 @@ function projectStructure(projectPath) {
|
|
|
10908
10942
|
continue;
|
|
10909
10943
|
}
|
|
10910
10944
|
if (entry.isDirectory()) {
|
|
10911
|
-
visit(
|
|
10945
|
+
visit(join14(directory, entry.name));
|
|
10912
10946
|
} else if (entry.isFile()) {
|
|
10913
10947
|
const extension = entry.name.split(".").pop()?.toLowerCase() ?? "";
|
|
10914
10948
|
if (SCENE_EXTENSIONS.has(extension)) {
|
|
@@ -10938,7 +10972,7 @@ function projectFiles(projectPath, extensions) {
|
|
|
10938
10972
|
}
|
|
10939
10973
|
const spelled = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
|
|
10940
10974
|
if (entry.isDirectory()) {
|
|
10941
|
-
visit(
|
|
10975
|
+
visit(join14(directory, entry.name), spelled);
|
|
10942
10976
|
} else if (entry.isFile() && extensions.includes(extname(entry.name).toLowerCase())) {
|
|
10943
10977
|
found.push(spelled);
|
|
10944
10978
|
}
|
|
@@ -10959,14 +10993,14 @@ function scriptsWithoutUid(projectPath) {
|
|
|
10959
10993
|
}
|
|
10960
10994
|
const spelled = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
|
|
10961
10995
|
if (entry.isDirectory()) {
|
|
10962
|
-
visit(
|
|
10996
|
+
visit(join14(directory, entry.name), spelled);
|
|
10963
10997
|
continue;
|
|
10964
10998
|
}
|
|
10965
10999
|
if (!entry.isFile()) {
|
|
10966
11000
|
continue;
|
|
10967
11001
|
}
|
|
10968
11002
|
const extension = entry.name.split(".").pop()?.toLowerCase() ?? "";
|
|
10969
|
-
if (CARRIES_A_UID.has(extension) && !existsSync12(
|
|
11003
|
+
if (CARRIES_A_UID.has(extension) && !existsSync12(join14(directory, `${entry.name}.uid`))) {
|
|
10970
11004
|
found.push(spelled);
|
|
10971
11005
|
}
|
|
10972
11006
|
}
|
|
@@ -10977,7 +11011,7 @@ function scriptsWithoutUid(projectPath) {
|
|
|
10977
11011
|
function pathsInUidCache(projectPath) {
|
|
10978
11012
|
let data;
|
|
10979
11013
|
try {
|
|
10980
|
-
data = readFileSync9(
|
|
11014
|
+
data = readFileSync9(join14(projectPath, ".godot", "uid_cache.bin"));
|
|
10981
11015
|
} catch {
|
|
10982
11016
|
return null;
|
|
10983
11017
|
}
|
|
@@ -11015,7 +11049,7 @@ function projectTexts(projectPath) {
|
|
|
11015
11049
|
if (skipped(entry.name)) {
|
|
11016
11050
|
continue;
|
|
11017
11051
|
}
|
|
11018
|
-
const absolute =
|
|
11052
|
+
const absolute = join14(directory, entry.name);
|
|
11019
11053
|
const spelled = `${prefix}${entry.name}`;
|
|
11020
11054
|
if (entry.isDirectory()) {
|
|
11021
11055
|
visit(absolute, `${spelled}/`);
|
|
@@ -11045,7 +11079,7 @@ function ignoredTexts(projectPath) {
|
|
|
11045
11079
|
if (skipped(entry.name)) {
|
|
11046
11080
|
continue;
|
|
11047
11081
|
}
|
|
11048
|
-
const absolute =
|
|
11082
|
+
const absolute = join14(directory, entry.name);
|
|
11049
11083
|
const spelled = `${prefix}${entry.name}`;
|
|
11050
11084
|
if (entry.isDirectory()) {
|
|
11051
11085
|
visit(absolute, `${spelled}/`, under);
|
|
@@ -11075,6 +11109,14 @@ function plainMatch(line, needle, caseSensitive) {
|
|
|
11075
11109
|
}
|
|
11076
11110
|
return [lowered.length === line.length ? line.slice(at, at + needle.length) : needle];
|
|
11077
11111
|
}
|
|
11112
|
+
function compiledQuery(query, caseSensitive) {
|
|
11113
|
+
try {
|
|
11114
|
+
return new RegExp(query, caseSensitive ? "" : "i");
|
|
11115
|
+
} catch (error) {
|
|
11116
|
+
const why = errorMessage(error).split(": ").at(-1) ?? "";
|
|
11117
|
+
throw new Error(`'${query}' is not a regular expression: ${why.charAt(0).toLowerCase()}${why.slice(1)}. With regex true, a character such as ( ) [ ] { } . * + ? | ^ $ or \\ stands for itself only after a backslash, as in \\(; or leave regex out to search for the text as written.`);
|
|
11118
|
+
}
|
|
11119
|
+
}
|
|
11078
11120
|
function searchProject(projectPath, options) {
|
|
11079
11121
|
const extensions = options.fileTypes === undefined ? null : new Set(options.fileTypes.map((ext) => ext.replace(/^\./, "").toLowerCase()).filter(Boolean));
|
|
11080
11122
|
const result = {
|
|
@@ -11082,7 +11124,7 @@ function searchProject(projectPath, options) {
|
|
|
11082
11124
|
results: [],
|
|
11083
11125
|
summary: { files_searched: 0, files_with_matches: 0, total_matches: 0, truncated: false }
|
|
11084
11126
|
};
|
|
11085
|
-
const regex = options.regex ?
|
|
11127
|
+
const regex = options.regex ? compiledQuery(options.query, options.caseSensitive) : null;
|
|
11086
11128
|
const needle = options.caseSensitive ? options.query : options.query.toLowerCase();
|
|
11087
11129
|
const full = () => result.summary.truncated;
|
|
11088
11130
|
const visit = (directory) => {
|
|
@@ -11096,7 +11138,7 @@ function searchProject(projectPath, options) {
|
|
|
11096
11138
|
if (skipped(entry.name)) {
|
|
11097
11139
|
continue;
|
|
11098
11140
|
}
|
|
11099
|
-
const entryPath =
|
|
11141
|
+
const entryPath = join14(directory, entry.name);
|
|
11100
11142
|
if (entry.isDirectory()) {
|
|
11101
11143
|
visit(entryPath);
|
|
11102
11144
|
continue;
|
|
@@ -11135,6 +11177,7 @@ function searchProject(projectPath, options) {
|
|
|
11135
11177
|
}
|
|
11136
11178
|
var SKIPPED, VERSION_MARKER = ".gdharness-version", SCENE_EXTENSIONS, ASSET_EXTENSIONS, CARRIES_A_UID, BINARY_EXTENSIONS;
|
|
11137
11179
|
var init_project_scan = __esm(() => {
|
|
11180
|
+
init_errors();
|
|
11138
11181
|
SKIPPED = new Set(["node_modules"]);
|
|
11139
11182
|
SCENE_EXTENSIONS = new Set(["tscn", "scn"]);
|
|
11140
11183
|
ASSET_EXTENSIONS = new Set([
|
|
@@ -11192,7 +11235,7 @@ var init_answer_json = __esm(() => {
|
|
|
11192
11235
|
});
|
|
11193
11236
|
|
|
11194
11237
|
// src/resources.ts
|
|
11195
|
-
import { readFileSync as readFileSync10, realpathSync as
|
|
11238
|
+
import { readFileSync as readFileSync10, realpathSync as realpathSync2 } from "node:fs";
|
|
11196
11239
|
import { extname as extname2, resolve as resolve3 } from "node:path";
|
|
11197
11240
|
function ensureProjectPath(getProjectPath) {
|
|
11198
11241
|
const projectPath = getProjectPath();
|
|
@@ -11215,13 +11258,13 @@ function resolveProjectFile(projectPath, resourcePath) {
|
|
|
11215
11258
|
}
|
|
11216
11259
|
let root;
|
|
11217
11260
|
try {
|
|
11218
|
-
root =
|
|
11261
|
+
root = realpathSync2.native(projectPath);
|
|
11219
11262
|
} catch {
|
|
11220
11263
|
throw new Refusal(`The project directory ${projectPath} is not there.`);
|
|
11221
11264
|
}
|
|
11222
11265
|
let real;
|
|
11223
11266
|
try {
|
|
11224
|
-
real =
|
|
11267
|
+
real = realpathSync2.native(contained.absolutePath);
|
|
11225
11268
|
} catch {
|
|
11226
11269
|
throw new Refusal(`No file at ${resourcePath} in the project.`);
|
|
11227
11270
|
}
|
|
@@ -11550,7 +11593,7 @@ import {
|
|
|
11550
11593
|
statSync as statSync7,
|
|
11551
11594
|
writeFileSync as writeFileSync6
|
|
11552
11595
|
} from "node:fs";
|
|
11553
|
-
import { dirname as
|
|
11596
|
+
import { dirname as dirname8, join as join15 } from "node:path";
|
|
11554
11597
|
import { fileURLToPath } from "node:url";
|
|
11555
11598
|
function editorAddonDigest(from = shippedAddonsDirectory()) {
|
|
11556
11599
|
return addonsDigest(EDITOR_PLUGINS, from);
|
|
@@ -11565,7 +11608,7 @@ function addonsDigest(names, from) {
|
|
|
11565
11608
|
if (name === VERSION_MARKER || name === DIGEST_MARKER) {
|
|
11566
11609
|
continue;
|
|
11567
11610
|
}
|
|
11568
|
-
const path =
|
|
11611
|
+
const path = join15(directory, name);
|
|
11569
11612
|
const inside = `${relative}/${name}`;
|
|
11570
11613
|
if (statSync7(path).isDirectory()) {
|
|
11571
11614
|
walk(path, inside);
|
|
@@ -11579,7 +11622,7 @@ function addonsDigest(names, from) {
|
|
|
11579
11622
|
}
|
|
11580
11623
|
};
|
|
11581
11624
|
for (const name of names) {
|
|
11582
|
-
walk(
|
|
11625
|
+
walk(join15(from, name), name);
|
|
11583
11626
|
}
|
|
11584
11627
|
return hash.digest("hex");
|
|
11585
11628
|
}
|
|
@@ -11598,7 +11641,7 @@ function installedRuntimeDigest(projectPath) {
|
|
|
11598
11641
|
return digestMarker(projectPath, RUNTIME_ADDON);
|
|
11599
11642
|
}
|
|
11600
11643
|
function digestMarker(projectPath, addon) {
|
|
11601
|
-
const marker =
|
|
11644
|
+
const marker = join15(projectPath, "addons", addon, DIGEST_MARKER);
|
|
11602
11645
|
if (!existsSync13(marker)) {
|
|
11603
11646
|
return null;
|
|
11604
11647
|
}
|
|
@@ -11606,35 +11649,35 @@ function digestMarker(projectPath, addon) {
|
|
|
11606
11649
|
return said === "" ? null : said;
|
|
11607
11650
|
}
|
|
11608
11651
|
function shippedAddonsDirectory() {
|
|
11609
|
-
return
|
|
11652
|
+
return join15(dirname8(fileURLToPath(import.meta.url)), "godot", "addons");
|
|
11610
11653
|
}
|
|
11611
11654
|
function shippedOperationsScript() {
|
|
11612
|
-
return
|
|
11655
|
+
return join15(dirname8(fileURLToPath(import.meta.url)), "godot", "operations", "godot_operations.gd");
|
|
11613
11656
|
}
|
|
11614
11657
|
function installAddons(projectPath, from = shippedAddonsDirectory()) {
|
|
11615
11658
|
const installed = [];
|
|
11616
11659
|
for (const name of ADDONS) {
|
|
11617
|
-
const source =
|
|
11618
|
-
if (!existsSync13(
|
|
11660
|
+
const source = join15(from, name);
|
|
11661
|
+
if (!existsSync13(join15(source, name === RUNTIME_ADDON ? "runtime_autoload.gd" : "plugin.cfg"))) {
|
|
11619
11662
|
throw new Error(`The package holds no ${name} addon at ${source}.`);
|
|
11620
11663
|
}
|
|
11621
|
-
const target =
|
|
11664
|
+
const target = join15(projectPath, "addons", name);
|
|
11622
11665
|
const replaced = existsSync13(target);
|
|
11623
11666
|
rmSync5(target, { recursive: true, force: true });
|
|
11624
|
-
mkdirSync4(
|
|
11667
|
+
mkdirSync4(dirname8(target), { recursive: true });
|
|
11625
11668
|
cpSync(source, target, { recursive: true });
|
|
11626
|
-
writeFileSync6(
|
|
11669
|
+
writeFileSync6(join15(target, VERSION_MARKER), `${SERVER_VERSION}
|
|
11627
11670
|
`);
|
|
11628
11671
|
installed.push({ name, path: target, replaced });
|
|
11629
11672
|
}
|
|
11630
|
-
writeFileSync6(
|
|
11673
|
+
writeFileSync6(join15(projectPath, "addons", ADDONS[0], DIGEST_MARKER), `${editorAddonDigest(from)}
|
|
11631
11674
|
`);
|
|
11632
|
-
writeFileSync6(
|
|
11675
|
+
writeFileSync6(join15(projectPath, "addons", RUNTIME_ADDON, DIGEST_MARKER), `${runtimeAddonDigest(from)}
|
|
11633
11676
|
`);
|
|
11634
11677
|
return installed;
|
|
11635
11678
|
}
|
|
11636
11679
|
function installedAddonVersion(projectPath) {
|
|
11637
|
-
const marker =
|
|
11680
|
+
const marker = join15(projectPath, "addons", ADDONS[0], VERSION_MARKER);
|
|
11638
11681
|
if (!existsSync13(marker)) {
|
|
11639
11682
|
return null;
|
|
11640
11683
|
}
|
|
@@ -11644,13 +11687,13 @@ function installedAddonVersion(projectPath) {
|
|
|
11644
11687
|
function removeAddons(projectPath) {
|
|
11645
11688
|
const removed = [];
|
|
11646
11689
|
for (const name of ADDONS) {
|
|
11647
|
-
const target =
|
|
11690
|
+
const target = join15(projectPath, "addons", name);
|
|
11648
11691
|
if (existsSync13(target)) {
|
|
11649
11692
|
rmSync5(target, { recursive: true, force: true });
|
|
11650
11693
|
removed.push(target);
|
|
11651
11694
|
}
|
|
11652
11695
|
}
|
|
11653
|
-
const addons =
|
|
11696
|
+
const addons = join15(projectPath, "addons");
|
|
11654
11697
|
if (removed.length > 0 && existsSync13(addons) && readdirSync8(addons).length === 0) {
|
|
11655
11698
|
rmdirSync(addons);
|
|
11656
11699
|
removed.push(addons);
|
|
@@ -11685,7 +11728,7 @@ function loaderAmong(projectPath, named) {
|
|
|
11685
11728
|
}
|
|
11686
11729
|
let source = null;
|
|
11687
11730
|
try {
|
|
11688
|
-
source = readFileSync11(
|
|
11731
|
+
source = readFileSync11(join15(projectPath, path), "utf8");
|
|
11689
11732
|
} catch {}
|
|
11690
11733
|
if (source?.includes(RUNTIME_AUTOLOAD.path)) {
|
|
11691
11734
|
return { name: entry, path, exists: true };
|
|
@@ -11727,8 +11770,8 @@ function enabledPlugins(settings) {
|
|
|
11727
11770
|
function inspectProject(projectPath) {
|
|
11728
11771
|
const problems = [];
|
|
11729
11772
|
const addons = ADDONS.map((name) => {
|
|
11730
|
-
const directory =
|
|
11731
|
-
const marker =
|
|
11773
|
+
const directory = join15(projectPath, "addons", name);
|
|
11774
|
+
const marker = join15(directory, VERSION_MARKER);
|
|
11732
11775
|
const installed = existsSync13(directory);
|
|
11733
11776
|
const version = existsSync13(marker) ? readFileSync11(marker, "utf8").trim() : null;
|
|
11734
11777
|
const current = version === SERVER_VERSION;
|
|
@@ -11739,7 +11782,7 @@ function inspectProject(projectPath) {
|
|
|
11739
11782
|
}
|
|
11740
11783
|
return { name, installed, version, current };
|
|
11741
11784
|
});
|
|
11742
|
-
const settings = parseProjectGodot(readFileSync11(
|
|
11785
|
+
const settings = parseProjectGodot(readFileSync11(join15(projectPath, "project.godot"), "utf8"));
|
|
11743
11786
|
const pluginsEnabled = enabledPlugins(settings);
|
|
11744
11787
|
for (const name of EDITOR_PLUGINS) {
|
|
11745
11788
|
if (!pluginsEnabled.includes(name)) {
|
|
@@ -11755,7 +11798,7 @@ function inspectProject(projectPath) {
|
|
|
11755
11798
|
const tracked = trackedByGit(projectPath, [...named.values()]);
|
|
11756
11799
|
const addonMissing = (path) => addons.some((addon) => !addon.installed && path.startsWith(`addons/${addon.name}/`));
|
|
11757
11800
|
for (const [name, path] of named) {
|
|
11758
|
-
if (!existsSync13(
|
|
11801
|
+
if (!existsSync13(join15(projectPath, path))) {
|
|
11759
11802
|
if (!addonMissing(path)) {
|
|
11760
11803
|
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`);
|
|
11761
11804
|
}
|
|
@@ -11903,7 +11946,7 @@ function annotationsOf(spec) {
|
|
|
11903
11946
|
openWorldHint: false
|
|
11904
11947
|
};
|
|
11905
11948
|
}
|
|
11906
|
-
var PROJECT_INFO_SECTIONS, PROJECT_PATH, RUNNING_PROJECT_PATH, RUNNING_PID, SCENE_PATH, WITHOUT_AN_EDITOR = "Through the editor when one serves the project; with none, a headless engine runs the editor's own tool and the answer says so under writtenBy, or readBy for a read. That engine runs every script of the file, where the editor runs only tool scripts, so what a script changes as the file loads or as a property is set is put back before anything is saved, and a file where that cannot be done, such as a scene whose script does not compile, is refused and nothing is written. Each file the written one refers to is named by its uid, as the editor names it, except a file the project's uid cache has no uid for yet (one made since the project was last scanned), which is named by path and listed under namedByPath with the call that scans it. An editor of the project that is running and has not reached this server is waited for rather than written around.", IMPORTED_FIRST = "importedFirst names the files whose import was behind them on disk, such as a picture changed since it was imported or one never imported, which were imported before the run through project_import reimport, so the run reads them as they are now; it is left out when every import was current. notImportedFirst lists, as project_import reimport does, any it could not import, and importNote says that the run reads those as they were. The import gives every script without a .uid sidecar one, as the editor's scan does, and uidsWrittenFirst names those scripts. importTookMs is how long the import took; it is outside the run's own time limit, so a call that imported can take that much longer than the limit it was given.", WHOLE_NUMBERS = 'A whole number of 2^53 or more, such as a 64-bit seed or a resource uid, is written as its digits, {"_type": "int", "value": "9007199254740993"}, the form a read answers with: a JSON number that large can arrive as a neighbour of the one sent, so sent as a number it is refused.', SCRIPT_PATH, RESOURCE_FILE = 'File inside the project, such as "sprites/hero.png" or "materials/steel.tres".', RESOURCE_PATH, NODE_PATH, PROPERTIES, XY, ANIMATION_TRACK, SCRIPT_MODIFICATIONS, INPUT_EVENTS, TILESET_SOURCES, TILEMAP_CELLS, COLOUR2, TOOL_SPECS, SPECS_BY_NAME;
|
|
11949
|
+
var PROJECT_INFO_SECTIONS, PROJECT_PATH, RUNNING_PROJECT_PATH, RUNNING_PID, SCENE_PATH, WITHOUT_AN_EDITOR = "Through the editor when one serves the project; with none, a headless engine runs the editor's own tool and the answer says so under writtenBy, or readBy for a read. That engine runs every script of the file, where the editor runs only tool scripts, so what a script changes as the file loads or as a property is set is put back before anything is saved, and a file where that cannot be done, such as a scene whose script does not compile, is refused and nothing is written. Each file the written one refers to is named by its uid, as the editor names it, except a file the project's uid cache has no uid for yet (one made since the project was last scanned), which is named by path and listed under namedByPath with the call that scans it. An editor of the project that is running and has not reached this server is waited for rather than written around.", IMPORTED_FIRST = "importedFirst names the files whose import was behind them on disk, such as a picture changed since it was imported or one never imported, which were imported before the run through project_import reimport, so the run reads them as they are now; it is left out when every import was current. notImportedFirst lists, as project_import reimport does, any it could not import, and importNote says that the run reads those as they were. The import gives every script without a .uid sidecar one, as the editor's scan does, writing it beside the script in the project, and uidsWrittenFirst names those scripts. The files inside an addon are counted per addon rather than named, under importedInAddons and uidsWrittenInAddons. importTookMs is how long the import took; it is outside the run's own time limit, so a call that imported can take that much longer than the limit it was given.", WHOLE_NUMBERS = 'A whole number of 2^53 or more, such as a 64-bit seed or a resource uid, is written as its digits, {"_type": "int", "value": "9007199254740993"}, the form a read answers with: a JSON number that large can arrive as a neighbour of the one sent, so sent as a number it is refused.', SCRIPT_PATH, RESOURCE_FILE = 'File inside the project, such as "sprites/hero.png" or "materials/steel.tres".', RESOURCE_PATH, NODE_PATH, PROPERTIES, XY, ANIMATION_TRACK, SCRIPT_MODIFICATIONS, INPUT_EVENTS, TILESET_SOURCES, TILEMAP_CELLS, COLOUR2, TOOL_SPECS, SPECS_BY_NAME;
|
|
11907
11950
|
var init_tool_definitions = __esm(() => {
|
|
11908
11951
|
init_launch();
|
|
11909
11952
|
PROJECT_INFO_SECTIONS = [
|
|
@@ -11942,7 +11985,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
11942
11985
|
};
|
|
11943
11986
|
PROPERTIES = {
|
|
11944
11987
|
type: "object",
|
|
11945
|
-
description: `Properties to set, keyed by Godot property name. Vectors, colours and the like may be written as {"x": 1, "y": 2} or tagged {"_type": "Vector2", "x": 1, "y": 2}, which is the form a read answers with, so a value read off one node can be written straight to another. One carrying a field its type has not, such as a z for a Vector2, is refused naming it. A colour may also be given as its html code, "#ff8000" or "#ff800080". A key may also be a colon path into one of the components of a vector, a colour, a rectangle or a transform, as GDScript names them, so one axis is written alone: "position:x", "modulate:a", "region_rect:size:y"; a component the value does not have is refused, naming the ones it has. A property holding a node takes the path to it from the node being set, such as "../Player". Each value is read back once set, and one the engine changed or refused is refused with what it holds. An enum property takes the name of a value
|
|
11988
|
+
description: `Properties to set, keyed by Godot property name. Vectors, colours and the like may be written as {"x": 1, "y": 2} or tagged {"_type": "Vector2", "x": 1, "y": 2}, which is the form a read answers with, so a value read off one node can be written straight to another. One carrying a field its type has not, such as a z for a Vector2, is refused naming it. A colour may also be given as its html code, "#ff8000" or "#ff800080". A key may also be a colon path into one of the components of a vector, a colour, a rectangle or a transform, as GDScript names them, so one axis is written alone: "position:x", "modulate:a", "region_rect:size:y"; a component the value does not have is refused, naming the ones it has. A property holding a node takes the path to it from the node being set, such as "../Player". A property holding a Resource takes the res:// path of a file, which a read answers with its class and, when its script declares one, scriptClass, or a resource built into the scene as {"_type": "CircleShape2D", "radius": 8}, its class (an engine class or a script's class_name) beside its properties. A typed list or map of resources or nodes, such as Array[Item], takes each in the same forms, a node by its path; a read answers a resource built into the scene in that form, with what it holds away from the defaults, so it too can be written to another node. script takes the res:// path of a script to attach, one with no class_name included, and is attached before the rest are set, so the properties it declares can be given beside it. Each value is read back once set, and one the engine changed or refused is refused with what it holds. An enum property takes its number or the name of a value: as the script spells it, as the inspector shows it, or for an engine enum as the class's constant, so Add or BLEND_MODE_ADD; a value past an export range, or a number no value of an enum has, is written as given and named under outsideDeclared. A key may reach into a resource built into this one: the property holding it, a slash, then one of its own, so a TileSet's first atlas source takes "sources/0/0:0/0/probability". A resource kept in a file of its own, or built into the scene an instance comes from, is written in that file, or replaced here by one of this file's own. The next element of a list is made by writing to it, as the inspector's Add Element makes it: physics_layer_0/collision_layer on a TileSet with no physics layer makes its first, and "sources/0/0:0/0/physics_layer_0/polygon_0/points" a tile's first collision polygon on that layer. ${WHOLE_NUMBERS}`,
|
|
11946
11989
|
additionalProperties: true
|
|
11947
11990
|
};
|
|
11948
11991
|
XY = {
|
|
@@ -12252,7 +12295,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
12252
12295
|
direction: {
|
|
12253
12296
|
type: "string",
|
|
12254
12297
|
enum: ["forward", "reverse"],
|
|
12255
|
-
description: "forward: what this resource loads, with cycles reported, a script's dependencies including every script whose class_name it uses, marked by_class with the name. summary.total_dependencies counts each file once however many places load it
|
|
12298
|
+
description: "forward: what this resource loads, with cycles reported, a script's dependencies including every script whose class_name it uses, marked by_class with the name. summary.total_resources is the file asked about, so 1; total_dependencies counts each file reached, directly or through another, once however many places load it; total_references counts the entries in the tree, and missing names every file referenced that does not exist, with the files referring to it. reverse: every file that refers to it and how, for a file that is gone as well while anything still refers to it, answered with exists: false (a missing file nothing refers to is refused, naming the nearest files), a scene instancing it, a script extending, preloading or loading it, and for a script with a class_name every use of that name. Each reference carries a kind: ext_resource, extends, preload, load or path for a reference by path; setting, plugin, export_preset and import for one in project.godot, a plugin.cfg, export_presets.cfg or an import sidecar; class_name for a use of the declared name, the declaration itself being the script asked about. Two kinds are mentions rather than uses: doc is a name inside a ## documentation comment, which renaming would break in the generated docs, and comment is one in ordinary prose, which it would only make stale. summary.in_code is the count with both of those left out, which is the number to read when the question is whether anything still uses this. Default forward."
|
|
12256
12299
|
},
|
|
12257
12300
|
depth: { type: "number", description: "forward: how many levels to follow. Default unlimited." },
|
|
12258
12301
|
includeBuiltin: {
|
|
@@ -12282,7 +12325,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
12282
12325
|
options: {
|
|
12283
12326
|
type: "object",
|
|
12284
12327
|
ops: ["set_options"],
|
|
12285
|
-
description: `set_options: import options keyed as the .import file spells them, {"compress/mode": 1}. A dictionary option whose keys are numbers, a font's opentype_features or any option already holding number keys, takes them written as strings of digits, and opentype_features also takes an OpenType tag by its four-letter name: {"opentype_features": {"liga": 0}} turns ligatures off. Both are written as the numbers the engine reads.`,
|
|
12328
|
+
description: `set_options: import options keyed as the .import file spells them, {"compress/mode": 1}. A dictionary option whose keys are numbers, a font's opentype_features or any option already holding number keys, takes them written as strings of digits, and opentype_features also takes an OpenType tag by its four-letter name: {"opentype_features": {"liga": 0}} turns ligatures off. Both are written as the numbers the engine reads. One already holding the value given is answered under unchanged_options rather than updated_options, and when none changed nothing is written or reimported.`,
|
|
12286
12329
|
additionalProperties: true
|
|
12287
12330
|
},
|
|
12288
12331
|
reimport: {
|
|
@@ -12407,7 +12450,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
12407
12450
|
timeoutMs: {
|
|
12408
12451
|
type: "integer",
|
|
12409
12452
|
minimum: 1,
|
|
12410
|
-
description: "How long the run may take before it is killed. Default 600000. A run killed at the limit answers timedOut and silentForMs, how long it had printed nothing; it is called hung only when that was half the limit, or thirty seconds of a longer one, and otherwise it was still running and wants a longer timeoutMs or a narrower path, unless it ended repeating one script error, which the verdict names instead. A run whose autoload did not compile is stopped as soon as the engine says so, and names the script that did not under scriptErrors."
|
|
12453
|
+
description: "How long the run may take before it is killed, counted from the engine starting it: the class rebuild and any import before it are outside the limit, and tookMs counts them. Default 600000. A run killed at the limit answers timedOut and silentForMs, how long it had printed nothing; it is called hung only when that was half the limit, or thirty seconds of a longer one, and otherwise it was still running and wants a longer timeoutMs or a narrower path, unless it ended repeating one script error, which the verdict names instead. A run whose autoload did not compile is stopped as soon as the engine says so, and names the script that did not under scriptErrors."
|
|
12411
12454
|
},
|
|
12412
12455
|
printed: {
|
|
12413
12456
|
type: "string",
|
|
@@ -12457,7 +12500,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
12457
12500
|
name: "scene_tree",
|
|
12458
12501
|
title: "Scene tree",
|
|
12459
12502
|
effect: "read",
|
|
12460
|
-
description: "The nodes of a scene file: names, classes and hierarchy, with properties when asked. A node that instances another scene names that file under instanceOf, and the nodes listed under it come from that file rather than this one; its editableChildren says whether this scene, or a scene it inherits, keeps values of its own for them (scene_node set editableChildren). Needs no editor: one serving the project answers and says whether it holds unsaved changes to the scene, and with none a headless engine reads the file without making its nodes, so no script of theirs runs (autoloads still start once it has answered, as in every headless call). For the nodes of a game that is running, runtime_inspect tree.",
|
|
12503
|
+
description: "The nodes of a scene file: names, classes and hierarchy, with properties when asked. A node that instances another scene names that file under instanceOf, and the nodes listed under it come from that file rather than this one; its editableChildren says whether this scene, or a scene it inherits, keeps values of its own for them (scene_node set editableChildren). A node in any group answers groups, sorted, from this file and every scene it comes from. Needs no editor: one serving the project answers and says whether it holds unsaved changes to the scene, and with none a headless engine reads the file without making its nodes, so no script of theirs runs (autoloads still start once it has answered, as in every headless call). For the nodes of a game that is running, runtime_inspect tree.",
|
|
12461
12504
|
parameters: {
|
|
12462
12505
|
projectPath: PROJECT_PATH,
|
|
12463
12506
|
scenePath: SCENE_PATH,
|
|
@@ -12472,7 +12515,8 @@ var init_tool_definitions = __esm(() => {
|
|
|
12472
12515
|
{
|
|
12473
12516
|
name: "scene_node",
|
|
12474
12517
|
title: "Scene nodes",
|
|
12475
|
-
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, and answers nodePath as its new path. A rename or a reparent carries the node paths naming the node, as the editor's rename does: NodePath values on nodes (in arrays, dictionaries, built-in resources, %unique paths) and the tracks its players' animations hold, saving a library kept in a .tres of its own
|
|
12518
|
+
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, and answers nodePath as its new path. A rename or a reparent carries the node paths naming the node, as the editor's rename does: NodePath values on nodes (in arrays, dictionaries, built-in resources, %unique paths) and the tracks its players' animations hold, saving a library kept in a .tres of its own, and answers each rewrite under retargeted and each path it left under notRetargeted. The move is followed into every other .tscn that instances or inherits this scene at any depth: the editable children, values, added nodes and connections named by the old path are rewritten, since Godot finds a moved node's values by unique_id but editable children only by path and drops the values kept below them on the next save, and that scene's node paths and tracks are carried as above. A delete rewrites no path, and answers under stillNaming what named the deleted node and now names nothing. 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. A node this scene inherits takes values of this scene's own, and so does a node inside an instance of another scene once that instance's editableChildren is true; neither can be moved, renamed, copied or deleted, since the other scene makes it again on load. ${WITHOUT_AN_EDITOR}`,
|
|
12519
|
+
answer: "get answers nodeType, the node's engine class, with scriptClass when its script declares a class_name; nodePath, its path from the root however it was named; setHere, the properties this scene's own file sets on it; and valuesFrom, the scene every other value comes from, when another scene makes the node. retargeted lists each path a rename or a reparent rewrote, as {scenePath, nodePath, property or animation and track, from, to}, and notRetargeted each it left, with a reason: a track kept in another scene or an imported file, or a value another scene gives that the move left naming nothing. followed names the other scenes a move rewrote, and notFollowed any left as they were, with why. stillNaming lists each node path and track, here and in the scenes instancing or inheriting this one, that named a deleted node, and deletedWithIt each node of this scene's own that went with it, the nodes inside an instance going with the instance. groups lists, sorted, every group a node is in, on a get, an add that gave addGroups and every set that changes them; get adds groupsHere, the groups this file puts it in, when another scene puts it in others. Each of these lists is answered only when it names anything. alsoChanged gives the other properties the engine changed in step with a set, as a Control's offsets with its size, and those a reparent changed to keep the node where it is drawn, as {was, now}.",
|
|
12476
12520
|
parameters: {
|
|
12477
12521
|
projectPath: PROJECT_PATH,
|
|
12478
12522
|
scenePath: SCENE_PATH,
|
|
@@ -12499,17 +12543,34 @@ var init_tool_definitions = __esm(() => {
|
|
|
12499
12543
|
ops: ["set"],
|
|
12500
12544
|
description: "set: on an instance of another scene, whether its children are editable here, as the editor's Editable Children makes them. True lets scene_node set give the nodes inside that instance values of this scene's own, which no other instance shares. An instance nested inside another takes it once the one around it has editable children. False drops every value this scene keeps for them, and every node it added below one of them. The answer names the nodes whose values went under droppedValues (they stay, with the instance's own values) and the added nodes that went under droppedNodes; a node added straight under the instance stays. The answer says editableChildren as the file now holds it."
|
|
12501
12545
|
},
|
|
12546
|
+
addGroups: {
|
|
12547
|
+
type: "array",
|
|
12548
|
+
items: { type: "string" },
|
|
12549
|
+
ops: ["add", "set"],
|
|
12550
|
+
description: "add and set: groups to put the node in, saved in this scene's file as the editor's Groups dock saves them. One it is in already is kept as it is."
|
|
12551
|
+
},
|
|
12552
|
+
removeGroups: {
|
|
12553
|
+
type: "array",
|
|
12554
|
+
items: { type: "string" },
|
|
12555
|
+
ops: ["set"],
|
|
12556
|
+
description: "set: groups to take the node out of. One it is not in is refused, and so is one another scene puts it in (the scene that makes it, instances it or is inherited here), which only that scene can take it out of."
|
|
12557
|
+
},
|
|
12502
12558
|
newName: { type: "string", ops: ["duplicate"], description: "duplicate: the copy's name." },
|
|
12503
12559
|
newParentPath: {
|
|
12504
12560
|
type: "string",
|
|
12505
12561
|
ops: ["reparent"],
|
|
12506
|
-
description: "reparent: the new parent, which may be the one it has, to move it among its siblings."
|
|
12562
|
+
description: "reparent: the new parent, which may be the one it has, to move it among its siblings. In the scenes the move is followed into, the parent_id_path, from_uid_path and to_uid_path saved beside each rewritten path are dropped, since their ids ran through the old parent; the node's own unique_id is kept, and the next save writes the dropped ones again."
|
|
12563
|
+
},
|
|
12564
|
+
keepGlobalTransform: {
|
|
12565
|
+
type: "boolean",
|
|
12566
|
+
ops: ["reparent"],
|
|
12567
|
+
description: "reparent: whether the node stays where it is drawn, its transform worked out again under the new parent, as the editor's reparent and Node.reparent keep it. A Control keeps its position only. Default true; false keeps its own transform, so it moves with the new parent."
|
|
12507
12568
|
},
|
|
12508
12569
|
index: {
|
|
12509
12570
|
type: "integer",
|
|
12510
12571
|
minimum: 0,
|
|
12511
|
-
ops: ["add", "reparent"],
|
|
12512
|
-
description: "add and reparent: where the node goes among the parent's children, 0 for first. Default last. Child order is draw order in a CanvasItem and layout order in a Container."
|
|
12572
|
+
ops: ["add", "duplicate", "reparent"],
|
|
12573
|
+
description: "add, duplicate and reparent: where the node goes among the parent's children, 0 for first. Default last, except for a copy under the original's own parent, which goes right after it. Child order is draw order in a CanvasItem and layout order in a Container."
|
|
12513
12574
|
},
|
|
12514
12575
|
includeDefaults: {
|
|
12515
12576
|
type: "boolean",
|
|
@@ -12532,22 +12593,22 @@ var init_tool_definitions = __esm(() => {
|
|
|
12532
12593
|
},
|
|
12533
12594
|
get: {
|
|
12534
12595
|
effect: "read",
|
|
12535
|
-
summary: "read a node's properties, and a TileMapLayer's or TileMap's cells as set_tilemap_cells takes them, the first 1000 with cellCount for all. An instance of another scene also answers instanceOf and editableChildren, as scene_tree does",
|
|
12596
|
+
summary: "read a node's class, path, properties and where its values come from, and a TileMapLayer's or TileMap's cells as set_tilemap_cells takes them, the first 1000 with cellCount for all. An instance of another scene also answers instanceOf and editableChildren, as scene_tree does",
|
|
12536
12597
|
requires: ["nodePath"]
|
|
12537
12598
|
},
|
|
12538
12599
|
set: {
|
|
12539
12600
|
effect: "destructive",
|
|
12540
|
-
summary: "set properties on a node,
|
|
12601
|
+
summary: "set properties on a node, make the children of an instance editable, or put it in groups or take it out of them; give any of them together",
|
|
12541
12602
|
requires: ["nodePath"]
|
|
12542
12603
|
},
|
|
12543
12604
|
duplicate: {
|
|
12544
12605
|
effect: "additive",
|
|
12545
|
-
summary: "copy a node and its children,
|
|
12606
|
+
summary: "copy a node and its children, right after it or under parentNodePath, answering the copy's newNodePath, parentNodePath and index",
|
|
12546
12607
|
requires: ["nodePath", "newName"]
|
|
12547
12608
|
},
|
|
12548
12609
|
reparent: {
|
|
12549
12610
|
effect: "destructive",
|
|
12550
|
-
summary: "move a node under another parent, or to another place among its siblings",
|
|
12611
|
+
summary: "move a node under another parent, keeping where it is drawn, or to another place among its siblings",
|
|
12551
12612
|
requires: ["nodePath", "newParentPath"]
|
|
12552
12613
|
},
|
|
12553
12614
|
delete: { effect: "destructive", summary: "remove a node and its children", requires: ["nodePath"] },
|
|
@@ -12561,7 +12622,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
12561
12622
|
{
|
|
12562
12623
|
name: "scene_signal",
|
|
12563
12624
|
title: "Scene signals",
|
|
12564
|
-
description: `Signal connections in a scene file. Each connection answers with its flags as the number and as flagNames; persist is on every connection, since a scene saves no other kind. A connect of a pair already connected says alreadyConnected, with flagsBefore when it changed the flags. A connect to a method the target does not have is still written, as the editor's own dialog allows, since the method may be written next; methodFound false says so, and until the method exists the signal fails each time it is emitted. list marks editable false on a connection an instanced scene makes inside itself, which only that scene can change. ${WITHOUT_AN_EDITOR}`,
|
|
12625
|
+
description: `Signal connections in a scene file. Each connection answers with its flags as the number and as flagNames; persist is on every connection, since a scene saves no other kind. A connect of a pair already connected says alreadyConnected, with flagsBefore when it changed the flags. A connect to a method the target does not have is still written, as the editor's own dialog allows, since the method may be written next; methodFound false says so, and until the method exists the signal fails each time it is emitted. One whose method cannot take what the signal passes, with its binds and unbinds (too few or too many arguments, an object where the method takes a value or the reverse, or an engine class unrelated to the one it takes), is written too, and answers argumentsFit false with misfit saying why; list gives each connection its methodFound, and misfit when it has one. list marks editable false on a connection an instanced scene makes inside itself, which only that scene can change. ${WITHOUT_AN_EDITOR}`,
|
|
12565
12626
|
parameters: {
|
|
12566
12627
|
projectPath: PROJECT_PATH,
|
|
12567
12628
|
scenePath: SCENE_PATH,
|
|
@@ -12618,7 +12679,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
12618
12679
|
animationName: {
|
|
12619
12680
|
type: "string",
|
|
12620
12681
|
ops: ["read", "create", "add_track", "set", "rename", "remove", "add_state"],
|
|
12621
|
-
description: "The animation in the player. read: the one to answer with whole, its tracks a list; without it, read lists them, each with the number of its tracks under tracks. add_state: the one that state plays."
|
|
12682
|
+
description: "The animation in the player. read: the one to answer with whole, its tracks a list; without it, read lists them, each with the number of its tracks under tracks. add_state: the one that state plays. One the player does not have yet is written all the same, answering animationFound false and why under animationMissing."
|
|
12622
12683
|
},
|
|
12623
12684
|
newName: {
|
|
12624
12685
|
type: "string",
|
|
@@ -12743,7 +12804,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
12743
12804
|
extends: {
|
|
12744
12805
|
type: "string",
|
|
12745
12806
|
ops: ["create"],
|
|
12746
|
-
description: "create: the base class. Default Node. Not with content."
|
|
12807
|
+
description: "create: the base class. Default the class the template is written for, or Node with no template. Not with content."
|
|
12747
12808
|
},
|
|
12748
12809
|
content: {
|
|
12749
12810
|
type: "string",
|
|
@@ -12754,13 +12815,13 @@ var init_tool_definitions = __esm(() => {
|
|
|
12754
12815
|
type: "string",
|
|
12755
12816
|
enum: ["singleton", "state_machine", "component", "resource"],
|
|
12756
12817
|
ops: ["create"],
|
|
12757
|
-
description: "create: a starting shape."
|
|
12818
|
+
description: "create: a starting shape. resource is written for a Resource and the others for a Node; an extends outside that class is refused."
|
|
12758
12819
|
},
|
|
12759
12820
|
modifications: { ...SCRIPT_MODIFICATIONS, ops: ["modify"] },
|
|
12760
12821
|
symbol: {
|
|
12761
12822
|
type: "string",
|
|
12762
12823
|
ops: ["rename"],
|
|
12763
|
-
description: "rename: what to rename, declared by the script at scriptPath: its class_name, or a method, variable, constant, signal, enum, enum value or inner class at its top level. A
|
|
12824
|
+
description: "rename: what to rename, declared by the script at scriptPath: its class_name, or a method, variable, constant, signal, enum, enum value or inner class at its top level. A class_name is renamed from the files and needs no editor: every bare use in code, the declaration, doc links such as [Told] and [method Told.say], and the script_class a .tres records. A script that declares something of its own by the old name, and a use after a dot, are left alone, since which of those mean the class cannot be told from the text. Everything else, an inner class and an enum value included, is renamed from what the editor's language server resolves to it, so it needs the editor open on this project; a method is renamed together with every override of it in the classes inheriting it, and from the topmost class declaring it when scriptPath's is itself an override. Doc links naming the member through any class inheriting it are renamed too, and so are a scene's or resource's saved value of a variable on a node or resource whose script inherits it, and a connection naming the signal on its emitter or the method on its receiver. A name the engine itself declares on the native base, such as _ready, is refused, as is a new name already taken: by a class, an autoload, an identifier in code, a native class or built-in type, or a member anywhere in the class's chain."
|
|
12764
12825
|
},
|
|
12765
12826
|
newName: { type: "string", ops: ["rename"], description: "rename: the new name." },
|
|
12766
12827
|
newScriptPath: {
|
|
@@ -12814,7 +12875,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
12814
12875
|
operations: {
|
|
12815
12876
|
structure: {
|
|
12816
12877
|
effect: "read",
|
|
12817
|
-
summary: "functions, variables, signals, constants, enums, inner_classes, class_name, extends and line_count, read from the file, and dependencies: the paths it loads or preloads as written, then the scripts declaring each project class_name its code uses, which classes_used names with the class; autoloads_used names each autoload the code uses by its global name, with the autoload's script, which the project loads rather than this script, so it is not under dependencies",
|
|
12878
|
+
summary: "functions, variables (static ones marked is_static; one written with := has the type of a literal or built-in constructor it is given, marked inferred, as a constant always does), signals, constants, enums, inner_classes (each with its name, line and extends, and its own members in these same fields), class_name, extends and line_count, read from the file, and dependencies: the paths it loads or preloads as written, then the scripts declaring each project class_name its code uses, which classes_used names with the class; autoloads_used names each autoload the code uses by its global name, with the autoload's script, which the project loads rather than this script, so it is not under dependencies",
|
|
12818
12879
|
requires: []
|
|
12819
12880
|
},
|
|
12820
12881
|
symbols: {
|
|
@@ -12835,7 +12896,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
12835
12896
|
name: "script_diagnostics",
|
|
12836
12897
|
title: "Script diagnostics",
|
|
12837
12898
|
effect: "read",
|
|
12838
|
-
description: "Errors and warnings for a script, or for several, from the editor's language server, and whether each is clean. With no editor of this project open, so no language server of it to ask, a short headless engine compiles the scripts instead and the answer says checkedBy: engine: every error that stops a script compiling, with its line and no column, dependenciesWithErrors naming the other scripts that failed while it loaded, typesMissingFromTheClassCache naming a class the project declares that the engine could not resolve because the class cache lacks it, with staleAnalysis saying what rewrites the cache, and warnings null, since the engine reports warnings only to an editor. Each diagnostic carries its severity (error or warning), its message, and where it is as the editor shows it: line and column counted from 1, and endLine and endColumn just past its end. Diagnostics are checked against the project's own files, and the two that the files disprove are named rather than passed on: a member the class cache's file declares, or a project class it extends does, a method, property, constant, static function or enum member, comes back under contradictedByTheFile with the script declaring it, and so does one a diagnostic names alone as an external class member, read against the class it is looked up on in the line it is on; and a class this project declares that the diagnostic could not resolve comes back under typesTheEditorHasNotLoaded, with inTheClassCache saying whether a launched game would resolve it. staleAnalysis then says what to do, and for both states it is editor_rescan first: it asks the editor already running, starts nothing, and has cleared each in every reproduction measured. Two exceptions are named when they apply. For an enum member, constant or static function of a global class the reload comes first: in the one project that has reproduced a stale enum member, editor_rescan with reloadScript on the declaring script cleared it and a plain rescan did not. And an editor whose last scan wrote the class cache shorter than the file holds a list behind the files and writes it on every scan, so on that editor the rescan loads nothing and staleAnalysis says editor_launch restart instead. What follows differs: for a stale analysed type, editor_rescan with reloadScript recompiles the held copy and editor_launch restart is the last resort that costs a window; for a class missing from the cache, project_import refresh_classes rewrites the cache from disk at the cost of a short headless engine, which matters beside a running bench.",
|
|
12899
|
+
description: "Errors and warnings for a script, or for several, from the editor's language server, and whether each is clean. With no editor of this project open, so no language server of it to ask, a short headless engine compiles the scripts instead and the answer says checkedBy: engine: every error that stops a script compiling, with its line and no column (a call to a method another class lacks is not one, since GDScript checks those only on self: the object may be a subclass that has it, so it fails at run time instead), dependenciesWithErrors naming the other scripts that failed while it loaded, typesMissingFromTheClassCache naming a class the project declares that the engine could not resolve because the class cache lacks it, with staleAnalysis saying what rewrites the cache, and warnings null, since the engine reports warnings only to an editor. Each diagnostic carries its severity (error or warning), its message, and where it is as the editor shows it: line and column counted from 1, and endLine and endColumn just past its end. Diagnostics are checked against the project's own files, and the two that the files disprove are named rather than passed on: a member the class cache's file declares, or a project class it extends does, a method, property, constant, static function or enum member, comes back under contradictedByTheFile with the script declaring it, and so does one a diagnostic names alone as an external class member, read against the class it is looked up on in the line it is on; and a class this project declares that the diagnostic could not resolve comes back under typesTheEditorHasNotLoaded, with inTheClassCache saying whether a launched game would resolve it. staleAnalysis then says what to do, and for both states it is editor_rescan first: it asks the editor already running, starts nothing, and has cleared each in every reproduction measured. Two exceptions are named when they apply. For an enum member, constant or static function of a global class the reload comes first: in the one project that has reproduced a stale enum member, editor_rescan with reloadScript on the declaring script cleared it and a plain rescan did not. And an editor whose last scan wrote the class cache shorter than the file holds a list behind the files and writes it on every scan, so on that editor the rescan loads nothing and staleAnalysis says editor_launch restart instead. What follows differs: for a stale analysed type, editor_rescan with reloadScript recompiles the held copy and editor_launch restart is the last resort that costs a window; for a class missing from the cache, project_import refresh_classes rewrites the cache from disk at the cost of a short headless engine, which matters beside a running bench.",
|
|
12839
12900
|
parameters: {
|
|
12840
12901
|
projectPath: PROJECT_PATH,
|
|
12841
12902
|
scriptPath: {
|
|
@@ -12928,7 +12989,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
12928
12989
|
create_shader: { effect: "destructive", summary: "a .gdshader file", requires: ["shaderType"] },
|
|
12929
12990
|
create_tileset: {
|
|
12930
12991
|
effect: "destructive",
|
|
12931
|
-
summary: "a TileSet from texture atlases",
|
|
12992
|
+
summary: "a TileSet from texture atlases; its physics, terrain and custom data layers, and each tile's collision and other values, are written with modify after it",
|
|
12932
12993
|
requires: ["sources"]
|
|
12933
12994
|
},
|
|
12934
12995
|
set_theme_color: {
|
|
@@ -12951,7 +13012,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
12951
13012
|
{
|
|
12952
13013
|
name: "editor_launch",
|
|
12953
13014
|
title: "Open, restart or close the editor",
|
|
12954
|
-
description: "Opens the Godot editor on a project, or restarts or closes the one already connected. close saves open scenes, quits the editor and waits for its process to go, which is the only way to end a hidden one short of killing it; settingsDropped in its answer
|
|
13015
|
+
description: "Opens the Godot editor on a project, or restarts or closes the one already connected. close saves open scenes, quits the editor and waits for its process to go, which is the only way to end a hidden one short of killing it; settingsDropped in its answer names what that save took out of project.godot, and is left out when it took nothing; settingsNote says so when the file could not be read to tell. An editor this server opened that has not connected yet has nothing of this server's to save, so close stops its process instead and answers beforeConnecting. open answers once the process has started, and the editor dials in after its import; a call needing the editor made before then waits for the one this server opened, for as long as a restart waits, and is refused naming it if it has not arrived. open refuses while an editor of the project is already running and has not reached this server, naming its pid, rather than putting a second on the project. The editor is an ordinary visible one on the desktop in use, and on Windows it is shown without being activated, so opening or reopening it does not take the keyboard from whatever the user is typing in. hidden: true opens it headless instead and, on Windows, on a desktop of its own, where the games it plays go too; a hidden editor serves the bridge, the language server and the debug adapter as a visible one does. A game a visible editor plays opens as a window on the desktop in use. An editor goes on serving the addon it read at startup, so restart is what puts a gdharness upgrade into effect, and nothing else here restarts one; it saves open scenes on the way out and answers with the version that came back. An editor a server opened is started again by the server, visible unless hidden: true is passed again, once the old one has gone: it waits 90 seconds for that, or GDHARNESS_EDITOR_RESTART_TIMEOUT_MS, and an old editor still running after it is not replaced, since the new one would find its ports held. One opened by hand restarts itself, which only one with a window can do, because the engine hands back none of the arguments it was started with. editor_status says which editor is connected and whether it is holding an old addon. Opening a project is a save and so is closing one, which is the part that is not obvious from outside: the editor imports the project on open and writes project.godot back, and Godot writes only what differs from its own defaults, so a key named deliberately at its default value is dropped either way. A restart says which under settingsDropped in its own answer because it waits for the editor to return. An open cannot, since it answers as soon as the process exists and the save happens during the import minutes later, so editor_status carries that reading once the editor has connected. Nothing else will say a key went until something depends on one. A restart of an editor this server opened is a quit and then a launch, and this server can be ended between them, usually by the reconnect the restart was being taken for: the editor is gone and nothing replaces it. That is written down before the quit, so the next server says so under restartInterrupted in editor_status rather than waiting for an editor that is not coming, and open finishes it, saying so under finishesRestart. An open is refused when the editor it would start could never reach this server: a project other than the one this server serves, whose editor dials that project's own server; a project without the gdharness editor addon installed and enabled; and a project whose editor a restart is already bringing back.",
|
|
12955
13016
|
parameters: {
|
|
12956
13017
|
projectPath: { ...PROJECT_PATH, ops: ["open"] },
|
|
12957
13018
|
hidden: {
|
|
@@ -13188,7 +13249,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
13188
13249
|
title: "Editor status",
|
|
13189
13250
|
effect: "read",
|
|
13190
13251
|
description: "Whether the editor addon is connected, which Godot answers, whether the editor is playing something, and which games with the runtime addon are reachable, of this project and of every other on the machine. Read connected with mayYetConnect: the editor dials this server and backs off between tries, so for the first half-minute after a server starts false means not reached yet as often as no editor; mayYetConnect true is worth waiting out, false is final, and editorCannotConnect, restartInterrupted, restartUnderway, closedHere and editorsNotYetConnected say why. An editor that has connected and not yet greeted reports nothing about itself, and every other call made in that window waits up to ten seconds for it to settle. playingInEditor null means unanswered rather than nothing playing: processActive and runtimes do not depend on the editor answering. game.run names the run editor_output and editor_run stop answer for, and stays named after it ends. addonIsStale, on a connected editor and on each running game (absent with neither), says it runs addon code other than what this server ships, and staleNote says which half moves: the project, by a gdharness upgrade it spells out to run in a shell, the editor by a restart, or this server by a reconnect. A false editor.lspListening or editor.dapListening comes with serversNote, and editor_launch restart binds the port again. editor.scan says whether the editor is scanning, which editor_run waits out before starting a game. A game announced in a protocol this server does not speak is listed under unreadable, during an upgrade that moves the protocol, until the halves match.",
|
|
13191
|
-
answer: "projectIs, when present, is the gdharness version of the addons in the project, which differs from this server's (project_info says which of the two to move), and configIs, when present, the version the project's MCP config starts, which differs from this server's, so the next connection runs that one. server names this server: its pid, its version, and lifecycleLog, the file every gdharness server of the user appends a line to when it starts, when its client first names itself, when it replaces another server of its project, and when it ends, with why it ended (the client closing its end as stdin:end, a signal by name, superseded with the successor's pid, an uncaught exception or rejection with its stack, or the exit code) and the calls it was answering then. The parent's command line follows on its own line a few seconds after the start. A server killed without warning writes no ended line, and on Windows every kill is that, so a started line with no ended line after it is a server something killed. " + "runtimeConnected counts the games of the project this server serves, and runtimes lists every game on the machine with ofThisProject saying which is which, since a game of another project is reached only by naming its projectPath. playingInEditor is null when the editor was not asked or did not answer, which is not the same as a no: with no editor connected there is nobody to ask, and an editor that does not serve the question is given a moment and then left. A game can be playing while this is null, so read it as unanswered rather than as nothing playing, and use processActive and runtimes, which come from elsewhere and do not depend on the editor answering. Its play says which play the editor is on: play.play counts the plays that editor has started since it opened, whoever started them, and play.editorPid is the editor, so two reads with different pairs are two plays even when both say playing. game.run names the run processActive is about, the one editor_output and editor_run stop answer for: its pid, its project, and adopted true when this server read it back from another server rather than starting it. It stays named after the run ends, beside processActive false, so editor_output can still read what that run printed; null when there is no run at all. startedAt says when it began, and endedAt when it ended, wherever something saw the end: a run adopted from a note another server wrote can have ended hours before this server started. editor.lspListening and editor.dapListening say whether anything accepts connections on the ports the editor names, lspPort and dapPort: Godot binds each once, as the editor starts, and reports a failure only in the log panel of the editor itself, so a false comes with serversNote, and editor_launch restart binds it again. editor.scan says whether the editor is scanning or importing and how many milliseconds ago its last scan finished (finishedMsAgo), or is null when it did not answer: the editor rewrites the class cache just after a scan, and editor_run waits that out before starting a game, saying so under waitedForEditorScanMs. Each game under runtimes is pinged with a short wait, so one held at a breakpoint is listed as unreachable rather than making this call wait out the runtime timeout; for the run this server is reporting on, heldAt says whether that is what it is, with the same three answers editor_output gives. A game the editor played announces the editor under editorPid, and so does whatever that game started for itself; a game some other server started announces none, which is how the two are told apart when both are running from one project. Each game also says which runtime addon it loaded, under addonVersion (null when it cannot say); addonIsStale is true when that code is not what this server ships, and staleNote says whether the project needs the gdharness upgrade command, spelled out to run in a shell, the game a restart, or this server a reconnect. A second checkout of a project, such as a git worktree, carries addons of its own and is where this shows. A runtime_* call to such a game is refused with the staleNote and not sent. When connected is false, mayYetConnect says whether that is final: the editor dials this server rather than the other way round and backs off between tries, so for the first half-minute after a server starts, false means \"not reached yet\" as often as it means \"no editor\". True is worth waiting out; false is an editor that is not there. False beside editorCannotConnect is a project without the gdharness editor addon installed or enabled, where no editor can ever dial in, however young the server or whichever editor is open; its remedies say how to install or enable it. False beside restartInterrupted is one that an editor_launch restart on a previous server asked to quit, where the launch half never ran because that server was ended in between, usually by the reconnect the restart was taken for; editor_launch open finishes it. True beside restartUnderway is a restart still in progress, its server alive and within the wait for the old editor to go: the editor is coming, and opening another would put two on one project. listeningSince is when this server took the bridge port, which is what that window is measured from and what a caller wanting its own window should read. An editor this server opened counts for as long as its process lives, however far past that window the import runs, and awaitingLaunchedEditor names its pid so the two reasons for true are told apart. Any editor of this project running and not yet connected, whoever opened it, makes it true as well and is named under editorsNotYetConnected with its pid and start time, so a restart a reconnect interrupted after its launch is not read as nothing being there. settingsDropped appears here once when the editor this server opened saved project.godot on its way in, which is the same reading editor_launch restart makes in its own answer. A game that has announced itself in a protocol this server does not speak is listed under unreadable rather than left out, with the protocol it speaks and whether the server or the addon is the older half. This happens during an upgrade that moves the runtime protocol, which most do not: installing replaces the addon on disk as soon as the pin moves, while a server that is already running stays on the version it started with, so the two speak different protocols until the server is reconnected. Those games are real and running, but no tool here can reach them until the halves match, so runtimes stays empty and runtimeConnected stays false. An upgrade that leaves the protocol alone opens no such window and this list stays empty through it. addonIsStale is decided by the code of the editor addons rather than their version, so an editor started before an upgrade that left that code alone reports the older addonVersion, is not stale, and addonNote says so: it needs no restart. A stale editor is asked nothing but to quit: every other call to it is refused with staleNote, which names editor_launch restart, the upgrade command or a reconnect. port is the one the editor bridge is on; when that is not the one this server was configured with, portWanted names the configured one and portNote says why it moved and, when it was the server this one replaced, which pid kept it. A server replacing another of its project that holds the configured port waits for it to stand down and takes the port over, which takes a few seconds; bridgeHandover says so while it waits, and it moves only if that server has not let go within the wait. An editor that has connected and not yet greeted, which lasts a frame and longer while it imports, reports nothing about itself: greeting says so, addonIsStale is absent rather than guessed, and a restart asked then waits for the greeting to learn who opened it. startedByAnEditor is an editor another editor started, by Godot's own restart or the project manager, which has none of the arguments a server gave the one before it; startedByAnEditorNote says what that costs. An engine run that loads the editor addon to do one job and quit, such as a gate's godot --headless --import, an export or a script, is not taken as the editor: a process that greets without a server's mark has its command line read first, and
|
|
13252
|
+
answer: "projectIs, when present, is the gdharness version of the addons in the project, which differs from this server's (project_info says which of the two to move), and configIs, when present, the version the project's MCP config starts, which differs from this server's, so the next connection runs that one. server names this server: its pid, its version, and lifecycleLog, the file every gdharness server of the user appends a line to when it starts, when its client first names itself, when it replaces another server of its project, and when it ends, with why it ended (the client closing its end as stdin:end, a signal by name, superseded with the successor's pid, an uncaught exception or rejection with its stack, or the exit code) and the calls it was answering then. The parent's command line follows on its own line a few seconds after the start. A server killed without warning writes no ended line, and on Windows every kill is that, so a started line with no ended line after it is a server something killed. " + "runtimeConnected counts the games of the project this server serves, and runtimes lists every game on the machine with ofThisProject saying which is which, since a game of another project is reached only by naming its projectPath. playingInEditor is null when the editor was not asked or did not answer, which is not the same as a no: with no editor connected there is nobody to ask, and an editor that does not serve the question is given a moment and then left. A game can be playing while this is null, so read it as unanswered rather than as nothing playing, and use processActive and runtimes, which come from elsewhere and do not depend on the editor answering. Its play says which play the editor is on: play.play counts the plays that editor has started since it opened, whoever started them, and play.editorPid is the editor, so two reads with different pairs are two plays even when both say playing. game.run names the run processActive is about, the one editor_output and editor_run stop answer for: its pid, its project, and adopted true when this server read it back from another server rather than starting it. It stays named after the run ends, beside processActive false, so editor_output can still read what that run printed; null when there is no run at all. startedAt says when it began, and endedAt when it ended, wherever something saw the end: a run adopted from a note another server wrote can have ended hours before this server started. editor.lspListening and editor.dapListening say whether anything accepts connections on the ports the editor names, lspPort and dapPort: Godot binds each once, as the editor starts, and reports a failure only in the log panel of the editor itself, so a false comes with serversNote, and editor_launch restart binds it again. editor.scan says whether the editor is scanning or importing and how many milliseconds ago its last scan finished (finishedMsAgo), or is null when it did not answer: the editor rewrites the class cache just after a scan, and editor_run waits that out before starting a game, saying so under waitedForEditorScanMs. Each game under runtimes is pinged with a short wait, so one held at a breakpoint is listed as unreachable rather than making this call wait out the runtime timeout; for the run this server is reporting on, heldAt says whether that is what it is, with the same three answers editor_output gives. A game the editor played announces the editor under editorPid, and so does whatever that game started for itself; a game some other server started announces none, which is how the two are told apart when both are running from one project. Each game also says which runtime addon it loaded, under addonVersion (null when it cannot say); addonIsStale is true when that code is not what this server ships, and staleNote says whether the project needs the gdharness upgrade command, spelled out to run in a shell, the game a restart, or this server a reconnect. A second checkout of a project, such as a git worktree, carries addons of its own and is where this shows. A runtime_* call to such a game is refused with the staleNote and not sent. When connected is false, mayYetConnect says whether that is final: the editor dials this server rather than the other way round and backs off between tries, so for the first half-minute after a server starts, false means \"not reached yet\" as often as it means \"no editor\". True is worth waiting out; false is an editor that is not there. False beside editorCannotConnect is a project without the gdharness editor addon installed or enabled, where no editor can ever dial in, however young the server or whichever editor is open; its remedies say how to install or enable it. False beside restartInterrupted is one that an editor_launch restart on a previous server asked to quit, where the launch half never ran because that server was ended in between, usually by the reconnect the restart was taken for; editor_launch open finishes it. True beside restartUnderway is a restart still in progress, its server alive and within the wait for the old editor to go: the editor is coming, and opening another would put two on one project. listeningSince is when this server took the bridge port, which is what that window is measured from and what a caller wanting its own window should read. An editor this server opened counts for as long as its process lives, however far past that window the import runs, and awaitingLaunchedEditor names its pid so the two reasons for true are told apart. Any editor of this project running and not yet connected, whoever opened it, makes it true as well and is named under editorsNotYetConnected with its pid and start time, so a restart a reconnect interrupted after its launch is not read as nothing being there. settingsDropped appears here once when the editor this server opened saved project.godot on its way in and took a key out of it, which is the same reading editor_launch restart makes in its own answer. A game that has announced itself in a protocol this server does not speak is listed under unreadable rather than left out, with the protocol it speaks and whether the server or the addon is the older half. This happens during an upgrade that moves the runtime protocol, which most do not: installing replaces the addon on disk as soon as the pin moves, while a server that is already running stays on the version it started with, so the two speak different protocols until the server is reconnected. Those games are real and running, but no tool here can reach them until the halves match, so runtimes stays empty and runtimeConnected stays false. An upgrade that leaves the protocol alone opens no such window and this list stays empty through it. addonIsStale is decided by the code of the editor addons rather than their version, so an editor started before an upgrade that left that code alone reports the older addonVersion, is not stale, and addonNote says so: it needs no restart. A stale editor is asked nothing but to quit: every other call to it is refused with staleNote, which names editor_launch restart, the upgrade command or a reconnect. port is the one the editor bridge is on; when that is not the one this server was configured with, portWanted names the configured one and portNote says why it moved and, when it was the server this one replaced, which pid kept it. A server replacing another of its project that holds the configured port waits for it to stand down and takes the port over, which takes a few seconds; bridgeHandover says so while it waits, and it moves only if that server has not let go within the wait. An editor that has connected and not yet greeted, which lasts a frame and longer while it imports, reports nothing about itself: greeting says so, addonIsStale is absent rather than guessed, and a restart asked then waits for the greeting to learn who opened it. startedByAnEditor is an editor another editor started, by Godot's own restart or the project manager, which has none of the arguments a server gave the one before it; startedByAnEditorNote says what that costs. An engine run that loads the editor addon to do one job and quit, such as a gate's godot --headless --import, an export or a script, is not taken as the editor: a process that greets without a server's mark has its command line read first, and from the socket opening until its greeting and that read are done, identifying is true and connected false; one started with such an option is turned away, and turnedAway names the last one, its pid and the option. Every other call made while an editor is arriving, before its greeting or while it is looked up, waits up to ten seconds for it to settle rather than acting on connected as it reads in that window. serverVersion is the gdharness version of this server. bridgeAvailable says the editor bridge is listening; when it failed to start, startupError gives the error, and retryingBridge is true while this server tries again. announcedAt is the file where this server told editors of its project which port to dial. connectedAt is when the editor connected, and lastPongAt when it last answered this server's keep-alive ping. addonDigest is a hash of the editor addon's code, which addonIsStale compares with what this server ships. debugPort is the port of the editor's remote debugger, the one games it plays connect to. openedByAServer says a gdharness server started this editor, and syncsBreakpoints that it keeps its breakpoints when a debug session opens, which Godot otherwise clears. pendingRequests counts the requests sent to the editor and not yet answered, and queuedResources the files with writes waiting behind another write to the same file.",
|
|
13192
13253
|
parameters: {},
|
|
13193
13254
|
requires: []
|
|
13194
13255
|
},
|
|
@@ -13210,7 +13271,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
13210
13271
|
},
|
|
13211
13272
|
listMembers: {
|
|
13212
13273
|
type: "boolean",
|
|
13213
|
-
description: "With reloadScript: also answer the whole member lists, heldBeforeReload and reloadedMethods for the methods, heldConstantsBeforeReload and reloadedConstants for the constants. Default false: a script holding hundreds of constants answers hundreds of lines twice to say what one reload added."
|
|
13274
|
+
description: "With reloadScript: also answer the whole member lists, heldBeforeReload and reloadedMethods for the methods, heldConstantsBeforeReload and reloadedConstants for the constants, each with what the script inherits from the scripts it extends. Default false: a script holding hundreds of constants answers hundreds of lines twice to say what one reload added."
|
|
13214
13275
|
}
|
|
13215
13276
|
},
|
|
13216
13277
|
requires: ["projectPath"]
|
|
@@ -13254,7 +13315,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
13254
13315
|
includeInherited: {
|
|
13255
13316
|
type: "boolean",
|
|
13256
13317
|
ops: ["info"],
|
|
13257
|
-
description: "info:
|
|
13318
|
+
description: "info: also name every member the class inherits, under inherited: one entry per ancestor, nearest first, with the names of the methods, properties, signals and enums it declares. The class's own members stay in full; member reads any inherited one in full. Default false."
|
|
13258
13319
|
},
|
|
13259
13320
|
member: {
|
|
13260
13321
|
type: "string",
|
|
@@ -13392,7 +13453,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
13392
13453
|
{
|
|
13393
13454
|
name: "runtime_invoke",
|
|
13394
13455
|
title: "Set or call in the running game",
|
|
13395
|
-
description: "Sets a property or calls a method on a node, or on one of the engine singletons nodePath names, in the running game. Needs the game running with the runtime addon. The answer carries elapsed_usec, how long the call or the write took inside the game, measured around it there: the round trip to the game is a second or more, so timing from outside measures that instead. Errors the engine reported while a called method ran come back under errors and warnings under warnings, the first 20 of each, with errors_not_shown and warnings_not_shown counting the rest. A call that takes longer than timeoutMs is not cancelled: the answer is pending: true with a requestId, the call goes on and does everything it was asked, and op result with that requestId collects its reply once it comes.",
|
|
13456
|
+
description: "Sets a property or calls a method on a node, or on one of the engine singletons nodePath names, in the running game. Needs the game running with the runtime addon. The answer carries elapsed_usec, how long the call or the write took inside the game, measured around it there: the round trip to the game is a second or more, so timing from outside measures that instead. A method that awaits is answered with the value it returns once it ends, and awaited true; elapsed_usec then runs to its end, and one that outlasts timeout is collected with op result. Errors the engine reported while a called method ran come back under errors and warnings under warnings, the first 20 of each, with errors_not_shown and warnings_not_shown counting the rest. A call that takes longer than timeoutMs is not cancelled: the answer is pending: true with a requestId, the call goes on and does everything it was asked, and op result with that requestId collects its reply once it comes.",
|
|
13396
13457
|
parameters: {
|
|
13397
13458
|
projectPath: RUNNING_PROJECT_PATH,
|
|
13398
13459
|
pid: RUNNING_PID,
|
|
@@ -13409,7 +13470,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
13409
13470
|
value: {
|
|
13410
13471
|
ops: ["set"],
|
|
13411
13472
|
blank: true,
|
|
13412
|
-
description: `set: the value, fitted to the property's type. ${WHOLE_NUMBERS} A number the property would have to cut is refused rather than truncated: a fraction into an int, a number other than 0 or 1 into a bool. A vector, colour or the like may be written as its fields, {"x": 140, "y": 300}, or tagged, {"_type": "Vector2", "x": 140, "y": 300}, which is the form a read answers with, and one carrying a field its type has not, such as a z for a Vector2, is refused naming it. A colour may also be given as its html code, "#ff8000" or "#ff800080". "" writes an empty string. A property typed as or holding an object takes a path naming one the game holds, the way call's args do, and holds that instance, or null, which the text null also means, to empty it. One declared as Variant or untyped takes anything, as the engine does: a value of the kind it holds is fitted to it, an int staying an int, and any other is stored as sent, a fraction into one holding an int included. A typed list or map, such as Array[int] or Dictionary[String, int], takes a JSON list or object with each element fitted to its element type, and an element that cannot become one is refused by its index or key, as is an object of another script than the one the list holds, whether or not that script has a class_name.`
|
|
13473
|
+
description: `set: the value, fitted to the property's type. ${WHOLE_NUMBERS} A number the property would have to cut is refused rather than truncated: a fraction into an int, a number other than 0 or 1 into a bool. A vector, colour or the like may be written as its fields, {"x": 140, "y": 300}, or tagged, {"_type": "Vector2", "x": 140, "y": 300}, which is the form a read answers with, and one carrying a field its type has not, such as a z for a Vector2, is refused naming it. A colour may also be given as its html code, "#ff8000" or "#ff800080". Text is written as sent: the JSON string "" writes an empty string, and quote marks inside the string are written into the text. An enum property takes the name of a value as well as its number, as the script or the inspector spells it, and a name none of its values has is refused listing them. A number past the range a property is exported with, or one none of its enum values has, is written, as the engine writes it, and the answer says so under outside_declared. A property typed as or holding an object takes a path naming one the game holds, the way call's args do, and holds that instance, or null, which the text null also means, to empty it. One declared as Variant or untyped takes anything, as the engine does: a value of the kind it holds is fitted to it, an int staying an int, and any other is stored as sent, a fraction into one holding an int included. A typed list or map, such as Array[int] or Dictionary[String, int], takes a JSON list or object with each element fitted to its element type, and an element that cannot become one is refused by its index or key, as is an object of another script than the one the list holds, whether or not that script has a class_name.`
|
|
13413
13474
|
},
|
|
13414
13475
|
method: {
|
|
13415
13476
|
type: "string",
|
|
@@ -13513,7 +13574,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
13513
13574
|
name: "runtime_input",
|
|
13514
13575
|
title: "Send input to the game",
|
|
13515
13576
|
description: "Input to the running game: a whole click on a Control or a 3D node named by path or on a control named by the words on it, an item chosen out of a menu, typing into whatever has the focus, or a raw action, key, mouse button, mouse motion or drag. All of it works headless, where the viewport is the project's window size and the GUI only takes what is inside it. On gdharness's own desktop, where a windowed run goes, Windows refuses to say where the pointer is: every pointer answer there carries pointer_on_hidden_desktop, and the first of the run pointer_note saying why it matters: get_mouse_position() on the root window, and the mouse positions derived from it, read a meaningless position, while what reads the event itself (the control under it, hover, tooltips, input handlers) gets the injected position. A native window no real pointer has entered gets no hover from Godot, so the runtime supplies it there and the answers say hover_supplied.",
|
|
13516
|
-
answer: `click: what was under the pointer, and what became of the control: in_tree, removed or freed. Named by says, found says which match it was, its index being the index argument that presses the same match again, found.item names the tab, list entry, tree row or menu bar title pressed (kind; index, the item's own number in its control, which is not found.index; says; and for a row the column of the cell saying the words), and found.pressedOn says "the words" for words in a RichTextLabel pressed where they are drawn, or "the middle of the label" with a note saying why they could not be placed; on a rich label link_pressed says whether the press reached a link, with link_meta giving the meta the label reports clicked, an empty one included. A control scrolled into view first is scrolled each container out from it a frame after the one inside, and scrolled_into_view says whether a scroll value changed. A button in toggle mode, a CheckBox or CheckButton included, answers pressed with before and after, its button_pressed either side of the click: the same value twice is a click the game undid, and after is null when the click freed it. A refusal for a pointer turned off names where mouse_behavior_recursive was set. On a 3D node landed says the interface did not swallow the press: no control under the pointer stopped it, a control letting the pointer pass included, and every viewport on the way out from the room had the pointer over what shows the room. choose: what was chosen, read before the press, which is heard rather than assumed; what became of the control named (in_tree, removed or freed, since a game can rebuild its screen on the pick); and what the button in front of the menu shows now when it is still there. A menu is opened rather than clicked because its items are drawn rather than built, and the item is looked for as the menu stands once open, since a game can fill a menu as it opens. action, key, mouse_click and joypad_button: the event as it was sent, with whole saying whether the press was let go of again, for a key the keycode, physical_keycode and unicode it carried, and for a controller button its number, its name and the device. mouse_motion: the position and the relative movement sent. mouse_drag: from, to, motions sent and relative_total. joypad_motion: the axis by number and name, the value and the device. None of these is aimed at anything, so none says what the event reached: click on a nodePath or says answers what was under the pointer.`,
|
|
13577
|
+
answer: `click: what was under the pointer, and what became of the control: in_tree, removed or freed. Named by says, found says which match it was, its index being the index argument that presses the same match again, found.item names the tab, list entry, tree row or menu bar title pressed (kind; index, the item's own number in its control, which is not found.index; says; and for a row the column of the cell saying the words), and found.pressedOn says "the words" for words in a RichTextLabel pressed where they are drawn, or "the middle of the label" with a note saying why they could not be placed; on a rich label link_pressed says whether the press reached a link, with link_meta giving the meta the label reports clicked, an empty one included. A control scrolled into view first is scrolled each container out from it a frame after the one inside, and scrolled_into_view says whether a scroll value changed. A button in toggle mode, a CheckBox or CheckButton included, answers pressed with before and after, its button_pressed either side of the click: the same value twice is a click the game undid, and after is null when the click freed it. A refusal for a pointer turned off names where mouse_behavior_recursive was set. On a 3D node landed says the interface did not swallow the press: no control under the pointer stopped it, a control letting the pointer pass included, and every viewport on the way out from the room had the pointer over what shows the room. choose: what was chosen, read before the press, which is heard rather than assumed; what became of the control named (in_tree, removed or freed, since a game can rebuild its screen on the pick); and what the button in front of the menu shows now when it is still there. A menu is opened rather than clicked because its items are drawn rather than built, and the item is looked for as the menu stands once open, since a game can fill a menu as it opens. action, key, mouse_click and joypad_button: the event as it was sent, with whole saying whether the press was let go of again and pressed whether it is left held down (true only after pressed true), for a key the keycode, physical_keycode and unicode it carried, and for a controller button its number, its name and the device. mouse_motion: the position and the relative movement sent. mouse_drag: from, to, motions sent and relative_total. joypad_motion: the axis by number and name, the value and the device. None of these is aimed at anything, so none says what the event reached: click on a nodePath or says answers what was under the pointer. A mouse_click, mouse_motion or mouse_drag with a point outside the window's viewport says so under outside_viewport, with the viewport's rectangle, since no control there receives it.`,
|
|
13517
13578
|
parameters: {
|
|
13518
13579
|
projectPath: RUNNING_PROJECT_PATH,
|
|
13519
13580
|
pid: RUNNING_PID,
|
|
@@ -13542,7 +13603,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
13542
13603
|
minimum: 1,
|
|
13543
13604
|
maximum: 600,
|
|
13544
13605
|
ops: ["action", "key", "mouse_click", "joypad_button"],
|
|
13545
|
-
description: "action, key, mouse_click, joypad_button: hold a whole press down for this many process frames and then let go, both inside the game, so a pour, a charge or a long press runs to a set length. A press, a frames wait and a release sent as three calls hold it for the time between the calls as well. The answer says held_frames. Not with pressed, and not on a wheel step. Default 1."
|
|
13606
|
+
description: "action, key, mouse_click, joypad_button: hold a whole press down for this many process frames and then let go, both inside the game, so a pour, a charge or a long press runs to a set length. A press, a frames wait and a release sent as three calls hold it for the time between the calls as well. The answer says held_frames, and under held_for the seconds the hold lasted and the physics_frames that ran in it: a process frame is no set time, and a headless game, drawing nothing, runs them far faster than the sixty physics ticks a second that _physics_process sees. Not with pressed, and not on a wheel step. Default 1."
|
|
13546
13607
|
},
|
|
13547
13608
|
strength: {
|
|
13548
13609
|
type: "number",
|
|
@@ -13726,7 +13787,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
13726
13787
|
},
|
|
13727
13788
|
value: {
|
|
13728
13789
|
ops: ["until"],
|
|
13729
|
-
description: "until: the value to wait for, fitted to the property's type and compared at the precision the property keeps, so -1.1 meets a 32-bit float holding -1.10000002. A value the property would have to cut is refused: a fraction on a whole number, a number other than 0 or 1 on a bool. On a property holding an object, whether declared to or untyped and holding one or null, the text null means null. One declared to hold an object refuses any value other than null, since it can never hold it."
|
|
13790
|
+
description: "until: the value to wait for, fitted to the property's type and compared at the precision the property keeps, so -1.1 meets a 32-bit float holding -1.10000002. A value the property would have to cut is refused: a fraction on a whole number, a number other than 0 or 1 on a bool. An enum property is waited for by the name of a value as well as its number, as set takes it. On a property holding an object, whether declared to or untyped and holding one or null, the text null means null. One declared to hold an object refuses any value other than null, since it can never hold it."
|
|
13730
13791
|
},
|
|
13731
13792
|
compare: {
|
|
13732
13793
|
type: "string",
|
|
@@ -13775,16 +13836,21 @@ var init_tool_definitions = __esm(() => {
|
|
|
13775
13836
|
{
|
|
13776
13837
|
name: "debug_breakpoint",
|
|
13777
13838
|
title: "Breakpoints",
|
|
13778
|
-
description: "Sets or
|
|
13839
|
+
description: "Sets, removes or lists breakpoints. A set on a script that is not there, or past its last line, is refused, as nothing would ever stop there; a remove takes any line, and with no editor connected answers removed false, saying why, when none was held there. Set them first, then editor_run, and the game stops where you asked. With the editor connected it goes through the editor's debug adapter; with none, it is held here. A game this server starts itself, which editor_run start does for a run given args, savesIn, env or silent, or with no editor, is handed every breakpoint held here as it starts, and one set while that game runs reaches it at once (runningGame says so). The editor keeps a breakpoint set this way for one play, so every breakpoint this server holds is sent again before each play editor_run starts, and the start answer lists them under breakpoints. The set is also kept in the project, for the server that comes after a reconnect: a breakpoint stays until it is removed here, whichever server set it. Every answer lists the whole set under held, and the breakpoints the user set in the editor's own gutter under setInEditor; setting one here never takes those away, and removing a line here clears it whoever set it. Godot clears every breakpoint in the editor when a debug session opens unless network/debug_adapter/sync_breakpoints is on, which the addon turns on as it loads; an editor where it was turned off again is said so under breakpointsAtRisk, here and in editor_status.",
|
|
13779
13840
|
parameters: {
|
|
13780
13841
|
projectPath: PROJECT_PATH,
|
|
13781
|
-
scriptPath: SCRIPT_PATH,
|
|
13782
|
-
line: { type: "integer", minimum: 1, description: "One-based line." }
|
|
13842
|
+
scriptPath: { ...SCRIPT_PATH, ops: ["set", "remove"] },
|
|
13843
|
+
line: { type: "integer", minimum: 1, ops: ["set", "remove"], description: "One-based line." }
|
|
13783
13844
|
},
|
|
13784
|
-
requires: ["projectPath"
|
|
13845
|
+
requires: ["projectPath"],
|
|
13785
13846
|
operations: {
|
|
13786
|
-
set: { effect: "additive", summary: "set a breakpoint", requires: [] },
|
|
13787
|
-
remove: { effect: "destructive", summary: "remove a breakpoint", requires: [] }
|
|
13847
|
+
set: { effect: "additive", summary: "set a breakpoint", requires: ["scriptPath", "line"] },
|
|
13848
|
+
remove: { effect: "destructive", summary: "remove a breakpoint", requires: ["scriptPath", "line"] },
|
|
13849
|
+
list: {
|
|
13850
|
+
effect: "read",
|
|
13851
|
+
summary: "the breakpoints held here under held, and with the editor connected the ones set in its gutter under setInEditor",
|
|
13852
|
+
requires: []
|
|
13853
|
+
}
|
|
13788
13854
|
}
|
|
13789
13855
|
},
|
|
13790
13856
|
{
|
|
@@ -35605,12 +35671,12 @@ var init_stdio = __esm(() => {
|
|
|
35605
35671
|
|
|
35606
35672
|
// src/autoload-failure.ts
|
|
35607
35673
|
import { existsSync as existsSync15, readFileSync as readFileSync13 } from "node:fs";
|
|
35608
|
-
import { join as
|
|
35674
|
+
import { join as join18 } from "node:path";
|
|
35609
35675
|
function scriptsAutoloaded(projectPath) {
|
|
35610
35676
|
const scripts = new Map;
|
|
35611
35677
|
let settings;
|
|
35612
35678
|
try {
|
|
35613
|
-
settings = parseProjectGodot(readFileSync13(
|
|
35679
|
+
settings = parseProjectGodot(readFileSync13(join18(projectPath, "project.godot"), "utf8"));
|
|
35614
35680
|
} catch {
|
|
35615
35681
|
return scripts;
|
|
35616
35682
|
}
|
|
@@ -35619,7 +35685,7 @@ function scriptsAutoloaded(projectPath) {
|
|
|
35619
35685
|
scripts.set(`res://${path}`, name);
|
|
35620
35686
|
continue;
|
|
35621
35687
|
}
|
|
35622
|
-
const scene =
|
|
35688
|
+
const scene = join18(projectPath, path);
|
|
35623
35689
|
if (!existsSync15(scene)) {
|
|
35624
35690
|
continue;
|
|
35625
35691
|
}
|
|
@@ -35646,7 +35712,7 @@ var init_autoload_failure = __esm(() => {
|
|
|
35646
35712
|
import { existsSync as existsSync16, readdirSync as readdirSync10, readFileSync as readFileSync14, statSync as statSync8, unlinkSync as unlinkSync2 } from "node:fs";
|
|
35647
35713
|
import { createConnection } from "node:net";
|
|
35648
35714
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
35649
|
-
import { dirname as
|
|
35715
|
+
import { dirname as dirname11, join as join19, resolve as resolve4 } from "node:path";
|
|
35650
35716
|
import { setTimeout as delay } from "node:timers/promises";
|
|
35651
35717
|
function runtimeDirectory(variables = process.env) {
|
|
35652
35718
|
const explicit = envValue("GDHARNESS_RUNTIME_DIR", variables);
|
|
@@ -35654,19 +35720,19 @@ function runtimeDirectory(variables = process.env) {
|
|
|
35654
35720
|
return explicit;
|
|
35655
35721
|
}
|
|
35656
35722
|
const perUser = envValue("XDG_RUNTIME_DIR", variables);
|
|
35657
|
-
return
|
|
35723
|
+
return join19(perUser ?? tmpdir4(), "gdharness");
|
|
35658
35724
|
}
|
|
35659
35725
|
function runtimeDirectories(variables = process.env) {
|
|
35660
35726
|
const candidates = [runtimeDirectory(variables)];
|
|
35661
35727
|
const fallbacks = process.platform === "win32" ? [envValue("SystemRoot", variables) ?? envValue("windir", variables) ?? "C:\\Windows"] : ["/tmp", "/var/tmp"];
|
|
35662
35728
|
for (const base of fallbacks) {
|
|
35663
|
-
candidates.push(
|
|
35729
|
+
candidates.push(join19(base, process.platform === "win32" ? "Temp" : "", "gdharness"));
|
|
35664
35730
|
}
|
|
35665
35731
|
return [...new Set(candidates.map((path) => resolve4(path)))];
|
|
35666
35732
|
}
|
|
35667
35733
|
function errorReportOf(pid, directories = runtimeDirectories()) {
|
|
35668
35734
|
for (const directory of directories) {
|
|
35669
|
-
const path =
|
|
35735
|
+
const path = join19(directory, `runtime-${pid}.log`);
|
|
35670
35736
|
if (existsSync16(path)) {
|
|
35671
35737
|
return path;
|
|
35672
35738
|
}
|
|
@@ -35820,14 +35886,14 @@ function announcedIn(directory) {
|
|
|
35820
35886
|
for (const entry of readdirSync10(directory)) {
|
|
35821
35887
|
const report = ERROR_REPORT_PATTERN.exec(entry);
|
|
35822
35888
|
if (report) {
|
|
35823
|
-
sweepErrorReport(
|
|
35889
|
+
sweepErrorReport(join19(directory, entry), Number.parseInt(report[1] ?? "", 10));
|
|
35824
35890
|
continue;
|
|
35825
35891
|
}
|
|
35826
35892
|
const match = ANNOUNCEMENT_PATTERN.exec(entry);
|
|
35827
35893
|
if (!match) {
|
|
35828
35894
|
continue;
|
|
35829
35895
|
}
|
|
35830
|
-
const file =
|
|
35896
|
+
const file = join19(directory, entry);
|
|
35831
35897
|
const pid = Number.parseInt(match[1] ?? "", 10);
|
|
35832
35898
|
const running = alive(pid);
|
|
35833
35899
|
entries.push({ file, pid, running });
|
|
@@ -35855,16 +35921,16 @@ function announcedIn(directory) {
|
|
|
35855
35921
|
found.push(announced);
|
|
35856
35922
|
}
|
|
35857
35923
|
const held = new Set(entries.map((one) => one.file));
|
|
35858
|
-
const here =
|
|
35924
|
+
const here = join19(directory, ".");
|
|
35859
35925
|
for (const file of verdicts.keys()) {
|
|
35860
|
-
if (
|
|
35926
|
+
if (dirname11(file) === here && !held.has(file)) {
|
|
35861
35927
|
verdicts.delete(file);
|
|
35862
35928
|
}
|
|
35863
35929
|
}
|
|
35864
35930
|
return found;
|
|
35865
35931
|
}
|
|
35866
35932
|
async function announcementEnded(pid, directories = runtimeDirectories()) {
|
|
35867
|
-
const files = directories.map((directory) =>
|
|
35933
|
+
const files = directories.map((directory) => join19(directory, `runtime-${pid}.json`)).filter((file) => existsSync16(file));
|
|
35868
35934
|
const first = files[0];
|
|
35869
35935
|
if (first === undefined) {
|
|
35870
35936
|
return;
|
|
@@ -35920,7 +35986,7 @@ function theOneProject(endpoints) {
|
|
|
35920
35986
|
if (endpoints.some((endpoint) => endpoint.project.path === "")) {
|
|
35921
35987
|
return;
|
|
35922
35988
|
}
|
|
35923
|
-
const paths = new Set(endpoints.map((endpoint) =>
|
|
35989
|
+
const paths = new Set(endpoints.map((endpoint) => realAsFarAsItExists(resolve4(endpoint.project.path))));
|
|
35924
35990
|
const only = [...paths][0];
|
|
35925
35991
|
return paths.size === 1 ? only : undefined;
|
|
35926
35992
|
}
|
|
@@ -36284,9 +36350,9 @@ var init_runtime_client = __esm(() => {
|
|
|
36284
36350
|
|
|
36285
36351
|
// src/boot-note.ts
|
|
36286
36352
|
import { mkdirSync as mkdirSync6, readFileSync as readFileSync15, writeFileSync as writeFileSync8 } from "node:fs";
|
|
36287
|
-
import { dirname as
|
|
36353
|
+
import { dirname as dirname12, join as join20 } from "node:path";
|
|
36288
36354
|
function bootNotePath(projectPath) {
|
|
36289
|
-
return
|
|
36355
|
+
return join20(projectPath, ".godot", "gdharness-boot.json");
|
|
36290
36356
|
}
|
|
36291
36357
|
function readScenes(projectPath) {
|
|
36292
36358
|
try {
|
|
@@ -36306,7 +36372,7 @@ function readBootNote(projectPath, scene) {
|
|
|
36306
36372
|
function writeBootNote(projectPath, scene, announcedAfterMs) {
|
|
36307
36373
|
try {
|
|
36308
36374
|
const path = bootNotePath(projectPath);
|
|
36309
|
-
mkdirSync6(
|
|
36375
|
+
mkdirSync6(dirname12(path), { recursive: true });
|
|
36310
36376
|
const scenes = {
|
|
36311
36377
|
...readScenes(projectPath),
|
|
36312
36378
|
[scene]: {
|
|
@@ -36333,14 +36399,14 @@ var init_boot_note = __esm(() => {
|
|
|
36333
36399
|
|
|
36334
36400
|
// src/breakpoint-note.ts
|
|
36335
36401
|
import { mkdirSync as mkdirSync7, readFileSync as readFileSync16, rmSync as rmSync7, writeFileSync as writeFileSync9 } from "node:fs";
|
|
36336
|
-
import { dirname as
|
|
36402
|
+
import { dirname as dirname13, isAbsolute as isAbsolute4, join as join21, relative as relative3 } from "node:path";
|
|
36337
36403
|
function breakpointNotePath(projectPath) {
|
|
36338
|
-
return
|
|
36404
|
+
return join21(projectPath, ".godot", "gdharness-breakpoints.json");
|
|
36339
36405
|
}
|
|
36340
36406
|
function writeBreakpointNote(projectPath, held) {
|
|
36341
36407
|
const path = breakpointNotePath(projectPath);
|
|
36342
|
-
const root =
|
|
36343
|
-
const inside = held.map((one) => ({ scriptPath:
|
|
36408
|
+
const root = realAsFarAsItExists(projectPath);
|
|
36409
|
+
const inside = held.map((one) => ({ scriptPath: realAsFarAsItExists(one.scriptPath), lines: one.lines })).filter((one) => one.lines.length > 0 && withinProject(root, one.scriptPath));
|
|
36344
36410
|
try {
|
|
36345
36411
|
if (inside.length === 0) {
|
|
36346
36412
|
rmSync7(path, { force: true });
|
|
@@ -36352,7 +36418,7 @@ function writeBreakpointNote(projectPath, held) {
|
|
|
36352
36418
|
lines: [...one.lines]
|
|
36353
36419
|
}))
|
|
36354
36420
|
};
|
|
36355
|
-
mkdirSync7(
|
|
36421
|
+
mkdirSync7(dirname13(path), { recursive: true });
|
|
36356
36422
|
writeFileSync9(path, `${JSON.stringify(note, null, 2)}
|
|
36357
36423
|
`, "utf8");
|
|
36358
36424
|
} catch (error) {
|
|
@@ -36376,7 +36442,7 @@ function readBreakpointNote(projectPath) {
|
|
|
36376
36442
|
}
|
|
36377
36443
|
const lines = Array.isArray(listed) ? listed.filter((line) => Number.isInteger(line) && line > 0) : [];
|
|
36378
36444
|
if (lines.length > 0) {
|
|
36379
|
-
held.push({ scriptPath:
|
|
36445
|
+
held.push({ scriptPath: realAsFarAsItExists(join21(projectPath, script)), lines });
|
|
36380
36446
|
}
|
|
36381
36447
|
}
|
|
36382
36448
|
return held;
|
|
@@ -36426,9 +36492,9 @@ var init_whole_file = __esm(() => {
|
|
|
36426
36492
|
|
|
36427
36493
|
// src/class-note.ts
|
|
36428
36494
|
import { mkdirSync as mkdirSync8, readFileSync as readFileSync17 } from "node:fs";
|
|
36429
|
-
import { dirname as
|
|
36495
|
+
import { dirname as dirname14, join as join22 } from "node:path";
|
|
36430
36496
|
function classNotePath(projectPath) {
|
|
36431
|
-
return
|
|
36497
|
+
return join22(projectPath, ".godot", "gdharness-classes.json");
|
|
36432
36498
|
}
|
|
36433
36499
|
function readClassNote(projectPath) {
|
|
36434
36500
|
try {
|
|
@@ -36462,7 +36528,7 @@ function unfinishedScanIn(value) {
|
|
|
36462
36528
|
function writeClassNote(projectPath, note) {
|
|
36463
36529
|
const path = classNotePath(projectPath);
|
|
36464
36530
|
try {
|
|
36465
|
-
mkdirSync8(
|
|
36531
|
+
mkdirSync8(dirname14(path), { recursive: true });
|
|
36466
36532
|
} catch {
|
|
36467
36533
|
return;
|
|
36468
36534
|
}
|
|
@@ -36475,9 +36541,9 @@ var init_class_note = __esm(() => {
|
|
|
36475
36541
|
|
|
36476
36542
|
// src/config-pin.ts
|
|
36477
36543
|
import { existsSync as existsSync17, readFileSync as readFileSync18 } from "node:fs";
|
|
36478
|
-
import { join as
|
|
36544
|
+
import { join as join23 } from "node:path";
|
|
36479
36545
|
function versionsNamedInConfig(projectPath) {
|
|
36480
|
-
const config =
|
|
36546
|
+
const config = join23(projectPath, ".mcp.json");
|
|
36481
36547
|
if (!existsSync17(config)) {
|
|
36482
36548
|
return [];
|
|
36483
36549
|
}
|
|
@@ -36505,14 +36571,14 @@ var init_config_pin = __esm(() => {
|
|
|
36505
36571
|
|
|
36506
36572
|
// src/cut-test-run.ts
|
|
36507
36573
|
import { existsSync as existsSync18, mkdirSync as mkdirSync9, readFileSync as readFileSync19, rmSync as rmSync9, writeFileSync as writeFileSync11 } from "node:fs";
|
|
36508
|
-
import { dirname as
|
|
36574
|
+
import { dirname as dirname15, join as join24 } from "node:path";
|
|
36509
36575
|
function keptAt(projectPath) {
|
|
36510
|
-
return
|
|
36576
|
+
return join24(projectPath, ".godot", "gdharness-reports", "cut-run.json");
|
|
36511
36577
|
}
|
|
36512
36578
|
function keepCutTestRun(projectPath, run) {
|
|
36513
36579
|
const path = keptAt(projectPath);
|
|
36514
36580
|
try {
|
|
36515
|
-
mkdirSync9(
|
|
36581
|
+
mkdirSync9(dirname15(path), { recursive: true });
|
|
36516
36582
|
writeFileSync11(path, `${JSON.stringify(run, null, 2)}
|
|
36517
36583
|
`, "utf8");
|
|
36518
36584
|
} catch {}
|
|
@@ -37103,13 +37169,14 @@ class GodotDAPClient {
|
|
|
37103
37169
|
const key = fileKey(scriptPath);
|
|
37104
37170
|
const lines = this.breakpoints.get(key);
|
|
37105
37171
|
if (lines === undefined) {
|
|
37106
|
-
return;
|
|
37172
|
+
return false;
|
|
37107
37173
|
}
|
|
37108
|
-
lines.delete(line);
|
|
37174
|
+
const held = lines.delete(line);
|
|
37109
37175
|
if (lines.size === 0) {
|
|
37110
37176
|
this.breakpoints.delete(key);
|
|
37111
37177
|
this.spelled.delete(key);
|
|
37112
37178
|
}
|
|
37179
|
+
return held;
|
|
37113
37180
|
}
|
|
37114
37181
|
holdBreakpoints(held) {
|
|
37115
37182
|
for (const { scriptPath, lines } of held) {
|
|
@@ -37346,10 +37413,10 @@ import {
|
|
|
37346
37413
|
writeSync
|
|
37347
37414
|
} from "node:fs";
|
|
37348
37415
|
import { tmpdir as tmpdir5 } from "node:os";
|
|
37349
|
-
import { join as
|
|
37416
|
+
import { join as join25 } from "node:path";
|
|
37350
37417
|
import { setTimeout as delay3 } from "node:timers/promises";
|
|
37351
37418
|
function helperAssembly(directory = tmpdir5()) {
|
|
37352
|
-
return
|
|
37419
|
+
return join25(directory, `gdharness-desktop-${DIGEST}.dll`);
|
|
37353
37420
|
}
|
|
37354
37421
|
async function ensureHelperCompiled(directory = tmpdir5(), budgetMs = HELPER_COMPILE_MS, wait = true) {
|
|
37355
37422
|
const assembly = helperAssembly(directory);
|
|
@@ -37389,8 +37456,8 @@ async function ensureHelperCompiled(directory = tmpdir5(), budgetMs = HELPER_COM
|
|
|
37389
37456
|
return `the desktop helper was not compiled within ${String(budgetMs)} ms`;
|
|
37390
37457
|
}
|
|
37391
37458
|
async function compileHelper(directory, assembly, budgetMs) {
|
|
37392
|
-
const scratch = mkdtempSync2(
|
|
37393
|
-
const source =
|
|
37459
|
+
const scratch = mkdtempSync2(join25(directory, "gdharness-desktop-source-"));
|
|
37460
|
+
const source = join25(scratch, "helper.cs");
|
|
37394
37461
|
writeFileSync12(source, SOURCE, "utf8");
|
|
37395
37462
|
const literal = (path) => `'${path.replaceAll("'", "''")}'`;
|
|
37396
37463
|
const script = [
|
|
@@ -37604,12 +37671,12 @@ import {
|
|
|
37604
37671
|
statSync as statSync10,
|
|
37605
37672
|
writeFileSync as writeFileSync13
|
|
37606
37673
|
} from "node:fs";
|
|
37607
|
-
import { dirname as
|
|
37674
|
+
import { dirname as dirname16, join as join26 } from "node:path";
|
|
37608
37675
|
function editorLogPath(projectPath) {
|
|
37609
|
-
return
|
|
37676
|
+
return join26(projectPath, ".godot", "gdharness-editor.log");
|
|
37610
37677
|
}
|
|
37611
37678
|
function editorLogNotePath(projectPath) {
|
|
37612
|
-
return
|
|
37679
|
+
return join26(projectPath, ".godot", "gdharness-editor.json");
|
|
37613
37680
|
}
|
|
37614
37681
|
function readEditorLogNote(projectPath) {
|
|
37615
37682
|
try {
|
|
@@ -37624,7 +37691,7 @@ function readEditorLogNote(projectPath) {
|
|
|
37624
37691
|
function clearEditorLog(projectPath) {
|
|
37625
37692
|
try {
|
|
37626
37693
|
const path = editorLogPath(projectPath);
|
|
37627
|
-
mkdirSync10(
|
|
37694
|
+
mkdirSync10(dirname16(path), { recursive: true });
|
|
37628
37695
|
rmSync11(path, { force: true });
|
|
37629
37696
|
rmSync11(editorLogNotePath(projectPath), { force: true });
|
|
37630
37697
|
} catch {}
|
|
@@ -37632,7 +37699,7 @@ function clearEditorLog(projectPath) {
|
|
|
37632
37699
|
function writeEditorLogNote(projectPath, pid) {
|
|
37633
37700
|
try {
|
|
37634
37701
|
const path = editorLogNotePath(projectPath);
|
|
37635
|
-
mkdirSync10(
|
|
37702
|
+
mkdirSync10(dirname16(path), { recursive: true });
|
|
37636
37703
|
writeFileSync13(path, JSON.stringify({ pid, startedAt: new Date().toISOString() }, null, 2), "utf8");
|
|
37637
37704
|
} catch {}
|
|
37638
37705
|
}
|
|
@@ -37874,7 +37941,7 @@ var init_editor_log = __esm(() => {
|
|
|
37874
37941
|
|
|
37875
37942
|
// src/rename.ts
|
|
37876
37943
|
import { existsSync as existsSync21, mkdirSync as mkdirSync11, readFileSync as readFileSync22, renameSync as renameSync3, unlinkSync as unlinkSync3, writeFileSync as writeFileSync14 } from "node:fs";
|
|
37877
|
-
import { dirname as
|
|
37944
|
+
import { dirname as dirname17, posix as posix2 } from "node:path";
|
|
37878
37945
|
|
|
37879
37946
|
class Script {
|
|
37880
37947
|
path;
|
|
@@ -38589,7 +38656,7 @@ function applyRewrites(rewrites) {
|
|
|
38589
38656
|
const done = [];
|
|
38590
38657
|
try {
|
|
38591
38658
|
for (const rewrite of rewrites) {
|
|
38592
|
-
mkdirSync11(
|
|
38659
|
+
mkdirSync11(dirname17(rewrite.writeTo), { recursive: true });
|
|
38593
38660
|
const staging = `${rewrite.writeTo}.gdharness-rename`;
|
|
38594
38661
|
writeFileSync14(staging, rewrite.after, "utf8");
|
|
38595
38662
|
renameSync3(staging, rewrite.writeTo);
|
|
@@ -38642,10 +38709,10 @@ var init_rename = __esm(() => {
|
|
|
38642
38709
|
});
|
|
38643
38710
|
|
|
38644
38711
|
// src/lsp_client.ts
|
|
38645
|
-
import { existsSync as existsSync22, realpathSync as
|
|
38712
|
+
import { existsSync as existsSync22, realpathSync as realpathSync3 } from "node:fs";
|
|
38646
38713
|
import { readFile, realpath } from "node:fs/promises";
|
|
38647
38714
|
import { createConnection as createConnection3 } from "node:net";
|
|
38648
|
-
import { dirname as
|
|
38715
|
+
import { dirname as dirname18, join as join27, resolve as resolve5 } from "node:path";
|
|
38649
38716
|
import { setTimeout as delay4 } from "node:timers/promises";
|
|
38650
38717
|
import { fileURLToPath as fileURLToPath3, pathToFileURL } from "node:url";
|
|
38651
38718
|
function initializeTimeoutMs() {
|
|
@@ -38663,7 +38730,7 @@ function diagnosticsKey(uri) {
|
|
|
38663
38730
|
return decoded;
|
|
38664
38731
|
}
|
|
38665
38732
|
try {
|
|
38666
|
-
return
|
|
38733
|
+
return realpathSync3.native(fileURLToPath3(decoded)).toLowerCase();
|
|
38667
38734
|
} catch {
|
|
38668
38735
|
return decoded.toLowerCase();
|
|
38669
38736
|
}
|
|
@@ -38807,7 +38874,7 @@ class GodotLSPClient {
|
|
|
38807
38874
|
if (this.initialized) {
|
|
38808
38875
|
return;
|
|
38809
38876
|
}
|
|
38810
|
-
const rootPath = this.rootPath ??
|
|
38877
|
+
const rootPath = this.rootPath ?? dirname18(resolve5(filePath));
|
|
38811
38878
|
await this.initialize(rootPath);
|
|
38812
38879
|
}
|
|
38813
38880
|
async sendRequest(method, params, timeoutMs = this.timeouts.requestMs) {
|
|
@@ -39323,7 +39390,7 @@ async function resolveLSPPaths(projectPathValue, scriptPathValue) {
|
|
|
39323
39390
|
} catch {
|
|
39324
39391
|
throw new ArgumentRefusal(`Project path does not exist: ${requestedProjectPath}`);
|
|
39325
39392
|
}
|
|
39326
|
-
if (!existsSync22(
|
|
39393
|
+
if (!existsSync22(join27(projectPath, "project.godot"))) {
|
|
39327
39394
|
throw new ArgumentRefusal(`Not a Godot project: ${projectPath}. Point projectPath at the directory holding project.godot.`);
|
|
39328
39395
|
}
|
|
39329
39396
|
const contained = resolveWithinProject(projectPath, scriptPathValue);
|
|
@@ -39483,8 +39550,8 @@ var init_lsp_client = __esm(() => {
|
|
|
39483
39550
|
});
|
|
39484
39551
|
|
|
39485
39552
|
// src/rename-symbol.ts
|
|
39486
|
-
import { existsSync as existsSync23, mkdirSync as mkdirSync12, readdirSync as readdirSync11, realpathSync as
|
|
39487
|
-
import { basename as
|
|
39553
|
+
import { existsSync as existsSync23, mkdirSync as mkdirSync12, readdirSync as readdirSync11, realpathSync as realpathSync4, rmSync as rmSync12, writeFileSync as writeFileSync15 } from "node:fs";
|
|
39554
|
+
import { basename as basename8, dirname as dirname19, join as join28, relative as relative4, resolve as resolve6 } from "node:path";
|
|
39488
39555
|
function resourcePathOf(projectPath, absolute) {
|
|
39489
39556
|
const inside = relative4(onDiskSpelling(projectPath), onDiskSpelling(absolute));
|
|
39490
39557
|
if (inside === "" || inside.startsWith("..") || /^[A-Za-z]:/.test(inside)) {
|
|
@@ -39497,13 +39564,13 @@ function onDiskSpelling(path) {
|
|
|
39497
39564
|
let at = resolve6(path);
|
|
39498
39565
|
for (;; ) {
|
|
39499
39566
|
try {
|
|
39500
|
-
return
|
|
39567
|
+
return join28(realpathSync4.native(at), ...missing.reverse());
|
|
39501
39568
|
} catch {
|
|
39502
|
-
const parent =
|
|
39569
|
+
const parent = dirname19(at);
|
|
39503
39570
|
if (parent === at) {
|
|
39504
39571
|
return resolve6(path);
|
|
39505
39572
|
}
|
|
39506
|
-
missing.push(
|
|
39573
|
+
missing.push(basename8(at));
|
|
39507
39574
|
at = parent;
|
|
39508
39575
|
}
|
|
39509
39576
|
}
|
|
@@ -39535,7 +39602,8 @@ async function renameSymbol(request, services) {
|
|
|
39535
39602
|
};
|
|
39536
39603
|
}
|
|
39537
39604
|
if (newName === symbol) {
|
|
39538
|
-
|
|
39605
|
+
const moving = request.newScriptPath === null ? "" : " resource_edit move moves the script alone, with every path in the project naming it.";
|
|
39606
|
+
return { ok: false, reason: `${symbol} is already called ${newName}; nothing to rename.${moving}` };
|
|
39539
39607
|
}
|
|
39540
39608
|
const declaringPath = resourcePathOf(projectPath, request.scriptPath);
|
|
39541
39609
|
if (declaringPath === null) {
|
|
@@ -39925,15 +39993,15 @@ ${mention.kind}`;
|
|
|
39925
39993
|
};
|
|
39926
39994
|
}
|
|
39927
39995
|
function writeLeftAloneDetail(projectPath, symbol, detail) {
|
|
39928
|
-
const directory =
|
|
39996
|
+
const directory = join28(projectPath, ".godot", "gdharness-renames");
|
|
39929
39997
|
mkdirSync12(directory, { recursive: true });
|
|
39930
39998
|
detailWrites += 1;
|
|
39931
|
-
const path =
|
|
39999
|
+
const path = join28(directory, `${String(Date.now())}-${String(detailWrites).padStart(6, "0")}-${String(process.pid)}-${symbol}.json`);
|
|
39932
40000
|
writeFileSync15(path, `${JSON.stringify(detail, null, 2)}
|
|
39933
40001
|
`, "utf8");
|
|
39934
40002
|
const kept = readdirSync11(directory).filter((name) => name.endsWith(".json")).sort().reverse();
|
|
39935
40003
|
for (const old of kept.slice(10)) {
|
|
39936
|
-
rmSync12(
|
|
40004
|
+
rmSync12(join28(directory, old), { force: true });
|
|
39937
40005
|
}
|
|
39938
40006
|
return path;
|
|
39939
40007
|
}
|
|
@@ -39982,10 +40050,10 @@ import {
|
|
|
39982
40050
|
statSync as statSync11,
|
|
39983
40051
|
writeFileSync as writeFileSync16
|
|
39984
40052
|
} from "node:fs";
|
|
39985
|
-
import { basename as
|
|
40053
|
+
import { basename as basename9, dirname as dirname20, extname as extname3, join as join29, relative as relative5 } from "node:path";
|
|
39986
40054
|
function filesUnder(directory) {
|
|
39987
40055
|
return readdirSync12(directory, { withFileTypes: true }).flatMap((entry) => {
|
|
39988
|
-
const path =
|
|
40056
|
+
const path = join29(directory, entry.name);
|
|
39989
40057
|
return entry.isDirectory() ? filesUnder(path) : [path];
|
|
39990
40058
|
});
|
|
39991
40059
|
}
|
|
@@ -40006,8 +40074,8 @@ function unseenByTheEngine(projectPath, path) {
|
|
|
40006
40074
|
return `${path} is under ${step}, and the engine passes over every directory whose name starts with a dot`;
|
|
40007
40075
|
}
|
|
40008
40076
|
if (at < steps.length - 1) {
|
|
40009
|
-
directory =
|
|
40010
|
-
if (existsSync24(
|
|
40077
|
+
directory = join29(directory, step);
|
|
40078
|
+
if (existsSync24(join29(directory, ".gdignore"))) {
|
|
40011
40079
|
return `${path} is under a directory holding a .gdignore, which the engine does not load`;
|
|
40012
40080
|
}
|
|
40013
40081
|
}
|
|
@@ -40019,12 +40087,12 @@ function installedAddonOf(projectPath, path) {
|
|
|
40019
40087
|
if (steps[0] !== "addons" || steps[1] === undefined) {
|
|
40020
40088
|
return null;
|
|
40021
40089
|
}
|
|
40022
|
-
return existsSync24(
|
|
40090
|
+
return existsSync24(join29(projectPath, "addons", steps[1], VERSION_MARKER)) ? `res://addons/${steps[1]}` : null;
|
|
40023
40091
|
}
|
|
40024
40092
|
function binaryResources(projectPath) {
|
|
40025
40093
|
const found = [];
|
|
40026
40094
|
const visit = (directory, prefix) => {
|
|
40027
|
-
if (existsSync24(
|
|
40095
|
+
if (existsSync24(join29(directory, ".gdignore"))) {
|
|
40028
40096
|
return;
|
|
40029
40097
|
}
|
|
40030
40098
|
for (const entry of readdirSync12(directory, { withFileTypes: true })) {
|
|
@@ -40033,7 +40101,7 @@ function binaryResources(projectPath) {
|
|
|
40033
40101
|
}
|
|
40034
40102
|
const path = `${prefix}${entry.name}`;
|
|
40035
40103
|
if (entry.isDirectory()) {
|
|
40036
|
-
visit(
|
|
40104
|
+
visit(join29(directory, entry.name), `${path}/`);
|
|
40037
40105
|
} else if (/\.(scn|res)$/i.test(entry.name)) {
|
|
40038
40106
|
found.push(path);
|
|
40039
40107
|
}
|
|
@@ -40043,7 +40111,7 @@ function binaryResources(projectPath) {
|
|
|
40043
40111
|
return found;
|
|
40044
40112
|
}
|
|
40045
40113
|
function targetPathOf(projectPath, absolute) {
|
|
40046
|
-
const inside = relative5(onDiskSpelling(projectPath),
|
|
40114
|
+
const inside = relative5(onDiskSpelling(projectPath), join29(onDiskSpelling(dirname20(absolute)), basename9(absolute)));
|
|
40047
40115
|
if (inside === "" || inside.startsWith("..") || /^[A-Za-z]:/.test(inside)) {
|
|
40048
40116
|
return null;
|
|
40049
40117
|
}
|
|
@@ -40098,7 +40166,7 @@ function planFileMove(projectPath, absoluteFrom, absoluteTo) {
|
|
|
40098
40166
|
return { ok: false, reason: `${unseen}, so nothing there could load what was moved.` };
|
|
40099
40167
|
}
|
|
40100
40168
|
if (!isDirectory) {
|
|
40101
|
-
const owner = SIDECARS.map((sidecar) => from.endsWith(sidecar) ? from.slice(0, -sidecar.length) : null).find((path) => path !== null && existsSync24(
|
|
40169
|
+
const owner = SIDECARS.map((sidecar) => from.endsWith(sidecar) ? from.slice(0, -sidecar.length) : null).find((path) => path !== null && existsSync24(join29(projectPath, path.slice("res://".length))));
|
|
40102
40170
|
if (owner !== undefined && owner !== null) {
|
|
40103
40171
|
return { ok: false, reason: `${from} belongs to ${owner} and moves with it: move ${owner} instead.` };
|
|
40104
40172
|
}
|
|
@@ -40132,12 +40200,12 @@ function planFileMove(projectPath, absoluteFrom, absoluteTo) {
|
|
|
40132
40200
|
scripts,
|
|
40133
40201
|
moves,
|
|
40134
40202
|
named: [from],
|
|
40135
|
-
exists: (path) => existsSync24(
|
|
40203
|
+
exists: (path) => existsSync24(join29(projectPath, path.slice("res://".length)))
|
|
40136
40204
|
}, { edits: [], mentions: [] });
|
|
40137
40205
|
plan = mentionIgnored(plan, ignoredTexts(projectPath), null, [from]);
|
|
40138
40206
|
const binaries = [];
|
|
40139
40207
|
for (const path of binaryResources(projectPath)) {
|
|
40140
|
-
const bytes = readFileSync23(
|
|
40208
|
+
const bytes = readFileSync23(join29(projectPath, path.slice("res://".length)));
|
|
40141
40209
|
const names = [...moves.keys()].find((moving) => bytes.includes(moving));
|
|
40142
40210
|
if (names !== undefined) {
|
|
40143
40211
|
binaries.push({
|
|
@@ -40156,7 +40224,7 @@ function planFileMove(projectPath, absoluteFrom, absoluteTo) {
|
|
|
40156
40224
|
}));
|
|
40157
40225
|
const left = leftAloneReport(mentions);
|
|
40158
40226
|
const rewrites = rewritesOf(plan, texts, null);
|
|
40159
|
-
const absoluteOf = (path) =>
|
|
40227
|
+
const absoluteOf = (path) => join29(projectPath, path.slice("res://".length));
|
|
40160
40228
|
const rewritten = new Map(rewrites.map((rewrite) => [rewrite.path, rewrite]));
|
|
40161
40229
|
const moved = [...moves].map(([source, target]) => {
|
|
40162
40230
|
const rewrite = rewritten.get(source);
|
|
@@ -40215,7 +40283,7 @@ function applyFileMove(outcome) {
|
|
|
40215
40283
|
}
|
|
40216
40284
|
const undo = [];
|
|
40217
40285
|
const rename = (source, target) => {
|
|
40218
|
-
mkdirSync13(
|
|
40286
|
+
mkdirSync13(dirname20(target), { recursive: true });
|
|
40219
40287
|
renameSync4(source, target);
|
|
40220
40288
|
undo.push(() => {
|
|
40221
40289
|
renameSync4(target, source);
|
|
@@ -43224,10 +43292,7 @@ function mayYetConnect(listeningSince, now = Date.now()) {
|
|
|
43224
43292
|
return listeningSince === undefined || now - listeningSince.getTime() < CONNECT_WINDOW_MS;
|
|
43225
43293
|
}
|
|
43226
43294
|
function theEditorHasComeBack(status, startedAt) {
|
|
43227
|
-
return
|
|
43228
|
-
}
|
|
43229
|
-
function hasSaidWhoItIs(status) {
|
|
43230
|
-
return status.connected && status.addonVersion !== undefined;
|
|
43295
|
+
return status.connected && (status.connectedAt?.getTime() ?? 0) > startedAt;
|
|
43231
43296
|
}
|
|
43232
43297
|
function withoutTrailingSeparator(path) {
|
|
43233
43298
|
if (path.length <= 1 || !/[\\/]$/.test(path) || /^[A-Za-z]:[\\/]$/.test(path)) {
|
|
@@ -43417,7 +43482,8 @@ var init_godot_bridge = __esm(() => {
|
|
|
43417
43482
|
this.log("info", "WebSocket bridge stopped");
|
|
43418
43483
|
}
|
|
43419
43484
|
isConnected() {
|
|
43420
|
-
|
|
43485
|
+
const info = this.connectionInfo;
|
|
43486
|
+
return this.socket?.readyState === import_websocket.default.OPEN && info?.greeted === true && info.identifying !== true;
|
|
43421
43487
|
}
|
|
43422
43488
|
isArriving() {
|
|
43423
43489
|
const info = this.connectionInfo;
|
|
@@ -43427,7 +43493,7 @@ var init_godot_bridge = __esm(() => {
|
|
|
43427
43493
|
return [...this.pendingRequests.values()].filter((pending) => pending.counted).map((pending) => ({ tool: pending.toolName, startedAt: pending.startedAt })).sort((a, b) => a.startedAt - b.startedAt);
|
|
43428
43494
|
}
|
|
43429
43495
|
getStatus() {
|
|
43430
|
-
const identifying = this.
|
|
43496
|
+
const identifying = this.isArriving();
|
|
43431
43497
|
const editor = identifying ? null : this.connectionInfo;
|
|
43432
43498
|
return {
|
|
43433
43499
|
host: this.host,
|
|
@@ -43699,7 +43765,7 @@ var init_godot_bridge = __esm(() => {
|
|
|
43699
43765
|
startKeepalive() {
|
|
43700
43766
|
this.stopKeepalive();
|
|
43701
43767
|
this.pingInterval = setInterval(() => {
|
|
43702
|
-
if (
|
|
43768
|
+
if (this.socket?.readyState !== import_websocket.default.OPEN) {
|
|
43703
43769
|
return;
|
|
43704
43770
|
}
|
|
43705
43771
|
try {
|
|
@@ -43709,6 +43775,7 @@ var init_godot_bridge = __esm(() => {
|
|
|
43709
43775
|
this.log("warn", `Failed to send ping: ${errorMessage(error)}`);
|
|
43710
43776
|
}
|
|
43711
43777
|
}, KEEPALIVE_INTERVAL_MS);
|
|
43778
|
+
this.pingInterval.unref();
|
|
43712
43779
|
}
|
|
43713
43780
|
stopKeepalive() {
|
|
43714
43781
|
if (!this.pingInterval) {
|
|
@@ -43880,7 +43947,7 @@ var init_godot_bridge = __esm(() => {
|
|
|
43880
43947
|
// src/import-freshness.ts
|
|
43881
43948
|
import { createHash as createHash5 } from "node:crypto";
|
|
43882
43949
|
import { existsSync as existsSync25, readdirSync as readdirSync13, readFileSync as readFileSync24, statSync as statSync12 } from "node:fs";
|
|
43883
|
-
import { join as
|
|
43950
|
+
import { join as join30 } from "node:path";
|
|
43884
43951
|
function md5Of(file) {
|
|
43885
43952
|
let stats;
|
|
43886
43953
|
try {
|
|
@@ -43924,7 +43991,7 @@ function listed2(value) {
|
|
|
43924
43991
|
return [...(value ?? "").matchAll(/"((?:[^"\\]|\\.)*)"/g)].map((match) => match[1] ?? "");
|
|
43925
43992
|
}
|
|
43926
43993
|
function onDisk(projectPath, resPath) {
|
|
43927
|
-
return
|
|
43994
|
+
return join30(projectPath, resPath.slice("res://".length));
|
|
43928
43995
|
}
|
|
43929
43996
|
function behind(projectPath, resPath, sidecar) {
|
|
43930
43997
|
const remap = sectionOf(sidecar, "remap");
|
|
@@ -43937,7 +44004,7 @@ function behind(projectPath, resPath, sidecar) {
|
|
|
43937
44004
|
return "its imported output is not on disk";
|
|
43938
44005
|
}
|
|
43939
44006
|
const name = resPath.slice(resPath.lastIndexOf("/") + 1);
|
|
43940
|
-
const record =
|
|
44007
|
+
const record = join30(projectPath, ".godot", "imported", `${name}-${createHash5("md5").update(resPath, "utf8").digest("hex")}.md5`);
|
|
43941
44008
|
let recorded = "";
|
|
43942
44009
|
try {
|
|
43943
44010
|
recorded = /^source_md5="([0-9a-f]*)"/m.exec(readFileSync24(record, "utf8"))?.[1] ?? "";
|
|
@@ -43953,7 +44020,7 @@ function behind(projectPath, resPath, sidecar) {
|
|
|
43953
44020
|
function staleImports(projectPath) {
|
|
43954
44021
|
const stale = [];
|
|
43955
44022
|
const visit = (directory, resDirectory) => {
|
|
43956
|
-
if (existsSync25(
|
|
44023
|
+
if (existsSync25(join30(directory, ".gdignore")) || existsSync25(join30(directory, VERSION_MARKER))) {
|
|
43957
44024
|
return;
|
|
43958
44025
|
}
|
|
43959
44026
|
let entries;
|
|
@@ -43969,7 +44036,7 @@ function staleImports(projectPath) {
|
|
|
43969
44036
|
}
|
|
43970
44037
|
const resPath = `${resDirectory}${entry.name}`;
|
|
43971
44038
|
if (entry.isDirectory()) {
|
|
43972
|
-
visit(
|
|
44039
|
+
visit(join30(directory, entry.name), `${resPath}/`);
|
|
43973
44040
|
continue;
|
|
43974
44041
|
}
|
|
43975
44042
|
if (!entry.isFile()) {
|
|
@@ -43982,7 +44049,7 @@ function staleImports(projectPath) {
|
|
|
43982
44049
|
}
|
|
43983
44050
|
let sidecar;
|
|
43984
44051
|
try {
|
|
43985
|
-
sidecar = readFileSync24(
|
|
44052
|
+
sidecar = readFileSync24(join30(directory, entry.name), "utf8");
|
|
43986
44053
|
} catch {
|
|
43987
44054
|
continue;
|
|
43988
44055
|
}
|
|
@@ -44103,15 +44170,15 @@ var init_keeper_channel = () => {};
|
|
|
44103
44170
|
|
|
44104
44171
|
// src/lifecycle-log.ts
|
|
44105
44172
|
import { appendFileSync, mkdirSync as mkdirSync14, renameSync as renameSync5, statSync as statSync13 } from "node:fs";
|
|
44106
|
-
import { dirname as
|
|
44173
|
+
import { dirname as dirname21, join as join31 } from "node:path";
|
|
44107
44174
|
import process11 from "node:process";
|
|
44108
44175
|
function lifecycleLogPath() {
|
|
44109
|
-
return
|
|
44176
|
+
return join31(runtimeDirectory(), "servers.log");
|
|
44110
44177
|
}
|
|
44111
44178
|
function writeLifecycle(event, fields = {}) {
|
|
44112
44179
|
const path = lifecycleLogPath();
|
|
44113
44180
|
try {
|
|
44114
|
-
mkdirSync14(
|
|
44181
|
+
mkdirSync14(dirname21(path), { recursive: true });
|
|
44115
44182
|
if ((statSync13(path, { throwIfNoEntry: false })?.size ?? 0) > LIFECYCLE_LOG_LIMIT) {
|
|
44116
44183
|
try {
|
|
44117
44184
|
renameSync5(path, `${path}.1`);
|
|
@@ -44467,14 +44534,14 @@ var init_prompts = __esm(() => {
|
|
|
44467
44534
|
|
|
44468
44535
|
// src/restart-note.ts
|
|
44469
44536
|
import { mkdirSync as mkdirSync15, readFileSync as readFileSync25, rmSync as rmSync13, writeFileSync as writeFileSync17 } from "node:fs";
|
|
44470
|
-
import { dirname as
|
|
44537
|
+
import { dirname as dirname22, join as join32 } from "node:path";
|
|
44471
44538
|
function restartNotePath(projectPath) {
|
|
44472
|
-
return
|
|
44539
|
+
return join32(projectPath, ".godot", "gdharness-restart.json");
|
|
44473
44540
|
}
|
|
44474
44541
|
function noteRestartBegun(note) {
|
|
44475
44542
|
const path = restartNotePath(note.projectPath);
|
|
44476
44543
|
try {
|
|
44477
|
-
mkdirSync15(
|
|
44544
|
+
mkdirSync15(dirname22(path), { recursive: true });
|
|
44478
44545
|
writeFileSync17(path, `${JSON.stringify(note, null, 2)}
|
|
44479
44546
|
`, "utf8");
|
|
44480
44547
|
} catch (error) {
|
|
@@ -44509,26 +44576,10 @@ var init_restart_note = __esm(() => {
|
|
|
44509
44576
|
|
|
44510
44577
|
// src/run-record.ts
|
|
44511
44578
|
import { createHash as createHash6 } from "node:crypto";
|
|
44512
|
-
import { mkdirSync as mkdirSync16, openSync as openSync3, readdirSync as readdirSync14, readFileSync as readFileSync26,
|
|
44513
|
-
import { basename as
|
|
44579
|
+
import { mkdirSync as mkdirSync16, openSync as openSync3, readdirSync as readdirSync14, readFileSync as readFileSync26, rmSync as rmSync14, statSync as statSync14 } from "node:fs";
|
|
44580
|
+
import { basename as basename10, join as join33, resolve as resolve7 } from "node:path";
|
|
44514
44581
|
function runsDirectory() {
|
|
44515
|
-
return
|
|
44516
|
-
}
|
|
44517
|
-
function realAsFarAsItExists(path) {
|
|
44518
|
-
const missing = [];
|
|
44519
|
-
let at = path;
|
|
44520
|
-
for (;; ) {
|
|
44521
|
-
try {
|
|
44522
|
-
return join32(realpathSync6.native(at), ...missing);
|
|
44523
|
-
} catch {
|
|
44524
|
-
const parent = dirname22(at);
|
|
44525
|
-
if (parent === at) {
|
|
44526
|
-
return path;
|
|
44527
|
-
}
|
|
44528
|
-
missing.unshift(basename9(at));
|
|
44529
|
-
at = parent;
|
|
44530
|
-
}
|
|
44531
|
-
}
|
|
44582
|
+
return join33(runtimeDirectory(), "runs");
|
|
44532
44583
|
}
|
|
44533
44584
|
function projectKey(projectPath) {
|
|
44534
44585
|
const real = realAsFarAsItExists(resolve7(projectPath)).replace(/[\\/]+$/, "");
|
|
@@ -44536,10 +44587,10 @@ function projectKey(projectPath) {
|
|
|
44536
44587
|
return createHash6("sha256").update(folded.replaceAll("\\", "/")).digest("hex").slice(0, 16);
|
|
44537
44588
|
}
|
|
44538
44589
|
function runRecordPath(projectPath, directory = runtimeDirectory()) {
|
|
44539
|
-
return
|
|
44590
|
+
return join33(directory, "runs", `note-${projectKey(projectPath)}.json`);
|
|
44540
44591
|
}
|
|
44541
44592
|
function latestRunPath(servedBy, directory = runtimeDirectory()) {
|
|
44542
|
-
return
|
|
44593
|
+
return join33(directory, "runs", `latest-${projectKey(servedBy)}.json`);
|
|
44543
44594
|
}
|
|
44544
44595
|
function writeLatestRun(servedBy, latest) {
|
|
44545
44596
|
try {
|
|
@@ -44564,20 +44615,20 @@ function readLatestRun(servedBy, directory = runtimeDirectory()) {
|
|
|
44564
44615
|
}
|
|
44565
44616
|
function openTranscript(startedAt) {
|
|
44566
44617
|
mkdirSync16(runsDirectory(), { recursive: true });
|
|
44567
|
-
const path =
|
|
44618
|
+
const path = join33(runsDirectory(), `run-${startedAt}.log`);
|
|
44568
44619
|
return { path, fd: openSync3(path, "a") };
|
|
44569
44620
|
}
|
|
44570
44621
|
function profilePath(startedAt) {
|
|
44571
|
-
return
|
|
44622
|
+
return join33(runsDirectory(), `run-${startedAt}.profile.json`);
|
|
44572
44623
|
}
|
|
44573
44624
|
function profileMarkPath(startedAt) {
|
|
44574
|
-
return
|
|
44625
|
+
return join33(runsDirectory(), `run-${startedAt}.profile-mark.json`);
|
|
44575
44626
|
}
|
|
44576
44627
|
function noteIsAbout(note, playing) {
|
|
44577
44628
|
return note.play !== undefined && playing !== null && note.play.play === playing.play && note.play.editorPid === playing.editorPid;
|
|
44578
44629
|
}
|
|
44579
44630
|
function editorNotePath(projectPath, directory = runtimeDirectory()) {
|
|
44580
|
-
return
|
|
44631
|
+
return join33(directory, "runs", `editor-${projectKey(projectPath)}.json`);
|
|
44581
44632
|
}
|
|
44582
44633
|
function writeEditorRunNote(note) {
|
|
44583
44634
|
mkdirSync16(runsDirectory(), { recursive: true });
|
|
@@ -44630,13 +44681,13 @@ function everyRunRecord() {
|
|
|
44630
44681
|
for (const directory of runtimeDirectories()) {
|
|
44631
44682
|
let entries;
|
|
44632
44683
|
try {
|
|
44633
|
-
entries = readdirSync14(
|
|
44684
|
+
entries = readdirSync14(join33(directory, "runs"));
|
|
44634
44685
|
} catch {
|
|
44635
44686
|
continue;
|
|
44636
44687
|
}
|
|
44637
44688
|
for (const entry of entries) {
|
|
44638
44689
|
if (entry.startsWith("note-") && entry.endsWith(".json")) {
|
|
44639
|
-
const record = recordAt(
|
|
44690
|
+
const record = recordAt(join33(directory, "runs", entry));
|
|
44640
44691
|
if (record !== null) {
|
|
44641
44692
|
found.push(record);
|
|
44642
44693
|
}
|
|
@@ -44811,7 +44862,7 @@ function whyNotTheRun(record, running, needed) {
|
|
|
44811
44862
|
if (after > SAME_RUN_WINDOW_MS) {
|
|
44812
44863
|
return `that process started ${Math.round(after / 1000)} seconds after the run had`;
|
|
44813
44864
|
}
|
|
44814
|
-
const engine =
|
|
44865
|
+
const engine = basename10(record.command);
|
|
44815
44866
|
const said = process.platform === "win32" ? running.text.toLowerCase() : running.text;
|
|
44816
44867
|
const wanted = process.platform === "win32" ? engine.toLowerCase() : engine;
|
|
44817
44868
|
if (!said.includes(wanted)) {
|
|
@@ -44874,7 +44925,7 @@ function sweepTranscripts(keepMs = 24 * 60 * 60 * 1000, now = Date.now()) {
|
|
|
44874
44925
|
if (!transcript && !entry.endsWith(".partial")) {
|
|
44875
44926
|
continue;
|
|
44876
44927
|
}
|
|
44877
|
-
const path =
|
|
44928
|
+
const path = join33(directory, entry);
|
|
44878
44929
|
if (kept.has(path) || ageMs(path, now) < keepMs) {
|
|
44879
44930
|
continue;
|
|
44880
44931
|
}
|
|
@@ -45123,15 +45174,15 @@ var init_server_instructions = __esm(() => {
|
|
|
45123
45174
|
|
|
45124
45175
|
// src/test-selection.ts
|
|
45125
45176
|
import { existsSync as existsSync26, readFileSync as readFileSync28 } from "node:fs";
|
|
45126
|
-
import { basename as
|
|
45177
|
+
import { basename as basename11, join as join34 } from "node:path";
|
|
45127
45178
|
function casesIn(projectPath, suite) {
|
|
45128
|
-
const text = readFileSync28(
|
|
45179
|
+
const text = readFileSync28(join34(projectPath, suite.slice("res://".length)), "utf8");
|
|
45129
45180
|
return [...text.matchAll(/^func\s+(test_\w*)\s*\(/gm)].map((match) => match[1] ?? "");
|
|
45130
45181
|
}
|
|
45131
45182
|
function suitesUnder(projectPath, asked) {
|
|
45132
45183
|
const scripts = projectFiles(projectPath, [".gd"]).map((path) => `res://${path}`);
|
|
45133
45184
|
return [
|
|
45134
|
-
...new Set(asked.flatMap((path) => path.endsWith(".gd") ? existsSync26(
|
|
45185
|
+
...new Set(asked.flatMap((path) => path.endsWith(".gd") ? existsSync26(join34(projectPath, path.slice("res://".length))) ? [path] : [] : scripts.filter((script) => script.startsWith(`${path}/`))))
|
|
45135
45186
|
];
|
|
45136
45187
|
}
|
|
45137
45188
|
function selectTests(projectPath, asked, given) {
|
|
@@ -45143,7 +45194,7 @@ function selectTests(projectPath, asked, given) {
|
|
|
45143
45194
|
return { ok: false, reason: "tests is an empty list: name a case, or leave tests out to run every one." };
|
|
45144
45195
|
}
|
|
45145
45196
|
const declared = suitesUnder(projectPath, asked).flatMap((suite) => casesIn(projectPath, suite).map((test) => ({ suite, test })));
|
|
45146
|
-
const nameOf = (suite) =>
|
|
45197
|
+
const nameOf = (suite) => basename11(suite, ".gd");
|
|
45147
45198
|
const selected = [];
|
|
45148
45199
|
for (const [index, entry] of entries.entries()) {
|
|
45149
45200
|
if (typeof entry !== "string" || entry.trim() === "") {
|
|
@@ -45203,7 +45254,7 @@ import {
|
|
|
45203
45254
|
statSync as statSync15,
|
|
45204
45255
|
writeFileSync as writeFileSync18
|
|
45205
45256
|
} from "node:fs";
|
|
45206
|
-
import { basename as
|
|
45257
|
+
import { basename as basename12, dirname as dirname23, extname as extname4, isAbsolute as isAbsolute5, join as join35, normalize as normalize2, relative as relative6 } from "node:path";
|
|
45207
45258
|
import { setTimeout as delay5 } from "node:timers/promises";
|
|
45208
45259
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
45209
45260
|
function editorRestartTimeoutMs() {
|
|
@@ -45239,12 +45290,12 @@ function writtenFileRefusal(path, rule) {
|
|
|
45239
45290
|
if (!rule.extensions.includes(extname4(path).toLowerCase())) {
|
|
45240
45291
|
return `${named} ${path} does not end in ${rule.extensions.join(" or ")}, which is what ${what} is.`;
|
|
45241
45292
|
}
|
|
45242
|
-
const directory =
|
|
45293
|
+
const directory = realAsFarAsItExists(dirname23(path));
|
|
45243
45294
|
if (!existsSync27(directory)) {
|
|
45244
45295
|
return `${named}'s directory ${dirname23(path)} does not exist; ${what} creates no directories.`;
|
|
45245
45296
|
}
|
|
45246
|
-
const where =
|
|
45247
|
-
const inside = rule.projects.find((project) => project !== "" && isWithinRoot(
|
|
45297
|
+
const where = join35(directory, basename12(path));
|
|
45298
|
+
const inside = rule.projects.find((project) => project !== "" && isWithinRoot(realAsFarAsItExists(project), where));
|
|
45248
45299
|
if (inside !== undefined) {
|
|
45249
45300
|
return `${named} ${path} is inside the project at ${inside}; ${what} is saved outside it, so it never becomes part of the game.`;
|
|
45250
45301
|
}
|
|
@@ -45334,7 +45385,7 @@ function keptGameRequest(tool, op, args) {
|
|
|
45334
45385
|
}
|
|
45335
45386
|
}
|
|
45336
45387
|
function projectSpelling(projectPath, absolutePath) {
|
|
45337
|
-
return `res://${relative6(
|
|
45388
|
+
return `res://${relative6(realAsFarAsItExists(projectPath), realAsFarAsItExists(absolutePath)).replaceAll("\\", "/")}`;
|
|
45338
45389
|
}
|
|
45339
45390
|
function whichGame(args) {
|
|
45340
45391
|
return { projectPath: args["projectPath"], pid: args["pid"] };
|
|
@@ -45709,19 +45760,37 @@ function describeValue(value) {
|
|
|
45709
45760
|
return VALUE_NAMES[typeof value] ?? typeof value;
|
|
45710
45761
|
}
|
|
45711
45762
|
function wrongTypes(spec, args) {
|
|
45712
|
-
|
|
45713
|
-
|
|
45714
|
-
|
|
45715
|
-
|
|
45763
|
+
return Object.entries(args).flatMap(([name, value]) => misshapen(spec.parameters[name], value, name));
|
|
45764
|
+
}
|
|
45765
|
+
function misshapen(schema, value, path) {
|
|
45766
|
+
if (!isPlainObject3(schema) || value === undefined || value === null) {
|
|
45767
|
+
return [];
|
|
45768
|
+
}
|
|
45769
|
+
const declared = schema["type"];
|
|
45770
|
+
const wanted = typeof declared === "string" ? [declared] : Array.isArray(declared) ? declared.filter((kind) => typeof kind === "string") : [];
|
|
45771
|
+
if (wanted.length > 0 && !wanted.some((kind) => isOfType(value, kind))) {
|
|
45772
|
+
return [`${path} as ${wanted.join(" or ")}, not ${describeValue(value)}`];
|
|
45773
|
+
}
|
|
45774
|
+
if (Array.isArray(value)) {
|
|
45775
|
+
return value.flatMap((element, index) => misshapen(schema["items"], element, `${path}[${index}]`));
|
|
45776
|
+
}
|
|
45777
|
+
const properties = schema["properties"];
|
|
45778
|
+
if (!isPlainObject3(properties) || !isPlainObject3(value)) {
|
|
45779
|
+
return [];
|
|
45780
|
+
}
|
|
45781
|
+
return Object.entries(value).flatMap(([field, held]) => {
|
|
45782
|
+
if (Object.hasOwn(properties, field)) {
|
|
45783
|
+
return misshapen(properties[field], held, `${path}.${field}`);
|
|
45716
45784
|
}
|
|
45717
|
-
|
|
45718
|
-
|
|
45719
|
-
if (wanted.length === 0 || wanted.some((kind) => isOfType(value, kind))) {
|
|
45720
|
-
continue;
|
|
45785
|
+
if (schema["additionalProperties"] !== false) {
|
|
45786
|
+
return [];
|
|
45721
45787
|
}
|
|
45722
|
-
|
|
45723
|
-
|
|
45724
|
-
|
|
45788
|
+
const fields = Object.keys(properties);
|
|
45789
|
+
const close = nearest(field, fields);
|
|
45790
|
+
return [
|
|
45791
|
+
`${path} with only the fields ${fields.join(", ")}, and ${field} is none of them${close.length === 0 ? "" : ` (the nearest is ${close[0]})`}`
|
|
45792
|
+
];
|
|
45793
|
+
});
|
|
45725
45794
|
}
|
|
45726
45795
|
function unlistedValues(schema, value, path) {
|
|
45727
45796
|
if (typeof schema !== "object" || schema === null) {
|
|
@@ -45775,10 +45844,14 @@ function importedFirstAnswer(importedFirst) {
|
|
|
45775
45844
|
if (importedFirst === undefined) {
|
|
45776
45845
|
return {};
|
|
45777
45846
|
}
|
|
45847
|
+
const imported = withAddonPathsCounted(importedFirst.imported);
|
|
45848
|
+
const uids = withAddonPathsCounted(importedFirst.uidsWritten);
|
|
45778
45849
|
return {
|
|
45779
|
-
...
|
|
45850
|
+
...imported.own.length === 0 ? {} : { importedFirst: imported.own },
|
|
45851
|
+
...Object.keys(imported.inAddons).length === 0 ? {} : { importedInAddons: imported.inAddons },
|
|
45780
45852
|
...importedFirst.notImported.length === 0 ? {} : { notImportedFirst: importedFirst.notImported },
|
|
45781
|
-
...
|
|
45853
|
+
...uids.own.length === 0 ? {} : { uidsWrittenFirst: uids.own },
|
|
45854
|
+
...Object.keys(uids.inAddons).length === 0 ? {} : { uidsWrittenInAddons: uids.inAddons },
|
|
45782
45855
|
importTookMs: importedFirst.tookMs,
|
|
45783
45856
|
...importedFirst.note === undefined ? {} : { importNote: importedFirst.note }
|
|
45784
45857
|
};
|
|
@@ -45811,7 +45884,7 @@ function notRunNote(notRun, failFast) {
|
|
|
45811
45884
|
function exportPresetRefusal(projectPath, preset) {
|
|
45812
45885
|
let text;
|
|
45813
45886
|
try {
|
|
45814
|
-
text = readFileSync29(
|
|
45887
|
+
text = readFileSync29(join35(projectPath, "export_presets.cfg"), "utf8");
|
|
45815
45888
|
} catch {
|
|
45816
45889
|
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.`;
|
|
45817
45890
|
}
|
|
@@ -45877,7 +45950,7 @@ function filesIn(folder) {
|
|
|
45877
45950
|
try {
|
|
45878
45951
|
for (const entry of readdirSync15(folder, { withFileTypes: true })) {
|
|
45879
45952
|
if (entry.isFile()) {
|
|
45880
|
-
found.set(entry.name, statSync15(
|
|
45953
|
+
found.set(entry.name, statSync15(join35(folder, entry.name)).mtimeMs);
|
|
45881
45954
|
}
|
|
45882
45955
|
}
|
|
45883
45956
|
} catch {}
|
|
@@ -45909,7 +45982,7 @@ function unsavedInEditorNote(scripts) {
|
|
|
45909
45982
|
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.`;
|
|
45910
45983
|
}
|
|
45911
45984
|
function editorAddonMissing(projectPath) {
|
|
45912
|
-
if (!existsSync27(
|
|
45985
|
+
if (!existsSync27(join35(projectPath, "project.godot"))) {
|
|
45913
45986
|
return null;
|
|
45914
45987
|
}
|
|
45915
45988
|
const report = inspectProject(projectPath);
|
|
@@ -46174,7 +46247,7 @@ function profileFrameFunctions(projectPath) {
|
|
|
46174
46247
|
let projectGodot = null;
|
|
46175
46248
|
if (projectPath !== null) {
|
|
46176
46249
|
try {
|
|
46177
|
-
projectGodot = readFileSync29(
|
|
46250
|
+
projectGodot = readFileSync29(join35(projectPath, "project.godot"), "utf8");
|
|
46178
46251
|
} catch {}
|
|
46179
46252
|
}
|
|
46180
46253
|
return frameFunctionsFor(queuedMessagesOf(projectGodot));
|
|
@@ -46353,7 +46426,7 @@ function foldedEntriesAnswer(log, floor = "warning") {
|
|
|
46353
46426
|
}
|
|
46354
46427
|
function gdUnitFailsOnScriptErrors(projectPath) {
|
|
46355
46428
|
try {
|
|
46356
|
-
return settingKeys(readFileSync29(
|
|
46429
|
+
return settingKeys(readFileSync29(join35(projectPath, "project.godot"), "utf8")).get("gdunit4/report/godot/script_error") !== false;
|
|
46357
46430
|
} catch {
|
|
46358
46431
|
return true;
|
|
46359
46432
|
}
|
|
@@ -46368,7 +46441,7 @@ function uncaughtNote(uncaught) {
|
|
|
46368
46441
|
}
|
|
46369
46442
|
function elsewhereIn(projectPath, asked) {
|
|
46370
46443
|
const wanted = asked.replace(/^res:\/\//, "").replace(/\/+$/, "");
|
|
46371
|
-
if (wanted === "" || existsSync27(
|
|
46444
|
+
if (wanted === "" || existsSync27(join35(projectPath, wanted))) {
|
|
46372
46445
|
return "";
|
|
46373
46446
|
}
|
|
46374
46447
|
const root = (wanted.split("/")[0] ?? "").toLowerCase();
|
|
@@ -46471,7 +46544,7 @@ function isDirectory(path) {
|
|
|
46471
46544
|
}
|
|
46472
46545
|
}
|
|
46473
46546
|
function scriptsUnder(directory, prefix) {
|
|
46474
|
-
if (existsSync27(
|
|
46547
|
+
if (existsSync27(join35(directory, ".gdignore"))) {
|
|
46475
46548
|
return [];
|
|
46476
46549
|
}
|
|
46477
46550
|
const found = [];
|
|
@@ -46481,7 +46554,7 @@ function scriptsUnder(directory, prefix) {
|
|
|
46481
46554
|
}
|
|
46482
46555
|
const path = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
|
|
46483
46556
|
if (entry.isDirectory()) {
|
|
46484
|
-
found.push(...scriptsUnder(
|
|
46557
|
+
found.push(...scriptsUnder(join35(directory, entry.name), path));
|
|
46485
46558
|
} else if (entry.isFile() && entry.name.endsWith(".gd")) {
|
|
46486
46559
|
found.push(path);
|
|
46487
46560
|
}
|
|
@@ -46511,7 +46584,7 @@ function needsItsOwnProcess(asked) {
|
|
|
46511
46584
|
class GodotServer {
|
|
46512
46585
|
stdio = null;
|
|
46513
46586
|
locator = new GodotLocator;
|
|
46514
|
-
operationsScript =
|
|
46587
|
+
operationsScript = join35(__dirname2, "godot", "operations", "godot_operations.gd");
|
|
46515
46588
|
godotBridge;
|
|
46516
46589
|
tools = buildToolDefinitions();
|
|
46517
46590
|
activeProcess = null;
|
|
@@ -46773,7 +46846,7 @@ class GodotServer {
|
|
|
46773
46846
|
}
|
|
46774
46847
|
noteTheStart() {
|
|
46775
46848
|
const cwd = process.cwd();
|
|
46776
|
-
const mcpJson =
|
|
46849
|
+
const mcpJson = join35(cwd, ".mcp.json");
|
|
46777
46850
|
const announced = this.ownProject === null ? null : readAnnouncement(announcementPath(this.ownProject));
|
|
46778
46851
|
const predecessor = announced !== null && announced.pid !== process.pid && alive(announced.pid) ? announced.pid : null;
|
|
46779
46852
|
writeLifecycle("started", {
|
|
@@ -46921,7 +46994,7 @@ class GodotServer {
|
|
|
46921
46994
|
return checked.response;
|
|
46922
46995
|
}
|
|
46923
46996
|
const named = args["projectPath"];
|
|
46924
|
-
if (typeof named === "string" && existsSync27(
|
|
46997
|
+
if (typeof named === "string" && existsSync27(join35(named, "project.godot"))) {
|
|
46925
46998
|
this.lastProjectPath = named;
|
|
46926
46999
|
}
|
|
46927
47000
|
this.updates.refresh();
|
|
@@ -46957,7 +47030,7 @@ class GodotServer {
|
|
|
46957
47030
|
let notSaved;
|
|
46958
47031
|
try {
|
|
46959
47032
|
savedAt = answer.content.map((item, index) => {
|
|
46960
|
-
const file =
|
|
47033
|
+
const file = join35(directory, item.type === "text" ? `${stem}-${index}.txt` : `${stem}-${index}.${item.mimeType.split("/")[1] ?? "bin"}`);
|
|
46961
47034
|
writeFileSync18(file, item.type === "text" ? item.text : Buffer.from(item.data, "base64"));
|
|
46962
47035
|
return file;
|
|
46963
47036
|
});
|
|
@@ -47236,6 +47309,14 @@ class GodotServer {
|
|
|
47236
47309
|
if (headless === "modify_script") {
|
|
47237
47310
|
return await this.handleModifyScript(answerable);
|
|
47238
47311
|
}
|
|
47312
|
+
if (tool === "editor_classes") {
|
|
47313
|
+
const project = this.project(answerable);
|
|
47314
|
+
if (!project.ok) {
|
|
47315
|
+
return project.response;
|
|
47316
|
+
}
|
|
47317
|
+
const projectClasses = Object.fromEntries(declaredClasses(project.value.path));
|
|
47318
|
+
return await this.headless(headless, { ...answerable, projectClasses });
|
|
47319
|
+
}
|
|
47239
47320
|
if (headless !== "refresh_class_cache") {
|
|
47240
47321
|
return await this.headless(headless, answerable);
|
|
47241
47322
|
}
|
|
@@ -47491,12 +47572,23 @@ class GodotServer {
|
|
|
47491
47572
|
if (!project.ok) {
|
|
47492
47573
|
return project.response;
|
|
47493
47574
|
}
|
|
47575
|
+
if (op === "list") {
|
|
47576
|
+
const session = this.dapHoldingBreakpointsOf(project.value.path);
|
|
47577
|
+
const editor = this.godotBridge.isConnected();
|
|
47578
|
+
return this.jsonTextResponse({
|
|
47579
|
+
held: this.breakpointsAsProjectSpells(project.value.path, session.breakpointsHeld()),
|
|
47580
|
+
...editor ? {
|
|
47581
|
+
setInEditor: this.breakpointsAsProjectSpells(project.value.path, session.breakpointsInEditor()),
|
|
47582
|
+
...this.breakpointsAtRisk()
|
|
47583
|
+
} : {}
|
|
47584
|
+
});
|
|
47585
|
+
}
|
|
47494
47586
|
const located = resolveWithinProject(project.value.path, readString(args, "scriptPath") ?? "");
|
|
47495
47587
|
if (!located.ok) {
|
|
47496
47588
|
return this.createErrorResponse(located.reason, PATH_SOLUTIONS);
|
|
47497
47589
|
}
|
|
47498
47590
|
const session = this.dapHoldingBreakpointsOf(project.value.path);
|
|
47499
|
-
const scriptPath =
|
|
47591
|
+
const scriptPath = realAsFarAsItExists(located.absolutePath);
|
|
47500
47592
|
const line = readPositiveNumber(args, "line");
|
|
47501
47593
|
if (line === undefined) {
|
|
47502
47594
|
return this.createErrorResponse("line is required: the one-based line to stop on.");
|
|
@@ -47524,10 +47616,13 @@ class GodotServer {
|
|
|
47524
47616
|
} else {
|
|
47525
47617
|
if (op === "set") {
|
|
47526
47618
|
session.holdBreakpoints([{ scriptPath, lines: [line] }]);
|
|
47619
|
+
answer = { set: true };
|
|
47527
47620
|
} else {
|
|
47528
|
-
session.forgetBreakpoint(scriptPath, line)
|
|
47621
|
+
answer = session.forgetBreakpoint(scriptPath, line) ? { removed: true } : {
|
|
47622
|
+
removed: false,
|
|
47623
|
+
why: `No breakpoint was held on line ${line} of res://${located.relativePath}, so there was nothing to remove; held lists the ones that are.`
|
|
47624
|
+
};
|
|
47529
47625
|
}
|
|
47530
|
-
answer = { [op === "set" ? "set" : "removed"]: true };
|
|
47531
47626
|
}
|
|
47532
47627
|
const held = session.breakpointsHeld();
|
|
47533
47628
|
writeBreakpointNote(project.value.path, held);
|
|
@@ -47549,7 +47644,7 @@ class GodotServer {
|
|
|
47549
47644
|
} : {
|
|
47550
47645
|
runningGame: `Told to the running game (pid ${kept?.pid ?? "?"}), which this server started.`
|
|
47551
47646
|
},
|
|
47552
|
-
note: editor ? "The editor keeps a breakpoint set this way for one play. Every breakpoint held here is sent again before each play editor_run starts, handed to every game this server starts itself, and kept in the project for the server after a reconnect." : "No editor is connected, so the
|
|
47647
|
+
note: editor ? "The editor keeps a breakpoint set this way for one play. Every breakpoint held here is sent again before each play editor_run starts, handed to every game this server starts itself, and kept in the project for the server after a reconnect." : "No editor is connected, so the breakpoints under held are kept here: every game this server starts is handed them as it starts, an editor that connects is sent them before its first play, and they are kept in the project for the server after a reconnect.",
|
|
47553
47648
|
...editor ? this.breakpointsAtRisk() : {}
|
|
47554
47649
|
});
|
|
47555
47650
|
}
|
|
@@ -47592,7 +47687,7 @@ class GodotServer {
|
|
|
47592
47687
|
if (path === undefined) {
|
|
47593
47688
|
return { ok: false, response: this.createErrorResponse("projectPath is required.") };
|
|
47594
47689
|
}
|
|
47595
|
-
const file =
|
|
47690
|
+
const file = join35(path, "project.godot");
|
|
47596
47691
|
if (!existsSync27(file)) {
|
|
47597
47692
|
return {
|
|
47598
47693
|
ok: false,
|
|
@@ -47603,34 +47698,8 @@ class GodotServer {
|
|
|
47603
47698
|
}
|
|
47604
47699
|
return { ok: true, value: { path, file } };
|
|
47605
47700
|
}
|
|
47606
|
-
gameArguments(args) {
|
|
47607
|
-
if (args["args"] === undefined) {
|
|
47608
|
-
return { ok: true, value: [] };
|
|
47609
|
-
}
|
|
47610
|
-
const given = readStringArray(args, "args");
|
|
47611
|
-
if (given === undefined) {
|
|
47612
|
-
return {
|
|
47613
|
-
ok: false,
|
|
47614
|
-
response: this.createErrorResponse("args must be a list of strings.", [
|
|
47615
|
-
`Pass the game's own flags, such as ["--level=2"]`
|
|
47616
|
-
])
|
|
47617
|
-
};
|
|
47618
|
-
}
|
|
47619
|
-
return { ok: true, value: given };
|
|
47620
|
-
}
|
|
47621
47701
|
engineArguments(args) {
|
|
47622
|
-
|
|
47623
|
-
return { ok: true, value: [] };
|
|
47624
|
-
}
|
|
47625
|
-
const given = readStringArray(args, "engineArgs");
|
|
47626
|
-
if (given === undefined) {
|
|
47627
|
-
return {
|
|
47628
|
-
ok: false,
|
|
47629
|
-
response: this.createErrorResponse("engineArgs must be a list of strings.", [
|
|
47630
|
-
'Pass engine flags, such as ["--verbose"] or ["--fixed-fps", "60"]'
|
|
47631
|
-
])
|
|
47632
|
-
};
|
|
47633
|
-
}
|
|
47702
|
+
const given = readStringArray(args, "engineArgs") ?? [];
|
|
47634
47703
|
const checked = engineFlags(given);
|
|
47635
47704
|
if (!checked.ok) {
|
|
47636
47705
|
return { ok: false, response: this.createErrorResponse(checked.reason) };
|
|
@@ -48032,6 +48101,16 @@ class GodotServer {
|
|
|
48032
48101
|
...params
|
|
48033
48102
|
} = contained.value;
|
|
48034
48103
|
const written = await this.operation("set_import_options", params, project.value.path);
|
|
48104
|
+
const changed = written.ok ? written.payload["updated_options"] : null;
|
|
48105
|
+
if (written.ok && Array.isArray(changed) && changed.length === 0) {
|
|
48106
|
+
return this.answer({
|
|
48107
|
+
...written,
|
|
48108
|
+
payload: {
|
|
48109
|
+
...written.payload,
|
|
48110
|
+
note: "Not reimported: every option given already held that value, so the import file was not written."
|
|
48111
|
+
}
|
|
48112
|
+
});
|
|
48113
|
+
}
|
|
48035
48114
|
if (!written.ok || args["reimport"] === false) {
|
|
48036
48115
|
return this.answer(written.ok ? {
|
|
48037
48116
|
...written,
|
|
@@ -48314,7 +48393,7 @@ class GodotServer {
|
|
|
48314
48393
|
const name = application["config/name"];
|
|
48315
48394
|
const mainScene = application["run/main_scene"];
|
|
48316
48395
|
const info = {
|
|
48317
|
-
name: typeof name === "string" && name !== "" ? name :
|
|
48396
|
+
name: typeof name === "string" && name !== "" ? name : basename12(project.value.path),
|
|
48318
48397
|
path: project.value.path,
|
|
48319
48398
|
mainScene: typeof mainScene === "string" && mainScene !== "" ? mainScene : null,
|
|
48320
48399
|
godotVersion: await this.godotVersion(engine.value),
|
|
@@ -48432,7 +48511,7 @@ class GodotServer {
|
|
|
48432
48511
|
const exportArgs = [
|
|
48433
48512
|
"--headless",
|
|
48434
48513
|
"--log-file",
|
|
48435
|
-
|
|
48514
|
+
join35(exportLogs, "engine.log"),
|
|
48436
48515
|
"--path",
|
|
48437
48516
|
project.value.path,
|
|
48438
48517
|
debug ? "--export-debug" : "--export-release",
|
|
@@ -48469,7 +48548,7 @@ class GodotServer {
|
|
|
48469
48548
|
if (ending.exitSignal === null && ending.exitCode === null) {
|
|
48470
48549
|
return this.createErrorResponse(`Export could not be run: ${ending.failure ?? "the engine gave no exit status"}`);
|
|
48471
48550
|
}
|
|
48472
|
-
const beside = writtenBeside(listedBefore, filesIn(folder),
|
|
48551
|
+
const beside = writtenBeside(listedBefore, filesIn(folder), basename12(output.absolutePath), dirname23(output.relativePath).replaceAll("\\", "/"));
|
|
48473
48552
|
return exportAnswer(ending, { preset, outputPath: output.relativePath, debug }, before, writtenAt(), beside, docCacheNote);
|
|
48474
48553
|
}
|
|
48475
48554
|
reportTestProgress(log) {
|
|
@@ -48523,10 +48602,10 @@ class GodotServer {
|
|
|
48523
48602
|
if (movie !== null) {
|
|
48524
48603
|
return this.createErrorResponse(movie);
|
|
48525
48604
|
}
|
|
48526
|
-
const missing = paths.value.filter((path) => !existsSync27(
|
|
48605
|
+
const missing = paths.value.filter((path) => !existsSync27(join35(project.value.path, path.slice("res://".length))));
|
|
48527
48606
|
const asked = paths.value.filter((path) => !missing.includes(path));
|
|
48528
48607
|
const runner = "addons/gdUnit4/bin/GdUnitCmdTool.gd";
|
|
48529
|
-
const noRunner = !existsSync27(
|
|
48608
|
+
const noRunner = !existsSync27(join35(project.value.path, runner));
|
|
48530
48609
|
if (asked.length === 0) {
|
|
48531
48610
|
const verdict = `nothing at ${missing.join(" and ")}`;
|
|
48532
48611
|
const andNoRunner = noRunner ? `, and gdUnit4 is not installed in this project: no ${runner}` : "";
|
|
@@ -48646,14 +48725,14 @@ class GodotServer {
|
|
|
48646
48725
|
await run.settled;
|
|
48647
48726
|
const tookMs = Date.now() - began;
|
|
48648
48727
|
const notCompiled = stopped.for ?? run.log.all.map((entry) => autoloadNotCompiled(entry, autoloaded)).find((found) => found !== null) ?? null;
|
|
48649
|
-
const reportsDir =
|
|
48728
|
+
const reportsDir = join35(project.value.path, ".godot", ...ours.split("/"));
|
|
48650
48729
|
let report = null;
|
|
48651
48730
|
let reportProblem = null;
|
|
48652
48731
|
try {
|
|
48653
48732
|
const written = existsSync27(reportsDir) && notCompiled === null ? readdirSync15(reportsDir).filter((name) => name.startsWith("report_")).sort((a, b) => Number(a.slice("report_".length)) - Number(b.slice("report_".length))) : [];
|
|
48654
48733
|
const newest = written.at(-1);
|
|
48655
48734
|
if (newest !== undefined) {
|
|
48656
|
-
report = parseJUnit(readFileSync29(
|
|
48735
|
+
report = parseJUnit(readFileSync29(join35(reportsDir, newest, "results.xml"), "utf8"));
|
|
48657
48736
|
}
|
|
48658
48737
|
} catch (error) {
|
|
48659
48738
|
reportProblem = errorMessage(error);
|
|
@@ -48781,7 +48860,7 @@ class GodotServer {
|
|
|
48781
48860
|
const listedAsUncaught = new Set(uncaughtFolded.map((entry) => entry.index));
|
|
48782
48861
|
const notApplied = ignoredYetRan(report, ignored);
|
|
48783
48862
|
const testsNotRun = [
|
|
48784
|
-
...new Set((selection.value?.selected ?? []).filter((one) => !report.suites.some((suite) => (suite.path === one.suite || suite.name ===
|
|
48863
|
+
...new Set((selection.value?.selected ?? []).filter((one) => !report.suites.some((suite) => (suite.path === one.suite || suite.name === basename12(one.suite, ".gd")) && suite.cases.some((ran) => ran.name === one.test || new RegExp(`^${one.test}\\W`).test(ran.name)))).map((one) => `${basename12(one.suite, ".gd")}:${one.test}`))
|
|
48785
48864
|
];
|
|
48786
48865
|
return this.jsonTextResponse({
|
|
48787
48866
|
passed: !timedOut && exitCode === 0 && nothingRan === null && report.tests > 0 && report.failures === 0 && report.errors === 0 && hookFailures.length === 0 && uncaught.length === 0 && missing.length === 0 && testsNotRun.length === 0,
|
|
@@ -49296,7 +49375,7 @@ class GodotServer {
|
|
|
49296
49375
|
...Array.isArray(stack) ? {
|
|
49297
49376
|
stack: stack.map((frame) => ({
|
|
49298
49377
|
...frame,
|
|
49299
|
-
...projectPath !== "" && typeof frame["script"] === "string" && frame["script"].startsWith("res://") ? { path:
|
|
49378
|
+
...projectPath !== "" && typeof frame["script"] === "string" && frame["script"].startsWith("res://") ? { path: join35(projectPath, frame["script"].slice("res://".length)) } : {}
|
|
49300
49379
|
}))
|
|
49301
49380
|
} : {}
|
|
49302
49381
|
});
|
|
@@ -49359,7 +49438,7 @@ class GodotServer {
|
|
|
49359
49438
|
const launchedIsUp = !status.connected && launchedWasUp;
|
|
49360
49439
|
const arriving = status.connected ? [] : arrivingFound;
|
|
49361
49440
|
const isPortConflict = this.bridgeStartupError?.includes("EADDRINUSE") ?? false;
|
|
49362
|
-
const greeted =
|
|
49441
|
+
const greeted = status.connected;
|
|
49363
49442
|
const staleNote = this.godotBridge.anotherRelease();
|
|
49364
49443
|
const unfinished = this.restartLeftUnfinished(status.connected);
|
|
49365
49444
|
const cannotConnect = status.connected || this.ownProject === null ? null : editorAddonMissing(this.ownProject);
|
|
@@ -49367,7 +49446,7 @@ class GodotServer {
|
|
|
49367
49446
|
...status,
|
|
49368
49447
|
serverVersion: SERVER_VERSION,
|
|
49369
49448
|
addonIsStale: greeted ? staleNote !== undefined : undefined,
|
|
49370
|
-
greeting: status.
|
|
49449
|
+
greeting: status.identifying === true ? "An editor has opened the bridge and has not yet said who it is: its project, pid, addon version and ports arrive with its greeting a moment after the socket opens, later while it imports, and one nobody's server opened is served once its command line has been read. Ask again for them." : undefined,
|
|
49371
49450
|
bridgeAvailable: this.bridgeStartupError === null && status.port !== null,
|
|
49372
49451
|
mayYetConnect: status.connected ? undefined : cannotConnect !== null ? false : arriving.length > 0 ? true : unfinished !== null ? unfinished.underway : this.closedHere !== null ? false : anEditorIsStillComing(status.listeningSince, launchedIsUp),
|
|
49373
49452
|
editorsNotYetConnected: arriving.length > 0 ? arriving : undefined,
|
|
@@ -49575,21 +49654,21 @@ class GodotServer {
|
|
|
49575
49654
|
return null;
|
|
49576
49655
|
}
|
|
49577
49656
|
try {
|
|
49578
|
-
return settingKeys(readFileSync29(
|
|
49657
|
+
return settingKeys(readFileSync29(join35(projectPath, "project.godot"), "utf8"));
|
|
49579
49658
|
} catch {
|
|
49580
49659
|
return null;
|
|
49581
49660
|
}
|
|
49582
49661
|
}
|
|
49583
49662
|
async handleRestartEditor(args) {
|
|
49584
49663
|
const before = this.godotBridge.getStatus();
|
|
49585
|
-
if (!before.connected) {
|
|
49664
|
+
if (!before.connected && before.identifying !== true) {
|
|
49586
49665
|
return this.createErrorResponse("No editor is connected, so there is nothing to restart.", [
|
|
49587
49666
|
"editor_launch opens one on a project",
|
|
49588
49667
|
"editor_status says whether the bridge is up and what has reached it"
|
|
49589
49668
|
]);
|
|
49590
49669
|
}
|
|
49591
|
-
if (!
|
|
49592
|
-
return this.createErrorResponse(`
|
|
49670
|
+
if (!before.connected) {
|
|
49671
|
+
return this.createErrorResponse(`An editor has opened the bridge and has not said who it is within ${greetingWaitMs() / 1000}s, so whether this server or the editor itself restarts it cannot be told.`, ["editor_status says when it has, under greeting while it has not", "Then ask for the restart again"]);
|
|
49593
49672
|
}
|
|
49594
49673
|
const mine = before.openedByAServer === true && before.projectPath !== undefined;
|
|
49595
49674
|
if (!mine && args["hidden"] === true) {
|
|
@@ -49631,7 +49710,7 @@ class GodotServer {
|
|
|
49631
49710
|
}
|
|
49632
49711
|
async handleCloseEditor() {
|
|
49633
49712
|
const before = this.godotBridge.getStatus();
|
|
49634
|
-
if (!before.connected) {
|
|
49713
|
+
if (!before.connected && before.identifying !== true) {
|
|
49635
49714
|
const launched = this.launchedEditor;
|
|
49636
49715
|
if (launched !== null && await this.launchedEditorIsUp() && !this.godotBridge.isConnected()) {
|
|
49637
49716
|
return await this.stopTheLaunchedEditor(launched);
|
|
@@ -49640,8 +49719,8 @@ class GodotServer {
|
|
|
49640
49719
|
"editor_status says whether the bridge is up and what has reached it"
|
|
49641
49720
|
]);
|
|
49642
49721
|
}
|
|
49643
|
-
if (!
|
|
49644
|
-
return this.createErrorResponse(`
|
|
49722
|
+
if (!before.connected || before.editorPid === undefined) {
|
|
49723
|
+
return this.createErrorResponse(`An editor has opened the bridge and has not said who it is within ${greetingWaitMs() / 1000}s, so there is no process to wait on once it is asked to go.`, ["editor_status says when it has, under greeting while it has not", "Then ask for the close again"]);
|
|
49645
49724
|
}
|
|
49646
49725
|
const pid = before.editorPid;
|
|
49647
49726
|
const settingsBefore = this.settingKeysOf(before.projectPath);
|
|
@@ -49697,7 +49776,7 @@ class GodotServer {
|
|
|
49697
49776
|
settleTheLaunchReading() {
|
|
49698
49777
|
const watched = this.launchedFrom;
|
|
49699
49778
|
const status = this.godotBridge.getStatus();
|
|
49700
|
-
if (watched === null || !
|
|
49779
|
+
if (watched === null || !status.connected) {
|
|
49701
49780
|
return;
|
|
49702
49781
|
}
|
|
49703
49782
|
this.launchedFrom = null;
|
|
@@ -49906,10 +49985,7 @@ class GodotServer {
|
|
|
49906
49985
|
if (exported !== null && readBoolean(args, "profile") === true) {
|
|
49907
49986
|
return this.createErrorResponse("A profile is read through the debugger, and an exported build is started without one, since a release build quits when given --remote-debug. Profile the project without executable.");
|
|
49908
49987
|
}
|
|
49909
|
-
const given =
|
|
49910
|
-
if (!given.ok) {
|
|
49911
|
-
return given.response;
|
|
49912
|
-
}
|
|
49988
|
+
const given = readStringArray(args, "args") ?? [];
|
|
49913
49989
|
const flags = this.engineArguments(args);
|
|
49914
49990
|
if (!flags.ok) {
|
|
49915
49991
|
return flags.response;
|
|
@@ -49941,7 +50017,7 @@ class GodotServer {
|
|
|
49941
50017
|
const scanned = await this.afterTheImport(project.value.path, waited, imported);
|
|
49942
50018
|
const sceneArgument = sceneToRun?.ok ? sceneToRun.relativePath : null;
|
|
49943
50019
|
if (op === "check") {
|
|
49944
|
-
return await this.checkBoot(engine.value, project.value.path, sceneArgument, args, given
|
|
50020
|
+
return await this.checkBoot(engine.value, project.value.path, sceneArgument, args, given, scanned, asked.value, flags.value);
|
|
49945
50021
|
}
|
|
49946
50022
|
await this.pickUpWhatTheEditorIsPlaying();
|
|
49947
50023
|
let ended = null;
|
|
@@ -49970,14 +50046,14 @@ class GodotServer {
|
|
|
49970
50046
|
const silent = resolveSilent(args["silent"]) && !headless;
|
|
49971
50047
|
const silentOnlyHere = silent && !editorPlaysHeadless(project.value.file);
|
|
49972
50048
|
const profiled = readBoolean(args, "profile") === true;
|
|
49973
|
-
if (exported === null && this.godotBridge.isConnected() && editorWouldPlay && given.
|
|
50049
|
+
if (exported === null && this.godotBridge.isConnected() && editorWouldPlay && given.length === 0 && flags.value.length === 0 && !needsItsOwnProcess(asked.value) && !silentOnlyHere) {
|
|
49974
50050
|
return await this.playThroughEditor(sceneArgument, refreshed.value, scanned, project.value.path, alreadyPlaying, runtimeWaitMs, ended, profiled);
|
|
49975
50051
|
}
|
|
49976
50052
|
const cmdArgs = runArguments({
|
|
49977
50053
|
projectPath: exported === null ? project.value.path : null,
|
|
49978
50054
|
headless,
|
|
49979
50055
|
scene: sceneArgument,
|
|
49980
|
-
userArgs: given
|
|
50056
|
+
userArgs: given,
|
|
49981
50057
|
engineArgs: flags.value,
|
|
49982
50058
|
silent
|
|
49983
50059
|
});
|
|
@@ -49991,7 +50067,7 @@ class GodotServer {
|
|
|
49991
50067
|
started.announcedBefore = alreadyPlaying;
|
|
49992
50068
|
started.bootScene = runtimeWaitMs.scene;
|
|
49993
50069
|
this.activeProcess = started;
|
|
49994
|
-
const spawnedInstead = exported !== null ? " An exported build is started without a debugger, since a release build quits when given one, so breakpoints and debug_state do not reach it." : this.godotBridge.isConnected() && editorWouldPlay && (given.
|
|
50070
|
+
const spawnedInstead = exported !== null ? " An exported build is started without a debugger, since a release build quits when given one, so breakpoints and debug_state do not reach it." : this.godotBridge.isConnected() && editorWouldPlay && (given.length > 0 || flags.value.length > 0) ? ` The editor cannot be handed arguments for a game it plays, so this one was started here.${OWN_DEBUGGER}` : this.godotBridge.isConnected() && editorWouldPlay && needsItsOwnProcess(asked.value) ? ` The editor plays a game with its own environment, so a run given one was started here.${OWN_DEBUGGER}` : this.godotBridge.isConnected() && editorWouldPlay && silentOnlyHere ? " The editor cannot play a game silent, which takes an engine flag, so this one was " + `started here, silent as every run is unless asked otherwise, and silent: false plays it through the editor, with sound.${OWN_DEBUGGER}` : "";
|
|
49995
50071
|
const endedForThis = endedToStartThis(ended);
|
|
49996
50072
|
const stranded = wereHereFirst.filter((one) => one.pid !== ended?.pid && alive(one.pid));
|
|
49997
50073
|
const handed = this.dapHoldingBreakpointsOf(project.value.path).breakpointsHeld();
|
|
@@ -50004,7 +50080,7 @@ class GodotServer {
|
|
|
50004
50080
|
executable: exported?.ok === true ? exported.relativePath : undefined,
|
|
50005
50081
|
scene: exported !== null ? undefined : sceneArgument === null ? mainSceneOf(project.value.file) : `res://${sceneArgument}`,
|
|
50006
50082
|
arguments: cmdArgs,
|
|
50007
|
-
argsNote: engineFlagsNote(given
|
|
50083
|
+
argsNote: engineFlagsNote(given),
|
|
50008
50084
|
transcript: started.transcript,
|
|
50009
50085
|
breakpoints: started.keeper === undefined || handed.length === 0 ? undefined : this.breakpointsAsProjectSpells(project.value.path, handed),
|
|
50010
50086
|
profile: profileOnStart(profiled, "gdharness", runtimeAddonRegistered(project.value.path)),
|
|
@@ -50014,13 +50090,13 @@ class GodotServer {
|
|
|
50014
50090
|
endedPreviousRun: endedPreviousRun(ended),
|
|
50015
50091
|
previousRunLeft: previousRunLeft(ended),
|
|
50016
50092
|
...previousRunHow(ended),
|
|
50017
|
-
runtime: exported !== null ? undefined : await this.runtimeUp(project.value.path, alreadyPlaying, runtimeWaitMs, given.
|
|
50093
|
+
runtime: exported !== null ? undefined : await this.runtimeUp(project.value.path, alreadyPlaying, runtimeWaitMs, given.length > 0),
|
|
50018
50094
|
message: `Use editor_output for what it prints and editor_run stop to end it.${started.keeper !== undefined && handed.length > 0 ? " It stops at the lines under breakpoints, and debug_state stack reads where it is held." : ""}${spawnedInstead}${endedForThis}${alsoRunning}`
|
|
50019
50095
|
});
|
|
50020
50096
|
}
|
|
50021
50097
|
async runtimeUp(projectPath, before, budget, withArgs) {
|
|
50022
50098
|
const budgetMs = budget.ms;
|
|
50023
|
-
if (!existsSync27(
|
|
50099
|
+
if (!existsSync27(join35(projectPath, RUNTIME_AUTOLOAD.path))) {
|
|
50024
50100
|
return runtimeVerdict(null, {
|
|
50025
50101
|
addon: false,
|
|
50026
50102
|
budgetMs,
|
|
@@ -50997,7 +51073,7 @@ class GodotServer {
|
|
|
50997
51073
|
};
|
|
50998
51074
|
const going = await this.runStillGoing(run);
|
|
50999
51075
|
const projectPath = run.projectPath;
|
|
51000
|
-
const projectGone = projectPath !== null && !existsSync27(
|
|
51076
|
+
const projectGone = projectPath !== null && !existsSync27(join35(projectPath, "project.godot"));
|
|
51001
51077
|
if (projectGone && callersName !== undefined) {
|
|
51002
51078
|
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"]);
|
|
51003
51079
|
}
|
|
@@ -51222,10 +51298,10 @@ class GodotServer {
|
|
|
51222
51298
|
}
|
|
51223
51299
|
async handleEditorConsole(args) {
|
|
51224
51300
|
const status = this.godotBridge.getStatus();
|
|
51225
|
-
const greeted =
|
|
51301
|
+
const greeted = status.connected;
|
|
51226
51302
|
const launched = greeted ? null : this.launchedEditor?.pid ?? null;
|
|
51227
|
-
if (
|
|
51228
|
-
return this.createErrorResponse("
|
|
51303
|
+
if (launched === null && status.identifying === true) {
|
|
51304
|
+
return this.createErrorResponse("An editor has opened the bridge and has not yet said who it is, so whose console to read cannot be told yet.", ["Ask again in a moment; editor_status shows greeting until it has"]);
|
|
51229
51305
|
}
|
|
51230
51306
|
const projectPath = status.projectPath ?? this.launchedEditor?.projectPath ?? this.ownProject ?? undefined;
|
|
51231
51307
|
const elsewhere = this.anotherProjectNamed(args, projectPath);
|
|
@@ -51344,7 +51420,7 @@ class GodotServer {
|
|
|
51344
51420
|
notes.push(endedWithoutACode({
|
|
51345
51421
|
throughEditor: run.throughEditor,
|
|
51346
51422
|
announced: announced !== undefined || run.announcedPid !== undefined,
|
|
51347
|
-
couldAnnounce: run.projectPath !== null && existsSync27(
|
|
51423
|
+
couldAnnounce: run.projectPath !== null && existsSync27(join35(run.projectPath, RUNTIME_AUTOLOAD.path)),
|
|
51348
51424
|
endedHere: typeof run.endedHere === "string",
|
|
51349
51425
|
pickedUp: run.pickedUp === true
|
|
51350
51426
|
}));
|
|
@@ -52434,7 +52510,7 @@ class GodotServer {
|
|
|
52434
52510
|
const launched = this.launchedEditor;
|
|
52435
52511
|
if (launched !== null && await this.launchedEditorIsUp()) {
|
|
52436
52512
|
const began = Date.now();
|
|
52437
|
-
await this.waitForBridge(() =>
|
|
52513
|
+
await this.waitForBridge(() => this.godotBridge.isConnected() || !alive(launched.pid), began + editorRestartTimeoutMs());
|
|
52438
52514
|
if (this.godotBridge.isConnected()) {
|
|
52439
52515
|
return null;
|
|
52440
52516
|
}
|
|
@@ -52553,7 +52629,7 @@ class GodotServer {
|
|
|
52553
52629
|
async ownRunOnItsWay(projectPath, budgetMs) {
|
|
52554
52630
|
const run = await this.currentRun();
|
|
52555
52631
|
const project = projectPath ?? run?.projectPath ?? null;
|
|
52556
|
-
if (run === null || project === null || run.announcedPid !== undefined || run.projectPath !== null && !isSameDirectory(run.projectPath, project) || !existsSync27(
|
|
52632
|
+
if (run === null || project === null || run.announcedPid !== undefined || run.projectPath !== null && !isSameDirectory(run.projectPath, project) || !existsSync27(join35(project, RUNTIME_AUTOLOAD.path)) || !await this.runStillGoing(run)) {
|
|
52557
52633
|
return null;
|
|
52558
52634
|
}
|
|
52559
52635
|
const already = runtimesAnnounced().running;
|
|
@@ -52647,7 +52723,7 @@ class GodotServer {
|
|
|
52647
52723
|
return this.createErrorResponse(staleNote);
|
|
52648
52724
|
}
|
|
52649
52725
|
const screenshotDir = expectsScreenshot ? scratchDirectory("runtime-screenshot") : null;
|
|
52650
|
-
const screenshotPath = screenshotDir ?
|
|
52726
|
+
const screenshotPath = screenshotDir ? join35(screenshotDir, "capture.png") : null;
|
|
52651
52727
|
const refused = (message) => this.createErrorResponse(message);
|
|
52652
52728
|
const onGame = {
|
|
52653
52729
|
pid: choice.endpoint.pid,
|
|
@@ -52950,12 +53026,12 @@ init_bridge_announce();
|
|
|
52950
53026
|
init_errors();
|
|
52951
53027
|
init_godot_path();
|
|
52952
53028
|
import { existsSync as existsSync28 } from "node:fs";
|
|
52953
|
-
import { join as
|
|
53029
|
+
import { join as join36, resolve as resolve8 } from "node:path";
|
|
52954
53030
|
|
|
52955
53031
|
// src/harnesses.ts
|
|
52956
53032
|
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";
|
|
52957
53033
|
import { homedir as homedir2 } from "node:os";
|
|
52958
|
-
import { basename as
|
|
53034
|
+
import { basename as basename3, dirname as dirname4, join as join5, relative as relative2, resolve as resolve2 } from "node:path";
|
|
52959
53035
|
import process4 from "node:process";
|
|
52960
53036
|
|
|
52961
53037
|
// node_modules/yaml/dist/index.js
|
|
@@ -53129,7 +53205,7 @@ var HARNESSES = [
|
|
|
53129
53205
|
shape: "plain",
|
|
53130
53206
|
home: ".claude",
|
|
53131
53207
|
marker: ".claude",
|
|
53132
|
-
skills: { dir:
|
|
53208
|
+
skills: { dir: join5(".claude", "skills"), shared: false }
|
|
53133
53209
|
},
|
|
53134
53210
|
{
|
|
53135
53211
|
id: "copilot-cli",
|
|
@@ -53139,7 +53215,7 @@ var HARNESSES = [
|
|
|
53139
53215
|
container: "mcpServers",
|
|
53140
53216
|
shape: "plain",
|
|
53141
53217
|
home: ".copilot",
|
|
53142
|
-
skills: { dir:
|
|
53218
|
+
skills: { dir: join5(".github", "skills"), shared: true },
|
|
53143
53219
|
manual: "Trust the folder when Copilot CLI first asks, or it reads no config here."
|
|
53144
53220
|
},
|
|
53145
53221
|
{
|
|
@@ -53165,22 +53241,22 @@ var HARNESSES = [
|
|
|
53165
53241
|
id: "cursor",
|
|
53166
53242
|
name: "Cursor",
|
|
53167
53243
|
scope: "project",
|
|
53168
|
-
file:
|
|
53244
|
+
file: join5(".cursor", "mcp.json"),
|
|
53169
53245
|
container: "mcpServers",
|
|
53170
53246
|
shape: "plain",
|
|
53171
53247
|
home: ".cursor",
|
|
53172
|
-
skills: { dir:
|
|
53248
|
+
skills: { dir: join5(".cursor", "skills"), shared: true },
|
|
53173
53249
|
manual: "Cursor asks before each tool call by default, and the server can be toggled off."
|
|
53174
53250
|
},
|
|
53175
53251
|
{
|
|
53176
53252
|
id: "vscode",
|
|
53177
53253
|
name: "VS Code",
|
|
53178
53254
|
scope: "project",
|
|
53179
|
-
file:
|
|
53255
|
+
file: join5(".vscode", "mcp.json"),
|
|
53180
53256
|
container: "servers",
|
|
53181
53257
|
shape: "typed",
|
|
53182
53258
|
home: ".vscode",
|
|
53183
|
-
skills: { dir:
|
|
53259
|
+
skills: { dir: join5(".github", "skills"), shared: true },
|
|
53184
53260
|
manual: "VS Code asks you to trust a server the first time it starts one."
|
|
53185
53261
|
},
|
|
53186
53262
|
{
|
|
@@ -53190,45 +53266,45 @@ var HARNESSES = [
|
|
|
53190
53266
|
file: "opencode.json",
|
|
53191
53267
|
container: "mcp",
|
|
53192
53268
|
shape: "opencode",
|
|
53193
|
-
home:
|
|
53269
|
+
home: join5(".config", "opencode"),
|
|
53194
53270
|
marker: ".opencode",
|
|
53195
|
-
skills: { dir:
|
|
53271
|
+
skills: { dir: join5(".opencode", "skills"), shared: true }
|
|
53196
53272
|
},
|
|
53197
53273
|
{
|
|
53198
53274
|
id: "junie",
|
|
53199
53275
|
name: "Junie",
|
|
53200
53276
|
scope: "project",
|
|
53201
|
-
file:
|
|
53277
|
+
file: join5(".junie", "mcp", "mcp.json"),
|
|
53202
53278
|
container: "mcpServers",
|
|
53203
53279
|
shape: "plain",
|
|
53204
53280
|
home: ".junie",
|
|
53205
|
-
skills: { dir:
|
|
53281
|
+
skills: { dir: join5(".junie", "skills"), shared: true }
|
|
53206
53282
|
},
|
|
53207
53283
|
{
|
|
53208
53284
|
id: "kiro",
|
|
53209
53285
|
name: "Kiro",
|
|
53210
53286
|
scope: "project",
|
|
53211
|
-
file:
|
|
53287
|
+
file: join5(".kiro", "settings", "mcp.json"),
|
|
53212
53288
|
container: "mcpServers",
|
|
53213
53289
|
shape: "plain",
|
|
53214
53290
|
home: ".kiro",
|
|
53215
|
-
skills: { dir:
|
|
53291
|
+
skills: { dir: join5(".kiro", "skills"), shared: false }
|
|
53216
53292
|
},
|
|
53217
53293
|
{
|
|
53218
53294
|
id: "gemini",
|
|
53219
53295
|
name: "Gemini CLI",
|
|
53220
53296
|
scope: "project",
|
|
53221
|
-
file:
|
|
53297
|
+
file: join5(".gemini", "settings.json"),
|
|
53222
53298
|
container: "mcpServers",
|
|
53223
53299
|
shape: "plain",
|
|
53224
53300
|
home: ".gemini",
|
|
53225
|
-
skills: { dir:
|
|
53301
|
+
skills: { dir: join5(".gemini", "skills"), shared: true }
|
|
53226
53302
|
},
|
|
53227
53303
|
{
|
|
53228
53304
|
id: "roo",
|
|
53229
53305
|
name: "Roo Code",
|
|
53230
53306
|
scope: "project",
|
|
53231
|
-
file:
|
|
53307
|
+
file: join5(".roo", "mcp.json"),
|
|
53232
53308
|
container: "mcpServers",
|
|
53233
53309
|
shape: "plain"
|
|
53234
53310
|
},
|
|
@@ -53236,7 +53312,7 @@ var HARNESSES = [
|
|
|
53236
53312
|
id: "kilo",
|
|
53237
53313
|
name: "Kilo Code",
|
|
53238
53314
|
scope: "project",
|
|
53239
|
-
file:
|
|
53315
|
+
file: join5(".kilocode", "mcp.json"),
|
|
53240
53316
|
container: "mcpServers",
|
|
53241
53317
|
shape: "plain"
|
|
53242
53318
|
},
|
|
@@ -53244,16 +53320,16 @@ var HARNESSES = [
|
|
|
53244
53320
|
id: "amazon-q",
|
|
53245
53321
|
name: "Amazon Q Developer CLI",
|
|
53246
53322
|
scope: "project",
|
|
53247
|
-
file:
|
|
53323
|
+
file: join5(".amazonq", "mcp.json"),
|
|
53248
53324
|
container: "mcpServers",
|
|
53249
53325
|
shape: "plain",
|
|
53250
|
-
home:
|
|
53326
|
+
home: join5(".aws", "amazonq")
|
|
53251
53327
|
},
|
|
53252
53328
|
{
|
|
53253
53329
|
id: "zed",
|
|
53254
53330
|
name: "Zed",
|
|
53255
53331
|
scope: "project",
|
|
53256
|
-
file:
|
|
53332
|
+
file: join5(".zed", "settings.json"),
|
|
53257
53333
|
container: "context_servers",
|
|
53258
53334
|
shape: "plain"
|
|
53259
53335
|
},
|
|
@@ -53261,16 +53337,16 @@ var HARNESSES = [
|
|
|
53261
53337
|
id: "amp",
|
|
53262
53338
|
name: "Amp",
|
|
53263
53339
|
scope: "project",
|
|
53264
|
-
file:
|
|
53340
|
+
file: join5(".amp", "settings.json"),
|
|
53265
53341
|
container: "amp.mcpServers",
|
|
53266
53342
|
shape: "plain",
|
|
53267
|
-
home:
|
|
53343
|
+
home: join5(".config", "amp")
|
|
53268
53344
|
},
|
|
53269
53345
|
{
|
|
53270
53346
|
id: "warp",
|
|
53271
53347
|
name: "Warp",
|
|
53272
53348
|
scope: "project",
|
|
53273
|
-
file:
|
|
53349
|
+
file: join5(".warp", ".mcp.json"),
|
|
53274
53350
|
container: "mcpServers",
|
|
53275
53351
|
shape: "plain",
|
|
53276
53352
|
home: ".warp",
|
|
@@ -53280,7 +53356,7 @@ var HARNESSES = [
|
|
|
53280
53356
|
id: "trae",
|
|
53281
53357
|
name: "Trae",
|
|
53282
53358
|
scope: "project",
|
|
53283
|
-
file:
|
|
53359
|
+
file: join5(".trae", "mcp.json"),
|
|
53284
53360
|
container: "mcpServers",
|
|
53285
53361
|
shape: "plain",
|
|
53286
53362
|
home: ".trae"
|
|
@@ -53289,7 +53365,7 @@ var HARNESSES = [
|
|
|
53289
53365
|
id: "factory",
|
|
53290
53366
|
name: "Factory Droid",
|
|
53291
53367
|
scope: "project",
|
|
53292
|
-
file:
|
|
53368
|
+
file: join5(".factory", "mcp.json"),
|
|
53293
53369
|
container: "mcpServers",
|
|
53294
53370
|
shape: "typed",
|
|
53295
53371
|
home: ".factory"
|
|
@@ -53298,7 +53374,7 @@ var HARNESSES = [
|
|
|
53298
53374
|
id: "tabnine",
|
|
53299
53375
|
name: "Tabnine",
|
|
53300
53376
|
scope: "project",
|
|
53301
|
-
file:
|
|
53377
|
+
file: join5(".tabnine", "mcp_servers.json"),
|
|
53302
53378
|
container: "mcpServers",
|
|
53303
53379
|
shape: "plain",
|
|
53304
53380
|
home: ".tabnine"
|
|
@@ -53315,7 +53391,7 @@ var HARNESSES = [
|
|
|
53315
53391
|
id: "pi",
|
|
53316
53392
|
name: "pi",
|
|
53317
53393
|
scope: "project",
|
|
53318
|
-
file:
|
|
53394
|
+
file: join5(".pi", "mcp.json"),
|
|
53319
53395
|
container: "mcpServers",
|
|
53320
53396
|
shape: "plain",
|
|
53321
53397
|
home: ".pi"
|
|
@@ -53324,7 +53400,7 @@ var HARNESSES = [
|
|
|
53324
53400
|
id: "mistral-vibe",
|
|
53325
53401
|
name: "Mistral Vibe",
|
|
53326
53402
|
scope: "project",
|
|
53327
|
-
file:
|
|
53403
|
+
file: join5(".vibe", "config.toml"),
|
|
53328
53404
|
container: "mcp_servers",
|
|
53329
53405
|
shape: "plain",
|
|
53330
53406
|
toml: {
|
|
@@ -53380,7 +53456,7 @@ var HARNESSES = [
|
|
|
53380
53456
|
id: "codex",
|
|
53381
53457
|
name: "Codex CLI",
|
|
53382
53458
|
scope: "home",
|
|
53383
|
-
file:
|
|
53459
|
+
file: join5(".codex", "config.toml"),
|
|
53384
53460
|
container: "mcp_servers",
|
|
53385
53461
|
shape: "plain",
|
|
53386
53462
|
manual: "Codex reads its config at startup, so restart it.",
|
|
@@ -53399,20 +53475,20 @@ var HARNESSES = [
|
|
|
53399
53475
|
id: "cline",
|
|
53400
53476
|
name: "Cline",
|
|
53401
53477
|
scope: "home",
|
|
53402
|
-
file:
|
|
53478
|
+
file: join5(".cline", "data", "settings", "cline_mcp_settings.json"),
|
|
53403
53479
|
container: "mcpServers",
|
|
53404
53480
|
shape: "plain",
|
|
53405
53481
|
marker: ".cline",
|
|
53406
|
-
skills: { dir:
|
|
53482
|
+
skills: { dir: join5(".cline", "skills"), shared: false }
|
|
53407
53483
|
},
|
|
53408
53484
|
{
|
|
53409
53485
|
id: "goose",
|
|
53410
53486
|
name: "Goose",
|
|
53411
53487
|
scope: "home",
|
|
53412
53488
|
file: {
|
|
53413
|
-
win32: { appData:
|
|
53414
|
-
darwin:
|
|
53415
|
-
linux:
|
|
53489
|
+
win32: { appData: join5("Block", "goose", "config", "config.yaml") },
|
|
53490
|
+
darwin: join5(".config", "goose", "config.yaml"),
|
|
53491
|
+
linux: join5(".config", "goose", "config.yaml")
|
|
53416
53492
|
},
|
|
53417
53493
|
container: "extensions",
|
|
53418
53494
|
shape: "plain",
|
|
@@ -53433,19 +53509,19 @@ var HARNESSES = [
|
|
|
53433
53509
|
id: "windsurf",
|
|
53434
53510
|
name: "Windsurf",
|
|
53435
53511
|
scope: "home",
|
|
53436
|
-
file:
|
|
53512
|
+
file: join5(".codeium", "windsurf", "mcp_config.json"),
|
|
53437
53513
|
container: "mcpServers",
|
|
53438
53514
|
shape: "plain",
|
|
53439
|
-
skills: { dir:
|
|
53515
|
+
skills: { dir: join5(".windsurf", "skills"), shared: true }
|
|
53440
53516
|
},
|
|
53441
53517
|
{
|
|
53442
53518
|
id: "hermes",
|
|
53443
53519
|
name: "Hermes",
|
|
53444
53520
|
scope: "home",
|
|
53445
|
-
file:
|
|
53521
|
+
file: join5(".hermes", "config.yaml"),
|
|
53446
53522
|
container: "mcp_servers",
|
|
53447
53523
|
shape: "plain",
|
|
53448
|
-
skills: { dir:
|
|
53524
|
+
skills: { dir: join5(".hermes", "skills"), shared: true },
|
|
53449
53525
|
yaml: {
|
|
53450
53526
|
path: ["mcp_servers", SERVER_KEY],
|
|
53451
53527
|
entry: (launch) => ({
|
|
@@ -53460,7 +53536,7 @@ var HARNESSES = [
|
|
|
53460
53536
|
id: "openclaw",
|
|
53461
53537
|
name: "OpenClaw",
|
|
53462
53538
|
scope: "home",
|
|
53463
|
-
file:
|
|
53539
|
+
file: join5(".openclaw", "openclaw.json"),
|
|
53464
53540
|
container: "mcpServers",
|
|
53465
53541
|
shape: "plain"
|
|
53466
53542
|
},
|
|
@@ -53469,9 +53545,9 @@ var HARNESSES = [
|
|
|
53469
53545
|
name: "Claude Desktop",
|
|
53470
53546
|
scope: "home",
|
|
53471
53547
|
file: {
|
|
53472
|
-
win32: { appData:
|
|
53473
|
-
darwin:
|
|
53474
|
-
linux:
|
|
53548
|
+
win32: { appData: join5("Claude", "claude_desktop_config.json") },
|
|
53549
|
+
darwin: join5("Library", "Application Support", "Claude", "claude_desktop_config.json"),
|
|
53550
|
+
linux: join5(".config", "Claude", "claude_desktop_config.json")
|
|
53475
53551
|
},
|
|
53476
53552
|
container: "mcpServers",
|
|
53477
53553
|
shape: "plain"
|
|
@@ -53480,7 +53556,7 @@ var HARNESSES = [
|
|
|
53480
53556
|
id: "zeroclaw",
|
|
53481
53557
|
name: "ZeroClaw",
|
|
53482
53558
|
scope: "home",
|
|
53483
|
-
file:
|
|
53559
|
+
file: join5(".zeroclaw", "config.toml"),
|
|
53484
53560
|
container: "mcp.servers",
|
|
53485
53561
|
shape: "plain",
|
|
53486
53562
|
toml: {
|
|
@@ -53501,7 +53577,7 @@ var HARNESSES = [
|
|
|
53501
53577
|
id: "deepcode",
|
|
53502
53578
|
name: "Deep Code",
|
|
53503
53579
|
scope: "home",
|
|
53504
|
-
file:
|
|
53580
|
+
file: join5(".deepcode", "settings.json"),
|
|
53505
53581
|
container: "mcpServers",
|
|
53506
53582
|
shape: "plain"
|
|
53507
53583
|
},
|
|
@@ -53509,7 +53585,7 @@ var HARNESSES = [
|
|
|
53509
53585
|
id: "nanobot",
|
|
53510
53586
|
name: "nanobot",
|
|
53511
53587
|
scope: "home",
|
|
53512
|
-
file:
|
|
53588
|
+
file: join5(".nanobot", "config.json"),
|
|
53513
53589
|
container: "mcpServers",
|
|
53514
53590
|
shape: "plain",
|
|
53515
53591
|
snippet: (launch) => jsonSnippet(launch, "mcpServers", "plain")
|
|
@@ -53518,17 +53594,17 @@ var HARNESSES = [
|
|
|
53518
53594
|
id: "autohand",
|
|
53519
53595
|
name: "Autohand",
|
|
53520
53596
|
scope: "home",
|
|
53521
|
-
file:
|
|
53597
|
+
file: join5(".autohand", "config.json"),
|
|
53522
53598
|
container: "mcpServers",
|
|
53523
53599
|
shape: "plain",
|
|
53524
53600
|
snippet: (launch) => jsonSnippet(launch, "mcpServers", "plain")
|
|
53525
53601
|
}
|
|
53526
53602
|
];
|
|
53527
53603
|
function appDataRoot() {
|
|
53528
|
-
return process4.env["APPDATA"] ??
|
|
53604
|
+
return process4.env["APPDATA"] ?? join5(homedir2(), "AppData", "Roaming");
|
|
53529
53605
|
}
|
|
53530
53606
|
function homePath(path) {
|
|
53531
|
-
return typeof path === "string" ?
|
|
53607
|
+
return typeof path === "string" ? join5(homedir2(), path) : join5(appDataRoot(), path.appData);
|
|
53532
53608
|
}
|
|
53533
53609
|
function fileFor(harness, platform = process4.platform) {
|
|
53534
53610
|
if (typeof harness.file === "string") {
|
|
@@ -53539,7 +53615,7 @@ function fileFor(harness, platform = process4.platform) {
|
|
|
53539
53615
|
function configPath(harness, projectPath) {
|
|
53540
53616
|
const file = fileFor(harness);
|
|
53541
53617
|
if (harness.scope === "project") {
|
|
53542
|
-
return
|
|
53618
|
+
return join5(projectPath, file);
|
|
53543
53619
|
}
|
|
53544
53620
|
return homePath(file);
|
|
53545
53621
|
}
|
|
@@ -53598,11 +53674,11 @@ function present(harness, projectPath) {
|
|
|
53598
53674
|
return true;
|
|
53599
53675
|
}
|
|
53600
53676
|
const root = harness.scope === "project" ? projectPath : homedir2();
|
|
53601
|
-
const own =
|
|
53677
|
+
const own = dirname4(path);
|
|
53602
53678
|
if (own !== root && existsSync4(own)) {
|
|
53603
53679
|
return true;
|
|
53604
53680
|
}
|
|
53605
|
-
return harness.marker !== undefined && existsSync4(
|
|
53681
|
+
return harness.marker !== undefined && existsSync4(join5(root, harness.marker));
|
|
53606
53682
|
}
|
|
53607
53683
|
function candidates(projectPath) {
|
|
53608
53684
|
const found = [];
|
|
@@ -53615,7 +53691,7 @@ function candidates(projectPath) {
|
|
|
53615
53691
|
}
|
|
53616
53692
|
if (present(harness, projectPath)) {
|
|
53617
53693
|
found.push({ harness, reason: "configured" });
|
|
53618
|
-
} else if (harness.home !== undefined && existsSync4(
|
|
53694
|
+
} else if (harness.home !== undefined && existsSync4(join5(homedir2(), harness.home))) {
|
|
53619
53695
|
found.push({ harness, reason: "installed" });
|
|
53620
53696
|
}
|
|
53621
53697
|
}
|
|
@@ -53673,14 +53749,14 @@ function recordedEnginePath(projectPath) {
|
|
|
53673
53749
|
}
|
|
53674
53750
|
function mainCheckoutOf(projectPath) {
|
|
53675
53751
|
let top = resolve2(projectPath);
|
|
53676
|
-
while (!existsSync4(
|
|
53677
|
-
const up =
|
|
53752
|
+
while (!existsSync4(join5(top, ".git"))) {
|
|
53753
|
+
const up = dirname4(top);
|
|
53678
53754
|
if (up === top) {
|
|
53679
53755
|
return null;
|
|
53680
53756
|
}
|
|
53681
53757
|
top = up;
|
|
53682
53758
|
}
|
|
53683
|
-
const marker =
|
|
53759
|
+
const marker = join5(top, ".git");
|
|
53684
53760
|
if (!statSync3(marker).isFile()) {
|
|
53685
53761
|
return null;
|
|
53686
53762
|
}
|
|
@@ -53689,39 +53765,39 @@ function mainCheckoutOf(projectPath) {
|
|
|
53689
53765
|
return null;
|
|
53690
53766
|
}
|
|
53691
53767
|
const entry = resolve2(top, pointer);
|
|
53692
|
-
const commonFile =
|
|
53768
|
+
const commonFile = join5(entry, "commondir");
|
|
53693
53769
|
if (!existsSync4(commonFile)) {
|
|
53694
53770
|
return null;
|
|
53695
53771
|
}
|
|
53696
53772
|
const common = resolve2(entry, readFileSync2(commonFile, "utf8").trim());
|
|
53697
|
-
if (
|
|
53773
|
+
if (basename3(common) !== ".git") {
|
|
53698
53774
|
return null;
|
|
53699
53775
|
}
|
|
53700
|
-
const main =
|
|
53701
|
-
return isSameDirectory(main, projectPath) || !existsSync4(
|
|
53776
|
+
const main = join5(dirname4(common), relative2(top, resolve2(projectPath)));
|
|
53777
|
+
return isSameDirectory(main, projectPath) || !existsSync4(join5(main, "project.godot")) ? null : main;
|
|
53702
53778
|
}
|
|
53703
53779
|
function worktreeCheckoutsOf(projectPath) {
|
|
53704
53780
|
let top = resolve2(projectPath);
|
|
53705
|
-
while (!existsSync4(
|
|
53706
|
-
const up =
|
|
53781
|
+
while (!existsSync4(join5(top, ".git"))) {
|
|
53782
|
+
const up = dirname4(top);
|
|
53707
53783
|
if (up === top) {
|
|
53708
53784
|
return [];
|
|
53709
53785
|
}
|
|
53710
53786
|
top = up;
|
|
53711
53787
|
}
|
|
53712
|
-
const listed =
|
|
53713
|
-
if (!statSync3(
|
|
53788
|
+
const listed = join5(top, ".git", "worktrees");
|
|
53789
|
+
if (!statSync3(join5(top, ".git")).isDirectory() || !existsSync4(listed)) {
|
|
53714
53790
|
return [];
|
|
53715
53791
|
}
|
|
53716
53792
|
const below = relative2(top, resolve2(projectPath));
|
|
53717
53793
|
return readdirSync2(listed).flatMap((name) => {
|
|
53718
|
-
const pointer =
|
|
53794
|
+
const pointer = join5(listed, name, "gitdir");
|
|
53719
53795
|
if (!existsSync4(pointer)) {
|
|
53720
53796
|
return [];
|
|
53721
53797
|
}
|
|
53722
|
-
const checkout =
|
|
53723
|
-
const project =
|
|
53724
|
-
return existsSync4(
|
|
53798
|
+
const checkout = dirname4(resolve2(listed, name, readFileSync2(pointer, "utf8").trim()));
|
|
53799
|
+
const project = join5(checkout, below);
|
|
53800
|
+
return existsSync4(join5(project, "project.godot")) ? [project] : [];
|
|
53725
53801
|
});
|
|
53726
53802
|
}
|
|
53727
53803
|
function entryEnvironment(harness, projectPath) {
|
|
@@ -53863,7 +53939,7 @@ function connect(harness, projectPath, launch) {
|
|
|
53863
53939
|
return was === undefined || was === now ? written : { ...written, wasLaunchedBy: was };
|
|
53864
53940
|
}
|
|
53865
53941
|
function put(harness, path, contents, already) {
|
|
53866
|
-
mkdirSync2(
|
|
53942
|
+
mkdirSync2(dirname4(path), { recursive: true });
|
|
53867
53943
|
writeFileSync2(path, contents, "utf8");
|
|
53868
53944
|
return { harness, path, action: already ? "replaced" : "written" };
|
|
53869
53945
|
}
|
|
@@ -53916,9 +53992,9 @@ init_setup();
|
|
|
53916
53992
|
init_tool_definitions();
|
|
53917
53993
|
init_tool_reference();
|
|
53918
53994
|
import { existsSync as existsSync14, mkdirSync as mkdirSync5, readdirSync as readdirSync9, rmSync as rmSync6, writeFileSync as writeFileSync7 } from "node:fs";
|
|
53919
|
-
import { basename as
|
|
53995
|
+
import { basename as basename7, dirname as dirname9, join as join16 } from "node:path";
|
|
53920
53996
|
var SKILL_NAME = "gdharness";
|
|
53921
|
-
var SHARED_SKILLS =
|
|
53997
|
+
var SHARED_SKILLS = join16(".agents", "skills");
|
|
53922
53998
|
function skillMarkdown(version) {
|
|
53923
53999
|
return `---
|
|
53924
54000
|
name: ${SKILL_NAME}
|
|
@@ -54150,27 +54226,27 @@ what they report while starting comes back under \`engine_messages_before_operat
|
|
|
54150
54226
|
function skillFiles(version) {
|
|
54151
54227
|
return new Map([
|
|
54152
54228
|
["SKILL.md", skillMarkdown(version)],
|
|
54153
|
-
[
|
|
54229
|
+
[join16("references", "tools.md"), renderToolsMarkdown()]
|
|
54154
54230
|
]);
|
|
54155
54231
|
}
|
|
54156
54232
|
function skillDirectories(harnesses, projectPath, exists) {
|
|
54157
|
-
const directories = [
|
|
54233
|
+
const directories = [join16(projectPath, SHARED_SKILLS, SKILL_NAME)];
|
|
54158
54234
|
for (const harness of harnesses) {
|
|
54159
54235
|
if (harness.skills === undefined) {
|
|
54160
54236
|
continue;
|
|
54161
54237
|
}
|
|
54162
|
-
const own =
|
|
54163
|
-
if ((!harness.skills.shared || exists(own)) && !directories.includes(
|
|
54164
|
-
directories.push(
|
|
54238
|
+
const own = join16(projectPath, harness.skills.dir);
|
|
54239
|
+
if ((!harness.skills.shared || exists(own)) && !directories.includes(join16(own, SKILL_NAME))) {
|
|
54240
|
+
directories.push(join16(own, SKILL_NAME));
|
|
54165
54241
|
}
|
|
54166
54242
|
}
|
|
54167
54243
|
return directories;
|
|
54168
54244
|
}
|
|
54169
54245
|
function everySkillDirectory(projectPath, harnesses) {
|
|
54170
|
-
const directories = new Set([
|
|
54246
|
+
const directories = new Set([join16(projectPath, SHARED_SKILLS, SKILL_NAME)]);
|
|
54171
54247
|
for (const harness of harnesses) {
|
|
54172
54248
|
if (harness.skills !== undefined) {
|
|
54173
|
-
directories.add(
|
|
54249
|
+
directories.add(join16(projectPath, harness.skills.dir, SKILL_NAME));
|
|
54174
54250
|
}
|
|
54175
54251
|
}
|
|
54176
54252
|
return [...directories];
|
|
@@ -54180,21 +54256,21 @@ function removeSkill(directories, projectPath) {
|
|
|
54180
54256
|
for (const directory of directories) {
|
|
54181
54257
|
if (existsSync14(directory)) {
|
|
54182
54258
|
rmSync6(directory, { recursive: true, force: true });
|
|
54183
|
-
pruneEmpty(
|
|
54259
|
+
pruneEmpty(dirname9(directory), projectPath);
|
|
54184
54260
|
removed.push(directory);
|
|
54185
54261
|
}
|
|
54186
54262
|
}
|
|
54187
54263
|
return removed;
|
|
54188
54264
|
}
|
|
54189
54265
|
function pruneEmpty(directory, projectPath) {
|
|
54190
|
-
const shared =
|
|
54266
|
+
const shared = join16(projectPath, ".agents");
|
|
54191
54267
|
let path = directory;
|
|
54192
|
-
while (path !== projectPath && (
|
|
54268
|
+
while (path !== projectPath && (basename7(path) === "skills" || path === shared)) {
|
|
54193
54269
|
if (readdirSync9(path).length > 0) {
|
|
54194
54270
|
return;
|
|
54195
54271
|
}
|
|
54196
54272
|
rmSync6(path, { recursive: true, force: true });
|
|
54197
|
-
path =
|
|
54273
|
+
path = dirname9(path);
|
|
54198
54274
|
}
|
|
54199
54275
|
}
|
|
54200
54276
|
function writeSkill(directories, version) {
|
|
@@ -54203,8 +54279,8 @@ function writeSkill(directories, version) {
|
|
|
54203
54279
|
const replaced = existsSync14(directory);
|
|
54204
54280
|
rmSync6(directory, { recursive: true, force: true });
|
|
54205
54281
|
for (const [name, contents] of files) {
|
|
54206
|
-
const path =
|
|
54207
|
-
mkdirSync5(
|
|
54282
|
+
const path = join16(directory, name);
|
|
54283
|
+
mkdirSync5(dirname9(path), { recursive: true });
|
|
54208
54284
|
writeFileSync7(path, contents, "utf8");
|
|
54209
54285
|
}
|
|
54210
54286
|
return { path: directory, replaced };
|
|
@@ -54213,13 +54289,13 @@ function writeSkill(directories, version) {
|
|
|
54213
54289
|
|
|
54214
54290
|
// src/version.ts
|
|
54215
54291
|
import { readFileSync as readFileSync12 } from "node:fs";
|
|
54216
|
-
import { dirname as
|
|
54292
|
+
import { dirname as dirname10, join as join17 } from "node:path";
|
|
54217
54293
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
54218
54294
|
function getLocalVersion() {
|
|
54219
|
-
const currentDirectory =
|
|
54295
|
+
const currentDirectory = dirname10(fileURLToPath2(import.meta.url));
|
|
54220
54296
|
const packageCandidates = [
|
|
54221
|
-
|
|
54222
|
-
|
|
54297
|
+
join17(currentDirectory, "..", "package.json"),
|
|
54298
|
+
join17(currentDirectory, "..", "..", "package.json")
|
|
54223
54299
|
];
|
|
54224
54300
|
for (const packagePath of packageCandidates) {
|
|
54225
54301
|
try {
|
|
@@ -54241,7 +54317,7 @@ class UsageError extends Refusal {
|
|
|
54241
54317
|
function projectArgument(at) {
|
|
54242
54318
|
const named = args.slice(at).find((value) => !value.startsWith("--"));
|
|
54243
54319
|
const projectPath = resolve8(named ?? ".");
|
|
54244
|
-
if (!existsSync28(
|
|
54320
|
+
if (!existsSync28(join36(projectPath, "project.godot"))) {
|
|
54245
54321
|
throw new UsageError(`Not a Godot project: ${projectPath} holds no project.godot.`);
|
|
54246
54322
|
}
|
|
54247
54323
|
return projectPath;
|
|
@@ -54286,7 +54362,7 @@ async function registerRuntime(godot, projectPath) {
|
|
|
54286
54362
|
return;
|
|
54287
54363
|
}
|
|
54288
54364
|
console.log(`${RUNTIME_AUTOLOAD.name} autoload left at ${named}, which this project registers itself`);
|
|
54289
|
-
if (!existsSync28(
|
|
54365
|
+
if (!existsSync28(join36(projectPath, (named ?? "").replace(/^res:\/\//, "")))) {
|
|
54290
54366
|
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}`);
|
|
54291
54367
|
}
|
|
54292
54368
|
}
|