gdharness 0.13.12 → 0.13.13
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
CHANGED
|
@@ -7621,6 +7621,36 @@ function reason2(stdout, stderr) {
|
|
|
7621
7621
|
}
|
|
7622
7622
|
return stdout.trim().split(/\r?\n/).at(-1) ?? "no output at all";
|
|
7623
7623
|
}
|
|
7624
|
+
async function runImport(godotPath, projectPath) {
|
|
7625
|
+
const logDir = mkdtempSync2(join7(tmpdir3(), "gdharness-import-"));
|
|
7626
|
+
try {
|
|
7627
|
+
const { stderr } = await run3(godotPath, [
|
|
7628
|
+
"--headless",
|
|
7629
|
+
"--log-file",
|
|
7630
|
+
join7(logDir, "engine.log"),
|
|
7631
|
+
"--path",
|
|
7632
|
+
projectPath,
|
|
7633
|
+
"--import"
|
|
7634
|
+
]);
|
|
7635
|
+
return { ok: true, messages: problems2(stderr) };
|
|
7636
|
+
} catch (error) {
|
|
7637
|
+
if (error instanceof Error && "stdout" in error && "stderr" in error) {
|
|
7638
|
+
const failed = error;
|
|
7639
|
+
return {
|
|
7640
|
+
ok: false,
|
|
7641
|
+
message: `the import pass failed (exit ${failed.code ?? "unknown"}): ${reason2(failed.stdout, failed.stderr)}`,
|
|
7642
|
+
messages: problems2(failed.stderr)
|
|
7643
|
+
};
|
|
7644
|
+
}
|
|
7645
|
+
return {
|
|
7646
|
+
ok: false,
|
|
7647
|
+
message: `the import pass could not be run: ${error instanceof Error ? error.message : String(error)}`,
|
|
7648
|
+
messages: []
|
|
7649
|
+
};
|
|
7650
|
+
} finally {
|
|
7651
|
+
discard(logDir);
|
|
7652
|
+
}
|
|
7653
|
+
}
|
|
7624
7654
|
async function runOperation2(engine, operation, params, projectPath) {
|
|
7625
7655
|
const paramsDir = mkdtempSync2(join7(tmpdir3(), "gdharness-params-"));
|
|
7626
7656
|
const paramsFile = join7(paramsDir, `${operation}.json`);
|
|
@@ -15475,7 +15505,10 @@ var init_tool_definitions = __esm(() => {
|
|
|
15475
15505
|
requires: []
|
|
15476
15506
|
},
|
|
15477
15507
|
uid: { summary: "the UID of one file", requires: ["resourcePath"] },
|
|
15478
|
-
refresh_uids: {
|
|
15508
|
+
refresh_uids: {
|
|
15509
|
+
summary: "import the project so every script and shader has its .uid sidecar, and name under uidsCreated the ones this made and under stillWithoutUid the ones the engine would not import: it writes no scene and no script, so a project whose sidecars are all present is left untouched",
|
|
15510
|
+
requires: []
|
|
15511
|
+
},
|
|
15479
15512
|
refresh_classes: {
|
|
15480
15513
|
summary: 'rewrite .godot/global_script_class_cache.cfg from the class_name declarations on disk, and name under unseenByEditor any class the editor open on this project still cannot resolve: rewriting the file does not reach the list a running editor loaded, so "added: []" means the file was already right rather than that nothing is wrong',
|
|
15481
15514
|
requires: []
|
|
@@ -16223,7 +16256,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
16223
16256
|
});
|
|
16224
16257
|
|
|
16225
16258
|
// src/headless-operations.ts
|
|
16226
|
-
var HEADLESS_OPERATIONS, EDITOR_READS;
|
|
16259
|
+
var HEADLESS_OPERATIONS, ENGINE_PASSES, EDITOR_READS;
|
|
16227
16260
|
var init_headless_operations = __esm(() => {
|
|
16228
16261
|
HEADLESS_OPERATIONS = dictionary({
|
|
16229
16262
|
project_settings: dictionary({
|
|
@@ -16245,7 +16278,6 @@ var init_headless_operations = __esm(() => {
|
|
|
16245
16278
|
set_options: "set_import_options",
|
|
16246
16279
|
reimport: "reimport_resource",
|
|
16247
16280
|
uid: "get_uid",
|
|
16248
|
-
refresh_uids: "resave_resources",
|
|
16249
16281
|
refresh_classes: "refresh_class_cache"
|
|
16250
16282
|
}),
|
|
16251
16283
|
project_export: dictionary({ list: "list_export_presets" }),
|
|
@@ -16257,6 +16289,9 @@ var init_headless_operations = __esm(() => {
|
|
|
16257
16289
|
inheritance: "inspect_inheritance"
|
|
16258
16290
|
})
|
|
16259
16291
|
});
|
|
16292
|
+
ENGINE_PASSES = dictionary({
|
|
16293
|
+
project_import: dictionary({ refresh_uids: "import" })
|
|
16294
|
+
});
|
|
16260
16295
|
EDITOR_READS = dictionary({
|
|
16261
16296
|
project_settings: dictionary({ get: "get_project_setting" })
|
|
16262
16297
|
});
|
|
@@ -36049,6 +36084,33 @@ function projectStructure(projectPath) {
|
|
|
36049
36084
|
visit(projectPath);
|
|
36050
36085
|
return structure;
|
|
36051
36086
|
}
|
|
36087
|
+
function scriptsWithoutUid(projectPath) {
|
|
36088
|
+
const found = [];
|
|
36089
|
+
const visit = (directory, prefix) => {
|
|
36090
|
+
if (steppedOver(directory)) {
|
|
36091
|
+
return;
|
|
36092
|
+
}
|
|
36093
|
+
for (const entry of readdirSync4(directory, { withFileTypes: true })) {
|
|
36094
|
+
if (skipped(entry.name)) {
|
|
36095
|
+
continue;
|
|
36096
|
+
}
|
|
36097
|
+
const spelled = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
|
|
36098
|
+
if (entry.isDirectory()) {
|
|
36099
|
+
visit(join13(directory, entry.name), spelled);
|
|
36100
|
+
continue;
|
|
36101
|
+
}
|
|
36102
|
+
if (!entry.isFile()) {
|
|
36103
|
+
continue;
|
|
36104
|
+
}
|
|
36105
|
+
const extension = entry.name.split(".").pop()?.toLowerCase() ?? "";
|
|
36106
|
+
if (CARRIES_A_UID.has(extension) && !existsSync11(join13(directory, `${entry.name}.uid`))) {
|
|
36107
|
+
found.push(spelled);
|
|
36108
|
+
}
|
|
36109
|
+
}
|
|
36110
|
+
};
|
|
36111
|
+
visit(projectPath, "");
|
|
36112
|
+
return found.sort();
|
|
36113
|
+
}
|
|
36052
36114
|
function searchProject(projectPath, options) {
|
|
36053
36115
|
const extensions = new Set(options.fileTypes.map((ext) => ext.replace(/^\./, "").toLowerCase()).filter(Boolean));
|
|
36054
36116
|
const result = {
|
|
@@ -36108,10 +36170,11 @@ function searchProject(projectPath, options) {
|
|
|
36108
36170
|
visit(projectPath);
|
|
36109
36171
|
return result;
|
|
36110
36172
|
}
|
|
36111
|
-
var SKIPPED, ASSET_EXTENSIONS;
|
|
36173
|
+
var SKIPPED, ASSET_EXTENSIONS, CARRIES_A_UID;
|
|
36112
36174
|
var init_project_scan = __esm(() => {
|
|
36113
36175
|
SKIPPED = new Set(["node_modules"]);
|
|
36114
36176
|
ASSET_EXTENSIONS = new Set(["png", "jpg", "jpeg", "webp", "svg", "ttf", "otf", "wav", "mp3", "ogg"]);
|
|
36177
|
+
CARRIES_A_UID = new Set(["gd", "shader", "gdshader"]);
|
|
36115
36178
|
});
|
|
36116
36179
|
|
|
36117
36180
|
// src/runtime-client.ts
|
|
@@ -37218,6 +37281,9 @@ class GodotServer {
|
|
|
37218
37281
|
return { ok: true, op };
|
|
37219
37282
|
}
|
|
37220
37283
|
async dispatch(tool, op, args) {
|
|
37284
|
+
if (ENGINE_PASSES[tool]?.[op] !== undefined) {
|
|
37285
|
+
return await this.handleRefreshUids(args);
|
|
37286
|
+
}
|
|
37221
37287
|
const headless = HEADLESS_OPERATIONS[tool]?.[op];
|
|
37222
37288
|
if (headless !== undefined) {
|
|
37223
37289
|
const { from: asked, ...answerable } = args;
|
|
@@ -37588,6 +37654,33 @@ class GodotServer {
|
|
|
37588
37654
|
const { op: _op, projectPath: _projectPath, ...params } = contained.value;
|
|
37589
37655
|
return this.answer(await this.operation(operation, params, project.value.path));
|
|
37590
37656
|
}
|
|
37657
|
+
async handleRefreshUids(args) {
|
|
37658
|
+
const project = this.project(args);
|
|
37659
|
+
if (!project.ok) {
|
|
37660
|
+
return project.response;
|
|
37661
|
+
}
|
|
37662
|
+
const engine = await this.engine();
|
|
37663
|
+
if (!engine.ok) {
|
|
37664
|
+
return this.createErrorResponse("No Godot executable found.");
|
|
37665
|
+
}
|
|
37666
|
+
const projectPath = project.value.path;
|
|
37667
|
+
const before = scriptsWithoutUid(projectPath);
|
|
37668
|
+
const imported = await runImport(engine.value, projectPath);
|
|
37669
|
+
if (!imported.ok) {
|
|
37670
|
+
return this.answer(imported);
|
|
37671
|
+
}
|
|
37672
|
+
const after = scriptsWithoutUid(projectPath);
|
|
37673
|
+
const given = before.filter((script) => !after.includes(script));
|
|
37674
|
+
return this.answer({
|
|
37675
|
+
ok: true,
|
|
37676
|
+
messages: imported.messages,
|
|
37677
|
+
payload: {
|
|
37678
|
+
uidsCreated: given,
|
|
37679
|
+
stillWithoutUid: after,
|
|
37680
|
+
note: after.length > 0 ? `${after.length} still have no .uid, which is the engine declining to import them rather than this op skipping them. No scene was written.` : "No scene was written: this reads the project and imports it, and does not resave anything."
|
|
37681
|
+
}
|
|
37682
|
+
});
|
|
37683
|
+
}
|
|
37591
37684
|
async operation(operation, params, projectPath) {
|
|
37592
37685
|
const engine = await this.engine();
|
|
37593
37686
|
if (!engine.ok) {
|
|
@@ -40347,7 +40440,13 @@ function namedType(declared) {
|
|
|
40347
40440
|
return "any";
|
|
40348
40441
|
}
|
|
40349
40442
|
function headlessSection() {
|
|
40350
|
-
const
|
|
40443
|
+
const everyOp = new Map;
|
|
40444
|
+
for (const table of [HEADLESS_OPERATIONS, ENGINE_PASSES]) {
|
|
40445
|
+
for (const [tool, ops] of Object.entries(table)) {
|
|
40446
|
+
everyOp.set(tool, [...everyOp.get(tool) ?? [], ...Object.keys(ops)]);
|
|
40447
|
+
}
|
|
40448
|
+
}
|
|
40449
|
+
const byTool = [...everyOp].map(([tool, ops]) => `\`${tool}\` (${ops.join(", ")})`).sort();
|
|
40351
40450
|
return [
|
|
40352
40451
|
"## What a gate can call",
|
|
40353
40452
|
"",
|
|
@@ -108,8 +108,6 @@ func _run(operation: String, params: Dictionary) -> Dictionary:
|
|
|
108
108
|
# Resource files
|
|
109
109
|
"get_uid":
|
|
110
110
|
payload = ResourceFiles.new(_log).get_uid(params)
|
|
111
|
-
"resave_resources":
|
|
112
|
-
payload = ResourceFiles.new(_log).resave_resources(params)
|
|
113
111
|
"refresh_class_cache":
|
|
114
112
|
payload = ClassCache.new(_log).refresh_class_cache(params)
|
|
115
113
|
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
extends RefCounted
|
|
2
2
|
|
|
3
|
-
const FileWalk = preload("file_walk.gd")
|
|
4
3
|
const Log = preload("logger.gd")
|
|
5
4
|
|
|
6
5
|
var _log: Log
|
|
7
|
-
var _files: FileWalk = FileWalk.new()
|
|
8
6
|
|
|
9
7
|
|
|
10
8
|
func _init(p_log: Log) -> void:
|
|
@@ -41,92 +39,3 @@ func get_uid(params: Dictionary) -> Dictionary:
|
|
|
41
39
|
return {
|
|
42
40
|
"file": file_path, "absolute_path": absolute_path, "uid": uid_content.strip_edges(), "exists": true
|
|
43
41
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
# Resave every scene, script and shader so that UID references are current.
|
|
47
|
-
func resave_resources(_params: Dictionary) -> Dictionary:
|
|
48
|
-
_log.info("Resaving all resources to update UID references...")
|
|
49
|
-
var project_path: String = "res://"
|
|
50
|
-
|
|
51
|
-
var scenes: Array[String] = _files.find_files(project_path, ".tscn")
|
|
52
|
-
_log.debug("Found " + str(scenes.size()) + " scenes")
|
|
53
|
-
|
|
54
|
-
# Resave each scene
|
|
55
|
-
var success_count: int = 0
|
|
56
|
-
var error_count: int = 0
|
|
57
|
-
|
|
58
|
-
for scene_path: String in scenes:
|
|
59
|
-
_log.debug("Processing scene: " + scene_path)
|
|
60
|
-
|
|
61
|
-
if not FileAccess.file_exists(scene_path):
|
|
62
|
-
_log.error("Scene file does not exist at: " + scene_path)
|
|
63
|
-
error_count += 1
|
|
64
|
-
continue
|
|
65
|
-
|
|
66
|
-
var scene: Resource = load(scene_path)
|
|
67
|
-
if not scene:
|
|
68
|
-
error_count += 1
|
|
69
|
-
_log.error("Failed to load: " + scene_path)
|
|
70
|
-
continue
|
|
71
|
-
|
|
72
|
-
var error: Error = ResourceSaver.save(scene, scene_path)
|
|
73
|
-
if error != OK:
|
|
74
|
-
error_count += 1
|
|
75
|
-
_log.error("Failed to save: " + scene_path + ", error: " + str(error))
|
|
76
|
-
continue
|
|
77
|
-
|
|
78
|
-
success_count += 1
|
|
79
|
-
if _log.debug_mode:
|
|
80
|
-
_log.debug("Scene saved successfully: " + scene_path)
|
|
81
|
-
var file_check_after: bool = FileAccess.file_exists(scene_path)
|
|
82
|
-
_log.debug("File exists check after save: " + str(file_check_after))
|
|
83
|
-
if not file_check_after:
|
|
84
|
-
_log.error("File reported as saved but does not exist at: " + scene_path)
|
|
85
|
-
|
|
86
|
-
# A UID sits beside every script and shader as a .uid file. Outside the editor the save
|
|
87
|
-
# below answers OK and writes no sidecar, so what comes back is what was resaved and how
|
|
88
|
-
# many are still without one, rather than a count of UIDs this made.
|
|
89
|
-
var scripts: Array[String] = (
|
|
90
|
-
_files.find_files(project_path, ".gd")
|
|
91
|
-
+ _files.find_files(project_path, ".shader")
|
|
92
|
-
+ _files.find_files(project_path, ".gdshader")
|
|
93
|
-
)
|
|
94
|
-
_log.debug("Found " + str(scripts.size()) + " scripts/shaders")
|
|
95
|
-
|
|
96
|
-
var missing_uids: int = 0
|
|
97
|
-
var resaved_scripts: int = 0
|
|
98
|
-
|
|
99
|
-
for script_path: String in scripts:
|
|
100
|
-
_log.debug("Checking UID for: " + script_path)
|
|
101
|
-
var uid_path: String = script_path + ".uid"
|
|
102
|
-
|
|
103
|
-
var f: FileAccess = FileAccess.open(uid_path, FileAccess.READ)
|
|
104
|
-
if f:
|
|
105
|
-
_log.debug("UID file already exists for: " + script_path)
|
|
106
|
-
continue
|
|
107
|
-
|
|
108
|
-
missing_uids += 1
|
|
109
|
-
_log.debug("Missing UID file for: " + script_path + ", resaving...")
|
|
110
|
-
|
|
111
|
-
var res: Resource = load(script_path)
|
|
112
|
-
if not res:
|
|
113
|
-
_log.error("Failed to load resource: " + script_path)
|
|
114
|
-
continue
|
|
115
|
-
|
|
116
|
-
var error: Error = ResourceSaver.save(res, script_path)
|
|
117
|
-
if error != OK:
|
|
118
|
-
_log.error("Failed to resave: " + script_path + ", error: " + str(error))
|
|
119
|
-
continue
|
|
120
|
-
|
|
121
|
-
resaved_scripts += 1
|
|
122
|
-
_log.debug("Resaved: " + script_path)
|
|
123
|
-
|
|
124
|
-
_log.info("Resave operation complete")
|
|
125
|
-
|
|
126
|
-
return {
|
|
127
|
-
"scenes_processed": scenes.size(),
|
|
128
|
-
"scenes_saved": success_count,
|
|
129
|
-
"scenes_with_errors": error_count,
|
|
130
|
-
"scripts_missing_uids": missing_uids,
|
|
131
|
-
"scripts_resaved": resaved_scripts
|
|
132
|
-
}
|
package/build/index.js
CHANGED
|
@@ -26390,6 +26390,36 @@ function reason(stdout, stderr) {
|
|
|
26390
26390
|
}
|
|
26391
26391
|
return stdout.trim().split(/\r?\n/).at(-1) ?? "no output at all";
|
|
26392
26392
|
}
|
|
26393
|
+
async function runImport(godotPath, projectPath) {
|
|
26394
|
+
const logDir = mkdtempSync(join6(tmpdir2(), "gdharness-import-"));
|
|
26395
|
+
try {
|
|
26396
|
+
const { stderr } = await run2(godotPath, [
|
|
26397
|
+
"--headless",
|
|
26398
|
+
"--log-file",
|
|
26399
|
+
join6(logDir, "engine.log"),
|
|
26400
|
+
"--path",
|
|
26401
|
+
projectPath,
|
|
26402
|
+
"--import"
|
|
26403
|
+
]);
|
|
26404
|
+
return { ok: true, messages: problems(stderr) };
|
|
26405
|
+
} catch (error) {
|
|
26406
|
+
if (error instanceof Error && "stdout" in error && "stderr" in error) {
|
|
26407
|
+
const failed = error;
|
|
26408
|
+
return {
|
|
26409
|
+
ok: false,
|
|
26410
|
+
message: `the import pass failed (exit ${failed.code ?? "unknown"}): ${reason(failed.stdout, failed.stderr)}`,
|
|
26411
|
+
messages: problems(failed.stderr)
|
|
26412
|
+
};
|
|
26413
|
+
}
|
|
26414
|
+
return {
|
|
26415
|
+
ok: false,
|
|
26416
|
+
message: `the import pass could not be run: ${error instanceof Error ? error.message : String(error)}`,
|
|
26417
|
+
messages: []
|
|
26418
|
+
};
|
|
26419
|
+
} finally {
|
|
26420
|
+
discard(logDir);
|
|
26421
|
+
}
|
|
26422
|
+
}
|
|
26393
26423
|
async function runOperation(engine, operation, params, projectPath) {
|
|
26394
26424
|
const paramsDir = mkdtempSync(join6(tmpdir2(), "gdharness-params-"));
|
|
26395
26425
|
const paramsFile = join6(paramsDir, `${operation}.json`);
|
|
@@ -26459,7 +26489,6 @@ var HEADLESS_OPERATIONS = dictionary({
|
|
|
26459
26489
|
set_options: "set_import_options",
|
|
26460
26490
|
reimport: "reimport_resource",
|
|
26461
26491
|
uid: "get_uid",
|
|
26462
|
-
refresh_uids: "resave_resources",
|
|
26463
26492
|
refresh_classes: "refresh_class_cache"
|
|
26464
26493
|
}),
|
|
26465
26494
|
project_export: dictionary({ list: "list_export_presets" }),
|
|
@@ -26471,6 +26500,9 @@ var HEADLESS_OPERATIONS = dictionary({
|
|
|
26471
26500
|
inheritance: "inspect_inheritance"
|
|
26472
26501
|
})
|
|
26473
26502
|
});
|
|
26503
|
+
var ENGINE_PASSES = dictionary({
|
|
26504
|
+
project_import: dictionary({ refresh_uids: "import" })
|
|
26505
|
+
});
|
|
26474
26506
|
var EDITOR_READS = dictionary({
|
|
26475
26507
|
project_settings: dictionary({ get: "get_project_setting" })
|
|
26476
26508
|
});
|
|
@@ -27408,6 +27440,34 @@ function projectStructure(projectPath) {
|
|
|
27408
27440
|
visit(projectPath);
|
|
27409
27441
|
return structure;
|
|
27410
27442
|
}
|
|
27443
|
+
var CARRIES_A_UID = new Set(["gd", "shader", "gdshader"]);
|
|
27444
|
+
function scriptsWithoutUid(projectPath) {
|
|
27445
|
+
const found = [];
|
|
27446
|
+
const visit = (directory, prefix) => {
|
|
27447
|
+
if (steppedOver(directory)) {
|
|
27448
|
+
return;
|
|
27449
|
+
}
|
|
27450
|
+
for (const entry of readdirSync3(directory, { withFileTypes: true })) {
|
|
27451
|
+
if (skipped(entry.name)) {
|
|
27452
|
+
continue;
|
|
27453
|
+
}
|
|
27454
|
+
const spelled = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
|
|
27455
|
+
if (entry.isDirectory()) {
|
|
27456
|
+
visit(join7(directory, entry.name), spelled);
|
|
27457
|
+
continue;
|
|
27458
|
+
}
|
|
27459
|
+
if (!entry.isFile()) {
|
|
27460
|
+
continue;
|
|
27461
|
+
}
|
|
27462
|
+
const extension = entry.name.split(".").pop()?.toLowerCase() ?? "";
|
|
27463
|
+
if (CARRIES_A_UID.has(extension) && !existsSync6(join7(directory, `${entry.name}.uid`))) {
|
|
27464
|
+
found.push(spelled);
|
|
27465
|
+
}
|
|
27466
|
+
}
|
|
27467
|
+
};
|
|
27468
|
+
visit(projectPath, "");
|
|
27469
|
+
return found.sort();
|
|
27470
|
+
}
|
|
27411
27471
|
function searchProject(projectPath, options) {
|
|
27412
27472
|
const extensions = new Set(options.fileTypes.map((ext) => ext.replace(/^\./, "").toLowerCase()).filter(Boolean));
|
|
27413
27473
|
const result = {
|
|
@@ -28562,7 +28622,10 @@ var TOOL_SPECS = [
|
|
|
28562
28622
|
requires: []
|
|
28563
28623
|
},
|
|
28564
28624
|
uid: { summary: "the UID of one file", requires: ["resourcePath"] },
|
|
28565
|
-
refresh_uids: {
|
|
28625
|
+
refresh_uids: {
|
|
28626
|
+
summary: "import the project so every script and shader has its .uid sidecar, and name under uidsCreated the ones this made and under stillWithoutUid the ones the engine would not import: it writes no scene and no script, so a project whose sidecars are all present is left untouched",
|
|
28627
|
+
requires: []
|
|
28628
|
+
},
|
|
28566
28629
|
refresh_classes: {
|
|
28567
28630
|
summary: 'rewrite .godot/global_script_class_cache.cfg from the class_name declarations on disk, and name under unseenByEditor any class the editor open on this project still cannot resolve: rewriting the file does not reach the list a running editor loaded, so "added: []" means the file was already right rather than that nothing is wrong',
|
|
28568
28631
|
requires: []
|
|
@@ -29970,6 +30033,9 @@ class GodotServer {
|
|
|
29970
30033
|
return { ok: true, op };
|
|
29971
30034
|
}
|
|
29972
30035
|
async dispatch(tool, op, args) {
|
|
30036
|
+
if (ENGINE_PASSES[tool]?.[op] !== undefined) {
|
|
30037
|
+
return await this.handleRefreshUids(args);
|
|
30038
|
+
}
|
|
29973
30039
|
const headless = HEADLESS_OPERATIONS[tool]?.[op];
|
|
29974
30040
|
if (headless !== undefined) {
|
|
29975
30041
|
const { from: asked, ...answerable } = args;
|
|
@@ -30340,6 +30406,33 @@ class GodotServer {
|
|
|
30340
30406
|
const { op: _op, projectPath: _projectPath, ...params } = contained.value;
|
|
30341
30407
|
return this.answer(await this.operation(operation, params, project.value.path));
|
|
30342
30408
|
}
|
|
30409
|
+
async handleRefreshUids(args) {
|
|
30410
|
+
const project = this.project(args);
|
|
30411
|
+
if (!project.ok) {
|
|
30412
|
+
return project.response;
|
|
30413
|
+
}
|
|
30414
|
+
const engine = await this.engine();
|
|
30415
|
+
if (!engine.ok) {
|
|
30416
|
+
return this.createErrorResponse("No Godot executable found.");
|
|
30417
|
+
}
|
|
30418
|
+
const projectPath = project.value.path;
|
|
30419
|
+
const before = scriptsWithoutUid(projectPath);
|
|
30420
|
+
const imported = await runImport(engine.value, projectPath);
|
|
30421
|
+
if (!imported.ok) {
|
|
30422
|
+
return this.answer(imported);
|
|
30423
|
+
}
|
|
30424
|
+
const after = scriptsWithoutUid(projectPath);
|
|
30425
|
+
const given = before.filter((script) => !after.includes(script));
|
|
30426
|
+
return this.answer({
|
|
30427
|
+
ok: true,
|
|
30428
|
+
messages: imported.messages,
|
|
30429
|
+
payload: {
|
|
30430
|
+
uidsCreated: given,
|
|
30431
|
+
stillWithoutUid: after,
|
|
30432
|
+
note: after.length > 0 ? `${after.length} still have no .uid, which is the engine declining to import them rather than this op skipping them. No scene was written.` : "No scene was written: this reads the project and imports it, and does not resave anything."
|
|
30433
|
+
}
|
|
30434
|
+
});
|
|
30435
|
+
}
|
|
30343
30436
|
async operation(operation, params, projectPath) {
|
|
30344
30437
|
const engine = await this.engine();
|
|
30345
30438
|
if (!engine.ok) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gdharness",
|
|
3
|
-
"version": "0.13.
|
|
3
|
+
"version": "0.13.13",
|
|
4
4
|
"mcpName": "io.github.Aureliolo/gdharness",
|
|
5
5
|
"description": "A harness for driving a Godot 4 project from an agent: editor addons, a runtime bridge into the running game, an MCP server in front of them, and a CLI that installs and diagnoses the Godot side.",
|
|
6
6
|
"type": "module",
|