Avoid collision with the existing Opswitch product while keeping the repository name as opencode-provider-switch. This aligns the CLI, provider key, config paths, examples, and Trellis history under one public-facing name.
74 lines
2.5 KiB
Go
74 lines
2.5 KiB
Go
package cli
|
|
|
|
import (
|
|
"fmt"
|
|
|
|
"github.com/spf13/cobra"
|
|
|
|
"github.com/Apale7/opencode-provider-switch/internal/opencode"
|
|
)
|
|
|
|
func newDoctorCmd() *cobra.Command {
|
|
return &cobra.Command{
|
|
Use: "doctor",
|
|
Short: "Validate ocswitch config (static checks, no upstream requests)",
|
|
Long: `doctor performs static validation for local ocswitch config and the expected
|
|
OpenCode sync result.
|
|
|
|
It loads local ocswitch config, checks alias/provider consistency, resolves the
|
|
default OpenCode sync target, and validates what provider.ocswitch would look like
|
|
there. It does not send any real requests to upstream providers and does not
|
|
consume model quota.
|
|
|
|
Run doctor before opencode sync or serve whenever you changed providers,
|
|
aliases, or local server settings.`,
|
|
Example: ` ocswitch doctor
|
|
ocswitch --config /path/to/config.json doctor`,
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
cfg, err := loadCfg()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
issues := cfg.Validate()
|
|
|
|
path, existed := opencode.ResolveGlobalConfigPath()
|
|
raw, err := opencode.Load(path)
|
|
if err != nil {
|
|
issues = append(issues, fmt.Errorf("load opencode config target: %w", err))
|
|
} else {
|
|
aliasNames := cfg.AvailableAliasNames()
|
|
baseURL := fmt.Sprintf("http://%s:%d/v1", cfg.Server.Host, cfg.Server.Port)
|
|
opencode.EnsureOcswitchProvider(raw, baseURL, cfg.Server.APIKey, aliasNames)
|
|
if err := opencode.ValidateOcswitchProvider(raw, baseURL, cfg.Server.APIKey, aliasNames); err != nil {
|
|
issues = append(issues, fmt.Errorf("opencode provider.ocswitch invalid: %w", err))
|
|
}
|
|
}
|
|
ok := len(issues) == 0
|
|
if ok {
|
|
fmt.Fprintf(cmd.OutOrStdout(), "✓ config loaded: %s\n", cfg.Path())
|
|
} else {
|
|
fmt.Fprintf(cmd.OutOrStdout(), "✗ config has %d issue(s):\n", len(issues))
|
|
for _, e := range issues {
|
|
fmt.Fprintf(cmd.OutOrStdout(), " - %s\n", e)
|
|
}
|
|
}
|
|
fmt.Fprintf(cmd.OutOrStdout(), " providers: %d\n", len(cfg.Providers))
|
|
fmt.Fprintf(cmd.OutOrStdout(), " aliases: %d\n", len(cfg.Aliases))
|
|
|
|
// Preview resolved opencode config target
|
|
marker := "(will be created)"
|
|
if existed {
|
|
marker = "(exists)"
|
|
}
|
|
fmt.Fprintf(cmd.OutOrStdout(), " opencode config target: %s %s\n", path, marker)
|
|
fmt.Fprintf(cmd.OutOrStdout(), " provider.ocswitch preview: valid=%v\n", ok)
|
|
|
|
fmt.Fprintf(cmd.OutOrStdout(), " proxy bind: %s:%d\n", cfg.Server.Host, cfg.Server.Port)
|
|
if !ok {
|
|
return fmt.Errorf("%d config issue(s)", len(issues))
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
}
|