gdharness 1.3.4 → 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 +428 -383
- package/build/godot/addons/gdharness_editor/declared_values.gd +24 -0
- package/build/godot/addons/gdharness_editor/property_values.gd +5 -18
- package/build/godot/addons/gdharness_editor/tools/class_tools.gd +20 -12
- 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/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 +16 -13
- package/build/godot/addons/gdharness_runtime/runtime_mouse.gd +18 -0
- package/build/godot/addons/gdharness_runtime/runtime_values.gd +32 -5
- package/build/godot/addons/gdharness_runtime/runtime_waits.gd +48 -15
- package/build/godot/operations/classdb_queries.gd +112 -46
- package/build/godot/operations/declared_values.gd +24 -0
- package/build/godot/operations/gdscript_authoring.gd +15 -2
- package/build/godot/operations/property_values.gd +5 -18
- 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)) {
|
|
@@ -9271,19 +9282,12 @@ function refusalsIn(log) {
|
|
|
9271
9282
|
}
|
|
9272
9283
|
function scriptFault(problems, script) {
|
|
9273
9284
|
const spelled = (path) => `(${path.replaceAll("\\", "/").toLowerCase()}/`;
|
|
9274
|
-
const directories = [...new Set([spelled(
|
|
9285
|
+
const directories = [...new Set([spelled(dirname7(script)), spelled(realAsFarAsItExists(dirname7(script)))])];
|
|
9275
9286
|
return problems.find((entry) => {
|
|
9276
9287
|
const at = entry.detail.find((line) => line.startsWith("at: "))?.replaceAll("\\", "/").toLowerCase();
|
|
9277
9288
|
return entry.severity === "error" && at !== undefined && directories.some((directory) => at.includes(directory));
|
|
9278
9289
|
});
|
|
9279
9290
|
}
|
|
9280
|
-
function resolvedOr(path) {
|
|
9281
|
-
try {
|
|
9282
|
-
return realpathSync2.native(path);
|
|
9283
|
-
} catch {
|
|
9284
|
-
return path;
|
|
9285
|
-
}
|
|
9286
|
-
}
|
|
9287
9291
|
function librariesNotCopied(messages) {
|
|
9288
9292
|
const found = new Set;
|
|
9289
9293
|
for (const entry of messages) {
|
|
@@ -9335,7 +9339,7 @@ async function importOnce(godotPath, projectPath) {
|
|
|
9335
9339
|
ran = await runEngine(godotPath, [
|
|
9336
9340
|
"--headless",
|
|
9337
9341
|
"--log-file",
|
|
9338
|
-
|
|
9342
|
+
join11(logDir, "engine.log"),
|
|
9339
9343
|
"--path",
|
|
9340
9344
|
projectPath,
|
|
9341
9345
|
"--import"
|
|
@@ -9357,13 +9361,13 @@ async function importOnce(godotPath, projectPath) {
|
|
|
9357
9361
|
}
|
|
9358
9362
|
async function runOperation(engine, operation, params, projectPath) {
|
|
9359
9363
|
const paramsDir = scratchDirectory("params");
|
|
9360
|
-
const paramsFile =
|
|
9361
|
-
const answerFile =
|
|
9364
|
+
const paramsFile = join11(paramsDir, `${operation}.json`);
|
|
9365
|
+
const answerFile = join11(paramsDir, "answer.json");
|
|
9362
9366
|
writeFileSync4(paramsFile, JSON.stringify(snakeCased(params)), "utf8");
|
|
9363
9367
|
const args = [
|
|
9364
9368
|
"--headless",
|
|
9365
9369
|
"--log-file",
|
|
9366
|
-
|
|
9370
|
+
join11(paramsDir, "engine.log"),
|
|
9367
9371
|
"--path",
|
|
9368
9372
|
projectPath,
|
|
9369
9373
|
"--script",
|
|
@@ -9470,6 +9474,7 @@ var init_headless = __esm(() => {
|
|
|
9470
9474
|
init_doc_cache();
|
|
9471
9475
|
init_engine_run();
|
|
9472
9476
|
init_library_copies();
|
|
9477
|
+
init_paths();
|
|
9473
9478
|
init_reimport();
|
|
9474
9479
|
init_scratch();
|
|
9475
9480
|
init_script_origins();
|
|
@@ -9478,7 +9483,7 @@ var init_headless = __esm(() => {
|
|
|
9478
9483
|
// src/update-check.ts
|
|
9479
9484
|
import { existsSync as existsSync10, mkdirSync as mkdirSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "node:fs";
|
|
9480
9485
|
import { homedir as homedir4, tmpdir as tmpdir3 } from "node:os";
|
|
9481
|
-
import { join as
|
|
9486
|
+
import { join as join12 } from "node:path";
|
|
9482
9487
|
function packageUrl(root) {
|
|
9483
9488
|
return `${root.replace(/\/+$/, "")}/gdharness/latest`;
|
|
9484
9489
|
}
|
|
@@ -9500,20 +9505,20 @@ function cacheDirectory(environment) {
|
|
|
9500
9505
|
};
|
|
9501
9506
|
const home = set("HOME") ?? homedir4();
|
|
9502
9507
|
if (process.platform === "win32") {
|
|
9503
|
-
return
|
|
9508
|
+
return join12(set("LOCALAPPDATA") ?? home, "gdharness");
|
|
9504
9509
|
}
|
|
9505
9510
|
if (process.platform === "darwin") {
|
|
9506
|
-
return
|
|
9511
|
+
return join12(home, "Library", "Caches", "gdharness");
|
|
9507
9512
|
}
|
|
9508
|
-
return
|
|
9513
|
+
return join12(set("XDG_CACHE_HOME") ?? join12(home, ".cache"), "gdharness");
|
|
9509
9514
|
}
|
|
9510
9515
|
function cacheFile(environment = process.env) {
|
|
9511
9516
|
try {
|
|
9512
9517
|
const directory = cacheDirectory(environment);
|
|
9513
9518
|
mkdirSync3(directory, { recursive: true, mode: 448 });
|
|
9514
|
-
return
|
|
9519
|
+
return join12(directory, "update-check.json");
|
|
9515
9520
|
} catch {
|
|
9516
|
-
return
|
|
9521
|
+
return join12(tmpdir3(), "gdharness-update-check.json");
|
|
9517
9522
|
}
|
|
9518
9523
|
}
|
|
9519
9524
|
function readCache(path) {
|
|
@@ -10514,7 +10519,7 @@ var init_gdscript_source = __esm(() => {
|
|
|
10514
10519
|
|
|
10515
10520
|
// src/class-cache.ts
|
|
10516
10521
|
import { existsSync as existsSync11, readdirSync as readdirSync6, readFileSync as readFileSync8, statSync as statSync6 } from "node:fs";
|
|
10517
|
-
import { join as
|
|
10522
|
+
import { join as join13, posix } from "node:path";
|
|
10518
10523
|
function isToolScript(source) {
|
|
10519
10524
|
return TOOL.test(codeOf(source));
|
|
10520
10525
|
}
|
|
@@ -10530,14 +10535,14 @@ function declaredClasses(projectPath) {
|
|
|
10530
10535
|
}
|
|
10531
10536
|
function eachScript(projectPath, each) {
|
|
10532
10537
|
const visit = (directory, prefix) => {
|
|
10533
|
-
if (existsSync11(
|
|
10538
|
+
if (existsSync11(join13(directory, ".gdignore"))) {
|
|
10534
10539
|
return;
|
|
10535
10540
|
}
|
|
10536
10541
|
for (const entry of readdirSync6(directory, { withFileTypes: true })) {
|
|
10537
10542
|
if (entry.name.startsWith(".")) {
|
|
10538
10543
|
continue;
|
|
10539
10544
|
}
|
|
10540
|
-
const path =
|
|
10545
|
+
const path = join13(directory, entry.name);
|
|
10541
10546
|
if (entry.isDirectory()) {
|
|
10542
10547
|
visit(path, `${prefix}${entry.name}/`);
|
|
10543
10548
|
} else if (entry.isFile() && entry.name.endsWith(".gd")) {
|
|
@@ -10563,7 +10568,7 @@ function scriptsNaming(projectPath, classes) {
|
|
|
10563
10568
|
return naming.sort((a, b) => a.script.localeCompare(b.script));
|
|
10564
10569
|
}
|
|
10565
10570
|
function cachedClasses(projectPath) {
|
|
10566
|
-
const cache =
|
|
10571
|
+
const cache = join13(projectPath, ".godot", "global_script_class_cache.cfg");
|
|
10567
10572
|
if (!existsSync11(cache)) {
|
|
10568
10573
|
return null;
|
|
10569
10574
|
}
|
|
@@ -10574,6 +10579,22 @@ function cachedClasses(projectPath) {
|
|
|
10574
10579
|
}
|
|
10575
10580
|
return listed;
|
|
10576
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
|
+
}
|
|
10577
10598
|
function withAddonClassesCounted(rebuilt, listed) {
|
|
10578
10599
|
const counted = { ...rebuilt };
|
|
10579
10600
|
for (const key of ["added", "changed"]) {
|
|
@@ -10584,7 +10605,7 @@ function withAddonClassesCounted(rebuilt, listed) {
|
|
|
10584
10605
|
const own = [];
|
|
10585
10606
|
const perAddon = {};
|
|
10586
10607
|
for (const name of names) {
|
|
10587
|
-
const addon =
|
|
10608
|
+
const addon = addonOf(listed.get(String(name)) ?? "");
|
|
10588
10609
|
if (addon === undefined) {
|
|
10589
10610
|
own.push(name);
|
|
10590
10611
|
} else {
|
|
@@ -10845,12 +10866,12 @@ function uncachedForTheEngineNote(projectPath, types) {
|
|
|
10845
10866
|
}
|
|
10846
10867
|
const one = types.length === 1;
|
|
10847
10868
|
const named = types.map((type) => `${type.type} (${type.declaredIn})`).join(", ");
|
|
10848
|
-
const cacheExists = existsSync11(
|
|
10869
|
+
const cacheExists = existsSync11(join13(projectPath, ".godot", "global_script_class_cache.cfg"));
|
|
10849
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.`;
|
|
10850
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.";
|
|
10851
10872
|
}
|
|
10852
10873
|
function cacheWrittenAt(projectPath) {
|
|
10853
|
-
const cache =
|
|
10874
|
+
const cache = join13(projectPath, ".godot", "global_script_class_cache.cfg");
|
|
10854
10875
|
try {
|
|
10855
10876
|
return statSync6(cache).mtimeMs;
|
|
10856
10877
|
} catch {
|
|
@@ -10864,14 +10885,14 @@ function declaredSince(projectPath, paths, names, since) {
|
|
|
10864
10885
|
return false;
|
|
10865
10886
|
}
|
|
10866
10887
|
try {
|
|
10867
|
-
return statSync6(
|
|
10888
|
+
return statSync6(join13(projectPath, path.slice("res://".length))).mtimeMs > since;
|
|
10868
10889
|
} catch {
|
|
10869
10890
|
return false;
|
|
10870
10891
|
}
|
|
10871
10892
|
});
|
|
10872
10893
|
}
|
|
10873
10894
|
function cachedAtMissingPaths(cached, projectPath) {
|
|
10874
|
-
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();
|
|
10875
10896
|
}
|
|
10876
10897
|
function staleAgainst(cached, projectPath) {
|
|
10877
10898
|
return [...declaredClasses(projectPath)].filter(([name, path]) => cached.get(name) !== path).map(([name]) => name);
|
|
@@ -10897,7 +10918,7 @@ var init_class_cache = __esm(() => {
|
|
|
10897
10918
|
|
|
10898
10919
|
// src/project-scan.ts
|
|
10899
10920
|
import { existsSync as existsSync12, readdirSync as readdirSync7, readFileSync as readFileSync9 } from "node:fs";
|
|
10900
|
-
import { extname, join as
|
|
10921
|
+
import { extname, join as join14 } from "node:path";
|
|
10901
10922
|
function skipped(name) {
|
|
10902
10923
|
return name.startsWith(".") || SKIPPED.has(name);
|
|
10903
10924
|
}
|
|
@@ -10905,10 +10926,10 @@ function steppedOver(directory) {
|
|
|
10905
10926
|
return gdignored(directory) || installedByUs(directory);
|
|
10906
10927
|
}
|
|
10907
10928
|
function gdignored(directory) {
|
|
10908
|
-
return existsSync12(
|
|
10929
|
+
return existsSync12(join14(directory, ".gdignore"));
|
|
10909
10930
|
}
|
|
10910
10931
|
function installedByUs(directory) {
|
|
10911
|
-
return existsSync12(
|
|
10932
|
+
return existsSync12(join14(directory, VERSION_MARKER));
|
|
10912
10933
|
}
|
|
10913
10934
|
function projectStructure(projectPath) {
|
|
10914
10935
|
const structure = { scenes: 0, scripts: 0, assets: 0, other: 0 };
|
|
@@ -10921,7 +10942,7 @@ function projectStructure(projectPath) {
|
|
|
10921
10942
|
continue;
|
|
10922
10943
|
}
|
|
10923
10944
|
if (entry.isDirectory()) {
|
|
10924
|
-
visit(
|
|
10945
|
+
visit(join14(directory, entry.name));
|
|
10925
10946
|
} else if (entry.isFile()) {
|
|
10926
10947
|
const extension = entry.name.split(".").pop()?.toLowerCase() ?? "";
|
|
10927
10948
|
if (SCENE_EXTENSIONS.has(extension)) {
|
|
@@ -10951,7 +10972,7 @@ function projectFiles(projectPath, extensions) {
|
|
|
10951
10972
|
}
|
|
10952
10973
|
const spelled = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
|
|
10953
10974
|
if (entry.isDirectory()) {
|
|
10954
|
-
visit(
|
|
10975
|
+
visit(join14(directory, entry.name), spelled);
|
|
10955
10976
|
} else if (entry.isFile() && extensions.includes(extname(entry.name).toLowerCase())) {
|
|
10956
10977
|
found.push(spelled);
|
|
10957
10978
|
}
|
|
@@ -10972,14 +10993,14 @@ function scriptsWithoutUid(projectPath) {
|
|
|
10972
10993
|
}
|
|
10973
10994
|
const spelled = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
|
|
10974
10995
|
if (entry.isDirectory()) {
|
|
10975
|
-
visit(
|
|
10996
|
+
visit(join14(directory, entry.name), spelled);
|
|
10976
10997
|
continue;
|
|
10977
10998
|
}
|
|
10978
10999
|
if (!entry.isFile()) {
|
|
10979
11000
|
continue;
|
|
10980
11001
|
}
|
|
10981
11002
|
const extension = entry.name.split(".").pop()?.toLowerCase() ?? "";
|
|
10982
|
-
if (CARRIES_A_UID.has(extension) && !existsSync12(
|
|
11003
|
+
if (CARRIES_A_UID.has(extension) && !existsSync12(join14(directory, `${entry.name}.uid`))) {
|
|
10983
11004
|
found.push(spelled);
|
|
10984
11005
|
}
|
|
10985
11006
|
}
|
|
@@ -10990,7 +11011,7 @@ function scriptsWithoutUid(projectPath) {
|
|
|
10990
11011
|
function pathsInUidCache(projectPath) {
|
|
10991
11012
|
let data;
|
|
10992
11013
|
try {
|
|
10993
|
-
data = readFileSync9(
|
|
11014
|
+
data = readFileSync9(join14(projectPath, ".godot", "uid_cache.bin"));
|
|
10994
11015
|
} catch {
|
|
10995
11016
|
return null;
|
|
10996
11017
|
}
|
|
@@ -11028,7 +11049,7 @@ function projectTexts(projectPath) {
|
|
|
11028
11049
|
if (skipped(entry.name)) {
|
|
11029
11050
|
continue;
|
|
11030
11051
|
}
|
|
11031
|
-
const absolute =
|
|
11052
|
+
const absolute = join14(directory, entry.name);
|
|
11032
11053
|
const spelled = `${prefix}${entry.name}`;
|
|
11033
11054
|
if (entry.isDirectory()) {
|
|
11034
11055
|
visit(absolute, `${spelled}/`);
|
|
@@ -11058,7 +11079,7 @@ function ignoredTexts(projectPath) {
|
|
|
11058
11079
|
if (skipped(entry.name)) {
|
|
11059
11080
|
continue;
|
|
11060
11081
|
}
|
|
11061
|
-
const absolute =
|
|
11082
|
+
const absolute = join14(directory, entry.name);
|
|
11062
11083
|
const spelled = `${prefix}${entry.name}`;
|
|
11063
11084
|
if (entry.isDirectory()) {
|
|
11064
11085
|
visit(absolute, `${spelled}/`, under);
|
|
@@ -11088,6 +11109,14 @@ function plainMatch(line, needle, caseSensitive) {
|
|
|
11088
11109
|
}
|
|
11089
11110
|
return [lowered.length === line.length ? line.slice(at, at + needle.length) : needle];
|
|
11090
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
|
+
}
|
|
11091
11120
|
function searchProject(projectPath, options) {
|
|
11092
11121
|
const extensions = options.fileTypes === undefined ? null : new Set(options.fileTypes.map((ext) => ext.replace(/^\./, "").toLowerCase()).filter(Boolean));
|
|
11093
11122
|
const result = {
|
|
@@ -11095,7 +11124,7 @@ function searchProject(projectPath, options) {
|
|
|
11095
11124
|
results: [],
|
|
11096
11125
|
summary: { files_searched: 0, files_with_matches: 0, total_matches: 0, truncated: false }
|
|
11097
11126
|
};
|
|
11098
|
-
const regex = options.regex ?
|
|
11127
|
+
const regex = options.regex ? compiledQuery(options.query, options.caseSensitive) : null;
|
|
11099
11128
|
const needle = options.caseSensitive ? options.query : options.query.toLowerCase();
|
|
11100
11129
|
const full = () => result.summary.truncated;
|
|
11101
11130
|
const visit = (directory) => {
|
|
@@ -11109,7 +11138,7 @@ function searchProject(projectPath, options) {
|
|
|
11109
11138
|
if (skipped(entry.name)) {
|
|
11110
11139
|
continue;
|
|
11111
11140
|
}
|
|
11112
|
-
const entryPath =
|
|
11141
|
+
const entryPath = join14(directory, entry.name);
|
|
11113
11142
|
if (entry.isDirectory()) {
|
|
11114
11143
|
visit(entryPath);
|
|
11115
11144
|
continue;
|
|
@@ -11148,6 +11177,7 @@ function searchProject(projectPath, options) {
|
|
|
11148
11177
|
}
|
|
11149
11178
|
var SKIPPED, VERSION_MARKER = ".gdharness-version", SCENE_EXTENSIONS, ASSET_EXTENSIONS, CARRIES_A_UID, BINARY_EXTENSIONS;
|
|
11150
11179
|
var init_project_scan = __esm(() => {
|
|
11180
|
+
init_errors();
|
|
11151
11181
|
SKIPPED = new Set(["node_modules"]);
|
|
11152
11182
|
SCENE_EXTENSIONS = new Set(["tscn", "scn"]);
|
|
11153
11183
|
ASSET_EXTENSIONS = new Set([
|
|
@@ -11205,7 +11235,7 @@ var init_answer_json = __esm(() => {
|
|
|
11205
11235
|
});
|
|
11206
11236
|
|
|
11207
11237
|
// src/resources.ts
|
|
11208
|
-
import { readFileSync as readFileSync10, realpathSync as
|
|
11238
|
+
import { readFileSync as readFileSync10, realpathSync as realpathSync2 } from "node:fs";
|
|
11209
11239
|
import { extname as extname2, resolve as resolve3 } from "node:path";
|
|
11210
11240
|
function ensureProjectPath(getProjectPath) {
|
|
11211
11241
|
const projectPath = getProjectPath();
|
|
@@ -11228,13 +11258,13 @@ function resolveProjectFile(projectPath, resourcePath) {
|
|
|
11228
11258
|
}
|
|
11229
11259
|
let root;
|
|
11230
11260
|
try {
|
|
11231
|
-
root =
|
|
11261
|
+
root = realpathSync2.native(projectPath);
|
|
11232
11262
|
} catch {
|
|
11233
11263
|
throw new Refusal(`The project directory ${projectPath} is not there.`);
|
|
11234
11264
|
}
|
|
11235
11265
|
let real;
|
|
11236
11266
|
try {
|
|
11237
|
-
real =
|
|
11267
|
+
real = realpathSync2.native(contained.absolutePath);
|
|
11238
11268
|
} catch {
|
|
11239
11269
|
throw new Refusal(`No file at ${resourcePath} in the project.`);
|
|
11240
11270
|
}
|
|
@@ -11563,7 +11593,7 @@ import {
|
|
|
11563
11593
|
statSync as statSync7,
|
|
11564
11594
|
writeFileSync as writeFileSync6
|
|
11565
11595
|
} from "node:fs";
|
|
11566
|
-
import { dirname as
|
|
11596
|
+
import { dirname as dirname8, join as join15 } from "node:path";
|
|
11567
11597
|
import { fileURLToPath } from "node:url";
|
|
11568
11598
|
function editorAddonDigest(from = shippedAddonsDirectory()) {
|
|
11569
11599
|
return addonsDigest(EDITOR_PLUGINS, from);
|
|
@@ -11578,7 +11608,7 @@ function addonsDigest(names, from) {
|
|
|
11578
11608
|
if (name === VERSION_MARKER || name === DIGEST_MARKER) {
|
|
11579
11609
|
continue;
|
|
11580
11610
|
}
|
|
11581
|
-
const path =
|
|
11611
|
+
const path = join15(directory, name);
|
|
11582
11612
|
const inside = `${relative}/${name}`;
|
|
11583
11613
|
if (statSync7(path).isDirectory()) {
|
|
11584
11614
|
walk(path, inside);
|
|
@@ -11592,7 +11622,7 @@ function addonsDigest(names, from) {
|
|
|
11592
11622
|
}
|
|
11593
11623
|
};
|
|
11594
11624
|
for (const name of names) {
|
|
11595
|
-
walk(
|
|
11625
|
+
walk(join15(from, name), name);
|
|
11596
11626
|
}
|
|
11597
11627
|
return hash.digest("hex");
|
|
11598
11628
|
}
|
|
@@ -11611,7 +11641,7 @@ function installedRuntimeDigest(projectPath) {
|
|
|
11611
11641
|
return digestMarker(projectPath, RUNTIME_ADDON);
|
|
11612
11642
|
}
|
|
11613
11643
|
function digestMarker(projectPath, addon) {
|
|
11614
|
-
const marker =
|
|
11644
|
+
const marker = join15(projectPath, "addons", addon, DIGEST_MARKER);
|
|
11615
11645
|
if (!existsSync13(marker)) {
|
|
11616
11646
|
return null;
|
|
11617
11647
|
}
|
|
@@ -11619,35 +11649,35 @@ function digestMarker(projectPath, addon) {
|
|
|
11619
11649
|
return said === "" ? null : said;
|
|
11620
11650
|
}
|
|
11621
11651
|
function shippedAddonsDirectory() {
|
|
11622
|
-
return
|
|
11652
|
+
return join15(dirname8(fileURLToPath(import.meta.url)), "godot", "addons");
|
|
11623
11653
|
}
|
|
11624
11654
|
function shippedOperationsScript() {
|
|
11625
|
-
return
|
|
11655
|
+
return join15(dirname8(fileURLToPath(import.meta.url)), "godot", "operations", "godot_operations.gd");
|
|
11626
11656
|
}
|
|
11627
11657
|
function installAddons(projectPath, from = shippedAddonsDirectory()) {
|
|
11628
11658
|
const installed = [];
|
|
11629
11659
|
for (const name of ADDONS) {
|
|
11630
|
-
const source =
|
|
11631
|
-
if (!existsSync13(
|
|
11660
|
+
const source = join15(from, name);
|
|
11661
|
+
if (!existsSync13(join15(source, name === RUNTIME_ADDON ? "runtime_autoload.gd" : "plugin.cfg"))) {
|
|
11632
11662
|
throw new Error(`The package holds no ${name} addon at ${source}.`);
|
|
11633
11663
|
}
|
|
11634
|
-
const target =
|
|
11664
|
+
const target = join15(projectPath, "addons", name);
|
|
11635
11665
|
const replaced = existsSync13(target);
|
|
11636
11666
|
rmSync5(target, { recursive: true, force: true });
|
|
11637
|
-
mkdirSync4(
|
|
11667
|
+
mkdirSync4(dirname8(target), { recursive: true });
|
|
11638
11668
|
cpSync(source, target, { recursive: true });
|
|
11639
|
-
writeFileSync6(
|
|
11669
|
+
writeFileSync6(join15(target, VERSION_MARKER), `${SERVER_VERSION}
|
|
11640
11670
|
`);
|
|
11641
11671
|
installed.push({ name, path: target, replaced });
|
|
11642
11672
|
}
|
|
11643
|
-
writeFileSync6(
|
|
11673
|
+
writeFileSync6(join15(projectPath, "addons", ADDONS[0], DIGEST_MARKER), `${editorAddonDigest(from)}
|
|
11644
11674
|
`);
|
|
11645
|
-
writeFileSync6(
|
|
11675
|
+
writeFileSync6(join15(projectPath, "addons", RUNTIME_ADDON, DIGEST_MARKER), `${runtimeAddonDigest(from)}
|
|
11646
11676
|
`);
|
|
11647
11677
|
return installed;
|
|
11648
11678
|
}
|
|
11649
11679
|
function installedAddonVersion(projectPath) {
|
|
11650
|
-
const marker =
|
|
11680
|
+
const marker = join15(projectPath, "addons", ADDONS[0], VERSION_MARKER);
|
|
11651
11681
|
if (!existsSync13(marker)) {
|
|
11652
11682
|
return null;
|
|
11653
11683
|
}
|
|
@@ -11657,13 +11687,13 @@ function installedAddonVersion(projectPath) {
|
|
|
11657
11687
|
function removeAddons(projectPath) {
|
|
11658
11688
|
const removed = [];
|
|
11659
11689
|
for (const name of ADDONS) {
|
|
11660
|
-
const target =
|
|
11690
|
+
const target = join15(projectPath, "addons", name);
|
|
11661
11691
|
if (existsSync13(target)) {
|
|
11662
11692
|
rmSync5(target, { recursive: true, force: true });
|
|
11663
11693
|
removed.push(target);
|
|
11664
11694
|
}
|
|
11665
11695
|
}
|
|
11666
|
-
const addons =
|
|
11696
|
+
const addons = join15(projectPath, "addons");
|
|
11667
11697
|
if (removed.length > 0 && existsSync13(addons) && readdirSync8(addons).length === 0) {
|
|
11668
11698
|
rmdirSync(addons);
|
|
11669
11699
|
removed.push(addons);
|
|
@@ -11698,7 +11728,7 @@ function loaderAmong(projectPath, named) {
|
|
|
11698
11728
|
}
|
|
11699
11729
|
let source = null;
|
|
11700
11730
|
try {
|
|
11701
|
-
source = readFileSync11(
|
|
11731
|
+
source = readFileSync11(join15(projectPath, path), "utf8");
|
|
11702
11732
|
} catch {}
|
|
11703
11733
|
if (source?.includes(RUNTIME_AUTOLOAD.path)) {
|
|
11704
11734
|
return { name: entry, path, exists: true };
|
|
@@ -11740,8 +11770,8 @@ function enabledPlugins(settings) {
|
|
|
11740
11770
|
function inspectProject(projectPath) {
|
|
11741
11771
|
const problems = [];
|
|
11742
11772
|
const addons = ADDONS.map((name) => {
|
|
11743
|
-
const directory =
|
|
11744
|
-
const marker =
|
|
11773
|
+
const directory = join15(projectPath, "addons", name);
|
|
11774
|
+
const marker = join15(directory, VERSION_MARKER);
|
|
11745
11775
|
const installed = existsSync13(directory);
|
|
11746
11776
|
const version = existsSync13(marker) ? readFileSync11(marker, "utf8").trim() : null;
|
|
11747
11777
|
const current = version === SERVER_VERSION;
|
|
@@ -11752,7 +11782,7 @@ function inspectProject(projectPath) {
|
|
|
11752
11782
|
}
|
|
11753
11783
|
return { name, installed, version, current };
|
|
11754
11784
|
});
|
|
11755
|
-
const settings = parseProjectGodot(readFileSync11(
|
|
11785
|
+
const settings = parseProjectGodot(readFileSync11(join15(projectPath, "project.godot"), "utf8"));
|
|
11756
11786
|
const pluginsEnabled = enabledPlugins(settings);
|
|
11757
11787
|
for (const name of EDITOR_PLUGINS) {
|
|
11758
11788
|
if (!pluginsEnabled.includes(name)) {
|
|
@@ -11768,7 +11798,7 @@ function inspectProject(projectPath) {
|
|
|
11768
11798
|
const tracked = trackedByGit(projectPath, [...named.values()]);
|
|
11769
11799
|
const addonMissing = (path) => addons.some((addon) => !addon.installed && path.startsWith(`addons/${addon.name}/`));
|
|
11770
11800
|
for (const [name, path] of named) {
|
|
11771
|
-
if (!existsSync13(
|
|
11801
|
+
if (!existsSync13(join15(projectPath, path))) {
|
|
11772
11802
|
if (!addonMissing(path)) {
|
|
11773
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`);
|
|
11774
11804
|
}
|
|
@@ -11916,7 +11946,7 @@ function annotationsOf(spec) {
|
|
|
11916
11946
|
openWorldHint: false
|
|
11917
11947
|
};
|
|
11918
11948
|
}
|
|
11919
|
-
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;
|
|
11920
11950
|
var init_tool_definitions = __esm(() => {
|
|
11921
11951
|
init_launch();
|
|
11922
11952
|
PROJECT_INFO_SECTIONS = [
|
|
@@ -12420,7 +12450,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
12420
12450
|
timeoutMs: {
|
|
12421
12451
|
type: "integer",
|
|
12422
12452
|
minimum: 1,
|
|
12423
|
-
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."
|
|
12424
12454
|
},
|
|
12425
12455
|
printed: {
|
|
12426
12456
|
type: "string",
|
|
@@ -13219,7 +13249,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
13219
13249
|
title: "Editor status",
|
|
13220
13250
|
effect: "read",
|
|
13221
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.",
|
|
13222
|
-
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
|
|
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.",
|
|
13223
13253
|
parameters: {},
|
|
13224
13254
|
requires: []
|
|
13225
13255
|
},
|
|
@@ -13241,7 +13271,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
13241
13271
|
},
|
|
13242
13272
|
listMembers: {
|
|
13243
13273
|
type: "boolean",
|
|
13244
|
-
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."
|
|
13245
13275
|
}
|
|
13246
13276
|
},
|
|
13247
13277
|
requires: ["projectPath"]
|
|
@@ -13285,7 +13315,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
13285
13315
|
includeInherited: {
|
|
13286
13316
|
type: "boolean",
|
|
13287
13317
|
ops: ["info"],
|
|
13288
|
-
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."
|
|
13289
13319
|
},
|
|
13290
13320
|
member: {
|
|
13291
13321
|
type: "string",
|
|
@@ -13423,7 +13453,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
13423
13453
|
{
|
|
13424
13454
|
name: "runtime_invoke",
|
|
13425
13455
|
title: "Set or call in the running game",
|
|
13426
|
-
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.",
|
|
13427
13457
|
parameters: {
|
|
13428
13458
|
projectPath: RUNNING_PROJECT_PATH,
|
|
13429
13459
|
pid: RUNNING_PID,
|
|
@@ -13440,7 +13470,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
13440
13470
|
value: {
|
|
13441
13471
|
ops: ["set"],
|
|
13442
13472
|
blank: true,
|
|
13443
|
-
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.`
|
|
13444
13474
|
},
|
|
13445
13475
|
method: {
|
|
13446
13476
|
type: "string",
|
|
@@ -13544,7 +13574,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
13544
13574
|
name: "runtime_input",
|
|
13545
13575
|
title: "Send input to the game",
|
|
13546
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.",
|
|
13547
|
-
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.`,
|
|
13548
13578
|
parameters: {
|
|
13549
13579
|
projectPath: RUNNING_PROJECT_PATH,
|
|
13550
13580
|
pid: RUNNING_PID,
|
|
@@ -13757,7 +13787,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
13757
13787
|
},
|
|
13758
13788
|
value: {
|
|
13759
13789
|
ops: ["until"],
|
|
13760
|
-
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."
|
|
13761
13791
|
},
|
|
13762
13792
|
compare: {
|
|
13763
13793
|
type: "string",
|
|
@@ -13806,16 +13836,21 @@ var init_tool_definitions = __esm(() => {
|
|
|
13806
13836
|
{
|
|
13807
13837
|
name: "debug_breakpoint",
|
|
13808
13838
|
title: "Breakpoints",
|
|
13809
|
-
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.",
|
|
13810
13840
|
parameters: {
|
|
13811
13841
|
projectPath: PROJECT_PATH,
|
|
13812
|
-
scriptPath: SCRIPT_PATH,
|
|
13813
|
-
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." }
|
|
13814
13844
|
},
|
|
13815
|
-
requires: ["projectPath"
|
|
13845
|
+
requires: ["projectPath"],
|
|
13816
13846
|
operations: {
|
|
13817
|
-
set: { effect: "additive", summary: "set a breakpoint", requires: [] },
|
|
13818
|
-
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
|
+
}
|
|
13819
13854
|
}
|
|
13820
13855
|
},
|
|
13821
13856
|
{
|
|
@@ -35636,12 +35671,12 @@ var init_stdio = __esm(() => {
|
|
|
35636
35671
|
|
|
35637
35672
|
// src/autoload-failure.ts
|
|
35638
35673
|
import { existsSync as existsSync15, readFileSync as readFileSync13 } from "node:fs";
|
|
35639
|
-
import { join as
|
|
35674
|
+
import { join as join18 } from "node:path";
|
|
35640
35675
|
function scriptsAutoloaded(projectPath) {
|
|
35641
35676
|
const scripts = new Map;
|
|
35642
35677
|
let settings;
|
|
35643
35678
|
try {
|
|
35644
|
-
settings = parseProjectGodot(readFileSync13(
|
|
35679
|
+
settings = parseProjectGodot(readFileSync13(join18(projectPath, "project.godot"), "utf8"));
|
|
35645
35680
|
} catch {
|
|
35646
35681
|
return scripts;
|
|
35647
35682
|
}
|
|
@@ -35650,7 +35685,7 @@ function scriptsAutoloaded(projectPath) {
|
|
|
35650
35685
|
scripts.set(`res://${path}`, name);
|
|
35651
35686
|
continue;
|
|
35652
35687
|
}
|
|
35653
|
-
const scene =
|
|
35688
|
+
const scene = join18(projectPath, path);
|
|
35654
35689
|
if (!existsSync15(scene)) {
|
|
35655
35690
|
continue;
|
|
35656
35691
|
}
|
|
@@ -35677,7 +35712,7 @@ var init_autoload_failure = __esm(() => {
|
|
|
35677
35712
|
import { existsSync as existsSync16, readdirSync as readdirSync10, readFileSync as readFileSync14, statSync as statSync8, unlinkSync as unlinkSync2 } from "node:fs";
|
|
35678
35713
|
import { createConnection } from "node:net";
|
|
35679
35714
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
35680
|
-
import { dirname as
|
|
35715
|
+
import { dirname as dirname11, join as join19, resolve as resolve4 } from "node:path";
|
|
35681
35716
|
import { setTimeout as delay } from "node:timers/promises";
|
|
35682
35717
|
function runtimeDirectory(variables = process.env) {
|
|
35683
35718
|
const explicit = envValue("GDHARNESS_RUNTIME_DIR", variables);
|
|
@@ -35685,19 +35720,19 @@ function runtimeDirectory(variables = process.env) {
|
|
|
35685
35720
|
return explicit;
|
|
35686
35721
|
}
|
|
35687
35722
|
const perUser = envValue("XDG_RUNTIME_DIR", variables);
|
|
35688
|
-
return
|
|
35723
|
+
return join19(perUser ?? tmpdir4(), "gdharness");
|
|
35689
35724
|
}
|
|
35690
35725
|
function runtimeDirectories(variables = process.env) {
|
|
35691
35726
|
const candidates = [runtimeDirectory(variables)];
|
|
35692
35727
|
const fallbacks = process.platform === "win32" ? [envValue("SystemRoot", variables) ?? envValue("windir", variables) ?? "C:\\Windows"] : ["/tmp", "/var/tmp"];
|
|
35693
35728
|
for (const base of fallbacks) {
|
|
35694
|
-
candidates.push(
|
|
35729
|
+
candidates.push(join19(base, process.platform === "win32" ? "Temp" : "", "gdharness"));
|
|
35695
35730
|
}
|
|
35696
35731
|
return [...new Set(candidates.map((path) => resolve4(path)))];
|
|
35697
35732
|
}
|
|
35698
35733
|
function errorReportOf(pid, directories = runtimeDirectories()) {
|
|
35699
35734
|
for (const directory of directories) {
|
|
35700
|
-
const path =
|
|
35735
|
+
const path = join19(directory, `runtime-${pid}.log`);
|
|
35701
35736
|
if (existsSync16(path)) {
|
|
35702
35737
|
return path;
|
|
35703
35738
|
}
|
|
@@ -35851,14 +35886,14 @@ function announcedIn(directory) {
|
|
|
35851
35886
|
for (const entry of readdirSync10(directory)) {
|
|
35852
35887
|
const report = ERROR_REPORT_PATTERN.exec(entry);
|
|
35853
35888
|
if (report) {
|
|
35854
|
-
sweepErrorReport(
|
|
35889
|
+
sweepErrorReport(join19(directory, entry), Number.parseInt(report[1] ?? "", 10));
|
|
35855
35890
|
continue;
|
|
35856
35891
|
}
|
|
35857
35892
|
const match = ANNOUNCEMENT_PATTERN.exec(entry);
|
|
35858
35893
|
if (!match) {
|
|
35859
35894
|
continue;
|
|
35860
35895
|
}
|
|
35861
|
-
const file =
|
|
35896
|
+
const file = join19(directory, entry);
|
|
35862
35897
|
const pid = Number.parseInt(match[1] ?? "", 10);
|
|
35863
35898
|
const running = alive(pid);
|
|
35864
35899
|
entries.push({ file, pid, running });
|
|
@@ -35886,16 +35921,16 @@ function announcedIn(directory) {
|
|
|
35886
35921
|
found.push(announced);
|
|
35887
35922
|
}
|
|
35888
35923
|
const held = new Set(entries.map((one) => one.file));
|
|
35889
|
-
const here =
|
|
35924
|
+
const here = join19(directory, ".");
|
|
35890
35925
|
for (const file of verdicts.keys()) {
|
|
35891
|
-
if (
|
|
35926
|
+
if (dirname11(file) === here && !held.has(file)) {
|
|
35892
35927
|
verdicts.delete(file);
|
|
35893
35928
|
}
|
|
35894
35929
|
}
|
|
35895
35930
|
return found;
|
|
35896
35931
|
}
|
|
35897
35932
|
async function announcementEnded(pid, directories = runtimeDirectories()) {
|
|
35898
|
-
const files = directories.map((directory) =>
|
|
35933
|
+
const files = directories.map((directory) => join19(directory, `runtime-${pid}.json`)).filter((file) => existsSync16(file));
|
|
35899
35934
|
const first = files[0];
|
|
35900
35935
|
if (first === undefined) {
|
|
35901
35936
|
return;
|
|
@@ -35951,7 +35986,7 @@ function theOneProject(endpoints) {
|
|
|
35951
35986
|
if (endpoints.some((endpoint) => endpoint.project.path === "")) {
|
|
35952
35987
|
return;
|
|
35953
35988
|
}
|
|
35954
|
-
const paths = new Set(endpoints.map((endpoint) =>
|
|
35989
|
+
const paths = new Set(endpoints.map((endpoint) => realAsFarAsItExists(resolve4(endpoint.project.path))));
|
|
35955
35990
|
const only = [...paths][0];
|
|
35956
35991
|
return paths.size === 1 ? only : undefined;
|
|
35957
35992
|
}
|
|
@@ -36315,9 +36350,9 @@ var init_runtime_client = __esm(() => {
|
|
|
36315
36350
|
|
|
36316
36351
|
// src/boot-note.ts
|
|
36317
36352
|
import { mkdirSync as mkdirSync6, readFileSync as readFileSync15, writeFileSync as writeFileSync8 } from "node:fs";
|
|
36318
|
-
import { dirname as
|
|
36353
|
+
import { dirname as dirname12, join as join20 } from "node:path";
|
|
36319
36354
|
function bootNotePath(projectPath) {
|
|
36320
|
-
return
|
|
36355
|
+
return join20(projectPath, ".godot", "gdharness-boot.json");
|
|
36321
36356
|
}
|
|
36322
36357
|
function readScenes(projectPath) {
|
|
36323
36358
|
try {
|
|
@@ -36337,7 +36372,7 @@ function readBootNote(projectPath, scene) {
|
|
|
36337
36372
|
function writeBootNote(projectPath, scene, announcedAfterMs) {
|
|
36338
36373
|
try {
|
|
36339
36374
|
const path = bootNotePath(projectPath);
|
|
36340
|
-
mkdirSync6(
|
|
36375
|
+
mkdirSync6(dirname12(path), { recursive: true });
|
|
36341
36376
|
const scenes = {
|
|
36342
36377
|
...readScenes(projectPath),
|
|
36343
36378
|
[scene]: {
|
|
@@ -36364,14 +36399,14 @@ var init_boot_note = __esm(() => {
|
|
|
36364
36399
|
|
|
36365
36400
|
// src/breakpoint-note.ts
|
|
36366
36401
|
import { mkdirSync as mkdirSync7, readFileSync as readFileSync16, rmSync as rmSync7, writeFileSync as writeFileSync9 } from "node:fs";
|
|
36367
|
-
import { dirname as
|
|
36402
|
+
import { dirname as dirname13, isAbsolute as isAbsolute4, join as join21, relative as relative3 } from "node:path";
|
|
36368
36403
|
function breakpointNotePath(projectPath) {
|
|
36369
|
-
return
|
|
36404
|
+
return join21(projectPath, ".godot", "gdharness-breakpoints.json");
|
|
36370
36405
|
}
|
|
36371
36406
|
function writeBreakpointNote(projectPath, held) {
|
|
36372
36407
|
const path = breakpointNotePath(projectPath);
|
|
36373
|
-
const root =
|
|
36374
|
-
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));
|
|
36375
36410
|
try {
|
|
36376
36411
|
if (inside.length === 0) {
|
|
36377
36412
|
rmSync7(path, { force: true });
|
|
@@ -36383,7 +36418,7 @@ function writeBreakpointNote(projectPath, held) {
|
|
|
36383
36418
|
lines: [...one.lines]
|
|
36384
36419
|
}))
|
|
36385
36420
|
};
|
|
36386
|
-
mkdirSync7(
|
|
36421
|
+
mkdirSync7(dirname13(path), { recursive: true });
|
|
36387
36422
|
writeFileSync9(path, `${JSON.stringify(note, null, 2)}
|
|
36388
36423
|
`, "utf8");
|
|
36389
36424
|
} catch (error) {
|
|
@@ -36407,7 +36442,7 @@ function readBreakpointNote(projectPath) {
|
|
|
36407
36442
|
}
|
|
36408
36443
|
const lines = Array.isArray(listed) ? listed.filter((line) => Number.isInteger(line) && line > 0) : [];
|
|
36409
36444
|
if (lines.length > 0) {
|
|
36410
|
-
held.push({ scriptPath:
|
|
36445
|
+
held.push({ scriptPath: realAsFarAsItExists(join21(projectPath, script)), lines });
|
|
36411
36446
|
}
|
|
36412
36447
|
}
|
|
36413
36448
|
return held;
|
|
@@ -36457,9 +36492,9 @@ var init_whole_file = __esm(() => {
|
|
|
36457
36492
|
|
|
36458
36493
|
// src/class-note.ts
|
|
36459
36494
|
import { mkdirSync as mkdirSync8, readFileSync as readFileSync17 } from "node:fs";
|
|
36460
|
-
import { dirname as
|
|
36495
|
+
import { dirname as dirname14, join as join22 } from "node:path";
|
|
36461
36496
|
function classNotePath(projectPath) {
|
|
36462
|
-
return
|
|
36497
|
+
return join22(projectPath, ".godot", "gdharness-classes.json");
|
|
36463
36498
|
}
|
|
36464
36499
|
function readClassNote(projectPath) {
|
|
36465
36500
|
try {
|
|
@@ -36493,7 +36528,7 @@ function unfinishedScanIn(value) {
|
|
|
36493
36528
|
function writeClassNote(projectPath, note) {
|
|
36494
36529
|
const path = classNotePath(projectPath);
|
|
36495
36530
|
try {
|
|
36496
|
-
mkdirSync8(
|
|
36531
|
+
mkdirSync8(dirname14(path), { recursive: true });
|
|
36497
36532
|
} catch {
|
|
36498
36533
|
return;
|
|
36499
36534
|
}
|
|
@@ -36506,9 +36541,9 @@ var init_class_note = __esm(() => {
|
|
|
36506
36541
|
|
|
36507
36542
|
// src/config-pin.ts
|
|
36508
36543
|
import { existsSync as existsSync17, readFileSync as readFileSync18 } from "node:fs";
|
|
36509
|
-
import { join as
|
|
36544
|
+
import { join as join23 } from "node:path";
|
|
36510
36545
|
function versionsNamedInConfig(projectPath) {
|
|
36511
|
-
const config =
|
|
36546
|
+
const config = join23(projectPath, ".mcp.json");
|
|
36512
36547
|
if (!existsSync17(config)) {
|
|
36513
36548
|
return [];
|
|
36514
36549
|
}
|
|
@@ -36536,14 +36571,14 @@ var init_config_pin = __esm(() => {
|
|
|
36536
36571
|
|
|
36537
36572
|
// src/cut-test-run.ts
|
|
36538
36573
|
import { existsSync as existsSync18, mkdirSync as mkdirSync9, readFileSync as readFileSync19, rmSync as rmSync9, writeFileSync as writeFileSync11 } from "node:fs";
|
|
36539
|
-
import { dirname as
|
|
36574
|
+
import { dirname as dirname15, join as join24 } from "node:path";
|
|
36540
36575
|
function keptAt(projectPath) {
|
|
36541
|
-
return
|
|
36576
|
+
return join24(projectPath, ".godot", "gdharness-reports", "cut-run.json");
|
|
36542
36577
|
}
|
|
36543
36578
|
function keepCutTestRun(projectPath, run) {
|
|
36544
36579
|
const path = keptAt(projectPath);
|
|
36545
36580
|
try {
|
|
36546
|
-
mkdirSync9(
|
|
36581
|
+
mkdirSync9(dirname15(path), { recursive: true });
|
|
36547
36582
|
writeFileSync11(path, `${JSON.stringify(run, null, 2)}
|
|
36548
36583
|
`, "utf8");
|
|
36549
36584
|
} catch {}
|
|
@@ -37134,13 +37169,14 @@ class GodotDAPClient {
|
|
|
37134
37169
|
const key = fileKey(scriptPath);
|
|
37135
37170
|
const lines = this.breakpoints.get(key);
|
|
37136
37171
|
if (lines === undefined) {
|
|
37137
|
-
return;
|
|
37172
|
+
return false;
|
|
37138
37173
|
}
|
|
37139
|
-
lines.delete(line);
|
|
37174
|
+
const held = lines.delete(line);
|
|
37140
37175
|
if (lines.size === 0) {
|
|
37141
37176
|
this.breakpoints.delete(key);
|
|
37142
37177
|
this.spelled.delete(key);
|
|
37143
37178
|
}
|
|
37179
|
+
return held;
|
|
37144
37180
|
}
|
|
37145
37181
|
holdBreakpoints(held) {
|
|
37146
37182
|
for (const { scriptPath, lines } of held) {
|
|
@@ -37377,10 +37413,10 @@ import {
|
|
|
37377
37413
|
writeSync
|
|
37378
37414
|
} from "node:fs";
|
|
37379
37415
|
import { tmpdir as tmpdir5 } from "node:os";
|
|
37380
|
-
import { join as
|
|
37416
|
+
import { join as join25 } from "node:path";
|
|
37381
37417
|
import { setTimeout as delay3 } from "node:timers/promises";
|
|
37382
37418
|
function helperAssembly(directory = tmpdir5()) {
|
|
37383
|
-
return
|
|
37419
|
+
return join25(directory, `gdharness-desktop-${DIGEST}.dll`);
|
|
37384
37420
|
}
|
|
37385
37421
|
async function ensureHelperCompiled(directory = tmpdir5(), budgetMs = HELPER_COMPILE_MS, wait = true) {
|
|
37386
37422
|
const assembly = helperAssembly(directory);
|
|
@@ -37420,8 +37456,8 @@ async function ensureHelperCompiled(directory = tmpdir5(), budgetMs = HELPER_COM
|
|
|
37420
37456
|
return `the desktop helper was not compiled within ${String(budgetMs)} ms`;
|
|
37421
37457
|
}
|
|
37422
37458
|
async function compileHelper(directory, assembly, budgetMs) {
|
|
37423
|
-
const scratch = mkdtempSync2(
|
|
37424
|
-
const source =
|
|
37459
|
+
const scratch = mkdtempSync2(join25(directory, "gdharness-desktop-source-"));
|
|
37460
|
+
const source = join25(scratch, "helper.cs");
|
|
37425
37461
|
writeFileSync12(source, SOURCE, "utf8");
|
|
37426
37462
|
const literal = (path) => `'${path.replaceAll("'", "''")}'`;
|
|
37427
37463
|
const script = [
|
|
@@ -37635,12 +37671,12 @@ import {
|
|
|
37635
37671
|
statSync as statSync10,
|
|
37636
37672
|
writeFileSync as writeFileSync13
|
|
37637
37673
|
} from "node:fs";
|
|
37638
|
-
import { dirname as
|
|
37674
|
+
import { dirname as dirname16, join as join26 } from "node:path";
|
|
37639
37675
|
function editorLogPath(projectPath) {
|
|
37640
|
-
return
|
|
37676
|
+
return join26(projectPath, ".godot", "gdharness-editor.log");
|
|
37641
37677
|
}
|
|
37642
37678
|
function editorLogNotePath(projectPath) {
|
|
37643
|
-
return
|
|
37679
|
+
return join26(projectPath, ".godot", "gdharness-editor.json");
|
|
37644
37680
|
}
|
|
37645
37681
|
function readEditorLogNote(projectPath) {
|
|
37646
37682
|
try {
|
|
@@ -37655,7 +37691,7 @@ function readEditorLogNote(projectPath) {
|
|
|
37655
37691
|
function clearEditorLog(projectPath) {
|
|
37656
37692
|
try {
|
|
37657
37693
|
const path = editorLogPath(projectPath);
|
|
37658
|
-
mkdirSync10(
|
|
37694
|
+
mkdirSync10(dirname16(path), { recursive: true });
|
|
37659
37695
|
rmSync11(path, { force: true });
|
|
37660
37696
|
rmSync11(editorLogNotePath(projectPath), { force: true });
|
|
37661
37697
|
} catch {}
|
|
@@ -37663,7 +37699,7 @@ function clearEditorLog(projectPath) {
|
|
|
37663
37699
|
function writeEditorLogNote(projectPath, pid) {
|
|
37664
37700
|
try {
|
|
37665
37701
|
const path = editorLogNotePath(projectPath);
|
|
37666
|
-
mkdirSync10(
|
|
37702
|
+
mkdirSync10(dirname16(path), { recursive: true });
|
|
37667
37703
|
writeFileSync13(path, JSON.stringify({ pid, startedAt: new Date().toISOString() }, null, 2), "utf8");
|
|
37668
37704
|
} catch {}
|
|
37669
37705
|
}
|
|
@@ -37905,7 +37941,7 @@ var init_editor_log = __esm(() => {
|
|
|
37905
37941
|
|
|
37906
37942
|
// src/rename.ts
|
|
37907
37943
|
import { existsSync as existsSync21, mkdirSync as mkdirSync11, readFileSync as readFileSync22, renameSync as renameSync3, unlinkSync as unlinkSync3, writeFileSync as writeFileSync14 } from "node:fs";
|
|
37908
|
-
import { dirname as
|
|
37944
|
+
import { dirname as dirname17, posix as posix2 } from "node:path";
|
|
37909
37945
|
|
|
37910
37946
|
class Script {
|
|
37911
37947
|
path;
|
|
@@ -38620,7 +38656,7 @@ function applyRewrites(rewrites) {
|
|
|
38620
38656
|
const done = [];
|
|
38621
38657
|
try {
|
|
38622
38658
|
for (const rewrite of rewrites) {
|
|
38623
|
-
mkdirSync11(
|
|
38659
|
+
mkdirSync11(dirname17(rewrite.writeTo), { recursive: true });
|
|
38624
38660
|
const staging = `${rewrite.writeTo}.gdharness-rename`;
|
|
38625
38661
|
writeFileSync14(staging, rewrite.after, "utf8");
|
|
38626
38662
|
renameSync3(staging, rewrite.writeTo);
|
|
@@ -38673,10 +38709,10 @@ var init_rename = __esm(() => {
|
|
|
38673
38709
|
});
|
|
38674
38710
|
|
|
38675
38711
|
// src/lsp_client.ts
|
|
38676
|
-
import { existsSync as existsSync22, realpathSync as
|
|
38712
|
+
import { existsSync as existsSync22, realpathSync as realpathSync3 } from "node:fs";
|
|
38677
38713
|
import { readFile, realpath } from "node:fs/promises";
|
|
38678
38714
|
import { createConnection as createConnection3 } from "node:net";
|
|
38679
|
-
import { dirname as
|
|
38715
|
+
import { dirname as dirname18, join as join27, resolve as resolve5 } from "node:path";
|
|
38680
38716
|
import { setTimeout as delay4 } from "node:timers/promises";
|
|
38681
38717
|
import { fileURLToPath as fileURLToPath3, pathToFileURL } from "node:url";
|
|
38682
38718
|
function initializeTimeoutMs() {
|
|
@@ -38694,7 +38730,7 @@ function diagnosticsKey(uri) {
|
|
|
38694
38730
|
return decoded;
|
|
38695
38731
|
}
|
|
38696
38732
|
try {
|
|
38697
|
-
return
|
|
38733
|
+
return realpathSync3.native(fileURLToPath3(decoded)).toLowerCase();
|
|
38698
38734
|
} catch {
|
|
38699
38735
|
return decoded.toLowerCase();
|
|
38700
38736
|
}
|
|
@@ -38838,7 +38874,7 @@ class GodotLSPClient {
|
|
|
38838
38874
|
if (this.initialized) {
|
|
38839
38875
|
return;
|
|
38840
38876
|
}
|
|
38841
|
-
const rootPath = this.rootPath ??
|
|
38877
|
+
const rootPath = this.rootPath ?? dirname18(resolve5(filePath));
|
|
38842
38878
|
await this.initialize(rootPath);
|
|
38843
38879
|
}
|
|
38844
38880
|
async sendRequest(method, params, timeoutMs = this.timeouts.requestMs) {
|
|
@@ -39354,7 +39390,7 @@ async function resolveLSPPaths(projectPathValue, scriptPathValue) {
|
|
|
39354
39390
|
} catch {
|
|
39355
39391
|
throw new ArgumentRefusal(`Project path does not exist: ${requestedProjectPath}`);
|
|
39356
39392
|
}
|
|
39357
|
-
if (!existsSync22(
|
|
39393
|
+
if (!existsSync22(join27(projectPath, "project.godot"))) {
|
|
39358
39394
|
throw new ArgumentRefusal(`Not a Godot project: ${projectPath}. Point projectPath at the directory holding project.godot.`);
|
|
39359
39395
|
}
|
|
39360
39396
|
const contained = resolveWithinProject(projectPath, scriptPathValue);
|
|
@@ -39514,8 +39550,8 @@ var init_lsp_client = __esm(() => {
|
|
|
39514
39550
|
});
|
|
39515
39551
|
|
|
39516
39552
|
// src/rename-symbol.ts
|
|
39517
|
-
import { existsSync as existsSync23, mkdirSync as mkdirSync12, readdirSync as readdirSync11, realpathSync as
|
|
39518
|
-
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";
|
|
39519
39555
|
function resourcePathOf(projectPath, absolute) {
|
|
39520
39556
|
const inside = relative4(onDiskSpelling(projectPath), onDiskSpelling(absolute));
|
|
39521
39557
|
if (inside === "" || inside.startsWith("..") || /^[A-Za-z]:/.test(inside)) {
|
|
@@ -39528,13 +39564,13 @@ function onDiskSpelling(path) {
|
|
|
39528
39564
|
let at = resolve6(path);
|
|
39529
39565
|
for (;; ) {
|
|
39530
39566
|
try {
|
|
39531
|
-
return
|
|
39567
|
+
return join28(realpathSync4.native(at), ...missing.reverse());
|
|
39532
39568
|
} catch {
|
|
39533
|
-
const parent =
|
|
39569
|
+
const parent = dirname19(at);
|
|
39534
39570
|
if (parent === at) {
|
|
39535
39571
|
return resolve6(path);
|
|
39536
39572
|
}
|
|
39537
|
-
missing.push(
|
|
39573
|
+
missing.push(basename8(at));
|
|
39538
39574
|
at = parent;
|
|
39539
39575
|
}
|
|
39540
39576
|
}
|
|
@@ -39957,15 +39993,15 @@ ${mention.kind}`;
|
|
|
39957
39993
|
};
|
|
39958
39994
|
}
|
|
39959
39995
|
function writeLeftAloneDetail(projectPath, symbol, detail) {
|
|
39960
|
-
const directory =
|
|
39996
|
+
const directory = join28(projectPath, ".godot", "gdharness-renames");
|
|
39961
39997
|
mkdirSync12(directory, { recursive: true });
|
|
39962
39998
|
detailWrites += 1;
|
|
39963
|
-
const path =
|
|
39999
|
+
const path = join28(directory, `${String(Date.now())}-${String(detailWrites).padStart(6, "0")}-${String(process.pid)}-${symbol}.json`);
|
|
39964
40000
|
writeFileSync15(path, `${JSON.stringify(detail, null, 2)}
|
|
39965
40001
|
`, "utf8");
|
|
39966
40002
|
const kept = readdirSync11(directory).filter((name) => name.endsWith(".json")).sort().reverse();
|
|
39967
40003
|
for (const old of kept.slice(10)) {
|
|
39968
|
-
rmSync12(
|
|
40004
|
+
rmSync12(join28(directory, old), { force: true });
|
|
39969
40005
|
}
|
|
39970
40006
|
return path;
|
|
39971
40007
|
}
|
|
@@ -40014,10 +40050,10 @@ import {
|
|
|
40014
40050
|
statSync as statSync11,
|
|
40015
40051
|
writeFileSync as writeFileSync16
|
|
40016
40052
|
} from "node:fs";
|
|
40017
|
-
import { basename as
|
|
40053
|
+
import { basename as basename9, dirname as dirname20, extname as extname3, join as join29, relative as relative5 } from "node:path";
|
|
40018
40054
|
function filesUnder(directory) {
|
|
40019
40055
|
return readdirSync12(directory, { withFileTypes: true }).flatMap((entry) => {
|
|
40020
|
-
const path =
|
|
40056
|
+
const path = join29(directory, entry.name);
|
|
40021
40057
|
return entry.isDirectory() ? filesUnder(path) : [path];
|
|
40022
40058
|
});
|
|
40023
40059
|
}
|
|
@@ -40038,8 +40074,8 @@ function unseenByTheEngine(projectPath, path) {
|
|
|
40038
40074
|
return `${path} is under ${step}, and the engine passes over every directory whose name starts with a dot`;
|
|
40039
40075
|
}
|
|
40040
40076
|
if (at < steps.length - 1) {
|
|
40041
|
-
directory =
|
|
40042
|
-
if (existsSync24(
|
|
40077
|
+
directory = join29(directory, step);
|
|
40078
|
+
if (existsSync24(join29(directory, ".gdignore"))) {
|
|
40043
40079
|
return `${path} is under a directory holding a .gdignore, which the engine does not load`;
|
|
40044
40080
|
}
|
|
40045
40081
|
}
|
|
@@ -40051,12 +40087,12 @@ function installedAddonOf(projectPath, path) {
|
|
|
40051
40087
|
if (steps[0] !== "addons" || steps[1] === undefined) {
|
|
40052
40088
|
return null;
|
|
40053
40089
|
}
|
|
40054
|
-
return existsSync24(
|
|
40090
|
+
return existsSync24(join29(projectPath, "addons", steps[1], VERSION_MARKER)) ? `res://addons/${steps[1]}` : null;
|
|
40055
40091
|
}
|
|
40056
40092
|
function binaryResources(projectPath) {
|
|
40057
40093
|
const found = [];
|
|
40058
40094
|
const visit = (directory, prefix) => {
|
|
40059
|
-
if (existsSync24(
|
|
40095
|
+
if (existsSync24(join29(directory, ".gdignore"))) {
|
|
40060
40096
|
return;
|
|
40061
40097
|
}
|
|
40062
40098
|
for (const entry of readdirSync12(directory, { withFileTypes: true })) {
|
|
@@ -40065,7 +40101,7 @@ function binaryResources(projectPath) {
|
|
|
40065
40101
|
}
|
|
40066
40102
|
const path = `${prefix}${entry.name}`;
|
|
40067
40103
|
if (entry.isDirectory()) {
|
|
40068
|
-
visit(
|
|
40104
|
+
visit(join29(directory, entry.name), `${path}/`);
|
|
40069
40105
|
} else if (/\.(scn|res)$/i.test(entry.name)) {
|
|
40070
40106
|
found.push(path);
|
|
40071
40107
|
}
|
|
@@ -40075,7 +40111,7 @@ function binaryResources(projectPath) {
|
|
|
40075
40111
|
return found;
|
|
40076
40112
|
}
|
|
40077
40113
|
function targetPathOf(projectPath, absolute) {
|
|
40078
|
-
const inside = relative5(onDiskSpelling(projectPath),
|
|
40114
|
+
const inside = relative5(onDiskSpelling(projectPath), join29(onDiskSpelling(dirname20(absolute)), basename9(absolute)));
|
|
40079
40115
|
if (inside === "" || inside.startsWith("..") || /^[A-Za-z]:/.test(inside)) {
|
|
40080
40116
|
return null;
|
|
40081
40117
|
}
|
|
@@ -40130,7 +40166,7 @@ function planFileMove(projectPath, absoluteFrom, absoluteTo) {
|
|
|
40130
40166
|
return { ok: false, reason: `${unseen}, so nothing there could load what was moved.` };
|
|
40131
40167
|
}
|
|
40132
40168
|
if (!isDirectory) {
|
|
40133
|
-
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))));
|
|
40134
40170
|
if (owner !== undefined && owner !== null) {
|
|
40135
40171
|
return { ok: false, reason: `${from} belongs to ${owner} and moves with it: move ${owner} instead.` };
|
|
40136
40172
|
}
|
|
@@ -40164,12 +40200,12 @@ function planFileMove(projectPath, absoluteFrom, absoluteTo) {
|
|
|
40164
40200
|
scripts,
|
|
40165
40201
|
moves,
|
|
40166
40202
|
named: [from],
|
|
40167
|
-
exists: (path) => existsSync24(
|
|
40203
|
+
exists: (path) => existsSync24(join29(projectPath, path.slice("res://".length)))
|
|
40168
40204
|
}, { edits: [], mentions: [] });
|
|
40169
40205
|
plan = mentionIgnored(plan, ignoredTexts(projectPath), null, [from]);
|
|
40170
40206
|
const binaries = [];
|
|
40171
40207
|
for (const path of binaryResources(projectPath)) {
|
|
40172
|
-
const bytes = readFileSync23(
|
|
40208
|
+
const bytes = readFileSync23(join29(projectPath, path.slice("res://".length)));
|
|
40173
40209
|
const names = [...moves.keys()].find((moving) => bytes.includes(moving));
|
|
40174
40210
|
if (names !== undefined) {
|
|
40175
40211
|
binaries.push({
|
|
@@ -40188,7 +40224,7 @@ function planFileMove(projectPath, absoluteFrom, absoluteTo) {
|
|
|
40188
40224
|
}));
|
|
40189
40225
|
const left = leftAloneReport(mentions);
|
|
40190
40226
|
const rewrites = rewritesOf(plan, texts, null);
|
|
40191
|
-
const absoluteOf = (path) =>
|
|
40227
|
+
const absoluteOf = (path) => join29(projectPath, path.slice("res://".length));
|
|
40192
40228
|
const rewritten = new Map(rewrites.map((rewrite) => [rewrite.path, rewrite]));
|
|
40193
40229
|
const moved = [...moves].map(([source, target]) => {
|
|
40194
40230
|
const rewrite = rewritten.get(source);
|
|
@@ -40247,7 +40283,7 @@ function applyFileMove(outcome) {
|
|
|
40247
40283
|
}
|
|
40248
40284
|
const undo = [];
|
|
40249
40285
|
const rename = (source, target) => {
|
|
40250
|
-
mkdirSync13(
|
|
40286
|
+
mkdirSync13(dirname20(target), { recursive: true });
|
|
40251
40287
|
renameSync4(source, target);
|
|
40252
40288
|
undo.push(() => {
|
|
40253
40289
|
renameSync4(target, source);
|
|
@@ -43256,10 +43292,7 @@ function mayYetConnect(listeningSince, now = Date.now()) {
|
|
|
43256
43292
|
return listeningSince === undefined || now - listeningSince.getTime() < CONNECT_WINDOW_MS;
|
|
43257
43293
|
}
|
|
43258
43294
|
function theEditorHasComeBack(status, startedAt) {
|
|
43259
|
-
return
|
|
43260
|
-
}
|
|
43261
|
-
function hasSaidWhoItIs(status) {
|
|
43262
|
-
return status.connected && status.addonVersion !== undefined;
|
|
43295
|
+
return status.connected && (status.connectedAt?.getTime() ?? 0) > startedAt;
|
|
43263
43296
|
}
|
|
43264
43297
|
function withoutTrailingSeparator(path) {
|
|
43265
43298
|
if (path.length <= 1 || !/[\\/]$/.test(path) || /^[A-Za-z]:[\\/]$/.test(path)) {
|
|
@@ -43449,7 +43482,8 @@ var init_godot_bridge = __esm(() => {
|
|
|
43449
43482
|
this.log("info", "WebSocket bridge stopped");
|
|
43450
43483
|
}
|
|
43451
43484
|
isConnected() {
|
|
43452
|
-
|
|
43485
|
+
const info = this.connectionInfo;
|
|
43486
|
+
return this.socket?.readyState === import_websocket.default.OPEN && info?.greeted === true && info.identifying !== true;
|
|
43453
43487
|
}
|
|
43454
43488
|
isArriving() {
|
|
43455
43489
|
const info = this.connectionInfo;
|
|
@@ -43459,7 +43493,7 @@ var init_godot_bridge = __esm(() => {
|
|
|
43459
43493
|
return [...this.pendingRequests.values()].filter((pending) => pending.counted).map((pending) => ({ tool: pending.toolName, startedAt: pending.startedAt })).sort((a, b) => a.startedAt - b.startedAt);
|
|
43460
43494
|
}
|
|
43461
43495
|
getStatus() {
|
|
43462
|
-
const identifying = this.
|
|
43496
|
+
const identifying = this.isArriving();
|
|
43463
43497
|
const editor = identifying ? null : this.connectionInfo;
|
|
43464
43498
|
return {
|
|
43465
43499
|
host: this.host,
|
|
@@ -43731,7 +43765,7 @@ var init_godot_bridge = __esm(() => {
|
|
|
43731
43765
|
startKeepalive() {
|
|
43732
43766
|
this.stopKeepalive();
|
|
43733
43767
|
this.pingInterval = setInterval(() => {
|
|
43734
|
-
if (
|
|
43768
|
+
if (this.socket?.readyState !== import_websocket.default.OPEN) {
|
|
43735
43769
|
return;
|
|
43736
43770
|
}
|
|
43737
43771
|
try {
|
|
@@ -43741,6 +43775,7 @@ var init_godot_bridge = __esm(() => {
|
|
|
43741
43775
|
this.log("warn", `Failed to send ping: ${errorMessage(error)}`);
|
|
43742
43776
|
}
|
|
43743
43777
|
}, KEEPALIVE_INTERVAL_MS);
|
|
43778
|
+
this.pingInterval.unref();
|
|
43744
43779
|
}
|
|
43745
43780
|
stopKeepalive() {
|
|
43746
43781
|
if (!this.pingInterval) {
|
|
@@ -43912,7 +43947,7 @@ var init_godot_bridge = __esm(() => {
|
|
|
43912
43947
|
// src/import-freshness.ts
|
|
43913
43948
|
import { createHash as createHash5 } from "node:crypto";
|
|
43914
43949
|
import { existsSync as existsSync25, readdirSync as readdirSync13, readFileSync as readFileSync24, statSync as statSync12 } from "node:fs";
|
|
43915
|
-
import { join as
|
|
43950
|
+
import { join as join30 } from "node:path";
|
|
43916
43951
|
function md5Of(file) {
|
|
43917
43952
|
let stats;
|
|
43918
43953
|
try {
|
|
@@ -43956,7 +43991,7 @@ function listed2(value) {
|
|
|
43956
43991
|
return [...(value ?? "").matchAll(/"((?:[^"\\]|\\.)*)"/g)].map((match) => match[1] ?? "");
|
|
43957
43992
|
}
|
|
43958
43993
|
function onDisk(projectPath, resPath) {
|
|
43959
|
-
return
|
|
43994
|
+
return join30(projectPath, resPath.slice("res://".length));
|
|
43960
43995
|
}
|
|
43961
43996
|
function behind(projectPath, resPath, sidecar) {
|
|
43962
43997
|
const remap = sectionOf(sidecar, "remap");
|
|
@@ -43969,7 +44004,7 @@ function behind(projectPath, resPath, sidecar) {
|
|
|
43969
44004
|
return "its imported output is not on disk";
|
|
43970
44005
|
}
|
|
43971
44006
|
const name = resPath.slice(resPath.lastIndexOf("/") + 1);
|
|
43972
|
-
const record =
|
|
44007
|
+
const record = join30(projectPath, ".godot", "imported", `${name}-${createHash5("md5").update(resPath, "utf8").digest("hex")}.md5`);
|
|
43973
44008
|
let recorded = "";
|
|
43974
44009
|
try {
|
|
43975
44010
|
recorded = /^source_md5="([0-9a-f]*)"/m.exec(readFileSync24(record, "utf8"))?.[1] ?? "";
|
|
@@ -43985,7 +44020,7 @@ function behind(projectPath, resPath, sidecar) {
|
|
|
43985
44020
|
function staleImports(projectPath) {
|
|
43986
44021
|
const stale = [];
|
|
43987
44022
|
const visit = (directory, resDirectory) => {
|
|
43988
|
-
if (existsSync25(
|
|
44023
|
+
if (existsSync25(join30(directory, ".gdignore")) || existsSync25(join30(directory, VERSION_MARKER))) {
|
|
43989
44024
|
return;
|
|
43990
44025
|
}
|
|
43991
44026
|
let entries;
|
|
@@ -44001,7 +44036,7 @@ function staleImports(projectPath) {
|
|
|
44001
44036
|
}
|
|
44002
44037
|
const resPath = `${resDirectory}${entry.name}`;
|
|
44003
44038
|
if (entry.isDirectory()) {
|
|
44004
|
-
visit(
|
|
44039
|
+
visit(join30(directory, entry.name), `${resPath}/`);
|
|
44005
44040
|
continue;
|
|
44006
44041
|
}
|
|
44007
44042
|
if (!entry.isFile()) {
|
|
@@ -44014,7 +44049,7 @@ function staleImports(projectPath) {
|
|
|
44014
44049
|
}
|
|
44015
44050
|
let sidecar;
|
|
44016
44051
|
try {
|
|
44017
|
-
sidecar = readFileSync24(
|
|
44052
|
+
sidecar = readFileSync24(join30(directory, entry.name), "utf8");
|
|
44018
44053
|
} catch {
|
|
44019
44054
|
continue;
|
|
44020
44055
|
}
|
|
@@ -44135,15 +44170,15 @@ var init_keeper_channel = () => {};
|
|
|
44135
44170
|
|
|
44136
44171
|
// src/lifecycle-log.ts
|
|
44137
44172
|
import { appendFileSync, mkdirSync as mkdirSync14, renameSync as renameSync5, statSync as statSync13 } from "node:fs";
|
|
44138
|
-
import { dirname as
|
|
44173
|
+
import { dirname as dirname21, join as join31 } from "node:path";
|
|
44139
44174
|
import process11 from "node:process";
|
|
44140
44175
|
function lifecycleLogPath() {
|
|
44141
|
-
return
|
|
44176
|
+
return join31(runtimeDirectory(), "servers.log");
|
|
44142
44177
|
}
|
|
44143
44178
|
function writeLifecycle(event, fields = {}) {
|
|
44144
44179
|
const path = lifecycleLogPath();
|
|
44145
44180
|
try {
|
|
44146
|
-
mkdirSync14(
|
|
44181
|
+
mkdirSync14(dirname21(path), { recursive: true });
|
|
44147
44182
|
if ((statSync13(path, { throwIfNoEntry: false })?.size ?? 0) > LIFECYCLE_LOG_LIMIT) {
|
|
44148
44183
|
try {
|
|
44149
44184
|
renameSync5(path, `${path}.1`);
|
|
@@ -44499,14 +44534,14 @@ var init_prompts = __esm(() => {
|
|
|
44499
44534
|
|
|
44500
44535
|
// src/restart-note.ts
|
|
44501
44536
|
import { mkdirSync as mkdirSync15, readFileSync as readFileSync25, rmSync as rmSync13, writeFileSync as writeFileSync17 } from "node:fs";
|
|
44502
|
-
import { dirname as
|
|
44537
|
+
import { dirname as dirname22, join as join32 } from "node:path";
|
|
44503
44538
|
function restartNotePath(projectPath) {
|
|
44504
|
-
return
|
|
44539
|
+
return join32(projectPath, ".godot", "gdharness-restart.json");
|
|
44505
44540
|
}
|
|
44506
44541
|
function noteRestartBegun(note) {
|
|
44507
44542
|
const path = restartNotePath(note.projectPath);
|
|
44508
44543
|
try {
|
|
44509
|
-
mkdirSync15(
|
|
44544
|
+
mkdirSync15(dirname22(path), { recursive: true });
|
|
44510
44545
|
writeFileSync17(path, `${JSON.stringify(note, null, 2)}
|
|
44511
44546
|
`, "utf8");
|
|
44512
44547
|
} catch (error) {
|
|
@@ -44541,26 +44576,10 @@ var init_restart_note = __esm(() => {
|
|
|
44541
44576
|
|
|
44542
44577
|
// src/run-record.ts
|
|
44543
44578
|
import { createHash as createHash6 } from "node:crypto";
|
|
44544
|
-
import { mkdirSync as mkdirSync16, openSync as openSync3, readdirSync as readdirSync14, readFileSync as readFileSync26,
|
|
44545
|
-
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";
|
|
44546
44581
|
function runsDirectory() {
|
|
44547
|
-
return
|
|
44548
|
-
}
|
|
44549
|
-
function realAsFarAsItExists(path) {
|
|
44550
|
-
const missing = [];
|
|
44551
|
-
let at = path;
|
|
44552
|
-
for (;; ) {
|
|
44553
|
-
try {
|
|
44554
|
-
return join32(realpathSync6.native(at), ...missing);
|
|
44555
|
-
} catch {
|
|
44556
|
-
const parent = dirname22(at);
|
|
44557
|
-
if (parent === at) {
|
|
44558
|
-
return path;
|
|
44559
|
-
}
|
|
44560
|
-
missing.unshift(basename9(at));
|
|
44561
|
-
at = parent;
|
|
44562
|
-
}
|
|
44563
|
-
}
|
|
44582
|
+
return join33(runtimeDirectory(), "runs");
|
|
44564
44583
|
}
|
|
44565
44584
|
function projectKey(projectPath) {
|
|
44566
44585
|
const real = realAsFarAsItExists(resolve7(projectPath)).replace(/[\\/]+$/, "");
|
|
@@ -44568,10 +44587,10 @@ function projectKey(projectPath) {
|
|
|
44568
44587
|
return createHash6("sha256").update(folded.replaceAll("\\", "/")).digest("hex").slice(0, 16);
|
|
44569
44588
|
}
|
|
44570
44589
|
function runRecordPath(projectPath, directory = runtimeDirectory()) {
|
|
44571
|
-
return
|
|
44590
|
+
return join33(directory, "runs", `note-${projectKey(projectPath)}.json`);
|
|
44572
44591
|
}
|
|
44573
44592
|
function latestRunPath(servedBy, directory = runtimeDirectory()) {
|
|
44574
|
-
return
|
|
44593
|
+
return join33(directory, "runs", `latest-${projectKey(servedBy)}.json`);
|
|
44575
44594
|
}
|
|
44576
44595
|
function writeLatestRun(servedBy, latest) {
|
|
44577
44596
|
try {
|
|
@@ -44596,20 +44615,20 @@ function readLatestRun(servedBy, directory = runtimeDirectory()) {
|
|
|
44596
44615
|
}
|
|
44597
44616
|
function openTranscript(startedAt) {
|
|
44598
44617
|
mkdirSync16(runsDirectory(), { recursive: true });
|
|
44599
|
-
const path =
|
|
44618
|
+
const path = join33(runsDirectory(), `run-${startedAt}.log`);
|
|
44600
44619
|
return { path, fd: openSync3(path, "a") };
|
|
44601
44620
|
}
|
|
44602
44621
|
function profilePath(startedAt) {
|
|
44603
|
-
return
|
|
44622
|
+
return join33(runsDirectory(), `run-${startedAt}.profile.json`);
|
|
44604
44623
|
}
|
|
44605
44624
|
function profileMarkPath(startedAt) {
|
|
44606
|
-
return
|
|
44625
|
+
return join33(runsDirectory(), `run-${startedAt}.profile-mark.json`);
|
|
44607
44626
|
}
|
|
44608
44627
|
function noteIsAbout(note, playing) {
|
|
44609
44628
|
return note.play !== undefined && playing !== null && note.play.play === playing.play && note.play.editorPid === playing.editorPid;
|
|
44610
44629
|
}
|
|
44611
44630
|
function editorNotePath(projectPath, directory = runtimeDirectory()) {
|
|
44612
|
-
return
|
|
44631
|
+
return join33(directory, "runs", `editor-${projectKey(projectPath)}.json`);
|
|
44613
44632
|
}
|
|
44614
44633
|
function writeEditorRunNote(note) {
|
|
44615
44634
|
mkdirSync16(runsDirectory(), { recursive: true });
|
|
@@ -44662,13 +44681,13 @@ function everyRunRecord() {
|
|
|
44662
44681
|
for (const directory of runtimeDirectories()) {
|
|
44663
44682
|
let entries;
|
|
44664
44683
|
try {
|
|
44665
|
-
entries = readdirSync14(
|
|
44684
|
+
entries = readdirSync14(join33(directory, "runs"));
|
|
44666
44685
|
} catch {
|
|
44667
44686
|
continue;
|
|
44668
44687
|
}
|
|
44669
44688
|
for (const entry of entries) {
|
|
44670
44689
|
if (entry.startsWith("note-") && entry.endsWith(".json")) {
|
|
44671
|
-
const record = recordAt(
|
|
44690
|
+
const record = recordAt(join33(directory, "runs", entry));
|
|
44672
44691
|
if (record !== null) {
|
|
44673
44692
|
found.push(record);
|
|
44674
44693
|
}
|
|
@@ -44843,7 +44862,7 @@ function whyNotTheRun(record, running, needed) {
|
|
|
44843
44862
|
if (after > SAME_RUN_WINDOW_MS) {
|
|
44844
44863
|
return `that process started ${Math.round(after / 1000)} seconds after the run had`;
|
|
44845
44864
|
}
|
|
44846
|
-
const engine =
|
|
44865
|
+
const engine = basename10(record.command);
|
|
44847
44866
|
const said = process.platform === "win32" ? running.text.toLowerCase() : running.text;
|
|
44848
44867
|
const wanted = process.platform === "win32" ? engine.toLowerCase() : engine;
|
|
44849
44868
|
if (!said.includes(wanted)) {
|
|
@@ -44906,7 +44925,7 @@ function sweepTranscripts(keepMs = 24 * 60 * 60 * 1000, now = Date.now()) {
|
|
|
44906
44925
|
if (!transcript && !entry.endsWith(".partial")) {
|
|
44907
44926
|
continue;
|
|
44908
44927
|
}
|
|
44909
|
-
const path =
|
|
44928
|
+
const path = join33(directory, entry);
|
|
44910
44929
|
if (kept.has(path) || ageMs(path, now) < keepMs) {
|
|
44911
44930
|
continue;
|
|
44912
44931
|
}
|
|
@@ -45155,15 +45174,15 @@ var init_server_instructions = __esm(() => {
|
|
|
45155
45174
|
|
|
45156
45175
|
// src/test-selection.ts
|
|
45157
45176
|
import { existsSync as existsSync26, readFileSync as readFileSync28 } from "node:fs";
|
|
45158
|
-
import { basename as
|
|
45177
|
+
import { basename as basename11, join as join34 } from "node:path";
|
|
45159
45178
|
function casesIn(projectPath, suite) {
|
|
45160
|
-
const text = readFileSync28(
|
|
45179
|
+
const text = readFileSync28(join34(projectPath, suite.slice("res://".length)), "utf8");
|
|
45161
45180
|
return [...text.matchAll(/^func\s+(test_\w*)\s*\(/gm)].map((match) => match[1] ?? "");
|
|
45162
45181
|
}
|
|
45163
45182
|
function suitesUnder(projectPath, asked) {
|
|
45164
45183
|
const scripts = projectFiles(projectPath, [".gd"]).map((path) => `res://${path}`);
|
|
45165
45184
|
return [
|
|
45166
|
-
...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}/`))))
|
|
45167
45186
|
];
|
|
45168
45187
|
}
|
|
45169
45188
|
function selectTests(projectPath, asked, given) {
|
|
@@ -45175,7 +45194,7 @@ function selectTests(projectPath, asked, given) {
|
|
|
45175
45194
|
return { ok: false, reason: "tests is an empty list: name a case, or leave tests out to run every one." };
|
|
45176
45195
|
}
|
|
45177
45196
|
const declared = suitesUnder(projectPath, asked).flatMap((suite) => casesIn(projectPath, suite).map((test) => ({ suite, test })));
|
|
45178
|
-
const nameOf = (suite) =>
|
|
45197
|
+
const nameOf = (suite) => basename11(suite, ".gd");
|
|
45179
45198
|
const selected = [];
|
|
45180
45199
|
for (const [index, entry] of entries.entries()) {
|
|
45181
45200
|
if (typeof entry !== "string" || entry.trim() === "") {
|
|
@@ -45235,7 +45254,7 @@ import {
|
|
|
45235
45254
|
statSync as statSync15,
|
|
45236
45255
|
writeFileSync as writeFileSync18
|
|
45237
45256
|
} from "node:fs";
|
|
45238
|
-
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";
|
|
45239
45258
|
import { setTimeout as delay5 } from "node:timers/promises";
|
|
45240
45259
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
45241
45260
|
function editorRestartTimeoutMs() {
|
|
@@ -45271,12 +45290,12 @@ function writtenFileRefusal(path, rule) {
|
|
|
45271
45290
|
if (!rule.extensions.includes(extname4(path).toLowerCase())) {
|
|
45272
45291
|
return `${named} ${path} does not end in ${rule.extensions.join(" or ")}, which is what ${what} is.`;
|
|
45273
45292
|
}
|
|
45274
|
-
const directory =
|
|
45293
|
+
const directory = realAsFarAsItExists(dirname23(path));
|
|
45275
45294
|
if (!existsSync27(directory)) {
|
|
45276
45295
|
return `${named}'s directory ${dirname23(path)} does not exist; ${what} creates no directories.`;
|
|
45277
45296
|
}
|
|
45278
|
-
const where =
|
|
45279
|
-
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));
|
|
45280
45299
|
if (inside !== undefined) {
|
|
45281
45300
|
return `${named} ${path} is inside the project at ${inside}; ${what} is saved outside it, so it never becomes part of the game.`;
|
|
45282
45301
|
}
|
|
@@ -45366,7 +45385,7 @@ function keptGameRequest(tool, op, args) {
|
|
|
45366
45385
|
}
|
|
45367
45386
|
}
|
|
45368
45387
|
function projectSpelling(projectPath, absolutePath) {
|
|
45369
|
-
return `res://${relative6(
|
|
45388
|
+
return `res://${relative6(realAsFarAsItExists(projectPath), realAsFarAsItExists(absolutePath)).replaceAll("\\", "/")}`;
|
|
45370
45389
|
}
|
|
45371
45390
|
function whichGame(args) {
|
|
45372
45391
|
return { projectPath: args["projectPath"], pid: args["pid"] };
|
|
@@ -45825,10 +45844,14 @@ function importedFirstAnswer(importedFirst) {
|
|
|
45825
45844
|
if (importedFirst === undefined) {
|
|
45826
45845
|
return {};
|
|
45827
45846
|
}
|
|
45847
|
+
const imported = withAddonPathsCounted(importedFirst.imported);
|
|
45848
|
+
const uids = withAddonPathsCounted(importedFirst.uidsWritten);
|
|
45828
45849
|
return {
|
|
45829
|
-
...
|
|
45850
|
+
...imported.own.length === 0 ? {} : { importedFirst: imported.own },
|
|
45851
|
+
...Object.keys(imported.inAddons).length === 0 ? {} : { importedInAddons: imported.inAddons },
|
|
45830
45852
|
...importedFirst.notImported.length === 0 ? {} : { notImportedFirst: importedFirst.notImported },
|
|
45831
|
-
...
|
|
45853
|
+
...uids.own.length === 0 ? {} : { uidsWrittenFirst: uids.own },
|
|
45854
|
+
...Object.keys(uids.inAddons).length === 0 ? {} : { uidsWrittenInAddons: uids.inAddons },
|
|
45832
45855
|
importTookMs: importedFirst.tookMs,
|
|
45833
45856
|
...importedFirst.note === undefined ? {} : { importNote: importedFirst.note }
|
|
45834
45857
|
};
|
|
@@ -45861,7 +45884,7 @@ function notRunNote(notRun, failFast) {
|
|
|
45861
45884
|
function exportPresetRefusal(projectPath, preset) {
|
|
45862
45885
|
let text;
|
|
45863
45886
|
try {
|
|
45864
|
-
text = readFileSync29(
|
|
45887
|
+
text = readFileSync29(join35(projectPath, "export_presets.cfg"), "utf8");
|
|
45865
45888
|
} catch {
|
|
45866
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.`;
|
|
45867
45890
|
}
|
|
@@ -45927,7 +45950,7 @@ function filesIn(folder) {
|
|
|
45927
45950
|
try {
|
|
45928
45951
|
for (const entry of readdirSync15(folder, { withFileTypes: true })) {
|
|
45929
45952
|
if (entry.isFile()) {
|
|
45930
|
-
found.set(entry.name, statSync15(
|
|
45953
|
+
found.set(entry.name, statSync15(join35(folder, entry.name)).mtimeMs);
|
|
45931
45954
|
}
|
|
45932
45955
|
}
|
|
45933
45956
|
} catch {}
|
|
@@ -45959,7 +45982,7 @@ function unsavedInEditorNote(scripts) {
|
|
|
45959
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.`;
|
|
45960
45983
|
}
|
|
45961
45984
|
function editorAddonMissing(projectPath) {
|
|
45962
|
-
if (!existsSync27(
|
|
45985
|
+
if (!existsSync27(join35(projectPath, "project.godot"))) {
|
|
45963
45986
|
return null;
|
|
45964
45987
|
}
|
|
45965
45988
|
const report = inspectProject(projectPath);
|
|
@@ -46224,7 +46247,7 @@ function profileFrameFunctions(projectPath) {
|
|
|
46224
46247
|
let projectGodot = null;
|
|
46225
46248
|
if (projectPath !== null) {
|
|
46226
46249
|
try {
|
|
46227
|
-
projectGodot = readFileSync29(
|
|
46250
|
+
projectGodot = readFileSync29(join35(projectPath, "project.godot"), "utf8");
|
|
46228
46251
|
} catch {}
|
|
46229
46252
|
}
|
|
46230
46253
|
return frameFunctionsFor(queuedMessagesOf(projectGodot));
|
|
@@ -46403,7 +46426,7 @@ function foldedEntriesAnswer(log, floor = "warning") {
|
|
|
46403
46426
|
}
|
|
46404
46427
|
function gdUnitFailsOnScriptErrors(projectPath) {
|
|
46405
46428
|
try {
|
|
46406
|
-
return settingKeys(readFileSync29(
|
|
46429
|
+
return settingKeys(readFileSync29(join35(projectPath, "project.godot"), "utf8")).get("gdunit4/report/godot/script_error") !== false;
|
|
46407
46430
|
} catch {
|
|
46408
46431
|
return true;
|
|
46409
46432
|
}
|
|
@@ -46418,7 +46441,7 @@ function uncaughtNote(uncaught) {
|
|
|
46418
46441
|
}
|
|
46419
46442
|
function elsewhereIn(projectPath, asked) {
|
|
46420
46443
|
const wanted = asked.replace(/^res:\/\//, "").replace(/\/+$/, "");
|
|
46421
|
-
if (wanted === "" || existsSync27(
|
|
46444
|
+
if (wanted === "" || existsSync27(join35(projectPath, wanted))) {
|
|
46422
46445
|
return "";
|
|
46423
46446
|
}
|
|
46424
46447
|
const root = (wanted.split("/")[0] ?? "").toLowerCase();
|
|
@@ -46521,7 +46544,7 @@ function isDirectory(path) {
|
|
|
46521
46544
|
}
|
|
46522
46545
|
}
|
|
46523
46546
|
function scriptsUnder(directory, prefix) {
|
|
46524
|
-
if (existsSync27(
|
|
46547
|
+
if (existsSync27(join35(directory, ".gdignore"))) {
|
|
46525
46548
|
return [];
|
|
46526
46549
|
}
|
|
46527
46550
|
const found = [];
|
|
@@ -46531,7 +46554,7 @@ function scriptsUnder(directory, prefix) {
|
|
|
46531
46554
|
}
|
|
46532
46555
|
const path = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
|
|
46533
46556
|
if (entry.isDirectory()) {
|
|
46534
|
-
found.push(...scriptsUnder(
|
|
46557
|
+
found.push(...scriptsUnder(join35(directory, entry.name), path));
|
|
46535
46558
|
} else if (entry.isFile() && entry.name.endsWith(".gd")) {
|
|
46536
46559
|
found.push(path);
|
|
46537
46560
|
}
|
|
@@ -46561,7 +46584,7 @@ function needsItsOwnProcess(asked) {
|
|
|
46561
46584
|
class GodotServer {
|
|
46562
46585
|
stdio = null;
|
|
46563
46586
|
locator = new GodotLocator;
|
|
46564
|
-
operationsScript =
|
|
46587
|
+
operationsScript = join35(__dirname2, "godot", "operations", "godot_operations.gd");
|
|
46565
46588
|
godotBridge;
|
|
46566
46589
|
tools = buildToolDefinitions();
|
|
46567
46590
|
activeProcess = null;
|
|
@@ -46823,7 +46846,7 @@ class GodotServer {
|
|
|
46823
46846
|
}
|
|
46824
46847
|
noteTheStart() {
|
|
46825
46848
|
const cwd = process.cwd();
|
|
46826
|
-
const mcpJson =
|
|
46849
|
+
const mcpJson = join35(cwd, ".mcp.json");
|
|
46827
46850
|
const announced = this.ownProject === null ? null : readAnnouncement(announcementPath(this.ownProject));
|
|
46828
46851
|
const predecessor = announced !== null && announced.pid !== process.pid && alive(announced.pid) ? announced.pid : null;
|
|
46829
46852
|
writeLifecycle("started", {
|
|
@@ -46971,7 +46994,7 @@ class GodotServer {
|
|
|
46971
46994
|
return checked.response;
|
|
46972
46995
|
}
|
|
46973
46996
|
const named = args["projectPath"];
|
|
46974
|
-
if (typeof named === "string" && existsSync27(
|
|
46997
|
+
if (typeof named === "string" && existsSync27(join35(named, "project.godot"))) {
|
|
46975
46998
|
this.lastProjectPath = named;
|
|
46976
46999
|
}
|
|
46977
47000
|
this.updates.refresh();
|
|
@@ -47007,7 +47030,7 @@ class GodotServer {
|
|
|
47007
47030
|
let notSaved;
|
|
47008
47031
|
try {
|
|
47009
47032
|
savedAt = answer.content.map((item, index) => {
|
|
47010
|
-
const file =
|
|
47033
|
+
const file = join35(directory, item.type === "text" ? `${stem}-${index}.txt` : `${stem}-${index}.${item.mimeType.split("/")[1] ?? "bin"}`);
|
|
47011
47034
|
writeFileSync18(file, item.type === "text" ? item.text : Buffer.from(item.data, "base64"));
|
|
47012
47035
|
return file;
|
|
47013
47036
|
});
|
|
@@ -47286,6 +47309,14 @@ class GodotServer {
|
|
|
47286
47309
|
if (headless === "modify_script") {
|
|
47287
47310
|
return await this.handleModifyScript(answerable);
|
|
47288
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
|
+
}
|
|
47289
47320
|
if (headless !== "refresh_class_cache") {
|
|
47290
47321
|
return await this.headless(headless, answerable);
|
|
47291
47322
|
}
|
|
@@ -47541,12 +47572,23 @@ class GodotServer {
|
|
|
47541
47572
|
if (!project.ok) {
|
|
47542
47573
|
return project.response;
|
|
47543
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
|
+
}
|
|
47544
47586
|
const located = resolveWithinProject(project.value.path, readString(args, "scriptPath") ?? "");
|
|
47545
47587
|
if (!located.ok) {
|
|
47546
47588
|
return this.createErrorResponse(located.reason, PATH_SOLUTIONS);
|
|
47547
47589
|
}
|
|
47548
47590
|
const session = this.dapHoldingBreakpointsOf(project.value.path);
|
|
47549
|
-
const scriptPath =
|
|
47591
|
+
const scriptPath = realAsFarAsItExists(located.absolutePath);
|
|
47550
47592
|
const line = readPositiveNumber(args, "line");
|
|
47551
47593
|
if (line === undefined) {
|
|
47552
47594
|
return this.createErrorResponse("line is required: the one-based line to stop on.");
|
|
@@ -47574,10 +47616,13 @@ class GodotServer {
|
|
|
47574
47616
|
} else {
|
|
47575
47617
|
if (op === "set") {
|
|
47576
47618
|
session.holdBreakpoints([{ scriptPath, lines: [line] }]);
|
|
47619
|
+
answer = { set: true };
|
|
47577
47620
|
} else {
|
|
47578
|
-
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
|
+
};
|
|
47579
47625
|
}
|
|
47580
|
-
answer = { [op === "set" ? "set" : "removed"]: true };
|
|
47581
47626
|
}
|
|
47582
47627
|
const held = session.breakpointsHeld();
|
|
47583
47628
|
writeBreakpointNote(project.value.path, held);
|
|
@@ -47599,7 +47644,7 @@ class GodotServer {
|
|
|
47599
47644
|
} : {
|
|
47600
47645
|
runningGame: `Told to the running game (pid ${kept?.pid ?? "?"}), which this server started.`
|
|
47601
47646
|
},
|
|
47602
|
-
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.",
|
|
47603
47648
|
...editor ? this.breakpointsAtRisk() : {}
|
|
47604
47649
|
});
|
|
47605
47650
|
}
|
|
@@ -47642,7 +47687,7 @@ class GodotServer {
|
|
|
47642
47687
|
if (path === undefined) {
|
|
47643
47688
|
return { ok: false, response: this.createErrorResponse("projectPath is required.") };
|
|
47644
47689
|
}
|
|
47645
|
-
const file =
|
|
47690
|
+
const file = join35(path, "project.godot");
|
|
47646
47691
|
if (!existsSync27(file)) {
|
|
47647
47692
|
return {
|
|
47648
47693
|
ok: false,
|
|
@@ -48348,7 +48393,7 @@ class GodotServer {
|
|
|
48348
48393
|
const name = application["config/name"];
|
|
48349
48394
|
const mainScene = application["run/main_scene"];
|
|
48350
48395
|
const info = {
|
|
48351
|
-
name: typeof name === "string" && name !== "" ? name :
|
|
48396
|
+
name: typeof name === "string" && name !== "" ? name : basename12(project.value.path),
|
|
48352
48397
|
path: project.value.path,
|
|
48353
48398
|
mainScene: typeof mainScene === "string" && mainScene !== "" ? mainScene : null,
|
|
48354
48399
|
godotVersion: await this.godotVersion(engine.value),
|
|
@@ -48466,7 +48511,7 @@ class GodotServer {
|
|
|
48466
48511
|
const exportArgs = [
|
|
48467
48512
|
"--headless",
|
|
48468
48513
|
"--log-file",
|
|
48469
|
-
|
|
48514
|
+
join35(exportLogs, "engine.log"),
|
|
48470
48515
|
"--path",
|
|
48471
48516
|
project.value.path,
|
|
48472
48517
|
debug ? "--export-debug" : "--export-release",
|
|
@@ -48503,7 +48548,7 @@ class GodotServer {
|
|
|
48503
48548
|
if (ending.exitSignal === null && ending.exitCode === null) {
|
|
48504
48549
|
return this.createErrorResponse(`Export could not be run: ${ending.failure ?? "the engine gave no exit status"}`);
|
|
48505
48550
|
}
|
|
48506
|
-
const beside = writtenBeside(listedBefore, filesIn(folder),
|
|
48551
|
+
const beside = writtenBeside(listedBefore, filesIn(folder), basename12(output.absolutePath), dirname23(output.relativePath).replaceAll("\\", "/"));
|
|
48507
48552
|
return exportAnswer(ending, { preset, outputPath: output.relativePath, debug }, before, writtenAt(), beside, docCacheNote);
|
|
48508
48553
|
}
|
|
48509
48554
|
reportTestProgress(log) {
|
|
@@ -48557,10 +48602,10 @@ class GodotServer {
|
|
|
48557
48602
|
if (movie !== null) {
|
|
48558
48603
|
return this.createErrorResponse(movie);
|
|
48559
48604
|
}
|
|
48560
|
-
const missing = paths.value.filter((path) => !existsSync27(
|
|
48605
|
+
const missing = paths.value.filter((path) => !existsSync27(join35(project.value.path, path.slice("res://".length))));
|
|
48561
48606
|
const asked = paths.value.filter((path) => !missing.includes(path));
|
|
48562
48607
|
const runner = "addons/gdUnit4/bin/GdUnitCmdTool.gd";
|
|
48563
|
-
const noRunner = !existsSync27(
|
|
48608
|
+
const noRunner = !existsSync27(join35(project.value.path, runner));
|
|
48564
48609
|
if (asked.length === 0) {
|
|
48565
48610
|
const verdict = `nothing at ${missing.join(" and ")}`;
|
|
48566
48611
|
const andNoRunner = noRunner ? `, and gdUnit4 is not installed in this project: no ${runner}` : "";
|
|
@@ -48680,14 +48725,14 @@ class GodotServer {
|
|
|
48680
48725
|
await run.settled;
|
|
48681
48726
|
const tookMs = Date.now() - began;
|
|
48682
48727
|
const notCompiled = stopped.for ?? run.log.all.map((entry) => autoloadNotCompiled(entry, autoloaded)).find((found) => found !== null) ?? null;
|
|
48683
|
-
const reportsDir =
|
|
48728
|
+
const reportsDir = join35(project.value.path, ".godot", ...ours.split("/"));
|
|
48684
48729
|
let report = null;
|
|
48685
48730
|
let reportProblem = null;
|
|
48686
48731
|
try {
|
|
48687
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))) : [];
|
|
48688
48733
|
const newest = written.at(-1);
|
|
48689
48734
|
if (newest !== undefined) {
|
|
48690
|
-
report = parseJUnit(readFileSync29(
|
|
48735
|
+
report = parseJUnit(readFileSync29(join35(reportsDir, newest, "results.xml"), "utf8"));
|
|
48691
48736
|
}
|
|
48692
48737
|
} catch (error) {
|
|
48693
48738
|
reportProblem = errorMessage(error);
|
|
@@ -48815,7 +48860,7 @@ class GodotServer {
|
|
|
48815
48860
|
const listedAsUncaught = new Set(uncaughtFolded.map((entry) => entry.index));
|
|
48816
48861
|
const notApplied = ignoredYetRan(report, ignored);
|
|
48817
48862
|
const testsNotRun = [
|
|
48818
|
-
...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}`))
|
|
48819
48864
|
];
|
|
48820
48865
|
return this.jsonTextResponse({
|
|
48821
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,
|
|
@@ -49330,7 +49375,7 @@ class GodotServer {
|
|
|
49330
49375
|
...Array.isArray(stack) ? {
|
|
49331
49376
|
stack: stack.map((frame) => ({
|
|
49332
49377
|
...frame,
|
|
49333
|
-
...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)) } : {}
|
|
49334
49379
|
}))
|
|
49335
49380
|
} : {}
|
|
49336
49381
|
});
|
|
@@ -49393,7 +49438,7 @@ class GodotServer {
|
|
|
49393
49438
|
const launchedIsUp = !status.connected && launchedWasUp;
|
|
49394
49439
|
const arriving = status.connected ? [] : arrivingFound;
|
|
49395
49440
|
const isPortConflict = this.bridgeStartupError?.includes("EADDRINUSE") ?? false;
|
|
49396
|
-
const greeted =
|
|
49441
|
+
const greeted = status.connected;
|
|
49397
49442
|
const staleNote = this.godotBridge.anotherRelease();
|
|
49398
49443
|
const unfinished = this.restartLeftUnfinished(status.connected);
|
|
49399
49444
|
const cannotConnect = status.connected || this.ownProject === null ? null : editorAddonMissing(this.ownProject);
|
|
@@ -49401,7 +49446,7 @@ class GodotServer {
|
|
|
49401
49446
|
...status,
|
|
49402
49447
|
serverVersion: SERVER_VERSION,
|
|
49403
49448
|
addonIsStale: greeted ? staleNote !== undefined : undefined,
|
|
49404
|
-
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,
|
|
49405
49450
|
bridgeAvailable: this.bridgeStartupError === null && status.port !== null,
|
|
49406
49451
|
mayYetConnect: status.connected ? undefined : cannotConnect !== null ? false : arriving.length > 0 ? true : unfinished !== null ? unfinished.underway : this.closedHere !== null ? false : anEditorIsStillComing(status.listeningSince, launchedIsUp),
|
|
49407
49452
|
editorsNotYetConnected: arriving.length > 0 ? arriving : undefined,
|
|
@@ -49609,21 +49654,21 @@ class GodotServer {
|
|
|
49609
49654
|
return null;
|
|
49610
49655
|
}
|
|
49611
49656
|
try {
|
|
49612
|
-
return settingKeys(readFileSync29(
|
|
49657
|
+
return settingKeys(readFileSync29(join35(projectPath, "project.godot"), "utf8"));
|
|
49613
49658
|
} catch {
|
|
49614
49659
|
return null;
|
|
49615
49660
|
}
|
|
49616
49661
|
}
|
|
49617
49662
|
async handleRestartEditor(args) {
|
|
49618
49663
|
const before = this.godotBridge.getStatus();
|
|
49619
|
-
if (!before.connected) {
|
|
49664
|
+
if (!before.connected && before.identifying !== true) {
|
|
49620
49665
|
return this.createErrorResponse("No editor is connected, so there is nothing to restart.", [
|
|
49621
49666
|
"editor_launch opens one on a project",
|
|
49622
49667
|
"editor_status says whether the bridge is up and what has reached it"
|
|
49623
49668
|
]);
|
|
49624
49669
|
}
|
|
49625
|
-
if (!
|
|
49626
|
-
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"]);
|
|
49627
49672
|
}
|
|
49628
49673
|
const mine = before.openedByAServer === true && before.projectPath !== undefined;
|
|
49629
49674
|
if (!mine && args["hidden"] === true) {
|
|
@@ -49665,7 +49710,7 @@ class GodotServer {
|
|
|
49665
49710
|
}
|
|
49666
49711
|
async handleCloseEditor() {
|
|
49667
49712
|
const before = this.godotBridge.getStatus();
|
|
49668
|
-
if (!before.connected) {
|
|
49713
|
+
if (!before.connected && before.identifying !== true) {
|
|
49669
49714
|
const launched = this.launchedEditor;
|
|
49670
49715
|
if (launched !== null && await this.launchedEditorIsUp() && !this.godotBridge.isConnected()) {
|
|
49671
49716
|
return await this.stopTheLaunchedEditor(launched);
|
|
@@ -49674,8 +49719,8 @@ class GodotServer {
|
|
|
49674
49719
|
"editor_status says whether the bridge is up and what has reached it"
|
|
49675
49720
|
]);
|
|
49676
49721
|
}
|
|
49677
|
-
if (!
|
|
49678
|
-
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"]);
|
|
49679
49724
|
}
|
|
49680
49725
|
const pid = before.editorPid;
|
|
49681
49726
|
const settingsBefore = this.settingKeysOf(before.projectPath);
|
|
@@ -49731,7 +49776,7 @@ class GodotServer {
|
|
|
49731
49776
|
settleTheLaunchReading() {
|
|
49732
49777
|
const watched = this.launchedFrom;
|
|
49733
49778
|
const status = this.godotBridge.getStatus();
|
|
49734
|
-
if (watched === null || !
|
|
49779
|
+
if (watched === null || !status.connected) {
|
|
49735
49780
|
return;
|
|
49736
49781
|
}
|
|
49737
49782
|
this.launchedFrom = null;
|
|
@@ -50051,7 +50096,7 @@ class GodotServer {
|
|
|
50051
50096
|
}
|
|
50052
50097
|
async runtimeUp(projectPath, before, budget, withArgs) {
|
|
50053
50098
|
const budgetMs = budget.ms;
|
|
50054
|
-
if (!existsSync27(
|
|
50099
|
+
if (!existsSync27(join35(projectPath, RUNTIME_AUTOLOAD.path))) {
|
|
50055
50100
|
return runtimeVerdict(null, {
|
|
50056
50101
|
addon: false,
|
|
50057
50102
|
budgetMs,
|
|
@@ -51028,7 +51073,7 @@ class GodotServer {
|
|
|
51028
51073
|
};
|
|
51029
51074
|
const going = await this.runStillGoing(run);
|
|
51030
51075
|
const projectPath = run.projectPath;
|
|
51031
|
-
const projectGone = projectPath !== null && !existsSync27(
|
|
51076
|
+
const projectGone = projectPath !== null && !existsSync27(join35(projectPath, "project.godot"));
|
|
51032
51077
|
if (projectGone && callersName !== undefined) {
|
|
51033
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"]);
|
|
51034
51079
|
}
|
|
@@ -51253,10 +51298,10 @@ class GodotServer {
|
|
|
51253
51298
|
}
|
|
51254
51299
|
async handleEditorConsole(args) {
|
|
51255
51300
|
const status = this.godotBridge.getStatus();
|
|
51256
|
-
const greeted =
|
|
51301
|
+
const greeted = status.connected;
|
|
51257
51302
|
const launched = greeted ? null : this.launchedEditor?.pid ?? null;
|
|
51258
|
-
if (
|
|
51259
|
-
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"]);
|
|
51260
51305
|
}
|
|
51261
51306
|
const projectPath = status.projectPath ?? this.launchedEditor?.projectPath ?? this.ownProject ?? undefined;
|
|
51262
51307
|
const elsewhere = this.anotherProjectNamed(args, projectPath);
|
|
@@ -51375,7 +51420,7 @@ class GodotServer {
|
|
|
51375
51420
|
notes.push(endedWithoutACode({
|
|
51376
51421
|
throughEditor: run.throughEditor,
|
|
51377
51422
|
announced: announced !== undefined || run.announcedPid !== undefined,
|
|
51378
|
-
couldAnnounce: run.projectPath !== null && existsSync27(
|
|
51423
|
+
couldAnnounce: run.projectPath !== null && existsSync27(join35(run.projectPath, RUNTIME_AUTOLOAD.path)),
|
|
51379
51424
|
endedHere: typeof run.endedHere === "string",
|
|
51380
51425
|
pickedUp: run.pickedUp === true
|
|
51381
51426
|
}));
|
|
@@ -52465,7 +52510,7 @@ class GodotServer {
|
|
|
52465
52510
|
const launched = this.launchedEditor;
|
|
52466
52511
|
if (launched !== null && await this.launchedEditorIsUp()) {
|
|
52467
52512
|
const began = Date.now();
|
|
52468
|
-
await this.waitForBridge(() =>
|
|
52513
|
+
await this.waitForBridge(() => this.godotBridge.isConnected() || !alive(launched.pid), began + editorRestartTimeoutMs());
|
|
52469
52514
|
if (this.godotBridge.isConnected()) {
|
|
52470
52515
|
return null;
|
|
52471
52516
|
}
|
|
@@ -52584,7 +52629,7 @@ class GodotServer {
|
|
|
52584
52629
|
async ownRunOnItsWay(projectPath, budgetMs) {
|
|
52585
52630
|
const run = await this.currentRun();
|
|
52586
52631
|
const project = projectPath ?? run?.projectPath ?? null;
|
|
52587
|
-
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)) {
|
|
52588
52633
|
return null;
|
|
52589
52634
|
}
|
|
52590
52635
|
const already = runtimesAnnounced().running;
|
|
@@ -52678,7 +52723,7 @@ class GodotServer {
|
|
|
52678
52723
|
return this.createErrorResponse(staleNote);
|
|
52679
52724
|
}
|
|
52680
52725
|
const screenshotDir = expectsScreenshot ? scratchDirectory("runtime-screenshot") : null;
|
|
52681
|
-
const screenshotPath = screenshotDir ?
|
|
52726
|
+
const screenshotPath = screenshotDir ? join35(screenshotDir, "capture.png") : null;
|
|
52682
52727
|
const refused = (message) => this.createErrorResponse(message);
|
|
52683
52728
|
const onGame = {
|
|
52684
52729
|
pid: choice.endpoint.pid,
|
|
@@ -52981,12 +53026,12 @@ init_bridge_announce();
|
|
|
52981
53026
|
init_errors();
|
|
52982
53027
|
init_godot_path();
|
|
52983
53028
|
import { existsSync as existsSync28 } from "node:fs";
|
|
52984
|
-
import { join as
|
|
53029
|
+
import { join as join36, resolve as resolve8 } from "node:path";
|
|
52985
53030
|
|
|
52986
53031
|
// src/harnesses.ts
|
|
52987
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";
|
|
52988
53033
|
import { homedir as homedir2 } from "node:os";
|
|
52989
|
-
import { basename as
|
|
53034
|
+
import { basename as basename3, dirname as dirname4, join as join5, relative as relative2, resolve as resolve2 } from "node:path";
|
|
52990
53035
|
import process4 from "node:process";
|
|
52991
53036
|
|
|
52992
53037
|
// node_modules/yaml/dist/index.js
|
|
@@ -53160,7 +53205,7 @@ var HARNESSES = [
|
|
|
53160
53205
|
shape: "plain",
|
|
53161
53206
|
home: ".claude",
|
|
53162
53207
|
marker: ".claude",
|
|
53163
|
-
skills: { dir:
|
|
53208
|
+
skills: { dir: join5(".claude", "skills"), shared: false }
|
|
53164
53209
|
},
|
|
53165
53210
|
{
|
|
53166
53211
|
id: "copilot-cli",
|
|
@@ -53170,7 +53215,7 @@ var HARNESSES = [
|
|
|
53170
53215
|
container: "mcpServers",
|
|
53171
53216
|
shape: "plain",
|
|
53172
53217
|
home: ".copilot",
|
|
53173
|
-
skills: { dir:
|
|
53218
|
+
skills: { dir: join5(".github", "skills"), shared: true },
|
|
53174
53219
|
manual: "Trust the folder when Copilot CLI first asks, or it reads no config here."
|
|
53175
53220
|
},
|
|
53176
53221
|
{
|
|
@@ -53196,22 +53241,22 @@ var HARNESSES = [
|
|
|
53196
53241
|
id: "cursor",
|
|
53197
53242
|
name: "Cursor",
|
|
53198
53243
|
scope: "project",
|
|
53199
|
-
file:
|
|
53244
|
+
file: join5(".cursor", "mcp.json"),
|
|
53200
53245
|
container: "mcpServers",
|
|
53201
53246
|
shape: "plain",
|
|
53202
53247
|
home: ".cursor",
|
|
53203
|
-
skills: { dir:
|
|
53248
|
+
skills: { dir: join5(".cursor", "skills"), shared: true },
|
|
53204
53249
|
manual: "Cursor asks before each tool call by default, and the server can be toggled off."
|
|
53205
53250
|
},
|
|
53206
53251
|
{
|
|
53207
53252
|
id: "vscode",
|
|
53208
53253
|
name: "VS Code",
|
|
53209
53254
|
scope: "project",
|
|
53210
|
-
file:
|
|
53255
|
+
file: join5(".vscode", "mcp.json"),
|
|
53211
53256
|
container: "servers",
|
|
53212
53257
|
shape: "typed",
|
|
53213
53258
|
home: ".vscode",
|
|
53214
|
-
skills: { dir:
|
|
53259
|
+
skills: { dir: join5(".github", "skills"), shared: true },
|
|
53215
53260
|
manual: "VS Code asks you to trust a server the first time it starts one."
|
|
53216
53261
|
},
|
|
53217
53262
|
{
|
|
@@ -53221,45 +53266,45 @@ var HARNESSES = [
|
|
|
53221
53266
|
file: "opencode.json",
|
|
53222
53267
|
container: "mcp",
|
|
53223
53268
|
shape: "opencode",
|
|
53224
|
-
home:
|
|
53269
|
+
home: join5(".config", "opencode"),
|
|
53225
53270
|
marker: ".opencode",
|
|
53226
|
-
skills: { dir:
|
|
53271
|
+
skills: { dir: join5(".opencode", "skills"), shared: true }
|
|
53227
53272
|
},
|
|
53228
53273
|
{
|
|
53229
53274
|
id: "junie",
|
|
53230
53275
|
name: "Junie",
|
|
53231
53276
|
scope: "project",
|
|
53232
|
-
file:
|
|
53277
|
+
file: join5(".junie", "mcp", "mcp.json"),
|
|
53233
53278
|
container: "mcpServers",
|
|
53234
53279
|
shape: "plain",
|
|
53235
53280
|
home: ".junie",
|
|
53236
|
-
skills: { dir:
|
|
53281
|
+
skills: { dir: join5(".junie", "skills"), shared: true }
|
|
53237
53282
|
},
|
|
53238
53283
|
{
|
|
53239
53284
|
id: "kiro",
|
|
53240
53285
|
name: "Kiro",
|
|
53241
53286
|
scope: "project",
|
|
53242
|
-
file:
|
|
53287
|
+
file: join5(".kiro", "settings", "mcp.json"),
|
|
53243
53288
|
container: "mcpServers",
|
|
53244
53289
|
shape: "plain",
|
|
53245
53290
|
home: ".kiro",
|
|
53246
|
-
skills: { dir:
|
|
53291
|
+
skills: { dir: join5(".kiro", "skills"), shared: false }
|
|
53247
53292
|
},
|
|
53248
53293
|
{
|
|
53249
53294
|
id: "gemini",
|
|
53250
53295
|
name: "Gemini CLI",
|
|
53251
53296
|
scope: "project",
|
|
53252
|
-
file:
|
|
53297
|
+
file: join5(".gemini", "settings.json"),
|
|
53253
53298
|
container: "mcpServers",
|
|
53254
53299
|
shape: "plain",
|
|
53255
53300
|
home: ".gemini",
|
|
53256
|
-
skills: { dir:
|
|
53301
|
+
skills: { dir: join5(".gemini", "skills"), shared: true }
|
|
53257
53302
|
},
|
|
53258
53303
|
{
|
|
53259
53304
|
id: "roo",
|
|
53260
53305
|
name: "Roo Code",
|
|
53261
53306
|
scope: "project",
|
|
53262
|
-
file:
|
|
53307
|
+
file: join5(".roo", "mcp.json"),
|
|
53263
53308
|
container: "mcpServers",
|
|
53264
53309
|
shape: "plain"
|
|
53265
53310
|
},
|
|
@@ -53267,7 +53312,7 @@ var HARNESSES = [
|
|
|
53267
53312
|
id: "kilo",
|
|
53268
53313
|
name: "Kilo Code",
|
|
53269
53314
|
scope: "project",
|
|
53270
|
-
file:
|
|
53315
|
+
file: join5(".kilocode", "mcp.json"),
|
|
53271
53316
|
container: "mcpServers",
|
|
53272
53317
|
shape: "plain"
|
|
53273
53318
|
},
|
|
@@ -53275,16 +53320,16 @@ var HARNESSES = [
|
|
|
53275
53320
|
id: "amazon-q",
|
|
53276
53321
|
name: "Amazon Q Developer CLI",
|
|
53277
53322
|
scope: "project",
|
|
53278
|
-
file:
|
|
53323
|
+
file: join5(".amazonq", "mcp.json"),
|
|
53279
53324
|
container: "mcpServers",
|
|
53280
53325
|
shape: "plain",
|
|
53281
|
-
home:
|
|
53326
|
+
home: join5(".aws", "amazonq")
|
|
53282
53327
|
},
|
|
53283
53328
|
{
|
|
53284
53329
|
id: "zed",
|
|
53285
53330
|
name: "Zed",
|
|
53286
53331
|
scope: "project",
|
|
53287
|
-
file:
|
|
53332
|
+
file: join5(".zed", "settings.json"),
|
|
53288
53333
|
container: "context_servers",
|
|
53289
53334
|
shape: "plain"
|
|
53290
53335
|
},
|
|
@@ -53292,16 +53337,16 @@ var HARNESSES = [
|
|
|
53292
53337
|
id: "amp",
|
|
53293
53338
|
name: "Amp",
|
|
53294
53339
|
scope: "project",
|
|
53295
|
-
file:
|
|
53340
|
+
file: join5(".amp", "settings.json"),
|
|
53296
53341
|
container: "amp.mcpServers",
|
|
53297
53342
|
shape: "plain",
|
|
53298
|
-
home:
|
|
53343
|
+
home: join5(".config", "amp")
|
|
53299
53344
|
},
|
|
53300
53345
|
{
|
|
53301
53346
|
id: "warp",
|
|
53302
53347
|
name: "Warp",
|
|
53303
53348
|
scope: "project",
|
|
53304
|
-
file:
|
|
53349
|
+
file: join5(".warp", ".mcp.json"),
|
|
53305
53350
|
container: "mcpServers",
|
|
53306
53351
|
shape: "plain",
|
|
53307
53352
|
home: ".warp",
|
|
@@ -53311,7 +53356,7 @@ var HARNESSES = [
|
|
|
53311
53356
|
id: "trae",
|
|
53312
53357
|
name: "Trae",
|
|
53313
53358
|
scope: "project",
|
|
53314
|
-
file:
|
|
53359
|
+
file: join5(".trae", "mcp.json"),
|
|
53315
53360
|
container: "mcpServers",
|
|
53316
53361
|
shape: "plain",
|
|
53317
53362
|
home: ".trae"
|
|
@@ -53320,7 +53365,7 @@ var HARNESSES = [
|
|
|
53320
53365
|
id: "factory",
|
|
53321
53366
|
name: "Factory Droid",
|
|
53322
53367
|
scope: "project",
|
|
53323
|
-
file:
|
|
53368
|
+
file: join5(".factory", "mcp.json"),
|
|
53324
53369
|
container: "mcpServers",
|
|
53325
53370
|
shape: "typed",
|
|
53326
53371
|
home: ".factory"
|
|
@@ -53329,7 +53374,7 @@ var HARNESSES = [
|
|
|
53329
53374
|
id: "tabnine",
|
|
53330
53375
|
name: "Tabnine",
|
|
53331
53376
|
scope: "project",
|
|
53332
|
-
file:
|
|
53377
|
+
file: join5(".tabnine", "mcp_servers.json"),
|
|
53333
53378
|
container: "mcpServers",
|
|
53334
53379
|
shape: "plain",
|
|
53335
53380
|
home: ".tabnine"
|
|
@@ -53346,7 +53391,7 @@ var HARNESSES = [
|
|
|
53346
53391
|
id: "pi",
|
|
53347
53392
|
name: "pi",
|
|
53348
53393
|
scope: "project",
|
|
53349
|
-
file:
|
|
53394
|
+
file: join5(".pi", "mcp.json"),
|
|
53350
53395
|
container: "mcpServers",
|
|
53351
53396
|
shape: "plain",
|
|
53352
53397
|
home: ".pi"
|
|
@@ -53355,7 +53400,7 @@ var HARNESSES = [
|
|
|
53355
53400
|
id: "mistral-vibe",
|
|
53356
53401
|
name: "Mistral Vibe",
|
|
53357
53402
|
scope: "project",
|
|
53358
|
-
file:
|
|
53403
|
+
file: join5(".vibe", "config.toml"),
|
|
53359
53404
|
container: "mcp_servers",
|
|
53360
53405
|
shape: "plain",
|
|
53361
53406
|
toml: {
|
|
@@ -53411,7 +53456,7 @@ var HARNESSES = [
|
|
|
53411
53456
|
id: "codex",
|
|
53412
53457
|
name: "Codex CLI",
|
|
53413
53458
|
scope: "home",
|
|
53414
|
-
file:
|
|
53459
|
+
file: join5(".codex", "config.toml"),
|
|
53415
53460
|
container: "mcp_servers",
|
|
53416
53461
|
shape: "plain",
|
|
53417
53462
|
manual: "Codex reads its config at startup, so restart it.",
|
|
@@ -53430,20 +53475,20 @@ var HARNESSES = [
|
|
|
53430
53475
|
id: "cline",
|
|
53431
53476
|
name: "Cline",
|
|
53432
53477
|
scope: "home",
|
|
53433
|
-
file:
|
|
53478
|
+
file: join5(".cline", "data", "settings", "cline_mcp_settings.json"),
|
|
53434
53479
|
container: "mcpServers",
|
|
53435
53480
|
shape: "plain",
|
|
53436
53481
|
marker: ".cline",
|
|
53437
|
-
skills: { dir:
|
|
53482
|
+
skills: { dir: join5(".cline", "skills"), shared: false }
|
|
53438
53483
|
},
|
|
53439
53484
|
{
|
|
53440
53485
|
id: "goose",
|
|
53441
53486
|
name: "Goose",
|
|
53442
53487
|
scope: "home",
|
|
53443
53488
|
file: {
|
|
53444
|
-
win32: { appData:
|
|
53445
|
-
darwin:
|
|
53446
|
-
linux:
|
|
53489
|
+
win32: { appData: join5("Block", "goose", "config", "config.yaml") },
|
|
53490
|
+
darwin: join5(".config", "goose", "config.yaml"),
|
|
53491
|
+
linux: join5(".config", "goose", "config.yaml")
|
|
53447
53492
|
},
|
|
53448
53493
|
container: "extensions",
|
|
53449
53494
|
shape: "plain",
|
|
@@ -53464,19 +53509,19 @@ var HARNESSES = [
|
|
|
53464
53509
|
id: "windsurf",
|
|
53465
53510
|
name: "Windsurf",
|
|
53466
53511
|
scope: "home",
|
|
53467
|
-
file:
|
|
53512
|
+
file: join5(".codeium", "windsurf", "mcp_config.json"),
|
|
53468
53513
|
container: "mcpServers",
|
|
53469
53514
|
shape: "plain",
|
|
53470
|
-
skills: { dir:
|
|
53515
|
+
skills: { dir: join5(".windsurf", "skills"), shared: true }
|
|
53471
53516
|
},
|
|
53472
53517
|
{
|
|
53473
53518
|
id: "hermes",
|
|
53474
53519
|
name: "Hermes",
|
|
53475
53520
|
scope: "home",
|
|
53476
|
-
file:
|
|
53521
|
+
file: join5(".hermes", "config.yaml"),
|
|
53477
53522
|
container: "mcp_servers",
|
|
53478
53523
|
shape: "plain",
|
|
53479
|
-
skills: { dir:
|
|
53524
|
+
skills: { dir: join5(".hermes", "skills"), shared: true },
|
|
53480
53525
|
yaml: {
|
|
53481
53526
|
path: ["mcp_servers", SERVER_KEY],
|
|
53482
53527
|
entry: (launch) => ({
|
|
@@ -53491,7 +53536,7 @@ var HARNESSES = [
|
|
|
53491
53536
|
id: "openclaw",
|
|
53492
53537
|
name: "OpenClaw",
|
|
53493
53538
|
scope: "home",
|
|
53494
|
-
file:
|
|
53539
|
+
file: join5(".openclaw", "openclaw.json"),
|
|
53495
53540
|
container: "mcpServers",
|
|
53496
53541
|
shape: "plain"
|
|
53497
53542
|
},
|
|
@@ -53500,9 +53545,9 @@ var HARNESSES = [
|
|
|
53500
53545
|
name: "Claude Desktop",
|
|
53501
53546
|
scope: "home",
|
|
53502
53547
|
file: {
|
|
53503
|
-
win32: { appData:
|
|
53504
|
-
darwin:
|
|
53505
|
-
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")
|
|
53506
53551
|
},
|
|
53507
53552
|
container: "mcpServers",
|
|
53508
53553
|
shape: "plain"
|
|
@@ -53511,7 +53556,7 @@ var HARNESSES = [
|
|
|
53511
53556
|
id: "zeroclaw",
|
|
53512
53557
|
name: "ZeroClaw",
|
|
53513
53558
|
scope: "home",
|
|
53514
|
-
file:
|
|
53559
|
+
file: join5(".zeroclaw", "config.toml"),
|
|
53515
53560
|
container: "mcp.servers",
|
|
53516
53561
|
shape: "plain",
|
|
53517
53562
|
toml: {
|
|
@@ -53532,7 +53577,7 @@ var HARNESSES = [
|
|
|
53532
53577
|
id: "deepcode",
|
|
53533
53578
|
name: "Deep Code",
|
|
53534
53579
|
scope: "home",
|
|
53535
|
-
file:
|
|
53580
|
+
file: join5(".deepcode", "settings.json"),
|
|
53536
53581
|
container: "mcpServers",
|
|
53537
53582
|
shape: "plain"
|
|
53538
53583
|
},
|
|
@@ -53540,7 +53585,7 @@ var HARNESSES = [
|
|
|
53540
53585
|
id: "nanobot",
|
|
53541
53586
|
name: "nanobot",
|
|
53542
53587
|
scope: "home",
|
|
53543
|
-
file:
|
|
53588
|
+
file: join5(".nanobot", "config.json"),
|
|
53544
53589
|
container: "mcpServers",
|
|
53545
53590
|
shape: "plain",
|
|
53546
53591
|
snippet: (launch) => jsonSnippet(launch, "mcpServers", "plain")
|
|
@@ -53549,17 +53594,17 @@ var HARNESSES = [
|
|
|
53549
53594
|
id: "autohand",
|
|
53550
53595
|
name: "Autohand",
|
|
53551
53596
|
scope: "home",
|
|
53552
|
-
file:
|
|
53597
|
+
file: join5(".autohand", "config.json"),
|
|
53553
53598
|
container: "mcpServers",
|
|
53554
53599
|
shape: "plain",
|
|
53555
53600
|
snippet: (launch) => jsonSnippet(launch, "mcpServers", "plain")
|
|
53556
53601
|
}
|
|
53557
53602
|
];
|
|
53558
53603
|
function appDataRoot() {
|
|
53559
|
-
return process4.env["APPDATA"] ??
|
|
53604
|
+
return process4.env["APPDATA"] ?? join5(homedir2(), "AppData", "Roaming");
|
|
53560
53605
|
}
|
|
53561
53606
|
function homePath(path) {
|
|
53562
|
-
return typeof path === "string" ?
|
|
53607
|
+
return typeof path === "string" ? join5(homedir2(), path) : join5(appDataRoot(), path.appData);
|
|
53563
53608
|
}
|
|
53564
53609
|
function fileFor(harness, platform = process4.platform) {
|
|
53565
53610
|
if (typeof harness.file === "string") {
|
|
@@ -53570,7 +53615,7 @@ function fileFor(harness, platform = process4.platform) {
|
|
|
53570
53615
|
function configPath(harness, projectPath) {
|
|
53571
53616
|
const file = fileFor(harness);
|
|
53572
53617
|
if (harness.scope === "project") {
|
|
53573
|
-
return
|
|
53618
|
+
return join5(projectPath, file);
|
|
53574
53619
|
}
|
|
53575
53620
|
return homePath(file);
|
|
53576
53621
|
}
|
|
@@ -53629,11 +53674,11 @@ function present(harness, projectPath) {
|
|
|
53629
53674
|
return true;
|
|
53630
53675
|
}
|
|
53631
53676
|
const root = harness.scope === "project" ? projectPath : homedir2();
|
|
53632
|
-
const own =
|
|
53677
|
+
const own = dirname4(path);
|
|
53633
53678
|
if (own !== root && existsSync4(own)) {
|
|
53634
53679
|
return true;
|
|
53635
53680
|
}
|
|
53636
|
-
return harness.marker !== undefined && existsSync4(
|
|
53681
|
+
return harness.marker !== undefined && existsSync4(join5(root, harness.marker));
|
|
53637
53682
|
}
|
|
53638
53683
|
function candidates(projectPath) {
|
|
53639
53684
|
const found = [];
|
|
@@ -53646,7 +53691,7 @@ function candidates(projectPath) {
|
|
|
53646
53691
|
}
|
|
53647
53692
|
if (present(harness, projectPath)) {
|
|
53648
53693
|
found.push({ harness, reason: "configured" });
|
|
53649
|
-
} else if (harness.home !== undefined && existsSync4(
|
|
53694
|
+
} else if (harness.home !== undefined && existsSync4(join5(homedir2(), harness.home))) {
|
|
53650
53695
|
found.push({ harness, reason: "installed" });
|
|
53651
53696
|
}
|
|
53652
53697
|
}
|
|
@@ -53704,14 +53749,14 @@ function recordedEnginePath(projectPath) {
|
|
|
53704
53749
|
}
|
|
53705
53750
|
function mainCheckoutOf(projectPath) {
|
|
53706
53751
|
let top = resolve2(projectPath);
|
|
53707
|
-
while (!existsSync4(
|
|
53708
|
-
const up =
|
|
53752
|
+
while (!existsSync4(join5(top, ".git"))) {
|
|
53753
|
+
const up = dirname4(top);
|
|
53709
53754
|
if (up === top) {
|
|
53710
53755
|
return null;
|
|
53711
53756
|
}
|
|
53712
53757
|
top = up;
|
|
53713
53758
|
}
|
|
53714
|
-
const marker =
|
|
53759
|
+
const marker = join5(top, ".git");
|
|
53715
53760
|
if (!statSync3(marker).isFile()) {
|
|
53716
53761
|
return null;
|
|
53717
53762
|
}
|
|
@@ -53720,39 +53765,39 @@ function mainCheckoutOf(projectPath) {
|
|
|
53720
53765
|
return null;
|
|
53721
53766
|
}
|
|
53722
53767
|
const entry = resolve2(top, pointer);
|
|
53723
|
-
const commonFile =
|
|
53768
|
+
const commonFile = join5(entry, "commondir");
|
|
53724
53769
|
if (!existsSync4(commonFile)) {
|
|
53725
53770
|
return null;
|
|
53726
53771
|
}
|
|
53727
53772
|
const common = resolve2(entry, readFileSync2(commonFile, "utf8").trim());
|
|
53728
|
-
if (
|
|
53773
|
+
if (basename3(common) !== ".git") {
|
|
53729
53774
|
return null;
|
|
53730
53775
|
}
|
|
53731
|
-
const main =
|
|
53732
|
-
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;
|
|
53733
53778
|
}
|
|
53734
53779
|
function worktreeCheckoutsOf(projectPath) {
|
|
53735
53780
|
let top = resolve2(projectPath);
|
|
53736
|
-
while (!existsSync4(
|
|
53737
|
-
const up =
|
|
53781
|
+
while (!existsSync4(join5(top, ".git"))) {
|
|
53782
|
+
const up = dirname4(top);
|
|
53738
53783
|
if (up === top) {
|
|
53739
53784
|
return [];
|
|
53740
53785
|
}
|
|
53741
53786
|
top = up;
|
|
53742
53787
|
}
|
|
53743
|
-
const listed =
|
|
53744
|
-
if (!statSync3(
|
|
53788
|
+
const listed = join5(top, ".git", "worktrees");
|
|
53789
|
+
if (!statSync3(join5(top, ".git")).isDirectory() || !existsSync4(listed)) {
|
|
53745
53790
|
return [];
|
|
53746
53791
|
}
|
|
53747
53792
|
const below = relative2(top, resolve2(projectPath));
|
|
53748
53793
|
return readdirSync2(listed).flatMap((name) => {
|
|
53749
|
-
const pointer =
|
|
53794
|
+
const pointer = join5(listed, name, "gitdir");
|
|
53750
53795
|
if (!existsSync4(pointer)) {
|
|
53751
53796
|
return [];
|
|
53752
53797
|
}
|
|
53753
|
-
const checkout =
|
|
53754
|
-
const project =
|
|
53755
|
-
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] : [];
|
|
53756
53801
|
});
|
|
53757
53802
|
}
|
|
53758
53803
|
function entryEnvironment(harness, projectPath) {
|
|
@@ -53894,7 +53939,7 @@ function connect(harness, projectPath, launch) {
|
|
|
53894
53939
|
return was === undefined || was === now ? written : { ...written, wasLaunchedBy: was };
|
|
53895
53940
|
}
|
|
53896
53941
|
function put(harness, path, contents, already) {
|
|
53897
|
-
mkdirSync2(
|
|
53942
|
+
mkdirSync2(dirname4(path), { recursive: true });
|
|
53898
53943
|
writeFileSync2(path, contents, "utf8");
|
|
53899
53944
|
return { harness, path, action: already ? "replaced" : "written" };
|
|
53900
53945
|
}
|
|
@@ -53947,9 +53992,9 @@ init_setup();
|
|
|
53947
53992
|
init_tool_definitions();
|
|
53948
53993
|
init_tool_reference();
|
|
53949
53994
|
import { existsSync as existsSync14, mkdirSync as mkdirSync5, readdirSync as readdirSync9, rmSync as rmSync6, writeFileSync as writeFileSync7 } from "node:fs";
|
|
53950
|
-
import { basename as
|
|
53995
|
+
import { basename as basename7, dirname as dirname9, join as join16 } from "node:path";
|
|
53951
53996
|
var SKILL_NAME = "gdharness";
|
|
53952
|
-
var SHARED_SKILLS =
|
|
53997
|
+
var SHARED_SKILLS = join16(".agents", "skills");
|
|
53953
53998
|
function skillMarkdown(version) {
|
|
53954
53999
|
return `---
|
|
53955
54000
|
name: ${SKILL_NAME}
|
|
@@ -54181,27 +54226,27 @@ what they report while starting comes back under \`engine_messages_before_operat
|
|
|
54181
54226
|
function skillFiles(version) {
|
|
54182
54227
|
return new Map([
|
|
54183
54228
|
["SKILL.md", skillMarkdown(version)],
|
|
54184
|
-
[
|
|
54229
|
+
[join16("references", "tools.md"), renderToolsMarkdown()]
|
|
54185
54230
|
]);
|
|
54186
54231
|
}
|
|
54187
54232
|
function skillDirectories(harnesses, projectPath, exists) {
|
|
54188
|
-
const directories = [
|
|
54233
|
+
const directories = [join16(projectPath, SHARED_SKILLS, SKILL_NAME)];
|
|
54189
54234
|
for (const harness of harnesses) {
|
|
54190
54235
|
if (harness.skills === undefined) {
|
|
54191
54236
|
continue;
|
|
54192
54237
|
}
|
|
54193
|
-
const own =
|
|
54194
|
-
if ((!harness.skills.shared || exists(own)) && !directories.includes(
|
|
54195
|
-
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));
|
|
54196
54241
|
}
|
|
54197
54242
|
}
|
|
54198
54243
|
return directories;
|
|
54199
54244
|
}
|
|
54200
54245
|
function everySkillDirectory(projectPath, harnesses) {
|
|
54201
|
-
const directories = new Set([
|
|
54246
|
+
const directories = new Set([join16(projectPath, SHARED_SKILLS, SKILL_NAME)]);
|
|
54202
54247
|
for (const harness of harnesses) {
|
|
54203
54248
|
if (harness.skills !== undefined) {
|
|
54204
|
-
directories.add(
|
|
54249
|
+
directories.add(join16(projectPath, harness.skills.dir, SKILL_NAME));
|
|
54205
54250
|
}
|
|
54206
54251
|
}
|
|
54207
54252
|
return [...directories];
|
|
@@ -54211,21 +54256,21 @@ function removeSkill(directories, projectPath) {
|
|
|
54211
54256
|
for (const directory of directories) {
|
|
54212
54257
|
if (existsSync14(directory)) {
|
|
54213
54258
|
rmSync6(directory, { recursive: true, force: true });
|
|
54214
|
-
pruneEmpty(
|
|
54259
|
+
pruneEmpty(dirname9(directory), projectPath);
|
|
54215
54260
|
removed.push(directory);
|
|
54216
54261
|
}
|
|
54217
54262
|
}
|
|
54218
54263
|
return removed;
|
|
54219
54264
|
}
|
|
54220
54265
|
function pruneEmpty(directory, projectPath) {
|
|
54221
|
-
const shared =
|
|
54266
|
+
const shared = join16(projectPath, ".agents");
|
|
54222
54267
|
let path = directory;
|
|
54223
|
-
while (path !== projectPath && (
|
|
54268
|
+
while (path !== projectPath && (basename7(path) === "skills" || path === shared)) {
|
|
54224
54269
|
if (readdirSync9(path).length > 0) {
|
|
54225
54270
|
return;
|
|
54226
54271
|
}
|
|
54227
54272
|
rmSync6(path, { recursive: true, force: true });
|
|
54228
|
-
path =
|
|
54273
|
+
path = dirname9(path);
|
|
54229
54274
|
}
|
|
54230
54275
|
}
|
|
54231
54276
|
function writeSkill(directories, version) {
|
|
@@ -54234,8 +54279,8 @@ function writeSkill(directories, version) {
|
|
|
54234
54279
|
const replaced = existsSync14(directory);
|
|
54235
54280
|
rmSync6(directory, { recursive: true, force: true });
|
|
54236
54281
|
for (const [name, contents] of files) {
|
|
54237
|
-
const path =
|
|
54238
|
-
mkdirSync5(
|
|
54282
|
+
const path = join16(directory, name);
|
|
54283
|
+
mkdirSync5(dirname9(path), { recursive: true });
|
|
54239
54284
|
writeFileSync7(path, contents, "utf8");
|
|
54240
54285
|
}
|
|
54241
54286
|
return { path: directory, replaced };
|
|
@@ -54244,13 +54289,13 @@ function writeSkill(directories, version) {
|
|
|
54244
54289
|
|
|
54245
54290
|
// src/version.ts
|
|
54246
54291
|
import { readFileSync as readFileSync12 } from "node:fs";
|
|
54247
|
-
import { dirname as
|
|
54292
|
+
import { dirname as dirname10, join as join17 } from "node:path";
|
|
54248
54293
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
54249
54294
|
function getLocalVersion() {
|
|
54250
|
-
const currentDirectory =
|
|
54295
|
+
const currentDirectory = dirname10(fileURLToPath2(import.meta.url));
|
|
54251
54296
|
const packageCandidates = [
|
|
54252
|
-
|
|
54253
|
-
|
|
54297
|
+
join17(currentDirectory, "..", "package.json"),
|
|
54298
|
+
join17(currentDirectory, "..", "..", "package.json")
|
|
54254
54299
|
];
|
|
54255
54300
|
for (const packagePath of packageCandidates) {
|
|
54256
54301
|
try {
|
|
@@ -54272,7 +54317,7 @@ class UsageError extends Refusal {
|
|
|
54272
54317
|
function projectArgument(at) {
|
|
54273
54318
|
const named = args.slice(at).find((value) => !value.startsWith("--"));
|
|
54274
54319
|
const projectPath = resolve8(named ?? ".");
|
|
54275
|
-
if (!existsSync28(
|
|
54320
|
+
if (!existsSync28(join36(projectPath, "project.godot"))) {
|
|
54276
54321
|
throw new UsageError(`Not a Godot project: ${projectPath} holds no project.godot.`);
|
|
54277
54322
|
}
|
|
54278
54323
|
return projectPath;
|
|
@@ -54317,7 +54362,7 @@ async function registerRuntime(godot, projectPath) {
|
|
|
54317
54362
|
return;
|
|
54318
54363
|
}
|
|
54319
54364
|
console.log(`${RUNTIME_AUTOLOAD.name} autoload left at ${named}, which this project registers itself`);
|
|
54320
|
-
if (!existsSync28(
|
|
54365
|
+
if (!existsSync28(join36(projectPath, (named ?? "").replace(/^res:\/\//, "")))) {
|
|
54321
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}`);
|
|
54322
54367
|
}
|
|
54323
54368
|
}
|