gdharness 1.1.0 → 1.1.2
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
|
@@ -15992,7 +15992,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
15992
15992
|
requires: ["projectPath"],
|
|
15993
15993
|
operations: {
|
|
15994
15994
|
status: {
|
|
15995
|
-
summary: "which resources are outdated or failed, or one resource with resourcePath",
|
|
15995
|
+
summary: "which resources are outdated or failed, or one resource with resourcePath: a source changed since its import, an import whose output under .godot/imported is gone (missing_outputs), and a glTF scene imported before the images it uses (imported_before), each with its reason",
|
|
15996
15996
|
requires: []
|
|
15997
15997
|
},
|
|
15998
15998
|
options: { summary: "the import options of one resource", requires: ["resourcePath"] },
|
|
@@ -16440,7 +16440,7 @@ var init_tool_definitions = __esm(() => {
|
|
|
16440
16440
|
},
|
|
16441
16441
|
{
|
|
16442
16442
|
name: "editor_rescan",
|
|
16443
|
-
description: "Makes the running editor scan the project filesystem, so files written outside it are picked up. The scan is a change-detecting walk rather than an unconditional reparse, so a file another engine has already imported reads as settled and the walk does not look inside it: its class_name then stays out of the list the editor resolves against, however many times you scan. Any class in that state is named under unseenByEditor, which is what no check on disk can see, since the declaration and the cache are both correct there and only the editor disagrees. The cure is this call on its own, with no change to the declaring script: measured against a real editor on three platforms, with idle waits of the same length ruled out so the scan is credited rather than the time it takes, and reproduced in a second project against its own reproduction in 203ms. editor_launch restart also does it and costs a window, and project_import refresh_classes does not: it rewrites the cache and does not touch what the editor is holding. A scan writes .godot/global_script_class_cache.cfg from the list the editor is holding, so a class the editor cannot resolve goes out of that file with it. That is not a reason to refuse the scan, because on a class the editor has merely not walked yet the scan is the cure; what the answer does instead is name the loss under cacheLost and rebuild the cache from the files, naming what came back under cacheRestored, so no fresh engine, CI run or clone inherits the short file. An editor that lost classes is still holding the short list, so the note says to restart it before scanning again. The other direction is guarded the same way: a class the editor still holds for a script that was renamed or deleted comes back into the cache at a path that is not there, and the next engine to read it fails on \"Could not parse global class\" in whichever correct script shares the bare name, so the cache is rebuilt without it and the answer names it under cacheDropped; the editor goes on holding it until it is restarted, and every scan until then ends in that rebuild. A script or shader written outside the editor has no .uid sidecar until something imports it, and the scan writes one beside it, which a project that commits sidecars needs before the commit. Needs the editor connected.",
|
|
16443
|
+
description: "Makes the running editor scan the project filesystem, so files written outside it are picked up. A scan or an import the editor is already running is waited out first rather than scanned over, which would start a second import over it, and the answer says how long under waitedForEditorScanMs. The scan is a change-detecting walk rather than an unconditional reparse, so a file another engine has already imported reads as settled and the walk does not look inside it: its class_name then stays out of the list the editor resolves against, however many times you scan. Any class in that state is named under unseenByEditor, which is what no check on disk can see, since the declaration and the cache are both correct there and only the editor disagrees. The cure is this call on its own, with no change to the declaring script: measured against a real editor on three platforms, with idle waits of the same length ruled out so the scan is credited rather than the time it takes, and reproduced in a second project against its own reproduction in 203ms. editor_launch restart also does it and costs a window, and project_import refresh_classes does not: it rewrites the cache and does not touch what the editor is holding. A scan writes .godot/global_script_class_cache.cfg from the list the editor is holding, so a class the editor cannot resolve goes out of that file with it. That is not a reason to refuse the scan, because on a class the editor has merely not walked yet the scan is the cure; what the answer does instead is name the loss under cacheLost and rebuild the cache from the files, naming what came back under cacheRestored, so no fresh engine, CI run or clone inherits the short file. An editor that lost classes is still holding the short list, so the note says to restart it before scanning again. The other direction is guarded the same way: a class the editor still holds for a script that was renamed or deleted comes back into the cache at a path that is not there, and the next engine to read it fails on \"Could not parse global class\" in whichever correct script shares the bare name, so the cache is rebuilt without it and the answer names it under cacheDropped; the editor goes on holding it until it is restarted, and every scan until then ends in that rebuild. A script or shader written outside the editor has no .uid sidecar until something imports it, and the scan writes one beside it, which a project that commits sidecars needs before the commit. Needs the editor connected.",
|
|
16444
16444
|
parameters: {
|
|
16445
16445
|
projectPath: PROJECT_PATH,
|
|
16446
16446
|
timeoutMs: {
|
|
@@ -41802,6 +41802,17 @@ class GodotServer {
|
|
|
41802
41802
|
const nearest = descendantsIn(tree, root).filter((pid) => isEngine(pid) && !ancestorsIn(tree, pid, root).some((one) => one !== root && isEngine(one)));
|
|
41803
41803
|
return nearest.length === 1 ? nearest[0] : undefined;
|
|
41804
41804
|
}
|
|
41805
|
+
async editorIsScanningOrImporting() {
|
|
41806
|
+
if (!this.godotBridge.isConnected()) {
|
|
41807
|
+
return false;
|
|
41808
|
+
}
|
|
41809
|
+
try {
|
|
41810
|
+
const status = asParams(await this.godotBridge.invokeTool("scan_status", {}));
|
|
41811
|
+
return status["scanning"] === true || status["importing"] === true;
|
|
41812
|
+
} catch {
|
|
41813
|
+
return false;
|
|
41814
|
+
}
|
|
41815
|
+
}
|
|
41805
41816
|
async handleRescanFilesystem(args) {
|
|
41806
41817
|
const timeoutMs = readPositiveNumber(args, "timeoutMs") ?? 30000;
|
|
41807
41818
|
const started = Date.now();
|
|
@@ -41811,9 +41822,26 @@ class GodotServer {
|
|
|
41811
41822
|
const blind = await this.classesTheEditorCannotSee(args);
|
|
41812
41823
|
const atRisk = before === null ? [] : blind.unseen.filter((one) => before.has(one.className));
|
|
41813
41824
|
const ghosts = blind.stillHeld ?? [];
|
|
41814
|
-
|
|
41815
|
-
|
|
41816
|
-
|
|
41825
|
+
let waitedForEditorMs = 0;
|
|
41826
|
+
let first;
|
|
41827
|
+
for (;; ) {
|
|
41828
|
+
const busySince = Date.now();
|
|
41829
|
+
let wasBusy = false;
|
|
41830
|
+
while (Date.now() - started < timeoutMs && await this.editorIsScanningOrImporting()) {
|
|
41831
|
+
wasBusy = true;
|
|
41832
|
+
await new Promise((settle) => setTimeout(settle, 100));
|
|
41833
|
+
}
|
|
41834
|
+
if (wasBusy) {
|
|
41835
|
+
waitedForEditorMs += Date.now() - busySince;
|
|
41836
|
+
}
|
|
41837
|
+
first = await this.handleViaBridge("rescan_filesystem", args);
|
|
41838
|
+
if (first.isError) {
|
|
41839
|
+
return first;
|
|
41840
|
+
}
|
|
41841
|
+
const declined = asParams(JSON.parse(first.content[0]?.text ?? "{}"))["started"] === false;
|
|
41842
|
+
if (!declined || Date.now() - started >= timeoutMs) {
|
|
41843
|
+
break;
|
|
41844
|
+
}
|
|
41817
41845
|
}
|
|
41818
41846
|
let busy = true;
|
|
41819
41847
|
while (busy && Date.now() - started < timeoutMs) {
|
|
@@ -41872,6 +41900,7 @@ class GodotServer {
|
|
|
41872
41900
|
ok: !busy && unseen.length === 0 && stillGone.length === 0 && stillAtMissingPaths.length === 0 && checked.unchecked === undefined,
|
|
41873
41901
|
stillWorking: busy,
|
|
41874
41902
|
waitedMs: Date.now() - started,
|
|
41903
|
+
waitedForEditorScanMs: waitedForEditorMs > 0 ? waitedForEditorMs : undefined,
|
|
41875
41904
|
heldBeforeReload: reloaded.heldBefore,
|
|
41876
41905
|
reloadedMethods: reloaded.methods,
|
|
41877
41906
|
reloadProblem: reloaded.problem,
|
|
@@ -656,10 +656,16 @@ func rescan_filesystem(args: Dictionary) -> Dictionary:
|
|
|
656
656
|
return {"ok": false, "error": "Editor plugin unavailable"}
|
|
657
657
|
|
|
658
658
|
var filesystem: EditorFileSystem = EditorInterface.get_resource_filesystem()
|
|
659
|
-
|
|
659
|
+
# Not over a scan or an import the editor is already running: a scan asked for then starts a
|
|
660
|
+
# second reimport over the first, and the editor logs "Task 'reimport' already exists" and two
|
|
661
|
+
# conditions from its progress dialog. Declined and said, so the caller waits and asks again.
|
|
662
|
+
var busy: bool = filesystem.is_scanning() or filesystem.is_importing()
|
|
663
|
+
var started: bool = false
|
|
664
|
+
if not Read.as_bool(args.get("statusOnly", false)) and not busy:
|
|
660
665
|
_scans_finished_at_request = _scans_finished
|
|
661
666
|
_scan_pending = true
|
|
662
667
|
filesystem.scan()
|
|
668
|
+
started = true
|
|
663
669
|
|
|
664
670
|
# Importing is reported separately from scanning, and a class is not registered until
|
|
665
671
|
# both are done, so a caller watching only one of them can look too early.
|
|
@@ -677,6 +683,7 @@ func rescan_filesystem(args: Dictionary) -> Dictionary:
|
|
|
677
683
|
_scan_pending = false
|
|
678
684
|
return {
|
|
679
685
|
"ok": true,
|
|
686
|
+
"started": started,
|
|
680
687
|
"scanning": scanning,
|
|
681
688
|
"importing": importing,
|
|
682
689
|
"pending": _scan_pending,
|
|
@@ -380,12 +380,106 @@ func _import_status_of(resource_path: String, import_file_path: String) -> Dicti
|
|
|
380
380
|
"source_exists": true
|
|
381
381
|
}
|
|
382
382
|
|
|
383
|
-
var
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
return {
|
|
387
|
-
"path": resource_path,
|
|
388
|
-
"status": "needs_reimport" if source_modified > import_modified else "up_to_date",
|
|
389
|
-
"import_file_exists": true,
|
|
390
|
-
"source_exists": true
|
|
383
|
+
var status: Dictionary = {
|
|
384
|
+
"path": resource_path, "status": "up_to_date", "import_file_exists": true, "source_exists": true
|
|
391
385
|
}
|
|
386
|
+
if FileAccess.get_modified_time(resource_path) > FileAccess.get_modified_time(import_file_path):
|
|
387
|
+
status["status"] = "needs_reimport"
|
|
388
|
+
status["reason"] = "the source changed after it was imported"
|
|
389
|
+
return status
|
|
390
|
+
|
|
391
|
+
# The sidecar alone does not say the import is there: deleting the outputs under
|
|
392
|
+
# .godot/imported is the ordinary way to force one, and a resource whose output is gone was
|
|
393
|
+
# answered as current, which is what a reimport is then skipped on.
|
|
394
|
+
var outputs: Array[String] = _outputs_of(import_file_path)
|
|
395
|
+
var missing: Array[String] = []
|
|
396
|
+
for output: String in outputs:
|
|
397
|
+
if not FileAccess.file_exists(output):
|
|
398
|
+
missing.append(output)
|
|
399
|
+
if not missing.is_empty():
|
|
400
|
+
status["status"] = "needs_reimport"
|
|
401
|
+
status["reason"] = "its imported output is not on disk"
|
|
402
|
+
status["missing_outputs"] = missing
|
|
403
|
+
return status
|
|
404
|
+
|
|
405
|
+
# A scene imported before the textures it uses were: it loads each image as it imports, so one
|
|
406
|
+
# imported ahead of them is built without them, and nothing about its own files says so. A second
|
|
407
|
+
# reimport started over one in progress did exactly that downstream, and 64 scenes that were
|
|
408
|
+
# untextured all read as up to date here.
|
|
409
|
+
var built_at: int = _oldest(outputs, import_file_path)
|
|
410
|
+
var before: Array[String] = []
|
|
411
|
+
for image: String in _images_of(resource_path):
|
|
412
|
+
if FileAccess.file_exists(image + ".import"):
|
|
413
|
+
if _newest(_outputs_of(image + ".import"), image + ".import") > built_at:
|
|
414
|
+
before.append(image)
|
|
415
|
+
if not before.is_empty():
|
|
416
|
+
status["status"] = "needs_reimport"
|
|
417
|
+
status["reason"] = "it was imported before the images it uses, so it was built without them"
|
|
418
|
+
status["imported_before"] = before
|
|
419
|
+
return status
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
## The files an import wrote, from its sidecar's [code]dest_files[/code]; empty when it lists none.
|
|
423
|
+
static func _outputs_of(import_file_path: String) -> Array[String]:
|
|
424
|
+
var config: ConfigFile = ConfigFile.new()
|
|
425
|
+
var outputs: Array[String] = []
|
|
426
|
+
if config.load(import_file_path) != OK:
|
|
427
|
+
return outputs
|
|
428
|
+
var listed: Variant = config.get_value("deps", "dest_files", [])
|
|
429
|
+
if listed is Array:
|
|
430
|
+
for one: Variant in listed:
|
|
431
|
+
outputs.append(str(one))
|
|
432
|
+
return outputs
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
## When the earliest of [param outputs] was written, or [param fallback] when none is on disk.
|
|
436
|
+
static func _oldest(outputs: Array[String], fallback: String) -> int:
|
|
437
|
+
var oldest: int = -1
|
|
438
|
+
for output: String in outputs:
|
|
439
|
+
if FileAccess.file_exists(output):
|
|
440
|
+
var at: int = FileAccess.get_modified_time(output)
|
|
441
|
+
oldest = at if oldest < 0 else mini(oldest, at)
|
|
442
|
+
return oldest if oldest >= 0 else FileAccess.get_modified_time(fallback)
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
## When the latest of [param outputs] was written, or [param fallback] when none is on disk.
|
|
446
|
+
static func _newest(outputs: Array[String], fallback: String) -> int:
|
|
447
|
+
var newest: int = -1
|
|
448
|
+
for output: String in outputs:
|
|
449
|
+
if FileAccess.file_exists(output):
|
|
450
|
+
newest = maxi(newest, FileAccess.get_modified_time(output))
|
|
451
|
+
return newest if newest >= 0 else FileAccess.get_modified_time(fallback)
|
|
452
|
+
|
|
453
|
+
|
|
454
|
+
## The image files a glTF scene refers to by path, resolved against the scene's own directory.
|
|
455
|
+
## Embedded images and every other scene format answer nothing.
|
|
456
|
+
static func _images_of(scene_path: String) -> Array[String]:
|
|
457
|
+
var images: Array[String] = []
|
|
458
|
+
var text: String = ""
|
|
459
|
+
match scene_path.get_extension().to_lower():
|
|
460
|
+
"gltf":
|
|
461
|
+
text = FileAccess.get_file_as_string(scene_path)
|
|
462
|
+
"glb":
|
|
463
|
+
# A 12-byte header, then the first chunk: its length, its type, and for "JSON" the text.
|
|
464
|
+
var bytes: PackedByteArray = FileAccess.get_file_as_bytes(scene_path)
|
|
465
|
+
if bytes.size() >= 20 and bytes.decode_u32(16) == 0x4E4F534A:
|
|
466
|
+
var length: int = bytes.decode_u32(12)
|
|
467
|
+
text = bytes.slice(20, mini(20 + length, bytes.size())).get_string_from_utf8()
|
|
468
|
+
if text.is_empty():
|
|
469
|
+
return images
|
|
470
|
+
var parsed: Variant = JSON.parse_string(text)
|
|
471
|
+
if not parsed is Dictionary:
|
|
472
|
+
return images
|
|
473
|
+
var document: Dictionary = parsed
|
|
474
|
+
var listed: Variant = document.get("images", [])
|
|
475
|
+
if not listed is Array:
|
|
476
|
+
return images
|
|
477
|
+
for image: Variant in listed:
|
|
478
|
+
if not image is Dictionary:
|
|
479
|
+
continue
|
|
480
|
+
var entry: Dictionary = image
|
|
481
|
+
var uri: String = str(entry.get("uri", ""))
|
|
482
|
+
if uri.is_empty() or uri.begins_with("data:"):
|
|
483
|
+
continue
|
|
484
|
+
images.append(scene_path.get_base_dir().path_join(uri.uri_decode()).simplify_path())
|
|
485
|
+
return images
|
package/build/index.js
CHANGED
|
@@ -30118,7 +30118,7 @@ var TOOL_SPECS = [
|
|
|
30118
30118
|
requires: ["projectPath"],
|
|
30119
30119
|
operations: {
|
|
30120
30120
|
status: {
|
|
30121
|
-
summary: "which resources are outdated or failed, or one resource with resourcePath",
|
|
30121
|
+
summary: "which resources are outdated or failed, or one resource with resourcePath: a source changed since its import, an import whose output under .godot/imported is gone (missing_outputs), and a glTF scene imported before the images it uses (imported_before), each with its reason",
|
|
30122
30122
|
requires: []
|
|
30123
30123
|
},
|
|
30124
30124
|
options: { summary: "the import options of one resource", requires: ["resourcePath"] },
|
|
@@ -30566,7 +30566,7 @@ var TOOL_SPECS = [
|
|
|
30566
30566
|
},
|
|
30567
30567
|
{
|
|
30568
30568
|
name: "editor_rescan",
|
|
30569
|
-
description: "Makes the running editor scan the project filesystem, so files written outside it are picked up. The scan is a change-detecting walk rather than an unconditional reparse, so a file another engine has already imported reads as settled and the walk does not look inside it: its class_name then stays out of the list the editor resolves against, however many times you scan. Any class in that state is named under unseenByEditor, which is what no check on disk can see, since the declaration and the cache are both correct there and only the editor disagrees. The cure is this call on its own, with no change to the declaring script: measured against a real editor on three platforms, with idle waits of the same length ruled out so the scan is credited rather than the time it takes, and reproduced in a second project against its own reproduction in 203ms. editor_launch restart also does it and costs a window, and project_import refresh_classes does not: it rewrites the cache and does not touch what the editor is holding. A scan writes .godot/global_script_class_cache.cfg from the list the editor is holding, so a class the editor cannot resolve goes out of that file with it. That is not a reason to refuse the scan, because on a class the editor has merely not walked yet the scan is the cure; what the answer does instead is name the loss under cacheLost and rebuild the cache from the files, naming what came back under cacheRestored, so no fresh engine, CI run or clone inherits the short file. An editor that lost classes is still holding the short list, so the note says to restart it before scanning again. The other direction is guarded the same way: a class the editor still holds for a script that was renamed or deleted comes back into the cache at a path that is not there, and the next engine to read it fails on \"Could not parse global class\" in whichever correct script shares the bare name, so the cache is rebuilt without it and the answer names it under cacheDropped; the editor goes on holding it until it is restarted, and every scan until then ends in that rebuild. A script or shader written outside the editor has no .uid sidecar until something imports it, and the scan writes one beside it, which a project that commits sidecars needs before the commit. Needs the editor connected.",
|
|
30569
|
+
description: "Makes the running editor scan the project filesystem, so files written outside it are picked up. A scan or an import the editor is already running is waited out first rather than scanned over, which would start a second import over it, and the answer says how long under waitedForEditorScanMs. The scan is a change-detecting walk rather than an unconditional reparse, so a file another engine has already imported reads as settled and the walk does not look inside it: its class_name then stays out of the list the editor resolves against, however many times you scan. Any class in that state is named under unseenByEditor, which is what no check on disk can see, since the declaration and the cache are both correct there and only the editor disagrees. The cure is this call on its own, with no change to the declaring script: measured against a real editor on three platforms, with idle waits of the same length ruled out so the scan is credited rather than the time it takes, and reproduced in a second project against its own reproduction in 203ms. editor_launch restart also does it and costs a window, and project_import refresh_classes does not: it rewrites the cache and does not touch what the editor is holding. A scan writes .godot/global_script_class_cache.cfg from the list the editor is holding, so a class the editor cannot resolve goes out of that file with it. That is not a reason to refuse the scan, because on a class the editor has merely not walked yet the scan is the cure; what the answer does instead is name the loss under cacheLost and rebuild the cache from the files, naming what came back under cacheRestored, so no fresh engine, CI run or clone inherits the short file. An editor that lost classes is still holding the short list, so the note says to restart it before scanning again. The other direction is guarded the same way: a class the editor still holds for a script that was renamed or deleted comes back into the cache at a path that is not there, and the next engine to read it fails on \"Could not parse global class\" in whichever correct script shares the bare name, so the cache is rebuilt without it and the answer names it under cacheDropped; the editor goes on holding it until it is restarted, and every scan until then ends in that rebuild. A script or shader written outside the editor has no .uid sidecar until something imports it, and the scan writes one beside it, which a project that commits sidecars needs before the commit. Needs the editor connected.",
|
|
30570
30570
|
parameters: {
|
|
30571
30571
|
projectPath: PROJECT_PATH,
|
|
30572
30572
|
timeoutMs: {
|
|
@@ -34539,6 +34539,17 @@ class GodotServer {
|
|
|
34539
34539
|
const nearest = descendantsIn(tree, root).filter((pid) => isEngine(pid) && !ancestorsIn(tree, pid, root).some((one) => one !== root && isEngine(one)));
|
|
34540
34540
|
return nearest.length === 1 ? nearest[0] : undefined;
|
|
34541
34541
|
}
|
|
34542
|
+
async editorIsScanningOrImporting() {
|
|
34543
|
+
if (!this.godotBridge.isConnected()) {
|
|
34544
|
+
return false;
|
|
34545
|
+
}
|
|
34546
|
+
try {
|
|
34547
|
+
const status = asParams(await this.godotBridge.invokeTool("scan_status", {}));
|
|
34548
|
+
return status["scanning"] === true || status["importing"] === true;
|
|
34549
|
+
} catch {
|
|
34550
|
+
return false;
|
|
34551
|
+
}
|
|
34552
|
+
}
|
|
34542
34553
|
async handleRescanFilesystem(args) {
|
|
34543
34554
|
const timeoutMs = readPositiveNumber(args, "timeoutMs") ?? 30000;
|
|
34544
34555
|
const started = Date.now();
|
|
@@ -34548,9 +34559,26 @@ class GodotServer {
|
|
|
34548
34559
|
const blind = await this.classesTheEditorCannotSee(args);
|
|
34549
34560
|
const atRisk = before === null ? [] : blind.unseen.filter((one) => before.has(one.className));
|
|
34550
34561
|
const ghosts = blind.stillHeld ?? [];
|
|
34551
|
-
|
|
34552
|
-
|
|
34553
|
-
|
|
34562
|
+
let waitedForEditorMs = 0;
|
|
34563
|
+
let first;
|
|
34564
|
+
for (;; ) {
|
|
34565
|
+
const busySince = Date.now();
|
|
34566
|
+
let wasBusy = false;
|
|
34567
|
+
while (Date.now() - started < timeoutMs && await this.editorIsScanningOrImporting()) {
|
|
34568
|
+
wasBusy = true;
|
|
34569
|
+
await new Promise((settle) => setTimeout(settle, 100));
|
|
34570
|
+
}
|
|
34571
|
+
if (wasBusy) {
|
|
34572
|
+
waitedForEditorMs += Date.now() - busySince;
|
|
34573
|
+
}
|
|
34574
|
+
first = await this.handleViaBridge("rescan_filesystem", args);
|
|
34575
|
+
if (first.isError) {
|
|
34576
|
+
return first;
|
|
34577
|
+
}
|
|
34578
|
+
const declined = asParams(JSON.parse(first.content[0]?.text ?? "{}"))["started"] === false;
|
|
34579
|
+
if (!declined || Date.now() - started >= timeoutMs) {
|
|
34580
|
+
break;
|
|
34581
|
+
}
|
|
34554
34582
|
}
|
|
34555
34583
|
let busy = true;
|
|
34556
34584
|
while (busy && Date.now() - started < timeoutMs) {
|
|
@@ -34609,6 +34637,7 @@ class GodotServer {
|
|
|
34609
34637
|
ok: !busy && unseen.length === 0 && stillGone.length === 0 && stillAtMissingPaths.length === 0 && checked.unchecked === undefined,
|
|
34610
34638
|
stillWorking: busy,
|
|
34611
34639
|
waitedMs: Date.now() - started,
|
|
34640
|
+
waitedForEditorScanMs: waitedForEditorMs > 0 ? waitedForEditorMs : undefined,
|
|
34612
34641
|
heldBeforeReload: reloaded.heldBefore,
|
|
34613
34642
|
reloadedMethods: reloaded.methods,
|
|
34614
34643
|
reloadProblem: reloaded.problem,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gdharness",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.2",
|
|
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",
|