更新godot ai 到最新版本
This commit is contained in:
@@ -29,9 +29,9 @@ const _LOOKUP_TIMEOUT_MS := 3000
|
||||
|
||||
## Find any of the supplied exe names; returns the first hit.
|
||||
## On Windows pass the .exe variant in `exe_names` if relevant.
|
||||
static func find(exe_names: Array[String]) -> String:
|
||||
static func find(exe_names: Array[String], trace: Callable = Callable()) -> String:
|
||||
for name in exe_names:
|
||||
var hit := _find_one(name)
|
||||
var hit := _find_one(name, trace)
|
||||
if not hit.is_empty():
|
||||
return hit
|
||||
return ""
|
||||
@@ -49,19 +49,21 @@ static func invalidate(exe_name: String = "") -> void:
|
||||
_mutex.unlock()
|
||||
|
||||
|
||||
static func _find_one(exe_name: String) -> String:
|
||||
static func _find_one(exe_name: String, trace: Callable = Callable()) -> String:
|
||||
var started := Time.get_ticks_msec()
|
||||
_mutex.lock()
|
||||
var already_searched: bool = _searched.get(exe_name, false)
|
||||
var cached: String = _cache.get(exe_name, "")
|
||||
_mutex.unlock()
|
||||
if already_searched:
|
||||
_trace_lookup(trace, "cache", "resolved" if not cached.is_empty() else "cached_miss", started, {}, true)
|
||||
return cached
|
||||
# `_resolve()` does FileAccess + bounded subprocess lookup (forks
|
||||
# `bash -lc` / `which`), which can take 100ms-1s. Holding the mutex across that
|
||||
# would let a concurrent `invalidate()` on the main thread freeze the
|
||||
# editor for the duration of the subprocess — which defeats the whole
|
||||
# point of running CLI lookup off the main thread.
|
||||
var hit := _resolve(exe_name)
|
||||
var hit := _resolve(exe_name, trace)
|
||||
_mutex.lock()
|
||||
_cache[exe_name] = hit
|
||||
_searched[exe_name] = true
|
||||
@@ -69,14 +71,17 @@ static func _find_one(exe_name: String) -> String:
|
||||
return hit
|
||||
|
||||
|
||||
static func _resolve(exe_name: String) -> String:
|
||||
static func _resolve(exe_name: String, trace: Callable = Callable()) -> String:
|
||||
var started := Time.get_ticks_msec()
|
||||
var is_windows := OS.get_name() == "Windows"
|
||||
|
||||
# 1. Well-known locations
|
||||
for dir in _well_known_dirs():
|
||||
var full := dir.path_join(exe_name)
|
||||
if FileAccess.file_exists(full):
|
||||
_trace_lookup(trace, "well_known", "resolved", started)
|
||||
return full
|
||||
_trace_lookup(trace, "well_known", "no_match", started)
|
||||
|
||||
# 2. Login shell lookup (Unix only)
|
||||
if not is_windows:
|
||||
@@ -87,24 +92,58 @@ static func _resolve(exe_name: String) -> String:
|
||||
if shell.is_empty():
|
||||
shell = "/bin/bash"
|
||||
var stripped := exe_name.trim_suffix(".exe")
|
||||
started = Time.get_ticks_msec()
|
||||
var login_result := McpCliExec.run(shell, ["-lc", "command -v %s" % stripped], _LOOKUP_TIMEOUT_MS, false)
|
||||
if int(login_result.get("exit_code", -1)) == 0:
|
||||
var login_found: String = str(login_result.get("stdout", "")).strip_edges()
|
||||
if not login_found.is_empty() and FileAccess.file_exists(login_found):
|
||||
_trace_lookup(trace, "login_shell", "resolved", started, login_result)
|
||||
return login_found
|
||||
_trace_lookup(trace, "login_shell", _lookup_failure(login_result, false), started, login_result)
|
||||
|
||||
# 3. which / where with inherited PATH
|
||||
var lookup := "where" if is_windows else "which"
|
||||
started = Time.get_ticks_msec()
|
||||
var result := McpCliExec.run(lookup, [exe_name], _LOOKUP_TIMEOUT_MS, false)
|
||||
if int(result.get("exit_code", -1)) == 0:
|
||||
var output := str(result.get("stdout", ""))
|
||||
var lines := PackedStringArray(output.split("\n"))
|
||||
var found := _pick_best_path(lines) if is_windows else lines[0].strip_edges()
|
||||
if not found.is_empty():
|
||||
_trace_lookup(trace, "inherited_path", "resolved", started, result)
|
||||
return found
|
||||
_trace_lookup(trace, "inherited_path", _lookup_failure(result, true), started, result)
|
||||
return ""
|
||||
|
||||
|
||||
static func _lookup_failure(result: Dictionary, absence_exit: bool) -> String:
|
||||
for flag in ["termination_failed", "cancelled", "timed_out", "spawn_failed"]:
|
||||
if bool(result.get(flag, false)):
|
||||
return flag
|
||||
var code := int(result.get("exit_code", -1))
|
||||
if code == 0:
|
||||
return "empty_output" if str(result.get("stdout", "")).strip_edges().is_empty() else "unusable_output"
|
||||
return "not_found" if absence_exit and code == 1 else "nonzero_exit"
|
||||
|
||||
|
||||
static func _trace_lookup(
|
||||
trace: Callable, tier: String, status: String, started: int,
|
||||
result: Dictionary = {}, cache_hit := false,
|
||||
) -> void:
|
||||
if not trace.is_valid():
|
||||
return
|
||||
trace.call({
|
||||
"tier": tier, "status": status,
|
||||
"elapsed_ms": clampi(Time.get_ticks_msec() - started, 0, 2147483647),
|
||||
"cache_hit": cache_hit,
|
||||
"exit_code": clampi(int(result.get("exit_code", -1)), -2147483648, 2147483647),
|
||||
"timed_out": bool(result.get("timed_out", false)),
|
||||
"spawn_failed": bool(result.get("spawn_failed", false)),
|
||||
"cancelled": bool(result.get("cancelled", false)),
|
||||
"termination_failed": bool(result.get("termination_failed", false)),
|
||||
})
|
||||
|
||||
|
||||
## Executable extensions Windows' CreateProcessW can launch from a path
|
||||
## (after the cmd.exe wrap in `_cli_exec.gd`). Order is preference: `.exe`
|
||||
## is a native PE binary; `.cmd` / `.bat` go through the shell; `.com` is
|
||||
|
||||
Reference in New Issue
Block a user