Files
fn_registry/functions/ml/sdcli_resolve_binary.go
egutierrez aec5d82011 feat(ml): auto-commit con 14 cambios
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 01:22:02 +02:00

89 lines
2.3 KiB
Go

package ml
import (
"context"
"fmt"
"os"
"os/exec"
"strings"
"time"
)
// SdcliBinary describe el binario sd resuelto: su path absoluto, version detectada
// y como fue localizado.
type SdcliBinary struct {
Path string `json:"path"`
Version string `json:"version,omitempty"`
Source string `json:"source"` // "config" | "path"
}
// SdcliResolveBinary localiza el binario sd / sd-cli de stable-diffusion.cpp.
//
// Orden de busqueda:
// 1. Si hint != "" y el archivo existe y es ejecutable: usar como "config".
// 2. exec.LookPath("sd"): primer candidato en PATH.
// 3. exec.LookPath("sd-cli"): segundo candidato en PATH.
// 4. Error descriptivo si no se encuentra ninguno.
//
// Tras localizar el binario, intenta obtener la version ejecutando
// `<bin> --version` con timeout de 3 segundos. Si el comando falla
// o no produce output, Version queda vacia (no es error fatal).
func SdcliResolveBinary(hint string) (SdcliBinary, error) {
var binPath string
var source string
if hint != "" {
info, err := os.Stat(hint)
if err != nil {
return SdcliBinary{}, fmt.Errorf("sdcli hint %q: %w", hint, err)
}
if info.Mode()&0o111 == 0 {
return SdcliBinary{}, fmt.Errorf("sdcli hint %q: file exists but is not executable", hint)
}
binPath = hint
source = "config"
}
if binPath == "" {
if p, err := exec.LookPath("sd"); err == nil {
binPath = p
source = "path"
}
}
if binPath == "" {
if p, err := exec.LookPath("sd-cli"); err == nil {
binPath = p
source = "path"
}
}
if binPath == "" {
return SdcliBinary{}, fmt.Errorf(
"sd binary not found in PATH (hint: install from leejet/stable-diffusion.cpp)",
)
}
version := sdcliProbeVersion(binPath)
return SdcliBinary{
Path: binPath,
Version: version,
Source: source,
}, nil
}
// sdcliProbeVersion ejecuta `<bin> --version` con timeout 3s y retorna
// la primera linea de la salida. Retorna "" si el comando falla o no
// produce output; no propaga el error (version es best-effort).
func sdcliProbeVersion(binPath string) string {
ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
defer cancel()
out, err := exec.CommandContext(ctx, binPath, "--version").Output()
if err != nil {
return ""
}
line := strings.TrimSpace(strings.SplitN(string(out), "\n", 2)[0])
return line
}