81 lines
2.8 KiB
C#
81 lines
2.8 KiB
C#
using System.Diagnostics;
|
|
|
|
namespace AxCopilot.Services;
|
|
|
|
/// <summary>
|
|
/// 런타임 환경(Python, Node.js 등) 설치 여부를 감지하는 정적 헬퍼.
|
|
/// 스킬 시스템에서 런타임 의존 스킬의 가용성을 판단하는 데 사용합니다.
|
|
/// 결과는 5분간 캐시되어 반복 호출 시 프로세스 생성을 최소화합니다.
|
|
/// </summary>
|
|
public static class RuntimeDetector
|
|
{
|
|
private static Dictionary<string, bool>? _cache;
|
|
private static DateTime _cacheTime;
|
|
private static readonly TimeSpan CacheTtl = TimeSpan.FromMinutes(5);
|
|
|
|
/// <summary>지정된 런타임이 설치되어 PATH에서 접근 가능한지 확인합니다.</summary>
|
|
/// <param name="runtime">런타임 이름 (python, node, dotnet 등)</param>
|
|
public static bool IsAvailable(string runtime)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(runtime)) return true;
|
|
runtime = runtime.Trim().ToLowerInvariant();
|
|
|
|
// 캐시 확인
|
|
if (_cache != null && (DateTime.UtcNow - _cacheTime) < CacheTtl)
|
|
{
|
|
if (_cache.TryGetValue(runtime, out var cached))
|
|
return cached;
|
|
}
|
|
else
|
|
{
|
|
_cache = new Dictionary<string, bool>(StringComparer.OrdinalIgnoreCase);
|
|
_cacheTime = DateTime.UtcNow;
|
|
}
|
|
|
|
var available = DetectRuntime(runtime);
|
|
_cache[runtime] = available;
|
|
return available;
|
|
}
|
|
|
|
/// <summary>캐시를 강제로 초기화합니다.</summary>
|
|
public static void ClearCache() => _cache = null;
|
|
|
|
private static bool DetectRuntime(string runtime)
|
|
{
|
|
// 1단계: where.exe로 PATH 존재 확인
|
|
var wherePath = RunQuick("where.exe", runtime);
|
|
if (string.IsNullOrEmpty(wherePath)) return false;
|
|
|
|
// 2단계: --version으로 실행 가능 확인
|
|
var versionArg = runtime switch
|
|
{
|
|
"java" => "-version",
|
|
_ => "--version",
|
|
};
|
|
var version = RunQuick(runtime, versionArg);
|
|
return !string.IsNullOrEmpty(version);
|
|
}
|
|
|
|
private static string? RunQuick(string exe, string args)
|
|
{
|
|
try
|
|
{
|
|
var psi = new ProcessStartInfo(exe, args)
|
|
{
|
|
RedirectStandardOutput = true,
|
|
RedirectStandardError = true,
|
|
UseShellExecute = false,
|
|
CreateNoWindow = true,
|
|
};
|
|
using var proc = Process.Start(psi);
|
|
if (proc == null) return null;
|
|
var output = proc.StandardOutput.ReadToEnd();
|
|
var error = proc.StandardError.ReadToEnd();
|
|
proc.WaitForExit(5000);
|
|
var result = string.IsNullOrWhiteSpace(output) ? error : output;
|
|
return string.IsNullOrWhiteSpace(result) ? null : result.Trim();
|
|
}
|
|
catch { return null; }
|
|
}
|
|
}
|