gdharness 1.1.2 → 1.1.4
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
|
@@ -114,11 +114,11 @@ function startTimesOf(pids) {
|
|
|
114
114
|
}
|
|
115
115
|
async function processTree() {
|
|
116
116
|
try {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
117
|
+
const tree = process.platform === "win32" ? parseProcessTable(await askWindows(), true) : parseProcessTable(await askPosix());
|
|
118
|
+
lastListingFailure = undefined;
|
|
119
|
+
return tree;
|
|
120
|
+
} catch (error) {
|
|
121
|
+
lastListingFailure = error instanceof Error ? error.message : String(error);
|
|
122
122
|
return;
|
|
123
123
|
}
|
|
124
124
|
}
|
|
@@ -256,7 +256,7 @@ async function askWindows() {
|
|
|
256
256
|
"-NoProfile",
|
|
257
257
|
"-NonInteractive",
|
|
258
258
|
"-Command",
|
|
259
|
-
`${POWERSHELL_UTF8}Get-CimInstance Win32_Process | ForEach-Object { $began = 0; if ($_.CreationDate) { $began = ([DateTimeOffset]$_.CreationDate).ToUnixTimeMilliseconds() }; "$($_.ProcessId) $($_.ParentProcessId) $began $($_.CommandLine)" }`
|
|
259
|
+
`${POWERSHELL_UTF8}Get-CimInstance Win32_Process -Property ProcessId,ParentProcessId,CreationDate,CommandLine | ForEach-Object { $began = 0; if ($_.CreationDate) { $began = ([DateTimeOffset]$_.CreationDate).ToUnixTimeMilliseconds() }; "$($_.ProcessId) $($_.ParentProcessId) $began $($_.CommandLine)" }`
|
|
260
260
|
], { timeout: ASK_TIMEOUT_MS, windowsHide: true, maxBuffer: 16 * 1024 * 1024 });
|
|
261
261
|
return stdout;
|
|
262
262
|
}
|
|
@@ -267,7 +267,7 @@ async function askPosix() {
|
|
|
267
267
|
});
|
|
268
268
|
return stdout;
|
|
269
269
|
}
|
|
270
|
-
var run, POWERSHELL_UTF8 = "[Console]::OutputEncoding = [System.Text.Encoding]::UTF8; ", ASK_TIMEOUT_MS = 15000;
|
|
270
|
+
var run, POWERSHELL_UTF8 = "[Console]::OutputEncoding = [System.Text.Encoding]::UTF8; ", ASK_TIMEOUT_MS = 15000, lastListingFailure;
|
|
271
271
|
var init_process_children = __esm(() => {
|
|
272
272
|
run = promisify(execFile);
|
|
273
273
|
});
|
|
@@ -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: a source
|
|
15995
|
+
summary: "which resources are outdated or failed, or one resource with resourcePath, judged by content as the editor judges it: a source whose hash differs from the one its import recorded, an import whose output under .godot/imported is gone (missing_outputs), and a glTF scene whose import does not depend on an image it names, because it was built before that image was imported (imported_without), each with its reason",
|
|
15996
15996
|
requires: []
|
|
15997
15997
|
},
|
|
15998
15998
|
options: { summary: "the import options of one resource", requires: ["resourcePath"] },
|
|
@@ -41334,7 +41334,10 @@ class GodotServer {
|
|
|
41334
41334
|
adopted.log.finish();
|
|
41335
41335
|
adopted.exitCode = record.exitCode ?? null;
|
|
41336
41336
|
adopted.exitSignal = record.exitSignal ?? null;
|
|
41337
|
-
|
|
41337
|
+
const exitMayBeComing = !alive(record.pid) && !exited(adopted);
|
|
41338
|
+
if (!exitMayBeComing) {
|
|
41339
|
+
adopted.endedUnwatched = !exited(adopted);
|
|
41340
|
+
}
|
|
41338
41341
|
}
|
|
41339
41342
|
this.activeProcess = adopted;
|
|
41340
41343
|
return adopted;
|
|
@@ -41842,13 +41845,23 @@ class GodotServer {
|
|
|
41842
41845
|
if (!declined || Date.now() - started >= timeoutMs) {
|
|
41843
41846
|
break;
|
|
41844
41847
|
}
|
|
41848
|
+
await new Promise((settle) => setTimeout(settle, 100));
|
|
41845
41849
|
}
|
|
41846
41850
|
let busy = true;
|
|
41847
|
-
|
|
41851
|
+
let idleSince = null;
|
|
41852
|
+
while (Date.now() - started < timeoutMs) {
|
|
41848
41853
|
await new Promise((settle) => setTimeout(settle, 100));
|
|
41849
41854
|
const status = asParams(await this.godotBridge.invokeTool("rescan_filesystem", { ...args, statusOnly: true }));
|
|
41850
41855
|
const pending = Boolean(status["pending"]) && Date.now() - started < SCAN_START_MS;
|
|
41851
41856
|
busy = Boolean(status["scanning"]) || Boolean(status["importing"]) || pending;
|
|
41857
|
+
if (busy) {
|
|
41858
|
+
idleSince = null;
|
|
41859
|
+
} else {
|
|
41860
|
+
idleSince ??= Date.now();
|
|
41861
|
+
if (Date.now() - idleSince >= SCAN_SETTLED_MS) {
|
|
41862
|
+
break;
|
|
41863
|
+
}
|
|
41864
|
+
}
|
|
41852
41865
|
}
|
|
41853
41866
|
if (!busy && projectPath !== "" && (atRisk.length > 0 || ghosts.length > 0 || blind.unchecked !== undefined)) {
|
|
41854
41867
|
const until = Date.now() + CACHE_WRITE_MS;
|
|
@@ -42318,7 +42331,7 @@ async function runGodotServer() {
|
|
|
42318
42331
|
const server = new GodotServer;
|
|
42319
42332
|
await server.run();
|
|
42320
42333
|
}
|
|
42321
|
-
var UPDATE_NOTICE_EVERY = 500, FEEDBACK_NOTICE_EVERY = 250, run7, __dirname2, EDITOR_RESTART_TIMEOUT_MS = 90000, HOLD_PING_MS = 2000, SLOWEST_FRAME_RATE = 20, PLAY_STARTS_WITHIN_MS = 30000, STOP_WAIT_MS = 1e4, EXIT_REPORTED_WITHIN_MS = 2000, CONSOLE_WRAPPER, LONGEST_BOOT_NOTED_MS, MOST_GROUPS = 20, VALUE_NAMES, BRIDGE_RETRY_MS = 2000, SUCCESSOR_CHECK_MS = 1e4, HANDOVER_MS, HANDOVER_POLL_MS = 250, CACHE_WRITE_MS = 2000, SCAN_WAIT_MS = 30000, PLAYING_STATUS_MS = 1000, SCAN_START_MS = 2000, WAIT_FOR_RUN_MS = 600000, RUN_POLL_MS = 250, PATH_SOLUTIONS, PROJECT_FILE_ARGUMENTS, DEBUG_STATE_CALLS, PROJECT_INFO_SECTIONS2, RUNNER_DIRECTORY = "addons/gdUnit4/";
|
|
42334
|
+
var UPDATE_NOTICE_EVERY = 500, FEEDBACK_NOTICE_EVERY = 250, run7, __dirname2, EDITOR_RESTART_TIMEOUT_MS = 90000, HOLD_PING_MS = 2000, SLOWEST_FRAME_RATE = 20, PLAY_STARTS_WITHIN_MS = 30000, STOP_WAIT_MS = 1e4, EXIT_REPORTED_WITHIN_MS = 2000, CONSOLE_WRAPPER, LONGEST_BOOT_NOTED_MS, MOST_GROUPS = 20, VALUE_NAMES, BRIDGE_RETRY_MS = 2000, SUCCESSOR_CHECK_MS = 1e4, HANDOVER_MS, HANDOVER_POLL_MS = 250, CACHE_WRITE_MS = 2000, SCAN_WAIT_MS = 30000, PLAYING_STATUS_MS = 1000, SCAN_START_MS = 2000, SCAN_SETTLED_MS = 500, WAIT_FOR_RUN_MS = 600000, RUN_POLL_MS = 250, PATH_SOLUTIONS, PROJECT_FILE_ARGUMENTS, DEBUG_STATE_CALLS, PROJECT_INFO_SECTIONS2, RUNNER_DIRECTORY = "addons/gdUnit4/";
|
|
42322
42335
|
var init_server3 = __esm(() => {
|
|
42323
42336
|
init_mcp();
|
|
42324
42337
|
init_stdio2();
|
|
@@ -11,12 +11,10 @@ var _editor_plugin: EditorPlugin = null
|
|
|
11
11
|
var _values: Serialisation = Serialisation.new()
|
|
12
12
|
var _properties: PropertyValues = PropertyValues.new()
|
|
13
13
|
|
|
14
|
-
## How many
|
|
15
|
-
##
|
|
16
|
-
##
|
|
14
|
+
## How many times the editor has said its filesystem changed, which a scan run on the main thread
|
|
15
|
+
## does before `scan()` returns: counted so a rescan can tell a scan that ran there from one the
|
|
16
|
+
## editor declined.
|
|
17
17
|
var _scans_finished: int = 0
|
|
18
|
-
var _scans_finished_at_request: int = 0
|
|
19
|
-
var _scan_pending: bool = false
|
|
20
18
|
|
|
21
19
|
## When the last scan finished, on the engine's clock, or -1 before the first. The editor writes
|
|
22
20
|
## the class cache a frame after it stops reporting a scan, measured on 4.7.2, so a game started
|
|
@@ -36,7 +34,6 @@ func set_editor_plugin(plugin: EditorPlugin) -> void:
|
|
|
36
34
|
|
|
37
35
|
func _scan_finished() -> void:
|
|
38
36
|
_scans_finished += 1
|
|
39
|
-
_scan_pending = false
|
|
40
37
|
_scan_finished_at_msec = Time.get_ticks_msec()
|
|
41
38
|
|
|
42
39
|
|
|
@@ -662,31 +659,24 @@ func rescan_filesystem(args: Dictionary) -> Dictionary:
|
|
|
662
659
|
var busy: bool = filesystem.is_scanning() or filesystem.is_importing()
|
|
663
660
|
var started: bool = false
|
|
664
661
|
if not Read.as_bool(args.get("statusOnly", false)) and not busy:
|
|
665
|
-
|
|
666
|
-
_scan_pending = true
|
|
662
|
+
var finished_before: int = _scans_finished
|
|
667
663
|
filesystem.scan()
|
|
668
|
-
|
|
664
|
+
# Whether the scan ran, read off the editor rather than assumed. `scan()` returns without
|
|
665
|
+
# a word while the thread of the scan before is still to be joined, which is a frame or
|
|
666
|
+
# more after that scan stops reporting itself, and that scan then signals as it is joined.
|
|
667
|
+
# Taken as started, the answer came from a scan begun before the files were written:
|
|
668
|
+
# downstream, a rescan after 698 files were rewritten answered finished in 1.5s and the
|
|
669
|
+
# one changed file stayed on its old import. A threaded scan reports itself from inside
|
|
670
|
+
# the call, and one run on this thread has finished and signalled before it returns.
|
|
671
|
+
started = filesystem.is_scanning() or _scans_finished != finished_before
|
|
669
672
|
|
|
670
673
|
# Importing is reported separately from scanning, and a class is not registered until
|
|
671
674
|
# both are done, so a caller watching only one of them can look too early.
|
|
672
|
-
#
|
|
673
|
-
# `pending` is the third state, and it is the one that was missing. A scan is queued rather
|
|
674
|
-
# than run, so for the first frames after asking, both flags are false and the editor has not
|
|
675
|
-
# started: a caller polling them reads "finished" off a scan that has not begun. Measured, a
|
|
676
|
-
# headless editor of 400 scripts takes 1619ms and answered honestly, while a downstream editor
|
|
677
|
-
# of 376 classes answered finished 296ms in and then wrote its own stale class list over the
|
|
678
|
-
# cache that had just been corrected. Whoever reads this is waiting for the scan to have
|
|
679
|
-
# landed, so "not yet" and "over" must not be spelled the same.
|
|
680
|
-
var scanning: bool = filesystem.is_scanning()
|
|
681
|
-
var importing: bool = filesystem.is_importing()
|
|
682
|
-
if scanning or importing or _scans_finished != _scans_finished_at_request:
|
|
683
|
-
_scan_pending = false
|
|
684
675
|
return {
|
|
685
676
|
"ok": true,
|
|
686
677
|
"started": started,
|
|
687
|
-
"scanning":
|
|
688
|
-
"importing":
|
|
689
|
-
"pending": _scan_pending,
|
|
678
|
+
"scanning": filesystem.is_scanning(),
|
|
679
|
+
"importing": filesystem.is_importing(),
|
|
690
680
|
"scansFinished": _scans_finished,
|
|
691
681
|
}
|
|
692
682
|
|
|
@@ -704,7 +694,6 @@ func scan_status(_args: Dictionary) -> Dictionary:
|
|
|
704
694
|
"ok": true,
|
|
705
695
|
"scanning": filesystem.is_scanning(),
|
|
706
696
|
"importing": filesystem.is_importing(),
|
|
707
|
-
"pending": _scan_pending,
|
|
708
697
|
"sinceScanFinishedMs":
|
|
709
698
|
-1 if _scan_finished_at_msec < 0 else Time.get_ticks_msec() - _scan_finished_at_msec,
|
|
710
699
|
}
|
|
@@ -383,10 +383,6 @@ func _import_status_of(resource_path: String, import_file_path: String) -> Dicti
|
|
|
383
383
|
var status: Dictionary = {
|
|
384
384
|
"path": resource_path, "status": "up_to_date", "import_file_exists": true, "source_exists": true
|
|
385
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
386
|
|
|
391
387
|
# The sidecar alone does not say the import is there: deleting the outputs under
|
|
392
388
|
# .godot/imported is the ordinary way to force one, and a resource whose output is gone was
|
|
@@ -402,23 +398,59 @@ func _import_status_of(resource_path: String, import_file_path: String) -> Dicti
|
|
|
402
398
|
status["missing_outputs"] = missing
|
|
403
399
|
return status
|
|
404
400
|
|
|
405
|
-
#
|
|
406
|
-
#
|
|
407
|
-
#
|
|
408
|
-
|
|
409
|
-
var
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
401
|
+
# By content, as the editor judges it: the hash of the source the import recorded, against the
|
|
402
|
+
# file. By time, an installer that rewrote 31 images byte for byte made every one read as changed,
|
|
403
|
+
# and the editor, which compares the hash, rightly imported none of them.
|
|
404
|
+
var recorded: String = _recorded_source_md5(resource_path)
|
|
405
|
+
var changed: bool = (
|
|
406
|
+
FileAccess.get_md5(resource_path) != recorded
|
|
407
|
+
if not recorded.is_empty()
|
|
408
|
+
else FileAccess.get_modified_time(resource_path) > FileAccess.get_modified_time(import_file_path)
|
|
409
|
+
)
|
|
410
|
+
if changed:
|
|
411
|
+
status["status"] = "needs_reimport"
|
|
412
|
+
status["reason"] = "the source changed after it was imported"
|
|
413
|
+
return status
|
|
414
|
+
|
|
415
|
+
# A scene built without an image it names: it loads each one as it imports, so one imported
|
|
416
|
+
# before the image was is built without it. Read off what the imported scene depends on, not off
|
|
417
|
+
# file times: a texture a 3D scene uses is reimported compressed after the scene, so its outputs
|
|
418
|
+
# are always the newer, and 33 correct scenes read as stale when judged that way.
|
|
419
|
+
var without: Array[String] = []
|
|
420
|
+
var images: Array[String] = _images_of(resource_path)
|
|
421
|
+
if not images.is_empty():
|
|
422
|
+
var depended: PackedStringArray = ResourceLoader.get_dependencies(resource_path)
|
|
423
|
+
for image: String in images:
|
|
424
|
+
if FileAccess.file_exists(image + ".import") and not _names(depended, image):
|
|
425
|
+
without.append(image)
|
|
426
|
+
if not without.is_empty():
|
|
416
427
|
status["status"] = "needs_reimport"
|
|
417
|
-
status["reason"] = "it was imported
|
|
418
|
-
status["
|
|
428
|
+
status["reason"] = "it was imported without images it uses, which were not imported yet"
|
|
429
|
+
status["imported_without"] = without
|
|
419
430
|
return status
|
|
420
431
|
|
|
421
432
|
|
|
433
|
+
## The hash of its source that [param resource_path]'s last import recorded, or "" when there is no
|
|
434
|
+
## record: the editor keeps it beside the outputs, under the same base name with [code].md5[/code].
|
|
435
|
+
static func _recorded_source_md5(resource_path: String) -> String:
|
|
436
|
+
var record: String = (
|
|
437
|
+
"res://.godot/imported/%s-%s.md5" % [resource_path.get_file(), resource_path.md5_text()]
|
|
438
|
+
)
|
|
439
|
+
var config: ConfigFile = ConfigFile.new()
|
|
440
|
+
if config.load(record) != OK:
|
|
441
|
+
return ""
|
|
442
|
+
return str(config.get_value("", "source_md5", ""))
|
|
443
|
+
|
|
444
|
+
|
|
445
|
+
## Whether [param dependencies], as [method ResourceLoader.get_dependencies] lists them, include
|
|
446
|
+
## [param path]; each entry may carry a uid and a type ahead of the path.
|
|
447
|
+
static func _names(dependencies: PackedStringArray, path: String) -> bool:
|
|
448
|
+
for entry: String in dependencies:
|
|
449
|
+
if entry == path or entry.ends_with("::" + path):
|
|
450
|
+
return true
|
|
451
|
+
return false
|
|
452
|
+
|
|
453
|
+
|
|
422
454
|
## The files an import wrote, from its sidecar's [code]dest_files[/code]; empty when it lists none.
|
|
423
455
|
static func _outputs_of(import_file_path: String) -> Array[String]:
|
|
424
456
|
var config: ConfigFile = ConfigFile.new()
|
|
@@ -432,25 +464,6 @@ static func _outputs_of(import_file_path: String) -> Array[String]:
|
|
|
432
464
|
return outputs
|
|
433
465
|
|
|
434
466
|
|
|
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
467
|
## The image files a glTF scene refers to by path, resolved against the scene's own directory.
|
|
455
468
|
## Embedded images and every other scene format answer nothing.
|
|
456
469
|
static func _images_of(scene_path: String) -> Array[String]:
|
package/build/index.js
CHANGED
|
@@ -24952,14 +24952,15 @@ function startTimesOf(pids) {
|
|
|
24952
24952
|
var ASK_TIMEOUT_MS = 15000;
|
|
24953
24953
|
async function processTree() {
|
|
24954
24954
|
try {
|
|
24955
|
-
|
|
24956
|
-
|
|
24957
|
-
|
|
24958
|
-
|
|
24959
|
-
|
|
24955
|
+
const tree = process.platform === "win32" ? parseProcessTable(await askWindows(), true) : parseProcessTable(await askPosix());
|
|
24956
|
+
lastListingFailure = undefined;
|
|
24957
|
+
return tree;
|
|
24958
|
+
} catch (error) {
|
|
24959
|
+
lastListingFailure = error instanceof Error ? error.message : String(error);
|
|
24960
24960
|
return;
|
|
24961
24961
|
}
|
|
24962
24962
|
}
|
|
24963
|
+
var lastListingFailure;
|
|
24963
24964
|
function parseProcessTable(printed, started = false) {
|
|
24964
24965
|
const tree = new Map;
|
|
24965
24966
|
for (const line of printed.split(/\r?\n/)) {
|
|
@@ -25094,7 +25095,7 @@ async function askWindows() {
|
|
|
25094
25095
|
"-NoProfile",
|
|
25095
25096
|
"-NonInteractive",
|
|
25096
25097
|
"-Command",
|
|
25097
|
-
`${POWERSHELL_UTF8}Get-CimInstance Win32_Process | ForEach-Object { $began = 0; if ($_.CreationDate) { $began = ([DateTimeOffset]$_.CreationDate).ToUnixTimeMilliseconds() }; "$($_.ProcessId) $($_.ParentProcessId) $began $($_.CommandLine)" }`
|
|
25098
|
+
`${POWERSHELL_UTF8}Get-CimInstance Win32_Process -Property ProcessId,ParentProcessId,CreationDate,CommandLine | ForEach-Object { $began = 0; if ($_.CreationDate) { $began = ([DateTimeOffset]$_.CreationDate).ToUnixTimeMilliseconds() }; "$($_.ProcessId) $($_.ParentProcessId) $began $($_.CommandLine)" }`
|
|
25098
25099
|
], { timeout: ASK_TIMEOUT_MS, windowsHide: true, maxBuffer: 16 * 1024 * 1024 });
|
|
25099
25100
|
return stdout;
|
|
25100
25101
|
}
|
|
@@ -30118,7 +30119,7 @@ var TOOL_SPECS = [
|
|
|
30118
30119
|
requires: ["projectPath"],
|
|
30119
30120
|
operations: {
|
|
30120
30121
|
status: {
|
|
30121
|
-
summary: "which resources are outdated or failed, or one resource with resourcePath: a source
|
|
30122
|
+
summary: "which resources are outdated or failed, or one resource with resourcePath, judged by content as the editor judges it: a source whose hash differs from the one its import recorded, an import whose output under .godot/imported is gone (missing_outputs), and a glTF scene whose import does not depend on an image it names, because it was built before that image was imported (imported_without), each with its reason",
|
|
30122
30123
|
requires: []
|
|
30123
30124
|
},
|
|
30124
30125
|
options: { summary: "the import options of one resource", requires: ["resourcePath"] },
|
|
@@ -31475,6 +31476,7 @@ function scanWaitAnswer(scanned) {
|
|
|
31475
31476
|
}
|
|
31476
31477
|
var PLAYING_STATUS_MS = 1000;
|
|
31477
31478
|
var SCAN_START_MS = 2000;
|
|
31479
|
+
var SCAN_SETTLED_MS = 500;
|
|
31478
31480
|
var WAIT_FOR_RUN_MS = 600000;
|
|
31479
31481
|
var RUN_POLL_MS = 250;
|
|
31480
31482
|
var PATH_SOLUTIONS = [
|
|
@@ -34071,7 +34073,10 @@ class GodotServer {
|
|
|
34071
34073
|
adopted.log.finish();
|
|
34072
34074
|
adopted.exitCode = record.exitCode ?? null;
|
|
34073
34075
|
adopted.exitSignal = record.exitSignal ?? null;
|
|
34074
|
-
|
|
34076
|
+
const exitMayBeComing = !alive(record.pid) && !exited(adopted);
|
|
34077
|
+
if (!exitMayBeComing) {
|
|
34078
|
+
adopted.endedUnwatched = !exited(adopted);
|
|
34079
|
+
}
|
|
34075
34080
|
}
|
|
34076
34081
|
this.activeProcess = adopted;
|
|
34077
34082
|
return adopted;
|
|
@@ -34579,13 +34584,23 @@ class GodotServer {
|
|
|
34579
34584
|
if (!declined || Date.now() - started >= timeoutMs) {
|
|
34580
34585
|
break;
|
|
34581
34586
|
}
|
|
34587
|
+
await new Promise((settle) => setTimeout(settle, 100));
|
|
34582
34588
|
}
|
|
34583
34589
|
let busy = true;
|
|
34584
|
-
|
|
34590
|
+
let idleSince = null;
|
|
34591
|
+
while (Date.now() - started < timeoutMs) {
|
|
34585
34592
|
await new Promise((settle) => setTimeout(settle, 100));
|
|
34586
34593
|
const status = asParams(await this.godotBridge.invokeTool("rescan_filesystem", { ...args, statusOnly: true }));
|
|
34587
34594
|
const pending = Boolean(status["pending"]) && Date.now() - started < SCAN_START_MS;
|
|
34588
34595
|
busy = Boolean(status["scanning"]) || Boolean(status["importing"]) || pending;
|
|
34596
|
+
if (busy) {
|
|
34597
|
+
idleSince = null;
|
|
34598
|
+
} else {
|
|
34599
|
+
idleSince ??= Date.now();
|
|
34600
|
+
if (Date.now() - idleSince >= SCAN_SETTLED_MS) {
|
|
34601
|
+
break;
|
|
34602
|
+
}
|
|
34603
|
+
}
|
|
34589
34604
|
}
|
|
34590
34605
|
if (!busy && projectPath !== "" && (atRisk.length > 0 || ghosts.length > 0 || blind.unchecked !== undefined)) {
|
|
34591
34606
|
const until = Date.now() + CACHE_WRITE_MS;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gdharness",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.4",
|
|
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",
|