RunServer executes the 'chatcli server' subcommand.
(args []string, llmMgr manager.LLMManager, logger *zap.Logger)
| 60 | |
| 61 | // RunServer executes the 'chatcli server' subcommand. |
| 62 | func RunServer(args []string, llmMgr manager.LLMManager, logger *zap.Logger) error { |
| 63 | fs := flag.NewFlagSet("server", flag.ContinueOnError) |
| 64 | |
| 65 | opts := &ServerOptions{} |
| 66 | fs.IntVar(&opts.Port, "port", getEnvInt("CHATCLI_SERVER_PORT", 50051), "gRPC server port") |
| 67 | fs.StringVar(&opts.Token, "token", os.Getenv("CHATCLI_SERVER_TOKEN"), "Authentication token (empty = no auth)") |
| 68 | fs.StringVar(&opts.CertFile, "tls-cert", os.Getenv("CHATCLI_SERVER_TLS_CERT"), "TLS certificate file path") |
| 69 | fs.StringVar(&opts.KeyFile, "tls-key", os.Getenv("CHATCLI_SERVER_TLS_KEY"), "TLS key file path") |
| 70 | fs.StringVar(&opts.Provider, "provider", os.Getenv("LLM_PROVIDER"), "Default LLM provider") |
| 71 | fs.StringVar(&opts.Model, "model", "", "Default LLM model") |
| 72 | fs.IntVar(&opts.MetricsPort, "metrics-port", getEnvInt("CHATCLI_METRICS_PORT", 9090), "Prometheus metrics HTTP port (0 = disabled)") |
| 73 | |
| 74 | // Fallback chain flags |
| 75 | fs.StringVar(&opts.FallbackProviders, "fallback-providers", os.Getenv("CHATCLI_FALLBACK_PROVIDERS"), "Comma-separated fallback providers (e.g. OPENAI,CLAUDEAI,GOOGLEAI,ZAI,MINIMAX)") |
| 76 | fs.IntVar(&opts.FallbackMaxRetries, "fallback-max-retries", getEnvInt("CHATCLI_FALLBACK_MAX_RETRIES", 2), "Max retries per provider before fallback") |
| 77 | fs.DurationVar(&opts.FallbackCooldownBase, "fallback-cooldown-base", getEnvDuration("CHATCLI_FALLBACK_COOLDOWN_BASE", 30*time.Second), "Base cooldown duration after provider failure") |
| 78 | fs.DurationVar(&opts.FallbackCooldownMax, "fallback-cooldown-max", getEnvDuration("CHATCLI_FALLBACK_COOLDOWN_MAX", 5*time.Minute), "Maximum cooldown duration") |
| 79 | |
| 80 | // MCP flags |
| 81 | fs.StringVar(&opts.MCPConfigPath, "mcp-config", os.Getenv("CHATCLI_MCP_CONFIG"), "Path to MCP servers config JSON") |
| 82 | |
| 83 | // K8s watcher flags |
| 84 | fs.StringVar(&opts.WatchDeployment, "watch-deployment", os.Getenv("CHATCLI_WATCH_DEPLOYMENT"), "K8s deployment to monitor (enables watcher)") |
| 85 | fs.StringVar(&opts.WatchNamespace, "watch-namespace", getEnvOrDefault("CHATCLI_WATCH_NAMESPACE", "default"), "K8s namespace for watcher") |
| 86 | fs.DurationVar(&opts.WatchInterval, "watch-interval", getEnvDuration("CHATCLI_WATCH_INTERVAL", 30*time.Second), "Watcher collection interval") |
| 87 | fs.DurationVar(&opts.WatchWindow, "watch-window", getEnvDuration("CHATCLI_WATCH_WINDOW", 2*time.Hour), "Watcher observation window") |
| 88 | fs.IntVar(&opts.WatchMaxLogs, "watch-max-log-lines", getEnvInt("CHATCLI_WATCH_MAX_LOG_LINES", 100), "Max log lines per pod") |
| 89 | fs.StringVar(&opts.WatchKubeconfig, "watch-kubeconfig", os.Getenv("CHATCLI_KUBECONFIG"), "Path to kubeconfig for watcher") |
| 90 | fs.StringVar(&opts.WatchConfig, "watch-config", os.Getenv("CHATCLI_WATCH_CONFIG"), "Path to multi-target watch config YAML") |
| 91 | |
| 92 | if err := fs.Parse(args); err != nil { |
| 93 | return err |
| 94 | } |
| 95 | |
| 96 | // Resolve provider if not set |
| 97 | if opts.Provider == "" { |
| 98 | available := llmMgr.GetAvailableProviders() |
| 99 | if len(available) > 0 { |
| 100 | opts.Provider = available[0] |
| 101 | } |
| 102 | } |
| 103 | |
| 104 | // Resolve model name from the client if possible |
| 105 | if opts.Model == "" && opts.Provider != "" { |
| 106 | if c, err := llmMgr.GetClient(opts.Provider, ""); err == nil { |
| 107 | opts.Model = c.GetModelName() |
| 108 | } |
| 109 | } |
| 110 | |
| 111 | // Create session manager for server-side session persistence |
| 112 | sessionMgr, err := cli.NewSessionManager(logger) |
| 113 | if err != nil { |
| 114 | logger.Warn(i18n.T("cmd.server.session_init_failed"), zap.Error(err)) |
| 115 | } |
| 116 | |
| 117 | cfg := server.Config{ |
| 118 | Port: opts.Port, |
| 119 | Token: opts.Token, |
no test coverage detected