mirror of
https://github.com/onyx-dot-app/onyx.git
synced 2026-04-09 17:02:48 +00:00
Compare commits
3 Commits
edge
...
jamison/cl
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
77ce667b21 | ||
|
|
1e0a8afc72 | ||
|
|
85302a1cde |
@@ -6,7 +6,6 @@ import (
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/onyx-dot-app/onyx/cli/internal/api"
|
||||
"github.com/onyx-dot-app/onyx/cli/internal/config"
|
||||
"github.com/onyx-dot-app/onyx/cli/internal/exitcodes"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@@ -25,7 +24,7 @@ Use --json for machine-readable output.`,
|
||||
onyx-cli agents --json
|
||||
onyx-cli agents --json | jq '.[].name'`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cfg := config.Load()
|
||||
cfg := loadConfig(cmd)
|
||||
if !cfg.IsConfigured() {
|
||||
return exitcodes.New(exitcodes.NotConfigured, "onyx CLI is not configured\n Run: onyx-cli configure")
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import (
|
||||
"syscall"
|
||||
|
||||
"github.com/onyx-dot-app/onyx/cli/internal/api"
|
||||
"github.com/onyx-dot-app/onyx/cli/internal/config"
|
||||
"github.com/onyx-dot-app/onyx/cli/internal/exitcodes"
|
||||
"github.com/onyx-dot-app/onyx/cli/internal/models"
|
||||
"github.com/onyx-dot-app/onyx/cli/internal/overflow"
|
||||
@@ -49,7 +48,7 @@ to a temp file. Set --max-output 0 to disable truncation.`,
|
||||
cat error.log | onyx-cli ask --prompt "Find the root cause"
|
||||
echo "what is onyx?" | onyx-cli ask`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cfg := config.Load()
|
||||
cfg := loadConfig(cmd)
|
||||
if !cfg.IsConfigured() {
|
||||
return exitcodes.New(exitcodes.NotConfigured, "onyx CLI is not configured\n Run: onyx-cli configure")
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ an interactive setup wizard will guide you through configuration.`,
|
||||
Example: ` onyx-cli chat
|
||||
onyx-cli`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cfg := config.Load()
|
||||
cfg := loadConfig(cmd)
|
||||
|
||||
// First-run: onboarding
|
||||
if !config.ConfigExists() || !cfg.IsConfigured() {
|
||||
|
||||
@@ -69,7 +69,7 @@ Use --dry-run to test the connection without saving the configuration.`,
|
||||
return exitcodes.New(exitcodes.BadRequest, "both --server-url and --api-key are required for non-interactive setup\n Run 'onyx-cli configure' without flags for interactive setup")
|
||||
}
|
||||
|
||||
cfg := config.Load()
|
||||
cfg := loadConfig(cmd)
|
||||
onboarding.Run(&cfg)
|
||||
return nil
|
||||
},
|
||||
|
||||
@@ -12,7 +12,7 @@ func newExperimentsCmd() *cobra.Command {
|
||||
Use: "experiments",
|
||||
Short: "List experimental features and their status",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cfg := config.Load()
|
||||
cfg := loadConfig(cmd)
|
||||
_, _ = fmt.Fprintln(cmd.OutOrStdout(), config.ExperimentsText(cfg.Features))
|
||||
return nil
|
||||
},
|
||||
|
||||
@@ -13,6 +13,20 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// loadConfig loads the CLI config, using the --config-file persistent flag if set.
|
||||
func loadConfig(cmd *cobra.Command) config.OnyxCliConfig {
|
||||
cf, _ := cmd.Flags().GetString("config-file")
|
||||
return config.Load(cf)
|
||||
}
|
||||
|
||||
// effectiveConfigPath returns the config file path, respecting --config-file.
|
||||
func effectiveConfigPath(cmd *cobra.Command) string {
|
||||
if cf, _ := cmd.Flags().GetString("config-file"); cf != "" {
|
||||
return cf
|
||||
}
|
||||
return config.ConfigFilePath()
|
||||
}
|
||||
|
||||
// Version and Commit are set via ldflags at build time.
|
||||
var (
|
||||
Version string
|
||||
@@ -29,7 +43,7 @@ func fullVersion() string {
|
||||
func printVersion(cmd *cobra.Command) {
|
||||
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "Client version: %s\n", fullVersion())
|
||||
|
||||
cfg := config.Load()
|
||||
cfg := loadConfig(cmd)
|
||||
if !cfg.IsConfigured() {
|
||||
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "Server version: unknown (not configured)\n")
|
||||
return
|
||||
@@ -84,6 +98,8 @@ func Execute() error {
|
||||
}
|
||||
|
||||
rootCmd.PersistentFlags().BoolVar(&opts.Debug, "debug", false, "run in debug mode")
|
||||
rootCmd.PersistentFlags().String("config-file", "",
|
||||
"Path to config file (default: "+config.ConfigFilePath()+")")
|
||||
|
||||
// Custom --version flag instead of Cobra's built-in (which only shows one version string)
|
||||
var showVersion bool
|
||||
|
||||
@@ -50,16 +50,14 @@ func sessionEnv(s ssh.Session, key string) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func validateAPIKey(serverURL string, apiKey string) error {
|
||||
func validateAPIKey(serverCfg config.OnyxCliConfig, apiKey string) error {
|
||||
trimmedKey := strings.TrimSpace(apiKey)
|
||||
if len(trimmedKey) > maxAPIKeyLength {
|
||||
return fmt.Errorf("API key is too long (max %d characters)", maxAPIKeyLength)
|
||||
}
|
||||
|
||||
cfg := config.OnyxCliConfig{
|
||||
ServerURL: serverURL,
|
||||
APIKey: trimmedKey,
|
||||
}
|
||||
cfg := serverCfg
|
||||
cfg.APIKey = trimmedKey
|
||||
client := api.NewClient(cfg)
|
||||
ctx, cancel := context.WithTimeout(context.Background(), apiKeyValidationTimeout)
|
||||
defer cancel()
|
||||
@@ -83,7 +81,7 @@ type authValidatedMsg struct {
|
||||
|
||||
type authModel struct {
|
||||
input textinput.Model
|
||||
serverURL string
|
||||
serverCfg config.OnyxCliConfig
|
||||
state authState
|
||||
apiKey string // set on successful validation
|
||||
errMsg string
|
||||
@@ -91,7 +89,7 @@ type authModel struct {
|
||||
aborted bool
|
||||
}
|
||||
|
||||
func newAuthModel(serverURL, initialErr string) authModel {
|
||||
func newAuthModel(serverCfg config.OnyxCliConfig, initialErr string) authModel {
|
||||
ti := textinput.New()
|
||||
ti.Prompt = " API Key: "
|
||||
ti.EchoMode = textinput.EchoPassword
|
||||
@@ -102,7 +100,7 @@ func newAuthModel(serverURL, initialErr string) authModel {
|
||||
|
||||
return authModel{
|
||||
input: ti,
|
||||
serverURL: serverURL,
|
||||
serverCfg: serverCfg,
|
||||
errMsg: initialErr,
|
||||
}
|
||||
}
|
||||
@@ -138,9 +136,9 @@ func (m authModel) Update(msg tea.Msg) (authModel, tea.Cmd) {
|
||||
}
|
||||
m.state = authValidating
|
||||
m.errMsg = ""
|
||||
serverURL := m.serverURL
|
||||
serverCfg := m.serverCfg
|
||||
return m, func() tea.Msg {
|
||||
return authValidatedMsg{key: key, err: validateAPIKey(serverURL, key)}
|
||||
return authValidatedMsg{key: key, err: validateAPIKey(serverCfg, key)}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -171,12 +169,13 @@ func (m authModel) Update(msg tea.Msg) (authModel, tea.Cmd) {
|
||||
}
|
||||
|
||||
func (m authModel) View() string {
|
||||
settingsURL := strings.TrimRight(m.serverURL, "/") + "/app/settings/accounts-access"
|
||||
serverURL := m.serverCfg.ServerURL
|
||||
settingsURL := strings.TrimRight(serverURL, "/") + "/app/settings/accounts-access"
|
||||
|
||||
var b strings.Builder
|
||||
b.WriteString("\n")
|
||||
b.WriteString(" \x1b[1;35mOnyx CLI\x1b[0m\n")
|
||||
b.WriteString(" \x1b[90m" + m.serverURL + "\x1b[0m\n")
|
||||
b.WriteString(" \x1b[90m" + serverURL + "\x1b[0m\n")
|
||||
b.WriteString("\n")
|
||||
b.WriteString(" Generate an API key at:\n")
|
||||
b.WriteString(" \x1b[4;34m" + settingsURL + "\x1b[0m\n")
|
||||
@@ -215,7 +214,7 @@ type serveModel struct {
|
||||
|
||||
func newServeModel(serverCfg config.OnyxCliConfig, initialErr string) serveModel {
|
||||
return serveModel{
|
||||
auth: newAuthModel(serverCfg.ServerURL, initialErr),
|
||||
auth: newAuthModel(serverCfg, initialErr),
|
||||
serverCfg: serverCfg,
|
||||
}
|
||||
}
|
||||
@@ -238,11 +237,8 @@ func (m serveModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
return m, tea.Quit
|
||||
}
|
||||
if m.auth.apiKey != "" {
|
||||
cfg := config.OnyxCliConfig{
|
||||
ServerURL: m.serverCfg.ServerURL,
|
||||
APIKey: m.auth.apiKey,
|
||||
DefaultAgentID: m.serverCfg.DefaultAgentID,
|
||||
}
|
||||
cfg := m.serverCfg
|
||||
cfg.APIKey = m.auth.apiKey
|
||||
m.tui = tui.NewModel(cfg)
|
||||
m.authed = true
|
||||
w, h := m.width, m.height
|
||||
@@ -280,6 +276,8 @@ func newServeCmd() *cobra.Command {
|
||||
rateLimitPerMin int
|
||||
rateLimitBurst int
|
||||
rateLimitCache int
|
||||
serverURL string
|
||||
apiServerURL string
|
||||
)
|
||||
|
||||
cmd := &cobra.Command{
|
||||
@@ -300,9 +298,18 @@ environment variable (the --host-key flag takes precedence).`,
|
||||
Example: ` onyx-cli serve --port 2222
|
||||
ssh localhost -p 2222
|
||||
onyx-cli serve --host 0.0.0.0 --port 2222
|
||||
onyx-cli serve --idle-timeout 30m --max-session-timeout 2h`,
|
||||
onyx-cli serve --idle-timeout 30m --max-session-timeout 2h
|
||||
onyx-cli serve --server-url https://my-onyx.example.com
|
||||
onyx-cli serve --api-server-url http://api_server:8080 # bypass nginx
|
||||
onyx-cli serve --config-file /etc/onyx-cli/config.json # global flag`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
serverCfg := config.Load()
|
||||
serverCfg := loadConfig(cmd)
|
||||
if cmd.Flags().Changed("server-url") {
|
||||
serverCfg.ServerURL = serverURL
|
||||
}
|
||||
if cmd.Flags().Changed("api-server-url") {
|
||||
serverCfg.InternalURL = apiServerURL
|
||||
}
|
||||
if serverCfg.ServerURL == "" {
|
||||
return exitcodes.New(exitcodes.NotConfigured, "server URL is not configured\n Run: onyx-cli configure")
|
||||
}
|
||||
@@ -333,7 +340,7 @@ environment variable (the --host-key flag takes precedence).`,
|
||||
var envErr string
|
||||
|
||||
if apiKey != "" {
|
||||
if err := validateAPIKey(serverCfg.ServerURL, apiKey); err != nil {
|
||||
if err := validateAPIKey(serverCfg, apiKey); err != nil {
|
||||
envErr = fmt.Sprintf("ONYX_API_KEY from SSH environment is invalid: %s", err.Error())
|
||||
apiKey = ""
|
||||
}
|
||||
@@ -341,11 +348,8 @@ environment variable (the --host-key flag takes precedence).`,
|
||||
|
||||
if apiKey != "" {
|
||||
// Env key is valid — go straight to the TUI.
|
||||
cfg := config.OnyxCliConfig{
|
||||
ServerURL: serverCfg.ServerURL,
|
||||
APIKey: apiKey,
|
||||
DefaultAgentID: serverCfg.DefaultAgentID,
|
||||
}
|
||||
cfg := serverCfg
|
||||
cfg.APIKey = apiKey
|
||||
return tui.NewModel(cfg), []tea.ProgramOption{
|
||||
tea.WithAltScreen(),
|
||||
tea.WithMouseCellMotion(),
|
||||
@@ -446,6 +450,10 @@ environment variable (the --host-key flag takes precedence).`,
|
||||
defaultServeRateLimitCacheSize,
|
||||
"Maximum number of IP limiter entries tracked in memory",
|
||||
)
|
||||
cmd.Flags().StringVar(&serverURL, "server-url", "",
|
||||
"Onyx server URL (overrides config file and $"+config.EnvServerURL+")")
|
||||
cmd.Flags().StringVar(&apiServerURL, "api-server-url", "",
|
||||
"API server URL for direct access, bypassing nginx (overrides $"+config.EnvAPIServerURL+")")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
@@ -4,10 +4,10 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/onyx-dot-app/onyx/cli/internal/api"
|
||||
"github.com/onyx-dot-app/onyx/cli/internal/config"
|
||||
"github.com/onyx-dot-app/onyx/cli/internal/exitcodes"
|
||||
"github.com/onyx-dot-app/onyx/cli/internal/version"
|
||||
log "github.com/sirupsen/logrus"
|
||||
@@ -23,19 +23,21 @@ is valid. Also reports the server version and warns if it is below the
|
||||
minimum required.`,
|
||||
Example: ` onyx-cli validate-config`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cfgPath := effectiveConfigPath(cmd)
|
||||
|
||||
// Check config file
|
||||
if !config.ConfigExists() {
|
||||
return exitcodes.Newf(exitcodes.NotConfigured, "config file not found at %s\n Run: onyx-cli configure", config.ConfigFilePath())
|
||||
if _, err := os.Stat(cfgPath); err != nil {
|
||||
return exitcodes.Newf(exitcodes.NotConfigured, "config file not found at %s\n Run: onyx-cli configure", cfgPath)
|
||||
}
|
||||
|
||||
cfg := config.Load()
|
||||
cfg := loadConfig(cmd)
|
||||
|
||||
// Check API key
|
||||
if !cfg.IsConfigured() {
|
||||
return exitcodes.New(exitcodes.NotConfigured, "API key is missing\n Run: onyx-cli configure")
|
||||
}
|
||||
|
||||
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "Config: %s\n", config.ConfigFilePath())
|
||||
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "Config: %s\n", cfgPath)
|
||||
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "Server: %s\n", cfg.ServerURL)
|
||||
|
||||
// Test connection
|
||||
|
||||
@@ -16,18 +16,30 @@ import (
|
||||
|
||||
"github.com/onyx-dot-app/onyx/cli/internal/config"
|
||||
"github.com/onyx-dot-app/onyx/cli/internal/models"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// Client is the Onyx API client.
|
||||
type Client struct {
|
||||
baseURL string
|
||||
serverURL string // root server URL (for reachability checks)
|
||||
baseURL string // API base URL (includes /api when going through nginx)
|
||||
apiKey string
|
||||
httpClient *http.Client // default 30s timeout for quick requests
|
||||
longHTTPClient *http.Client // 5min timeout for streaming/uploads
|
||||
}
|
||||
|
||||
// NewClient creates a new API client from config.
|
||||
//
|
||||
// When InternalURL is set, requests go directly to the API server (no /api
|
||||
// prefix needed — mirrors INTERNAL_URL in the web server). Otherwise,
|
||||
// requests go through the nginx proxy at ServerURL which strips /api.
|
||||
func NewClient(cfg config.OnyxCliConfig) *Client {
|
||||
baseURL := apiBaseURL(cfg)
|
||||
log.WithFields(log.Fields{
|
||||
"server_url": cfg.ServerURL,
|
||||
"internal_url": cfg.InternalURL,
|
||||
"base_url": baseURL,
|
||||
}).Debug("creating API client")
|
||||
var transport *http.Transport
|
||||
if t, ok := http.DefaultTransport.(*http.Transport); ok {
|
||||
transport = t.Clone()
|
||||
@@ -35,8 +47,9 @@ func NewClient(cfg config.OnyxCliConfig) *Client {
|
||||
transport = &http.Transport{}
|
||||
}
|
||||
return &Client{
|
||||
baseURL: strings.TrimRight(cfg.ServerURL, "/"),
|
||||
apiKey: cfg.APIKey,
|
||||
serverURL: strings.TrimRight(cfg.ServerURL, "/"),
|
||||
baseURL: baseURL,
|
||||
apiKey: cfg.APIKey,
|
||||
httpClient: &http.Client{
|
||||
Timeout: 30 * time.Second,
|
||||
Transport: transport,
|
||||
@@ -48,14 +61,27 @@ func NewClient(cfg config.OnyxCliConfig) *Client {
|
||||
}
|
||||
}
|
||||
|
||||
// apiBaseURL returns the base URL for API requests. When InternalURL is set,
|
||||
// it points directly at the API server. Otherwise it goes through the nginx
|
||||
// proxy at ServerURL/api.
|
||||
func apiBaseURL(cfg config.OnyxCliConfig) string {
|
||||
if cfg.InternalURL != "" {
|
||||
return strings.TrimRight(cfg.InternalURL, "/")
|
||||
}
|
||||
return strings.TrimRight(cfg.ServerURL, "/") + "/api"
|
||||
}
|
||||
|
||||
// UpdateConfig replaces the client's config.
|
||||
func (c *Client) UpdateConfig(cfg config.OnyxCliConfig) {
|
||||
c.baseURL = strings.TrimRight(cfg.ServerURL, "/")
|
||||
c.serverURL = strings.TrimRight(cfg.ServerURL, "/")
|
||||
c.baseURL = apiBaseURL(cfg)
|
||||
c.apiKey = cfg.APIKey
|
||||
}
|
||||
|
||||
func (c *Client) newRequest(ctx context.Context, method, path string, body io.Reader) (*http.Request, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, method, c.baseURL+path, body)
|
||||
url := c.baseURL + path
|
||||
log.WithFields(log.Fields{"method": method, "url": url}).Debug("API request")
|
||||
req, err := http.NewRequestWithContext(ctx, method, url, body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -87,12 +113,16 @@ func (c *Client) doJSON(ctx context.Context, method, path string, reqBody any, r
|
||||
|
||||
resp, err := c.httpClient.Do(req)
|
||||
if err != nil {
|
||||
log.WithError(err).WithField("url", req.URL.String()).Debug("API request failed")
|
||||
return err
|
||||
}
|
||||
defer func() { _ = resp.Body.Close() }()
|
||||
|
||||
log.WithFields(log.Fields{"url": req.URL.String(), "status": resp.StatusCode}).Debug("API response")
|
||||
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||
respBody, _ := io.ReadAll(resp.Body)
|
||||
log.WithFields(log.Fields{"status": resp.StatusCode, "body": string(respBody)}).Debug("API error response")
|
||||
return &OnyxAPIError{StatusCode: resp.StatusCode, Detail: string(respBody)}
|
||||
}
|
||||
|
||||
@@ -105,16 +135,26 @@ func (c *Client) doJSON(ctx context.Context, method, path string, reqBody any, r
|
||||
// TestConnection checks if the server is reachable and credentials are valid.
|
||||
// Returns nil on success, or an error with a descriptive message on failure.
|
||||
func (c *Client) TestConnection(ctx context.Context) error {
|
||||
// Step 1: Basic reachability
|
||||
req, err := c.newRequest(ctx, "GET", "/", nil)
|
||||
// Step 1: Basic reachability (hit the server root, not the API prefix)
|
||||
reachURL := c.serverURL
|
||||
if reachURL == "" {
|
||||
reachURL = c.baseURL
|
||||
}
|
||||
log.WithFields(log.Fields{
|
||||
"reach_url": reachURL,
|
||||
"base_url": c.baseURL,
|
||||
}).Debug("testing connection — step 1: reachability")
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", reachURL+"/", nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot connect to %s: %w", c.baseURL, err)
|
||||
return fmt.Errorf("cannot connect to %s: %w", reachURL, err)
|
||||
}
|
||||
|
||||
resp, err := c.httpClient.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot connect to %s — is the server running?", c.baseURL)
|
||||
log.WithError(err).Debug("reachability check failed")
|
||||
return fmt.Errorf("cannot connect to %s — is the server running?", reachURL)
|
||||
}
|
||||
log.WithField("status", resp.StatusCode).Debug("reachability check response")
|
||||
_ = resp.Body.Close()
|
||||
|
||||
serverHeader := strings.ToLower(resp.Header.Get("Server"))
|
||||
@@ -127,7 +167,8 @@ func (c *Client) TestConnection(ctx context.Context) error {
|
||||
}
|
||||
|
||||
// Step 2: Authenticated check
|
||||
req2, err := c.newRequest(ctx, "GET", "/api/me", nil)
|
||||
log.WithField("url", c.baseURL+"/me").Debug("testing connection — step 2: auth check")
|
||||
req2, err := c.newRequest(ctx, "GET", "/me", nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("server reachable but API error: %w", err)
|
||||
}
|
||||
@@ -167,7 +208,7 @@ func (c *Client) TestConnection(ctx context.Context) error {
|
||||
// ListAgents returns visible agents.
|
||||
func (c *Client) ListAgents(ctx context.Context) ([]models.AgentSummary, error) {
|
||||
var raw []models.AgentSummary
|
||||
if err := c.doJSON(ctx, "GET", "/api/persona", nil, &raw); err != nil {
|
||||
if err := c.doJSON(ctx, "GET", "/persona", nil, &raw); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var result []models.AgentSummary
|
||||
@@ -184,7 +225,7 @@ func (c *Client) ListChatSessions(ctx context.Context) ([]models.ChatSessionDeta
|
||||
var resp struct {
|
||||
Sessions []models.ChatSessionDetails `json:"sessions"`
|
||||
}
|
||||
if err := c.doJSON(ctx, "GET", "/api/chat/get-user-chat-sessions", nil, &resp); err != nil {
|
||||
if err := c.doJSON(ctx, "GET", "/chat/get-user-chat-sessions", nil, &resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return resp.Sessions, nil
|
||||
@@ -193,7 +234,7 @@ func (c *Client) ListChatSessions(ctx context.Context) ([]models.ChatSessionDeta
|
||||
// GetChatSession returns full details for a session.
|
||||
func (c *Client) GetChatSession(ctx context.Context, sessionID string) (*models.ChatSessionDetailResponse, error) {
|
||||
var resp models.ChatSessionDetailResponse
|
||||
if err := c.doJSON(ctx, "GET", "/api/chat/get-chat-session/"+sessionID, nil, &resp); err != nil {
|
||||
if err := c.doJSON(ctx, "GET", "/chat/get-chat-session/"+sessionID, nil, &resp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &resp, nil
|
||||
@@ -210,7 +251,7 @@ func (c *Client) RenameChatSession(ctx context.Context, sessionID string, name *
|
||||
var resp struct {
|
||||
NewName string `json:"new_name"`
|
||||
}
|
||||
if err := c.doJSON(ctx, "PUT", "/api/chat/rename-chat-session", payload, &resp); err != nil {
|
||||
if err := c.doJSON(ctx, "PUT", "/chat/rename-chat-session", payload, &resp); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return resp.NewName, nil
|
||||
@@ -236,7 +277,7 @@ func (c *Client) UploadFile(ctx context.Context, filePath string) (*models.FileD
|
||||
}
|
||||
_ = writer.Close()
|
||||
|
||||
req, err := c.newRequest(ctx, "POST", "/api/user/projects/file/upload", &buf)
|
||||
req, err := c.newRequest(ctx, "POST", "/user/projects/file/upload", &buf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -275,7 +316,7 @@ func (c *Client) GetBackendVersion(ctx context.Context) (string, error) {
|
||||
var resp struct {
|
||||
BackendVersion string `json:"backend_version"`
|
||||
}
|
||||
if err := c.doJSON(ctx, "GET", "/api/version", nil, &resp); err != nil {
|
||||
if err := c.doJSON(ctx, "GET", "/version", nil, &resp); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return resp.BackendVersion, nil
|
||||
@@ -283,7 +324,7 @@ func (c *Client) GetBackendVersion(ctx context.Context) (string, error) {
|
||||
|
||||
// StopChatSession sends a stop signal for a streaming session (best-effort).
|
||||
func (c *Client) StopChatSession(ctx context.Context, sessionID string) {
|
||||
req, err := c.newRequest(ctx, "POST", "/api/chat/stop-chat-session/"+sessionID, nil)
|
||||
req, err := c.newRequest(ctx, "POST", "/chat/stop-chat-session/"+sessionID, nil)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ func (c *Client) SendMessageStream(
|
||||
return
|
||||
}
|
||||
|
||||
req, err := http.NewRequestWithContext(ctx, "POST", c.baseURL+"/api/chat/send-chat-message", nil)
|
||||
req, err := http.NewRequestWithContext(ctx, "POST", c.baseURL+"/chat/send-chat-message", nil)
|
||||
if err != nil {
|
||||
ch <- models.ErrorEvent{Error: fmt.Sprintf("request error: %v", err), IsRetryable: false}
|
||||
return
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
|
||||
const (
|
||||
EnvServerURL = "ONYX_SERVER_URL"
|
||||
EnvAPIServerURL = "ONYX_API_SERVER_URL"
|
||||
EnvAPIKey = "ONYX_API_KEY"
|
||||
EnvAgentID = "ONYX_PERSONA_ID"
|
||||
EnvSSHHostKey = "ONYX_SSH_HOST_KEY"
|
||||
@@ -27,6 +28,7 @@ type Features struct {
|
||||
// OnyxCliConfig holds the CLI configuration.
|
||||
type OnyxCliConfig struct {
|
||||
ServerURL string `json:"server_url"`
|
||||
InternalURL string `json:"internal_url,omitempty"`
|
||||
APIKey string `json:"api_key"`
|
||||
DefaultAgentID int `json:"default_persona_id"`
|
||||
Features Features `json:"features,omitempty"`
|
||||
@@ -78,30 +80,47 @@ func ConfigExists() bool {
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// LoadFromDisk reads config from the file only, without applying environment
|
||||
// variable overrides. Use this when you need the persisted config values
|
||||
// (e.g., to preserve them during a save operation).
|
||||
func LoadFromDisk() OnyxCliConfig {
|
||||
// LoadFromDisk reads config from the given file path without applying
|
||||
// environment variable overrides. Use this when you need the persisted
|
||||
// config values (e.g., to preserve them during a save operation).
|
||||
// If no path is provided, the default config file path is used.
|
||||
func LoadFromDisk(path ...string) OnyxCliConfig {
|
||||
p := ConfigFilePath()
|
||||
if len(path) > 0 && path[0] != "" {
|
||||
p = path[0]
|
||||
}
|
||||
|
||||
cfg := DefaultConfig()
|
||||
|
||||
data, err := os.ReadFile(ConfigFilePath())
|
||||
data, err := os.ReadFile(p)
|
||||
if err == nil {
|
||||
if jsonErr := json.Unmarshal(data, &cfg); jsonErr != nil {
|
||||
fmt.Fprintf(os.Stderr, "warning: config file %s is malformed: %v (using defaults)\n", ConfigFilePath(), jsonErr)
|
||||
fmt.Fprintf(os.Stderr, "warning: config file %s is malformed: %v (using defaults)\n", p, jsonErr)
|
||||
}
|
||||
}
|
||||
|
||||
return cfg
|
||||
}
|
||||
|
||||
// Load reads config from file and applies environment variable overrides.
|
||||
func Load() OnyxCliConfig {
|
||||
cfg := LoadFromDisk()
|
||||
// Load reads config from the given file path and applies environment variable
|
||||
// overrides. If no path is provided, the default config file path is used.
|
||||
func Load(path ...string) OnyxCliConfig {
|
||||
cfg := LoadFromDisk(path...)
|
||||
applyEnvOverrides(&cfg)
|
||||
return cfg
|
||||
}
|
||||
|
||||
// Environment overrides
|
||||
func applyEnvOverrides(cfg *OnyxCliConfig) {
|
||||
if v := os.Getenv(EnvServerURL); v != "" {
|
||||
cfg.ServerURL = v
|
||||
}
|
||||
// ONYX_API_SERVER_URL takes precedence; fall back to INTERNAL_URL
|
||||
// (the env var used by the web server) for compatibility.
|
||||
if v := os.Getenv(EnvAPIServerURL); v != "" {
|
||||
cfg.InternalURL = v
|
||||
} else if v := os.Getenv("INTERNAL_URL"); v != "" {
|
||||
cfg.InternalURL = v
|
||||
}
|
||||
if v := os.Getenv(EnvAPIKey); v != "" {
|
||||
cfg.APIKey = v
|
||||
}
|
||||
@@ -117,8 +136,6 @@ func Load() OnyxCliConfig {
|
||||
fmt.Fprintf(os.Stderr, "warning: invalid value %q for %s (expected true/false), ignoring\n", v, EnvStreamMarkdown)
|
||||
}
|
||||
}
|
||||
|
||||
return cfg
|
||||
}
|
||||
|
||||
// Save writes the config to disk, creating parent directories if needed.
|
||||
|
||||
Reference in New Issue
Block a user