agent/.core/reference/app.go
Virgil de7844dcb9 fix(ax): restore live agent reference paths
Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-29 20:24:58 +00:00

93 lines
2.5 KiB
Go

// SPDX-License-Identifier: EUPL-1.2
// Application identity for the Core framework.
package core
import (
"os"
"path/filepath"
)
// App holds the application identity and optional GUI runtime.
//
// app := core.App{}.New(core.NewOptions(
// core.Option{Key: "name", Value: "Core CLI"},
// core.Option{Key: "version", Value: "1.0.0"},
// ))
type App struct {
Name string
Version string
Description string
Filename string
Path string
Runtime any // GUI runtime (e.g., Wails App). Nil for CLI-only.
}
// New creates an App from Options.
//
// app := core.App{}.New(core.NewOptions(
// core.Option{Key: "name", Value: "myapp"},
// core.Option{Key: "version", Value: "1.0.0"},
// ))
func (a App) New(opts Options) App {
if name := opts.String("name"); name != "" {
a.Name = name
}
if version := opts.String("version"); version != "" {
a.Version = version
}
if desc := opts.String("description"); desc != "" {
a.Description = desc
}
if filename := opts.String("filename"); filename != "" {
a.Filename = filename
}
return a
}
// Find locates a program on PATH and returns a Result containing the App.
// Uses os.Stat to search PATH directories — no os/exec dependency.
//
// r := core.App{}.Find("node", "Node.js")
// if r.OK { app := r.Value.(*App) }
func (a App) Find(filename, name string) Result {
// If filename contains a separator, check it directly
if Contains(filename, string(os.PathSeparator)) {
abs, err := filepath.Abs(filename)
if err != nil {
return Result{err, false}
}
if isExecutable(abs) {
return Result{&App{Name: name, Filename: filename, Path: abs}, true}
}
return Result{E("app.Find", Concat(filename, " not found"), nil), false}
}
// Search PATH
pathEnv := os.Getenv("PATH")
if pathEnv == "" {
return Result{E("app.Find", "PATH is empty", nil), false}
}
for _, dir := range Split(pathEnv, string(os.PathListSeparator)) {
candidate := filepath.Join(dir, filename)
if isExecutable(candidate) {
abs, err := filepath.Abs(candidate)
if err != nil {
continue
}
return Result{&App{Name: name, Filename: filename, Path: abs}, true}
}
}
return Result{E("app.Find", Concat(filename, " not found on PATH"), nil), false}
}
// isExecutable checks if a path exists and is executable.
func isExecutable(path string) bool {
info, err := os.Stat(path)
if err != nil {
return false
}
// Regular file with at least one execute bit
return !info.IsDir() && info.Mode()&0111 != 0
}