gdharness 0.13.30 → 0.13.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/cli.js +138 -123
- package/build/godot/operations/class_cache.gd +1 -4
- package/build/godot/operations/dependencies.gd +30 -9
- package/build/godot/operations/gdscript_analysis.gd +333 -48
- package/build/godot/operations/gdscript_authoring.gd +22 -11
- package/build/godot/operations/patterns.gd +64 -0
- package/build/index.js +20 -18
- package/package.json +1 -1
package/build/cli.js
CHANGED
|
@@ -7489,8 +7489,8 @@ function addonMismatch(addonVersion, serverVersion) {
|
|
|
7489
7489
|
return;
|
|
7490
7490
|
}
|
|
7491
7491
|
const reported = addonVersion ?? "";
|
|
7492
|
-
const editor = reported === "" ? UNVERSIONED : reported
|
|
7493
|
-
const both = `The editor is running
|
|
7492
|
+
const editor = reported === "" ? UNVERSIONED : `the ${reported} addon`;
|
|
7493
|
+
const both = `The editor is running ${editor} while this server ships ${serverVersion}.`;
|
|
7494
7494
|
if (reported !== "" && isNewer(reported, serverVersion)) {
|
|
7495
7495
|
return `${both} This server is the older half: reconnect it in your harness so it spawns ${reported}.`;
|
|
7496
7496
|
}
|
|
@@ -7503,7 +7503,7 @@ function markIfStale(answer, addonVersion, serverVersion) {
|
|
|
7503
7503
|
}
|
|
7504
7504
|
return { ...answer, addonIsStale: true, staleNote };
|
|
7505
7505
|
}
|
|
7506
|
-
var DEBUG_MODE, GODOT_DEBUG_MODE_DEFAULT, SERVER_VERSION, UNVERSIONED = "addon from before versions were reported";
|
|
7506
|
+
var DEBUG_MODE, GODOT_DEBUG_MODE_DEFAULT, SERVER_VERSION, UNVERSIONED = "an addon from before versions were reported";
|
|
7507
7507
|
var init_server_version = __esm(() => {
|
|
7508
7508
|
init_update_check();
|
|
7509
7509
|
DEBUG_MODE = process.env["DEBUG"] === "true";
|
|
@@ -7535,7 +7535,7 @@ function declaredClasses(projectPath) {
|
|
|
7535
7535
|
if (entry.isDirectory()) {
|
|
7536
7536
|
visit(path, `${prefix}${entry.name}/`);
|
|
7537
7537
|
} else if (entry.isFile() && entry.name.endsWith(".gd")) {
|
|
7538
|
-
const found =
|
|
7538
|
+
const found = DECLARATION.exec(readFileSync5(path, "utf8"));
|
|
7539
7539
|
if (found?.[1]) {
|
|
7540
7540
|
declared.set(found[1], `res://${prefix}${entry.name}`);
|
|
7541
7541
|
}
|
|
@@ -7656,9 +7656,12 @@ function heldButGone(projectPath, editorHolds) {
|
|
|
7656
7656
|
}
|
|
7657
7657
|
function unseenByEditor(projectPath, editorHolds) {
|
|
7658
7658
|
const held = new Set(editorHolds);
|
|
7659
|
-
return [...declaredClasses(projectPath)].filter(([name]) => !held.has(name)).map(([className, path]) => ({ className, path }));
|
|
7659
|
+
return [...declaredClasses(projectPath)].filter(([name]) => !held.has(name)).map(([className, path]) => ({ className, path })).sort((one, other) => one.className < other.className ? -1 : one.className > other.className ? 1 : 0);
|
|
7660
7660
|
}
|
|
7661
|
-
var
|
|
7661
|
+
var DECLARATION;
|
|
7662
|
+
var init_class_cache = __esm(() => {
|
|
7663
|
+
DECLARATION = /^(?:@[a-z_]+(?:\([^)\n]*\))?\s+)*class_name\s+([A-Za-z_][A-Za-z0-9_]*)/m;
|
|
7664
|
+
});
|
|
7662
7665
|
|
|
7663
7666
|
// src/headless.ts
|
|
7664
7667
|
import { execFile as execFile3 } from "node:child_process";
|
|
@@ -15249,6 +15252,30 @@ function readParams(params, key) {
|
|
|
15249
15252
|
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : undefined;
|
|
15250
15253
|
}
|
|
15251
15254
|
|
|
15255
|
+
// src/setup.ts
|
|
15256
|
+
import { cpSync as cpSync2, existsSync as existsSync8, mkdirSync as mkdirSync4, readFileSync as readFileSync8, rmSync as rmSync4, writeFileSync as writeFileSync6 } from "node:fs";
|
|
15257
|
+
import { dirname as dirname4, join as join9 } from "node:path";
|
|
15258
|
+
function installedAddonVersion(projectPath) {
|
|
15259
|
+
const marker = join9(projectPath, "addons", ADDONS2[0], VERSION_MARKER2);
|
|
15260
|
+
if (!existsSync8(marker)) {
|
|
15261
|
+
return null;
|
|
15262
|
+
}
|
|
15263
|
+
const said = readFileSync8(marker, "utf8").trim();
|
|
15264
|
+
return said === "" ? null : said;
|
|
15265
|
+
}
|
|
15266
|
+
var ADDONS2, RUNTIME_AUTOLOAD2, SCRIPT_RUNS_SETTING = "gdharness/runtime/serve_script_runs", BIND_ADDRESS_SETTING = "gdharness/runtime/bind_address", PORT_SETTING = "gdharness/runtime/port", VERSION_MARKER2 = ".gdharness-version";
|
|
15267
|
+
var init_setup = __esm(() => {
|
|
15268
|
+
init_class_cache();
|
|
15269
|
+
init_headless();
|
|
15270
|
+
init_resources();
|
|
15271
|
+
init_server_version();
|
|
15272
|
+
ADDONS2 = ["gdharness_editor", "gdharness_runtime", "auto_reload"];
|
|
15273
|
+
RUNTIME_AUTOLOAD2 = {
|
|
15274
|
+
name: "GdharnessRuntime",
|
|
15275
|
+
path: "addons/gdharness_runtime/runtime_autoload.gd"
|
|
15276
|
+
};
|
|
15277
|
+
});
|
|
15278
|
+
|
|
15252
15279
|
// src/tool-definitions.ts
|
|
15253
15280
|
function opTakes(spec, op, name) {
|
|
15254
15281
|
const ops = spec.parameters[name]?.ops;
|
|
@@ -15566,7 +15593,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
15566
15593
|
direction: {
|
|
15567
15594
|
type: "string",
|
|
15568
15595
|
enum: ["forward", "reverse"],
|
|
15569
|
-
description: "forward: what this resource loads, with cycles reported. reverse: every file that refers to it and how, a scene instancing it, a script extending, preloading or loading it, and for a script with a class_name every use of that name. Default forward."
|
|
15596
|
+
description: "forward: what this resource loads, with cycles reported. reverse: every file that refers to it and how, a scene instancing it, a script extending, preloading or loading it, and for a script with a class_name every use of that name. Each reference carries a kind, and two of them are mentions rather than uses: doc is a name inside a ## documentation comment, which renaming would break in the generated docs, and comment is one in ordinary prose, which it would only make stale. summary.in_code is the count with both of those left out, which is the number to read when the question is whether anything still uses this. Default forward."
|
|
15570
15597
|
},
|
|
15571
15598
|
depth: { type: "number", description: "forward: how many levels to follow. Default unlimited." },
|
|
15572
15599
|
includeBuiltin: {
|
|
@@ -15836,7 +15863,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
15836
15863
|
scriptPath: SCRIPT_PATH,
|
|
15837
15864
|
includeInherited: {
|
|
15838
15865
|
type: "boolean",
|
|
15839
|
-
description: "structure:
|
|
15866
|
+
description: "structure: also list what the script inherits, walking extends through the project's other scripts. Each such member carries inherited_from, the file declaring it, and the answer gains inherits_from, the chain that was walked. A name the script overrides is listed at both its lines rather than once. A native base is not walked, since it declares nothing in a file: editor_classes info answers for those. Default false."
|
|
15840
15867
|
},
|
|
15841
15868
|
line: { type: "number", description: "completion, hover: zero-based line." },
|
|
15842
15869
|
character: { type: "number", description: "completion, hover: zero-based column." }
|
|
@@ -15934,7 +15961,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
15934
15961
|
runtimeWaitMs: {
|
|
15935
15962
|
type: "number",
|
|
15936
15963
|
ops: ["start"],
|
|
15937
|
-
description: "start: how long to wait for the game to announce its runtime before answering. Default 5000.
|
|
15964
|
+
description: "start: how long to wait for the game to announce its runtime before answering. Default 5000. What it waits for is the first frame, so everything the game does before drawing one is inside it, including work the args just asked for: a flag that simulates six years of game time before anything is drawn makes the announcement that late, on a project that announces promptly without it. Raise it for such a run rather than reading runtime listening false as a fault. 0 does not wait at all, which is the one to pass for a scene that announces nothing by construction, such as a bench that prints and quits."
|
|
15938
15965
|
},
|
|
15939
15966
|
frames: {
|
|
15940
15967
|
type: "number",
|
|
@@ -31118,14 +31145,14 @@ var init_stdio2 = __esm(() => {
|
|
|
31118
31145
|
});
|
|
31119
31146
|
|
|
31120
31147
|
// src/bridge-announce.ts
|
|
31121
|
-
import { mkdirSync as
|
|
31122
|
-
import { dirname as
|
|
31148
|
+
import { mkdirSync as mkdirSync6, readFileSync as readFileSync10, rmSync as rmSync6, writeFileSync as writeFileSync8 } from "node:fs";
|
|
31149
|
+
import { dirname as dirname7, join as join12 } from "node:path";
|
|
31123
31150
|
function announcementPath(projectPath) {
|
|
31124
|
-
return
|
|
31151
|
+
return join12(projectPath, ".godot", "gdharness-bridge.json");
|
|
31125
31152
|
}
|
|
31126
31153
|
function readAnnouncement(path) {
|
|
31127
31154
|
try {
|
|
31128
|
-
const parsed = JSON.parse(
|
|
31155
|
+
const parsed = JSON.parse(readFileSync10(path, "utf8"));
|
|
31129
31156
|
if (typeof parsed !== "object" || parsed === null) {
|
|
31130
31157
|
return null;
|
|
31131
31158
|
}
|
|
@@ -31144,7 +31171,7 @@ function announcementIsOurs(path) {
|
|
|
31144
31171
|
function announceBridge(projectPath, bridge) {
|
|
31145
31172
|
const path = announcementPath(projectPath);
|
|
31146
31173
|
try {
|
|
31147
|
-
|
|
31174
|
+
mkdirSync6(dirname7(path), { recursive: true });
|
|
31148
31175
|
const announcement = {
|
|
31149
31176
|
protocol: BRIDGE_ANNOUNCE_PROTOCOL,
|
|
31150
31177
|
host: bridge.host,
|
|
@@ -31153,7 +31180,7 @@ function announceBridge(projectPath, bridge) {
|
|
|
31153
31180
|
version: bridge.version,
|
|
31154
31181
|
startedAt: new Date().toISOString()
|
|
31155
31182
|
};
|
|
31156
|
-
|
|
31183
|
+
writeFileSync8(path, `${JSON.stringify(announcement, null, 2)}
|
|
31157
31184
|
`, "utf8");
|
|
31158
31185
|
return path;
|
|
31159
31186
|
} catch (error) {
|
|
@@ -31166,7 +31193,7 @@ function withdrawBridge(path) {
|
|
|
31166
31193
|
return;
|
|
31167
31194
|
}
|
|
31168
31195
|
try {
|
|
31169
|
-
|
|
31196
|
+
rmSync6(path, { force: true });
|
|
31170
31197
|
} catch (error) {
|
|
31171
31198
|
console.error(`[SERVER] Could not withdraw the editor bridge announcement: ${errorMessage(error)}`);
|
|
31172
31199
|
}
|
|
@@ -31177,16 +31204,16 @@ var init_bridge_announce = __esm(() => {
|
|
|
31177
31204
|
});
|
|
31178
31205
|
|
|
31179
31206
|
// src/config-pin.ts
|
|
31180
|
-
import { existsSync as
|
|
31181
|
-
import { join as
|
|
31207
|
+
import { existsSync as existsSync10, readFileSync as readFileSync11 } from "node:fs";
|
|
31208
|
+
import { join as join13 } from "node:path";
|
|
31182
31209
|
function versionsNamedInConfig(projectPath) {
|
|
31183
|
-
const config =
|
|
31184
|
-
if (!
|
|
31210
|
+
const config = join13(projectPath, ".mcp.json");
|
|
31211
|
+
if (!existsSync10(config)) {
|
|
31185
31212
|
return [];
|
|
31186
31213
|
}
|
|
31187
31214
|
let text;
|
|
31188
31215
|
try {
|
|
31189
|
-
text =
|
|
31216
|
+
text = readFileSync11(config, "utf8");
|
|
31190
31217
|
JSON.parse(text);
|
|
31191
31218
|
} catch {
|
|
31192
31219
|
return [];
|
|
@@ -34727,6 +34754,9 @@ function servedPort(said) {
|
|
|
34727
34754
|
function mayYetConnect(listeningSince, now = Date.now()) {
|
|
34728
34755
|
return listeningSince === undefined || now - listeningSince.getTime() < CONNECT_WINDOW_MS;
|
|
34729
34756
|
}
|
|
34757
|
+
function theEditorHasComeBack(status, startedAt) {
|
|
34758
|
+
return status.connected && (status.connectedAt?.getTime() ?? 0) > startedAt && status.addonVersion !== undefined;
|
|
34759
|
+
}
|
|
34730
34760
|
function anEditorIsStillComing(listeningSince, launchedEditorIsAlive, now = Date.now()) {
|
|
34731
34761
|
return mayYetConnect(listeningSince, now) || launchedEditorIsAlive;
|
|
34732
34762
|
}
|
|
@@ -35192,7 +35222,7 @@ var init_godot_bridge = __esm(() => {
|
|
|
35192
35222
|
|
|
35193
35223
|
// src/godot-path.ts
|
|
35194
35224
|
import { execFile as execFile4 } from "node:child_process";
|
|
35195
|
-
import { existsSync as
|
|
35225
|
+
import { existsSync as existsSync11 } from "node:fs";
|
|
35196
35226
|
import { normalize as normalize2 } from "node:path";
|
|
35197
35227
|
import { promisify as promisify4 } from "node:util";
|
|
35198
35228
|
var run4, GodotLocator2;
|
|
@@ -35234,7 +35264,7 @@ var init_godot_path = __esm(() => {
|
|
|
35234
35264
|
return known;
|
|
35235
35265
|
}
|
|
35236
35266
|
let ok = false;
|
|
35237
|
-
if (path === "godot" ||
|
|
35267
|
+
if (path === "godot" || existsSync11(path)) {
|
|
35238
35268
|
try {
|
|
35239
35269
|
await run4(path, ["--version"]);
|
|
35240
35270
|
ok = true;
|
|
@@ -35582,7 +35612,7 @@ var init_junit = __esm(() => {
|
|
|
35582
35612
|
import { realpathSync as realpathSync2 } from "node:fs";
|
|
35583
35613
|
import { readFile, realpath } from "node:fs/promises";
|
|
35584
35614
|
import { createConnection as createConnection2 } from "node:net";
|
|
35585
|
-
import { dirname as
|
|
35615
|
+
import { dirname as dirname8, resolve as resolve3 } from "node:path";
|
|
35586
35616
|
import { fileURLToPath as fileURLToPath3, pathToFileURL } from "node:url";
|
|
35587
35617
|
function diagnosticsKey(uri) {
|
|
35588
35618
|
let decoded;
|
|
@@ -35710,7 +35740,7 @@ class GodotLSPClient {
|
|
|
35710
35740
|
if (this.initialized) {
|
|
35711
35741
|
return;
|
|
35712
35742
|
}
|
|
35713
|
-
const rootPath = this.rootPath ??
|
|
35743
|
+
const rootPath = this.rootPath ?? dirname8(resolve3(filePath));
|
|
35714
35744
|
await this.initialize(rootPath);
|
|
35715
35745
|
}
|
|
35716
35746
|
async sendRequest(method, params) {
|
|
@@ -36155,13 +36185,13 @@ var init_process_time = __esm(() => {
|
|
|
36155
36185
|
});
|
|
36156
36186
|
|
|
36157
36187
|
// src/project-scan.ts
|
|
36158
|
-
import { existsSync as
|
|
36159
|
-
import { join as
|
|
36188
|
+
import { existsSync as existsSync12, readdirSync as readdirSync4, readFileSync as readFileSync12 } from "node:fs";
|
|
36189
|
+
import { join as join14 } from "node:path";
|
|
36160
36190
|
function skipped(name) {
|
|
36161
36191
|
return name.startsWith(".") || SKIPPED.has(name);
|
|
36162
36192
|
}
|
|
36163
36193
|
function steppedOver(directory) {
|
|
36164
|
-
return
|
|
36194
|
+
return existsSync12(join14(directory, ".gdignore"));
|
|
36165
36195
|
}
|
|
36166
36196
|
function projectStructure(projectPath) {
|
|
36167
36197
|
const structure = { scenes: 0, scripts: 0, assets: 0, other: 0 };
|
|
@@ -36174,7 +36204,7 @@ function projectStructure(projectPath) {
|
|
|
36174
36204
|
continue;
|
|
36175
36205
|
}
|
|
36176
36206
|
if (entry.isDirectory()) {
|
|
36177
|
-
visit(
|
|
36207
|
+
visit(join14(directory, entry.name));
|
|
36178
36208
|
} else if (entry.isFile()) {
|
|
36179
36209
|
const extension = entry.name.split(".").pop()?.toLowerCase() ?? "";
|
|
36180
36210
|
if (extension === "tscn") {
|
|
@@ -36204,14 +36234,14 @@ function scriptsWithoutUid(projectPath) {
|
|
|
36204
36234
|
}
|
|
36205
36235
|
const spelled = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
|
|
36206
36236
|
if (entry.isDirectory()) {
|
|
36207
|
-
visit(
|
|
36237
|
+
visit(join14(directory, entry.name), spelled);
|
|
36208
36238
|
continue;
|
|
36209
36239
|
}
|
|
36210
36240
|
if (!entry.isFile()) {
|
|
36211
36241
|
continue;
|
|
36212
36242
|
}
|
|
36213
36243
|
const extension = entry.name.split(".").pop()?.toLowerCase() ?? "";
|
|
36214
|
-
if (CARRIES_A_UID.has(extension) && !
|
|
36244
|
+
if (CARRIES_A_UID.has(extension) && !existsSync12(join14(directory, `${entry.name}.uid`))) {
|
|
36215
36245
|
found.push(spelled);
|
|
36216
36246
|
}
|
|
36217
36247
|
}
|
|
@@ -36246,7 +36276,7 @@ function searchProject(projectPath, options) {
|
|
|
36246
36276
|
if (skipped(entry.name)) {
|
|
36247
36277
|
continue;
|
|
36248
36278
|
}
|
|
36249
|
-
const entryPath =
|
|
36279
|
+
const entryPath = join14(directory, entry.name);
|
|
36250
36280
|
if (entry.isDirectory()) {
|
|
36251
36281
|
visit(entryPath);
|
|
36252
36282
|
continue;
|
|
@@ -36257,7 +36287,7 @@ function searchProject(projectPath, options) {
|
|
|
36257
36287
|
}
|
|
36258
36288
|
result.summary.files_searched += 1;
|
|
36259
36289
|
const matches = [];
|
|
36260
|
-
for (const [index, line] of
|
|
36290
|
+
for (const [index, line] of readFileSync12(entryPath, "utf8").split(`
|
|
36261
36291
|
`).entries()) {
|
|
36262
36292
|
if (full()) {
|
|
36263
36293
|
break;
|
|
@@ -36286,10 +36316,10 @@ var init_project_scan = __esm(() => {
|
|
|
36286
36316
|
});
|
|
36287
36317
|
|
|
36288
36318
|
// src/runtime-client.ts
|
|
36289
|
-
import { existsSync as
|
|
36319
|
+
import { existsSync as existsSync13, readdirSync as readdirSync5, readFileSync as readFileSync13, unlinkSync } from "node:fs";
|
|
36290
36320
|
import { createConnection as createConnection3 } from "node:net";
|
|
36291
36321
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
36292
|
-
import { join as
|
|
36322
|
+
import { join as join15, resolve as resolve4 } from "node:path";
|
|
36293
36323
|
import { setTimeout as delay2 } from "node:timers/promises";
|
|
36294
36324
|
function runtimeDirectory(variables = process.env) {
|
|
36295
36325
|
const explicit = envValue("GDHARNESS_RUNTIME_DIR", variables);
|
|
@@ -36297,13 +36327,13 @@ function runtimeDirectory(variables = process.env) {
|
|
|
36297
36327
|
return explicit;
|
|
36298
36328
|
}
|
|
36299
36329
|
const perUser = envValue("XDG_RUNTIME_DIR", variables);
|
|
36300
|
-
return
|
|
36330
|
+
return join15(perUser ?? tmpdir4(), "gdharness");
|
|
36301
36331
|
}
|
|
36302
36332
|
function runtimeDirectories(variables = process.env) {
|
|
36303
36333
|
const candidates = [runtimeDirectory(variables)];
|
|
36304
36334
|
const fallbacks = process.platform === "win32" ? [envValue("SystemRoot", variables) ?? envValue("windir", variables) ?? "C:\\Windows"] : ["/tmp", "/var/tmp"];
|
|
36305
36335
|
for (const base of fallbacks) {
|
|
36306
|
-
candidates.push(
|
|
36336
|
+
candidates.push(join15(base, process.platform === "win32" ? "Temp" : "", "gdharness"));
|
|
36307
36337
|
}
|
|
36308
36338
|
return [...new Set(candidates.map((path) => resolve4(path)))];
|
|
36309
36339
|
}
|
|
@@ -36318,7 +36348,7 @@ function processAlive(pid) {
|
|
|
36318
36348
|
function parseAnnouncement(file, pid) {
|
|
36319
36349
|
let fields;
|
|
36320
36350
|
try {
|
|
36321
|
-
fields = asParams(JSON.parse(
|
|
36351
|
+
fields = asParams(JSON.parse(readFileSync13(file, "utf8")));
|
|
36322
36352
|
} catch {
|
|
36323
36353
|
return { kind: "rubbish" };
|
|
36324
36354
|
}
|
|
@@ -36357,7 +36387,7 @@ function discoverRuntimes(directories = runtimeDirectories()) {
|
|
|
36357
36387
|
return runtimesAnnounced(directories).running;
|
|
36358
36388
|
}
|
|
36359
36389
|
function announcedIn(directory) {
|
|
36360
|
-
if (!
|
|
36390
|
+
if (!existsSync13(directory)) {
|
|
36361
36391
|
return [];
|
|
36362
36392
|
}
|
|
36363
36393
|
const found = [];
|
|
@@ -36366,7 +36396,7 @@ function announcedIn(directory) {
|
|
|
36366
36396
|
if (!match) {
|
|
36367
36397
|
continue;
|
|
36368
36398
|
}
|
|
36369
|
-
const file =
|
|
36399
|
+
const file = join15(directory, entry);
|
|
36370
36400
|
const pid = Number.parseInt(match[1] ?? "", 10);
|
|
36371
36401
|
const announced = processAlive(pid) ? parseAnnouncement(file, pid) : { kind: "rubbish" };
|
|
36372
36402
|
if (announced.kind === "rubbish") {
|
|
@@ -36528,36 +36558,36 @@ var init_runtime_client = __esm(() => {
|
|
|
36528
36558
|
|
|
36529
36559
|
// src/run-record.ts
|
|
36530
36560
|
import { execFileSync } from "node:child_process";
|
|
36531
|
-
import { mkdirSync as
|
|
36532
|
-
import { basename as basename2, join as
|
|
36561
|
+
import { mkdirSync as mkdirSync7, openSync, readdirSync as readdirSync6, readFileSync as readFileSync14, rmSync as rmSync7, statSync as statSync3, writeFileSync as writeFileSync9 } from "node:fs";
|
|
36562
|
+
import { basename as basename2, join as join16 } from "node:path";
|
|
36533
36563
|
function runsDirectory() {
|
|
36534
|
-
return
|
|
36564
|
+
return join16(runtimeDirectory(), "runs");
|
|
36535
36565
|
}
|
|
36536
36566
|
function recordPath() {
|
|
36537
|
-
return
|
|
36567
|
+
return join16(runsDirectory(), "run.json");
|
|
36538
36568
|
}
|
|
36539
36569
|
function recordPaths() {
|
|
36540
|
-
return runtimeDirectories().map((directory) =>
|
|
36570
|
+
return runtimeDirectories().map((directory) => join16(directory, "runs", "run.json"));
|
|
36541
36571
|
}
|
|
36542
36572
|
function openTranscript(startedAt) {
|
|
36543
|
-
|
|
36544
|
-
const path =
|
|
36573
|
+
mkdirSync7(runsDirectory(), { recursive: true });
|
|
36574
|
+
const path = join16(runsDirectory(), `run-${startedAt}.log`);
|
|
36545
36575
|
return { path, fd: openSync(path, "a") };
|
|
36546
36576
|
}
|
|
36547
36577
|
function writeRunRecord(record) {
|
|
36548
|
-
|
|
36549
|
-
|
|
36578
|
+
mkdirSync7(runsDirectory(), { recursive: true });
|
|
36579
|
+
writeFileSync9(recordPath(), JSON.stringify(record, null, 2), "utf8");
|
|
36550
36580
|
}
|
|
36551
36581
|
function editorNotePath() {
|
|
36552
|
-
return
|
|
36582
|
+
return join16(runsDirectory(), "editor-run.json");
|
|
36553
36583
|
}
|
|
36554
36584
|
function writeEditorRunNote(note) {
|
|
36555
|
-
|
|
36556
|
-
|
|
36585
|
+
mkdirSync7(runsDirectory(), { recursive: true });
|
|
36586
|
+
writeFileSync9(editorNotePath(), JSON.stringify(note, null, 2), "utf8");
|
|
36557
36587
|
}
|
|
36558
36588
|
function readEditorRunNote() {
|
|
36559
36589
|
for (const directory of runtimeDirectories()) {
|
|
36560
|
-
const note = editorNoteAt(
|
|
36590
|
+
const note = editorNoteAt(join16(directory, "runs", "editor-run.json"));
|
|
36561
36591
|
if (note !== null) {
|
|
36562
36592
|
return note;
|
|
36563
36593
|
}
|
|
@@ -36567,7 +36597,7 @@ function readEditorRunNote() {
|
|
|
36567
36597
|
function editorNoteAt(path) {
|
|
36568
36598
|
let parsed;
|
|
36569
36599
|
try {
|
|
36570
|
-
parsed = JSON.parse(
|
|
36600
|
+
parsed = JSON.parse(readFileSync14(path, "utf8"));
|
|
36571
36601
|
} catch {
|
|
36572
36602
|
return null;
|
|
36573
36603
|
}
|
|
@@ -36590,7 +36620,7 @@ function recordRunEnded(pid, exitCode) {
|
|
|
36590
36620
|
return;
|
|
36591
36621
|
}
|
|
36592
36622
|
try {
|
|
36593
|
-
|
|
36623
|
+
writeFileSync9(path, JSON.stringify({ ...record, exitCode }, null, 2), "utf8");
|
|
36594
36624
|
} catch {}
|
|
36595
36625
|
}
|
|
36596
36626
|
function readRunRecord() {
|
|
@@ -36605,7 +36635,7 @@ function readRunRecord() {
|
|
|
36605
36635
|
function recordAt(path) {
|
|
36606
36636
|
let parsed;
|
|
36607
36637
|
try {
|
|
36608
|
-
parsed = JSON.parse(
|
|
36638
|
+
parsed = JSON.parse(readFileSync14(path, "utf8"));
|
|
36609
36639
|
} catch {
|
|
36610
36640
|
return null;
|
|
36611
36641
|
}
|
|
@@ -36641,7 +36671,7 @@ function runningAs(pid) {
|
|
|
36641
36671
|
}
|
|
36642
36672
|
try {
|
|
36643
36673
|
if (process.platform === "linux") {
|
|
36644
|
-
const raw =
|
|
36674
|
+
const raw = readFileSync14(`/proc/${pid}/cmdline`, "utf8").replaceAll("\x00", " ").trim();
|
|
36645
36675
|
return raw === "" ? null : { kind: "commandLine", text: raw };
|
|
36646
36676
|
}
|
|
36647
36677
|
const args = execFileSync("ps", ["-p", String(pid), "-o", "args="], {
|
|
@@ -36738,7 +36768,7 @@ function clearRunRecord(ours = () => true) {
|
|
|
36738
36768
|
continue;
|
|
36739
36769
|
}
|
|
36740
36770
|
try {
|
|
36741
|
-
|
|
36771
|
+
rmSync7(path);
|
|
36742
36772
|
} catch {}
|
|
36743
36773
|
}
|
|
36744
36774
|
}
|
|
@@ -36762,12 +36792,12 @@ function sweepTranscripts(keepMs = 24 * 60 * 60 * 1000, now = Date.now()) {
|
|
|
36762
36792
|
if (!entry.startsWith("run-") || !entry.endsWith(".log")) {
|
|
36763
36793
|
continue;
|
|
36764
36794
|
}
|
|
36765
|
-
const path =
|
|
36795
|
+
const path = join16(directory, entry);
|
|
36766
36796
|
if (path === keep || ageMs(path, now) < keepMs) {
|
|
36767
36797
|
continue;
|
|
36768
36798
|
}
|
|
36769
36799
|
try {
|
|
36770
|
-
|
|
36800
|
+
rmSync7(path);
|
|
36771
36801
|
} catch {}
|
|
36772
36802
|
}
|
|
36773
36803
|
}
|
|
@@ -36775,30 +36805,6 @@ var init_run_record = __esm(() => {
|
|
|
36775
36805
|
init_runtime_client();
|
|
36776
36806
|
});
|
|
36777
36807
|
|
|
36778
|
-
// src/setup.ts
|
|
36779
|
-
import { cpSync as cpSync2, existsSync as existsSync13, mkdirSync as mkdirSync7, readFileSync as readFileSync14, rmSync as rmSync7, writeFileSync as writeFileSync9 } from "node:fs";
|
|
36780
|
-
import { dirname as dirname8, join as join16 } from "node:path";
|
|
36781
|
-
function installedAddonVersion(projectPath) {
|
|
36782
|
-
const marker = join16(projectPath, "addons", ADDONS2[0], VERSION_MARKER2);
|
|
36783
|
-
if (!existsSync13(marker)) {
|
|
36784
|
-
return null;
|
|
36785
|
-
}
|
|
36786
|
-
const said = readFileSync14(marker, "utf8").trim();
|
|
36787
|
-
return said === "" ? null : said;
|
|
36788
|
-
}
|
|
36789
|
-
var ADDONS2, RUNTIME_AUTOLOAD2, VERSION_MARKER2 = ".gdharness-version";
|
|
36790
|
-
var init_setup = __esm(() => {
|
|
36791
|
-
init_class_cache();
|
|
36792
|
-
init_headless();
|
|
36793
|
-
init_resources();
|
|
36794
|
-
init_server_version();
|
|
36795
|
-
ADDONS2 = ["gdharness_editor", "gdharness_runtime", "auto_reload"];
|
|
36796
|
-
RUNTIME_AUTOLOAD2 = {
|
|
36797
|
-
name: "GdharnessRuntime",
|
|
36798
|
-
path: "addons/gdharness_runtime/runtime_autoload.gd"
|
|
36799
|
-
};
|
|
36800
|
-
});
|
|
36801
|
-
|
|
36802
36808
|
// src/server.ts
|
|
36803
36809
|
import { execFile as execFile6, spawn } from "node:child_process";
|
|
36804
36810
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
@@ -36859,7 +36865,7 @@ function runtimeVerdict(endpoint, after) {
|
|
|
36859
36865
|
return {
|
|
36860
36866
|
listening: false,
|
|
36861
36867
|
mayYetAnnounce: after.running,
|
|
36862
|
-
note: after.running ? `nothing announced itself within ${after.budgetMs}ms and the game is still running, so it may announce a moment from now: editor_status says whether it has, and editor_run start takes runtimeWaitMs to wait longer than this` : `nothing announced itself within ${after.budgetMs}ms and the game is no longer running, so nothing is going to: editor_output has what it printed on the way down`,
|
|
36868
|
+
note: after.running ? `nothing announced itself within ${after.budgetMs}ms and the game is still running, so it may announce a moment from now: editor_status says whether it has, and editor_run start takes runtimeWaitMs to wait longer than this${after.withArgs ? ", which a run carrying its own arguments may well need, since whatever they ask the game to do before its first frame is inside this wait" : ""}` : `nothing announced itself within ${after.budgetMs}ms and the game is no longer running, so nothing is going to: editor_output has what it printed on the way down`,
|
|
36863
36869
|
heldAt: null
|
|
36864
36870
|
};
|
|
36865
36871
|
}
|
|
@@ -38354,10 +38360,7 @@ class GodotServer {
|
|
|
38354
38360
|
}
|
|
38355
38361
|
const startedAt = before.connectedAt?.getTime() ?? 0;
|
|
38356
38362
|
const began = Date.now();
|
|
38357
|
-
const back = await this.waitForBridge(() =>
|
|
38358
|
-
const status = this.godotBridge.getStatus();
|
|
38359
|
-
return status.connected && (status.connectedAt?.getTime() ?? 0) > startedAt;
|
|
38360
|
-
}, began + EDITOR_RESTART_TIMEOUT_MS);
|
|
38363
|
+
const back = await this.waitForBridge(() => theEditorHasComeBack(this.godotBridge.getStatus(), startedAt), began + EDITOR_RESTART_TIMEOUT_MS);
|
|
38361
38364
|
if (!back) {
|
|
38362
38365
|
return this.createErrorResponse(`The editor was asked to restart and has not come back within ${EDITOR_RESTART_TIMEOUT_MS / 1000}s.`, [
|
|
38363
38366
|
"It may be asking what to do about an unsaved scene: look at the editor window",
|
|
@@ -38552,7 +38555,7 @@ class GodotServer {
|
|
|
38552
38555
|
transcript: started.transcript,
|
|
38553
38556
|
refreshedClasses: refreshed.value,
|
|
38554
38557
|
endedPreviousRun: ended ?? undefined,
|
|
38555
|
-
runtime: await this.runtimeUp(project.value.path, alreadyPlaying, runtimeWaitMs),
|
|
38558
|
+
runtime: await this.runtimeUp(project.value.path, alreadyPlaying, runtimeWaitMs, given.value.length > 0),
|
|
38556
38559
|
message: `Use editor_output for what it prints and editor_run stop to end it.${spawnedInstead}${endedForThis}`
|
|
38557
38560
|
});
|
|
38558
38561
|
}
|
|
@@ -38561,9 +38564,9 @@ class GodotServer {
|
|
|
38561
38564
|
const editorSays = run?.throughEditor === true ? (await this.editorPlayingState())?.playing ?? null : null;
|
|
38562
38565
|
return runIsUp(run, editorSays);
|
|
38563
38566
|
}
|
|
38564
|
-
async runtimeUp(projectPath, before, budgetMs) {
|
|
38567
|
+
async runtimeUp(projectPath, before, budgetMs, withArgs) {
|
|
38565
38568
|
if (!existsSync14(join17(projectPath, RUNTIME_AUTOLOAD2.path))) {
|
|
38566
|
-
return runtimeVerdict(null, { addon: false, budgetMs, heldAt: null, running: false });
|
|
38569
|
+
return runtimeVerdict(null, { addon: false, budgetMs, heldAt: null, running: false, withArgs });
|
|
38567
38570
|
}
|
|
38568
38571
|
const endpoint = await announcedSince(projectPath, before, {
|
|
38569
38572
|
budgetMs,
|
|
@@ -38573,7 +38576,8 @@ class GodotServer {
|
|
|
38573
38576
|
addon: true,
|
|
38574
38577
|
budgetMs,
|
|
38575
38578
|
heldAt: this.dapClient?.whereItStopped() ?? null,
|
|
38576
|
-
running: await this.gameIsUp()
|
|
38579
|
+
running: await this.gameIsUp(),
|
|
38580
|
+
withArgs
|
|
38577
38581
|
});
|
|
38578
38582
|
}
|
|
38579
38583
|
async playThroughEditor(scene, refreshedClasses, projectPath, alreadyPlaying, runtimeWaitMs, ended) {
|
|
@@ -38624,7 +38628,7 @@ class GodotServer {
|
|
|
38624
38628
|
refreshedClasses,
|
|
38625
38629
|
debugPort: readNumber(asParams(JSON.parse(answer.content[0]?.text ?? "{}")), "debugPort"),
|
|
38626
38630
|
endedPreviousRun: ended ?? undefined,
|
|
38627
|
-
runtime: await this.runtimeUp(projectPath, alreadyPlaying, runtimeWaitMs),
|
|
38631
|
+
runtime: await this.runtimeUp(projectPath, alreadyPlaying, runtimeWaitMs, false),
|
|
38628
38632
|
message: "The editor is playing it, so its debugger holds it: the debug_* tools can reach it, " + "editor_output reads its console through the debug adapter, and editor_run stop ends it." + (ended === null ? "" : ` The run that was going, pid ${ended}, was ended to start this one; its output is no longer what editor_output answers about.`)
|
|
38629
38633
|
});
|
|
38630
38634
|
}
|
|
@@ -40641,9 +40645,10 @@ function inspectProject(projectPath) {
|
|
|
40641
40645
|
}
|
|
40642
40646
|
|
|
40643
40647
|
// src/skill.ts
|
|
40648
|
+
init_setup();
|
|
40644
40649
|
init_tool_definitions();
|
|
40645
|
-
import { existsSync as
|
|
40646
|
-
import { basename, dirname as
|
|
40650
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync5, readdirSync as readdirSync3, rmSync as rmSync5, writeFileSync as writeFileSync7 } from "node:fs";
|
|
40651
|
+
import { basename, dirname as dirname5, join as join10 } from "node:path";
|
|
40647
40652
|
|
|
40648
40653
|
// src/tool-reference.ts
|
|
40649
40654
|
init_headless_operations();
|
|
@@ -40738,7 +40743,7 @@ function renderToolsMarkdown() {
|
|
|
40738
40743
|
|
|
40739
40744
|
// src/skill.ts
|
|
40740
40745
|
var SKILL_NAME = "gdharness";
|
|
40741
|
-
var SHARED_SKILLS =
|
|
40746
|
+
var SHARED_SKILLS = join10(".agents", "skills");
|
|
40742
40747
|
function skillMarkdown(version) {
|
|
40743
40748
|
return `---
|
|
40744
40749
|
name: ${SKILL_NAME}
|
|
@@ -40802,8 +40807,18 @@ that is running, and the project on disk. ${TOOL_SPECS.length} tools, named \`do
|
|
|
40802
40807
|
These need the runtime autoload, which \`gdharness setup\` registers. \`runtime_capture\` needs a
|
|
40803
40808
|
window and refuses headless rather than handing back the last frame anything drew.
|
|
40804
40809
|
|
|
40805
|
-
|
|
40806
|
-
|
|
40810
|
+
The autoload reads these settings and no others. \`${BIND_ADDRESS_SETTING}\`
|
|
40811
|
+
is \`127.0.0.1\` and should stay there: the command set includes \`call_method\`, \`set_property\`
|
|
40812
|
+
and input injection, none of it authenticated, and Godot's own \`listen()\` defaults to every
|
|
40813
|
+
interface rather than to loopback, so this is named on purpose rather than left out.
|
|
40814
|
+
\`${PORT_SETTING}\` is 0, meaning the operating system picks one and the game announces it, which is
|
|
40815
|
+
what the server reads; fix it only for a client that cannot read the announcement.
|
|
40816
|
+
|
|
40817
|
+
A headless game answers; a \`godot -s\` script run does not, by default. It has no game in it, so the
|
|
40818
|
+
autoload stays quiet there rather than announcing a test tier as the project: a gate starting sixteen
|
|
40819
|
+
engines would otherwise announce sixteen games that are not games. Set \`${SCRIPT_RUNS_SETTING}\`
|
|
40820
|
+
true in \`project.godot\` to serve one anyway, which is for driving a \`-s\` script on purpose. Read
|
|
40821
|
+
that setting rather than assuming the quiet: it is a default and not a property of script runs.
|
|
40807
40822
|
|
|
40808
40823
|
The autoload reaches an export. \`gdharness runtime off\` in the project before shipping.
|
|
40809
40824
|
|
|
@@ -40867,27 +40882,27 @@ whatever the engine said on stderr comes back under \`engine_messages\`.
|
|
|
40867
40882
|
function skillFiles(version) {
|
|
40868
40883
|
return new Map([
|
|
40869
40884
|
["SKILL.md", skillMarkdown(version)],
|
|
40870
|
-
[
|
|
40885
|
+
[join10("references", "tools.md"), renderToolsMarkdown()]
|
|
40871
40886
|
]);
|
|
40872
40887
|
}
|
|
40873
40888
|
function skillDirectories(harnesses, projectPath, exists) {
|
|
40874
|
-
const directories = [
|
|
40889
|
+
const directories = [join10(projectPath, SHARED_SKILLS, SKILL_NAME)];
|
|
40875
40890
|
for (const harness of harnesses) {
|
|
40876
40891
|
if (harness.skills === undefined) {
|
|
40877
40892
|
continue;
|
|
40878
40893
|
}
|
|
40879
|
-
const own =
|
|
40880
|
-
if ((!harness.skills.shared || exists(own)) && !directories.includes(
|
|
40881
|
-
directories.push(
|
|
40894
|
+
const own = join10(projectPath, harness.skills.dir);
|
|
40895
|
+
if ((!harness.skills.shared || exists(own)) && !directories.includes(join10(own, SKILL_NAME))) {
|
|
40896
|
+
directories.push(join10(own, SKILL_NAME));
|
|
40882
40897
|
}
|
|
40883
40898
|
}
|
|
40884
40899
|
return directories;
|
|
40885
40900
|
}
|
|
40886
40901
|
function everySkillDirectory(projectPath, harnesses) {
|
|
40887
|
-
const directories = new Set([
|
|
40902
|
+
const directories = new Set([join10(projectPath, SHARED_SKILLS, SKILL_NAME)]);
|
|
40888
40903
|
for (const harness of harnesses) {
|
|
40889
40904
|
if (harness.skills !== undefined) {
|
|
40890
|
-
directories.add(
|
|
40905
|
+
directories.add(join10(projectPath, harness.skills.dir, SKILL_NAME));
|
|
40891
40906
|
}
|
|
40892
40907
|
}
|
|
40893
40908
|
return [...directories];
|
|
@@ -40895,52 +40910,52 @@ function everySkillDirectory(projectPath, harnesses) {
|
|
|
40895
40910
|
function removeSkill(directories, projectPath) {
|
|
40896
40911
|
const removed = [];
|
|
40897
40912
|
for (const directory of directories) {
|
|
40898
|
-
if (
|
|
40899
|
-
|
|
40900
|
-
pruneEmpty(
|
|
40913
|
+
if (existsSync9(directory)) {
|
|
40914
|
+
rmSync5(directory, { recursive: true, force: true });
|
|
40915
|
+
pruneEmpty(dirname5(directory), projectPath);
|
|
40901
40916
|
removed.push(directory);
|
|
40902
40917
|
}
|
|
40903
40918
|
}
|
|
40904
40919
|
return removed;
|
|
40905
40920
|
}
|
|
40906
40921
|
function pruneEmpty(directory, projectPath) {
|
|
40907
|
-
const shared =
|
|
40922
|
+
const shared = join10(projectPath, ".agents");
|
|
40908
40923
|
let path = directory;
|
|
40909
40924
|
while (path !== projectPath && (basename(path) === "skills" || path === shared)) {
|
|
40910
40925
|
if (readdirSync3(path).length > 0) {
|
|
40911
40926
|
return;
|
|
40912
40927
|
}
|
|
40913
|
-
|
|
40914
|
-
path =
|
|
40928
|
+
rmSync5(path, { recursive: true, force: true });
|
|
40929
|
+
path = dirname5(path);
|
|
40915
40930
|
}
|
|
40916
40931
|
}
|
|
40917
40932
|
function writeSkill(directories, version) {
|
|
40918
40933
|
const files = skillFiles(version);
|
|
40919
40934
|
return directories.map((directory) => {
|
|
40920
|
-
const replaced =
|
|
40921
|
-
|
|
40935
|
+
const replaced = existsSync9(directory);
|
|
40936
|
+
rmSync5(directory, { recursive: true, force: true });
|
|
40922
40937
|
for (const [name, contents] of files) {
|
|
40923
|
-
const path =
|
|
40924
|
-
|
|
40925
|
-
|
|
40938
|
+
const path = join10(directory, name);
|
|
40939
|
+
mkdirSync5(dirname5(path), { recursive: true });
|
|
40940
|
+
writeFileSync7(path, contents, "utf8");
|
|
40926
40941
|
}
|
|
40927
40942
|
return { path: directory, replaced };
|
|
40928
40943
|
});
|
|
40929
40944
|
}
|
|
40930
40945
|
|
|
40931
40946
|
// src/version.ts
|
|
40932
|
-
import { readFileSync as
|
|
40933
|
-
import { dirname as
|
|
40947
|
+
import { readFileSync as readFileSync9 } from "node:fs";
|
|
40948
|
+
import { dirname as dirname6, join as join11 } from "node:path";
|
|
40934
40949
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
40935
40950
|
function getLocalVersion() {
|
|
40936
|
-
const currentDirectory =
|
|
40951
|
+
const currentDirectory = dirname6(fileURLToPath2(import.meta.url));
|
|
40937
40952
|
const packageCandidates = [
|
|
40938
|
-
|
|
40939
|
-
|
|
40953
|
+
join11(currentDirectory, "..", "package.json"),
|
|
40954
|
+
join11(currentDirectory, "..", "..", "package.json")
|
|
40940
40955
|
];
|
|
40941
40956
|
for (const packagePath of packageCandidates) {
|
|
40942
40957
|
try {
|
|
40943
|
-
const value = JSON.parse(
|
|
40958
|
+
const value = JSON.parse(readFileSync9(packagePath, "utf-8"));
|
|
40944
40959
|
if (typeof value === "object" && value !== null && "name" in value && value.name === "gdharness" && "version" in value && typeof value.version === "string") {
|
|
40945
40960
|
return value.version;
|
|
40946
40961
|
}
|