mirror of
https://github.com/Twigpine/zero.git
synced 2026-11-11 18:00:54 -10:00
* fix(lint): resolve static analysis baseline * fix(lint): clear linux staticcheck findings Amp-Thread-ID: https://ampcode.com/threads/T-019fa018-b2ee-7442-8a31-80b2cf9dd270 Co-authored-by: Pierre Bruno <[email protected]> --------- Co-authored-by: Amp <[email protected]>
179 lines
5.5 KiB
Go
179 lines
5.5 KiB
Go
package cli
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"strings"
|
|
|
|
"github.com/Gitlawb/zero/internal/config"
|
|
"github.com/Gitlawb/zero/internal/provideronboarding"
|
|
)
|
|
|
|
type providerDetectOptions struct {
|
|
json bool
|
|
}
|
|
|
|
// providerDetectAction is the JSON/text shape of a single suggested next step.
|
|
type providerDetectAction struct {
|
|
Label string `json:"label"`
|
|
Command string `json:"command"`
|
|
Detail string `json:"detail,omitempty"`
|
|
}
|
|
|
|
type providerDetectRuntime struct {
|
|
CatalogID string `json:"catalogID"`
|
|
Name string `json:"name"`
|
|
BaseURL string `json:"baseURL"`
|
|
Models []string `json:"models,omitempty"`
|
|
Action providerDetectAction `json:"action"`
|
|
}
|
|
|
|
type providerDetectProvider struct {
|
|
Name string `json:"name"`
|
|
Active bool `json:"active"`
|
|
Actions []providerDetectAction `json:"actions,omitempty"`
|
|
}
|
|
|
|
type providerDetectReport struct {
|
|
DetectedRuntimes []providerDetectRuntime `json:"detectedRuntimes"`
|
|
Providers []providerDetectProvider `json:"providers"`
|
|
}
|
|
|
|
// runProvidersDetect probes the machine for running local, OpenAI-compatible
|
|
// model runtimes (Ollama, LM Studio) and prints a no-key adopt command for each
|
|
// one it finds, followed by the next-step actions for every already-configured
|
|
// provider. It is the onboarding-advice surface — "what can I do right now?" —
|
|
// and never errors on a machine with nothing running locally (it just reports an
|
|
// empty detected list).
|
|
func runProvidersDetect(args []string, stdout io.Writer, stderr io.Writer, deps appDeps) int {
|
|
options, help, err := parseProviderDetectArgs(args)
|
|
if err != nil {
|
|
return writeExecUsageError(stderr, err.Error())
|
|
}
|
|
if help {
|
|
if err := writeProvidersHelp(stdout); err != nil {
|
|
return exitCrash
|
|
}
|
|
return exitSuccess
|
|
}
|
|
|
|
resolved, exitCode := resolveCommandCenterConfig(stderr, deps)
|
|
if exitCode != exitSuccess {
|
|
return exitCode
|
|
}
|
|
|
|
ctx, stop := signalContext()
|
|
defer stop()
|
|
detected := deps.detectLocalRuntimes(ctx, provideronboarding.LocalDetectOptions{})
|
|
|
|
report := buildProviderDetectReport(resolved, detected)
|
|
|
|
if options.json {
|
|
if err := writePrettyJSON(stdout, report); err != nil {
|
|
return exitCrash
|
|
}
|
|
return exitSuccess
|
|
}
|
|
if _, err := fmt.Fprintln(stdout, formatProviderDetectReport(report)); err != nil {
|
|
return exitCrash
|
|
}
|
|
return exitSuccess
|
|
}
|
|
|
|
func parseProviderDetectArgs(args []string) (providerDetectOptions, bool, error) {
|
|
options := providerDetectOptions{}
|
|
for _, arg := range args {
|
|
switch arg {
|
|
case "-h", "--help", "help":
|
|
return options, true, nil
|
|
case "--json":
|
|
options.json = true
|
|
default:
|
|
return options, false, execUsageError{fmt.Sprintf("unexpected argument %q", arg)}
|
|
}
|
|
}
|
|
return options, false, nil
|
|
}
|
|
|
|
func buildProviderDetectReport(resolved config.ResolvedConfig, detected []provideronboarding.DetectedLocalRuntime) providerDetectReport {
|
|
report := providerDetectReport{
|
|
DetectedRuntimes: make([]providerDetectRuntime, 0, len(detected)),
|
|
Providers: make([]providerDetectProvider, 0, len(resolved.Providers)),
|
|
}
|
|
for _, runtime := range detected {
|
|
report.DetectedRuntimes = append(report.DetectedRuntimes, providerDetectRuntime{
|
|
CatalogID: runtime.CatalogID,
|
|
Name: runtime.Name,
|
|
BaseURL: runtime.BaseURL,
|
|
Models: runtime.Models,
|
|
Action: providerDetectActionFrom(runtime.SetupAction()),
|
|
})
|
|
}
|
|
for _, profile := range resolved.Providers {
|
|
active := strings.TrimSpace(profile.Name) != "" && profile.Name == resolved.ActiveProvider
|
|
state := provideronboarding.ProviderState{Profile: profile, Active: active}
|
|
actions := state.Actions()
|
|
entry := providerDetectProvider{
|
|
Name: profile.Name,
|
|
Active: active,
|
|
Actions: make([]providerDetectAction, 0, len(actions)),
|
|
}
|
|
for _, action := range actions {
|
|
entry.Actions = append(entry.Actions, providerDetectActionFrom(action))
|
|
}
|
|
report.Providers = append(report.Providers, entry)
|
|
}
|
|
return report
|
|
}
|
|
|
|
func providerDetectActionFrom(action provideronboarding.Action) providerDetectAction {
|
|
return providerDetectAction{Label: action.Label, Command: action.Command, Detail: action.Detail}
|
|
}
|
|
|
|
func formatProviderDetectReport(report providerDetectReport) string {
|
|
lines := []string{"Detected local runtimes:"}
|
|
if len(report.DetectedRuntimes) == 0 {
|
|
lines = append(lines, " (none running on default ports)")
|
|
}
|
|
for _, runtime := range report.DetectedRuntimes {
|
|
name := strings.TrimSpace(runtime.Name)
|
|
if name == "" {
|
|
name = runtime.CatalogID
|
|
}
|
|
lines = append(lines, " "+name+" — "+runtime.BaseURL)
|
|
if len(runtime.Models) > 0 {
|
|
lines = append(lines, " models: "+strings.Join(runtime.Models, ", "))
|
|
}
|
|
if command := strings.TrimSpace(runtime.Action.Command); command != "" {
|
|
lines = append(lines, " "+runtime.Action.Label+": "+command)
|
|
}
|
|
}
|
|
|
|
lines = append(lines, "", "Configured providers:")
|
|
if len(report.Providers) == 0 {
|
|
lines = append(lines, " (none configured — run `zero setup`)")
|
|
}
|
|
for _, provider := range report.Providers {
|
|
name := strings.TrimSpace(provider.Name)
|
|
if name == "" {
|
|
name = "(unnamed)"
|
|
}
|
|
marker := ""
|
|
if provider.Active {
|
|
marker = " (active)"
|
|
}
|
|
lines = append(lines, " "+name+marker)
|
|
if len(provider.Actions) == 0 {
|
|
lines = append(lines, " (ready)")
|
|
}
|
|
for _, action := range provider.Actions {
|
|
line := " " + action.Label
|
|
if command := strings.TrimSpace(action.Command); command != "" {
|
|
line += ": " + command
|
|
}
|
|
lines = append(lines, line)
|
|
}
|
|
}
|
|
return strings.Join(lines, "\n")
|
|
}
|