Files
agent-wiki-ui/Core/EngineRunner.cs
T
davideandClaude Sonnet 5.5 8b20f5224e Impostazioni: controllo e aggiornamento dei CLI con pulsante evidenziato
Sezione Aggiornamenti nel riquadro Impostazioni: per ogni CLI installato
versione, esito del controllo (disponibile, aggiornato, non riuscito) e
pulsante Aggiorna che esegue il comando ufficiale. Il pulsante e un puntino
sul pulsante Impostazioni si evidenziano solo se c'è una versione nuova. Il
controllo legge l'ultima versione dalle sorgenti ufficiali (downloads.claude.ai
e release GitHub di Codex), parte all'avvio e all'apertura del riquadro con
cache di 5 minuti e non fallisce mai in modo invasivo. L'aggiornamento ha
tempo massimo di 10 minuti, blocca invio e cambio CLI, e in caso di errore
suggerisce il comando manuale per npm, Homebrew, WinGet o installazione nativa.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-15 11:49:22 +02:00

73 lines
3.2 KiB
C#

using System.Diagnostics;
namespace AgentWikiUi.Core;
// Unico punto di avvio dei motori, condiviso da CLI e GUI.
static class EngineRunner
{
public static ProcessStartInfo CreateStart(Engine engine, string prompt, bool captured, string? sessionId = null, bool resume = false, string model = "", string effort = "") =>
CreateStart(engine, engine.BuildArgs(prompt, sessionId, resume, model, effort, structured: captured), captured);
public static ProcessStartInfo CreateStart(Engine engine, IEnumerable<string> args, bool captured)
{
var psi = new ProcessStartInfo(engine.Executable)
{
WorkingDirectory = AppContext.BaseDirectory, // mai la cwd del chiamante né Assembly.Location (vuoto in single-file)
UseShellExecute = false,
RedirectStandardInput = true, // chiuso subito dal chiamante: evita l'attesa di stdin del motore
RedirectStandardOutput = captured,
RedirectStandardError = captured,
CreateNoWindow = true,
};
foreach (var a in args) psi.ArgumentList.Add(a);
return psi;
}
// CLI: stdout/stderr ereditati (streaming), exit code propagato, 127 se l'eseguibile manca.
public static int RunInherited(Engine engine, string prompt)
{
try
{
using var p = Process.Start(CreateStart(engine, prompt, captured: false))!;
p.StandardInput.Close();
p.WaitForExit();
return p.ExitCode;
}
catch (System.ComponentModel.Win32Exception)
{
Console.Error.WriteLine($"'{engine.Executable}' non trovato nel PATH.");
return 127;
}
}
// GUI: output catturato e interpretato dal motore. Win32Exception se l'eseguibile non è nel PATH.
public static async Task<EngineReply> RunCapturedAsync(Engine engine, string prompt, string? sessionId, bool resume, string model, string effort)
{
using var p = Process.Start(CreateStart(engine, prompt, captured: true, sessionId, resume, model, effort))!;
p.StandardInput.Close();
var o = p.StandardOutput.ReadToEndAsync();
var e = p.StandardError.ReadToEndAsync();
await p.WaitForExitAsync();
return engine.Parse(p.ExitCode, await o, await e);
}
// Comandi di servizio del motore (--version, update): output catturato, tempo massimo, nessuna chat.
// Win32Exception se l'eseguibile non è nel PATH; allo scadere il processo e i suoi figli sono terminati.
public static async Task<(int Code, string Out, string Err, bool TimedOut)> RunToolAsync(Engine engine, IEnumerable<string> args, TimeSpan timeout)
{
using var p = Process.Start(CreateStart(engine, args, captured: true))!;
p.StandardInput.Close();
var o = p.StandardOutput.ReadToEndAsync();
var e = p.StandardError.ReadToEndAsync();
using var cts = new CancellationTokenSource(timeout);
try { await p.WaitForExitAsync(cts.Token); }
catch (OperationCanceledException)
{
try { p.Kill(entireProcessTree: true); } catch (InvalidOperationException) { }
await p.WaitForExitAsync();
return (-1, await o, await e, true);
}
return (p.ExitCode, await o, await e, false);
}
}