diff --git a/examples/custom-provider/main.go b/examples/custom-provider/main.go index ffb5e346..930afdcf 100644 --- a/examples/custom-provider/main.go +++ b/examples/custom-provider/main.go @@ -23,13 +23,13 @@ import ( "time" "github.com/gin-gonic/gin" - "github.com/router-for-me/CLIProxyAPI/v6/internal/api" - "github.com/router-for-me/CLIProxyAPI/v6/internal/config" - "github.com/router-for-me/CLIProxyAPI/v6/internal/logging" + "github.com/router-for-me/CLIProxyAPI/v6/sdk/api" sdkAuth "github.com/router-for-me/CLIProxyAPI/v6/sdk/auth" "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy" coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth" clipexec "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/executor" + "github.com/router-for-me/CLIProxyAPI/v6/sdk/config" + "github.com/router-for-me/CLIProxyAPI/v6/sdk/logging" sdktr "github.com/router-for-me/CLIProxyAPI/v6/sdk/translator" ) diff --git a/internal/config/config.go b/internal/config/config.go index 63ac1cb0..2ced3796 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -12,7 +12,6 @@ import ( "strings" "syscall" - "github.com/router-for-me/CLIProxyAPI/v6/sdk/config" "golang.org/x/crypto/bcrypt" "gopkg.in/yaml.v3" ) @@ -21,7 +20,7 @@ const DefaultPanelGitHubRepository = "https://github.com/router-for-me/Cli-Proxy // Config represents the application's configuration, loaded from a YAML file. type Config struct { - config.SDKConfig `yaml:",inline"` + SDKConfig `yaml:",inline"` // Host is the network host/interface on which the API server will bind. // Default is empty ("") to bind all interfaces (IPv4 + IPv6). Use "127.0.0.1" or "localhost" for local-only access. Host string `yaml:"host" json:"-"` @@ -692,7 +691,7 @@ func sanitizeConfigForPersist(cfg *Config) *Config { } clone := *cfg clone.SDKConfig = cfg.SDKConfig - clone.SDKConfig.Access = config.AccessConfig{} + clone.SDKConfig.Access = AccessConfig{} return &clone } diff --git a/internal/config/sdk_config.go b/internal/config/sdk_config.go new file mode 100644 index 00000000..f6f20d5c --- /dev/null +++ b/internal/config/sdk_config.go @@ -0,0 +1,87 @@ +// Package config provides configuration management for the CLI Proxy API server. +// It handles loading and parsing YAML configuration files, and provides structured +// access to application settings including server port, authentication directory, +// debug settings, proxy configuration, and API keys. +package config + +// SDKConfig represents the application's configuration, loaded from a YAML file. +type SDKConfig struct { + // ProxyURL is the URL of an optional proxy server to use for outbound requests. + ProxyURL string `yaml:"proxy-url" json:"proxy-url"` + + // ForceModelPrefix requires explicit model prefixes (e.g., "teamA/gemini-3-pro-preview") + // to target prefixed credentials. When false, unprefixed model requests may use prefixed + // credentials as well. + ForceModelPrefix bool `yaml:"force-model-prefix" json:"force-model-prefix"` + + // RequestLog enables or disables detailed request logging functionality. + RequestLog bool `yaml:"request-log" json:"request-log"` + + // APIKeys is a list of keys for authenticating clients to this proxy server. + APIKeys []string `yaml:"api-keys" json:"api-keys"` + + // Access holds request authentication provider configuration. + Access AccessConfig `yaml:"auth,omitempty" json:"auth,omitempty"` +} + +// AccessConfig groups request authentication providers. +type AccessConfig struct { + // Providers lists configured authentication providers. + Providers []AccessProvider `yaml:"providers,omitempty" json:"providers,omitempty"` +} + +// AccessProvider describes a request authentication provider entry. +type AccessProvider struct { + // Name is the instance identifier for the provider. + Name string `yaml:"name" json:"name"` + + // Type selects the provider implementation registered via the SDK. + Type string `yaml:"type" json:"type"` + + // SDK optionally names a third-party SDK module providing this provider. + SDK string `yaml:"sdk,omitempty" json:"sdk,omitempty"` + + // APIKeys lists inline keys for providers that require them. + APIKeys []string `yaml:"api-keys,omitempty" json:"api-keys,omitempty"` + + // Config passes provider-specific options to the implementation. + Config map[string]any `yaml:"config,omitempty" json:"config,omitempty"` +} + +const ( + // AccessProviderTypeConfigAPIKey is the built-in provider validating inline API keys. + AccessProviderTypeConfigAPIKey = "config-api-key" + + // DefaultAccessProviderName is applied when no provider name is supplied. + DefaultAccessProviderName = "config-inline" +) + +// ConfigAPIKeyProvider returns the first inline API key provider if present. +func (c *SDKConfig) ConfigAPIKeyProvider() *AccessProvider { + if c == nil { + return nil + } + for i := range c.Access.Providers { + if c.Access.Providers[i].Type == AccessProviderTypeConfigAPIKey { + if c.Access.Providers[i].Name == "" { + c.Access.Providers[i].Name = DefaultAccessProviderName + } + return &c.Access.Providers[i] + } + } + return nil +} + +// MakeInlineAPIKeyProvider constructs an inline API key provider configuration. +// It returns nil when no keys are supplied. +func MakeInlineAPIKeyProvider(keys []string) *AccessProvider { + if len(keys) == 0 { + return nil + } + provider := &AccessProvider{ + Name: DefaultAccessProviderName, + Type: AccessProviderTypeConfigAPIKey, + APIKeys: append([]string(nil), keys...), + } + return provider +} diff --git a/sdk/api/options.go b/sdk/api/options.go new file mode 100644 index 00000000..8497884b --- /dev/null +++ b/sdk/api/options.go @@ -0,0 +1,46 @@ +// Package api exposes server option helpers for embedding CLIProxyAPI. +// +// It wraps internal server option types so external projects can configure the embedded +// HTTP server without importing internal packages. +package api + +import ( + "time" + + "github.com/gin-gonic/gin" + internalapi "github.com/router-for-me/CLIProxyAPI/v6/internal/api" + "github.com/router-for-me/CLIProxyAPI/v6/sdk/api/handlers" + "github.com/router-for-me/CLIProxyAPI/v6/sdk/config" + "github.com/router-for-me/CLIProxyAPI/v6/sdk/logging" +) + +// ServerOption customises HTTP server construction. +type ServerOption = internalapi.ServerOption + +// WithMiddleware appends additional Gin middleware during server construction. +func WithMiddleware(mw ...gin.HandlerFunc) ServerOption { return internalapi.WithMiddleware(mw...) } + +// WithEngineConfigurator allows callers to mutate the Gin engine prior to middleware setup. +func WithEngineConfigurator(fn func(*gin.Engine)) ServerOption { + return internalapi.WithEngineConfigurator(fn) +} + +// WithRouterConfigurator appends a callback after default routes are registered. +func WithRouterConfigurator(fn func(*gin.Engine, *handlers.BaseAPIHandler, *config.Config)) ServerOption { + return internalapi.WithRouterConfigurator(fn) +} + +// WithLocalManagementPassword stores a runtime-only management password accepted for localhost requests. +func WithLocalManagementPassword(password string) ServerOption { + return internalapi.WithLocalManagementPassword(password) +} + +// WithKeepAliveEndpoint enables a keep-alive endpoint with the provided timeout and callback. +func WithKeepAliveEndpoint(timeout time.Duration, onTimeout func()) ServerOption { + return internalapi.WithKeepAliveEndpoint(timeout, onTimeout) +} + +// WithRequestLoggerFactory customises request logger creation. +func WithRequestLoggerFactory(factory func(*config.Config, string) logging.RequestLogger) ServerOption { + return internalapi.WithRequestLoggerFactory(factory) +} diff --git a/sdk/cliproxy/builder.go b/sdk/cliproxy/builder.go index e1a1d503..a85e91d9 100644 --- a/sdk/cliproxy/builder.go +++ b/sdk/cliproxy/builder.go @@ -7,10 +7,10 @@ import ( "fmt" "github.com/router-for-me/CLIProxyAPI/v6/internal/api" - "github.com/router-for-me/CLIProxyAPI/v6/internal/config" sdkaccess "github.com/router-for-me/CLIProxyAPI/v6/sdk/access" sdkAuth "github.com/router-for-me/CLIProxyAPI/v6/sdk/auth" coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth" + "github.com/router-for-me/CLIProxyAPI/v6/sdk/config" ) // Builder constructs a Service instance with customizable providers. diff --git a/sdk/cliproxy/providers.go b/sdk/cliproxy/providers.go index 401885f5..7ce89f76 100644 --- a/sdk/cliproxy/providers.go +++ b/sdk/cliproxy/providers.go @@ -3,8 +3,8 @@ package cliproxy import ( "context" - "github.com/router-for-me/CLIProxyAPI/v6/internal/config" "github.com/router-for-me/CLIProxyAPI/v6/internal/watcher" + "github.com/router-for-me/CLIProxyAPI/v6/sdk/config" ) // NewFileTokenClientProvider returns the default token-backed client loader. diff --git a/sdk/cliproxy/service.go b/sdk/cliproxy/service.go index f3cbf484..e4cd9e5d 100644 --- a/sdk/cliproxy/service.go +++ b/sdk/cliproxy/service.go @@ -13,7 +13,6 @@ import ( "time" "github.com/router-for-me/CLIProxyAPI/v6/internal/api" - "github.com/router-for-me/CLIProxyAPI/v6/internal/config" "github.com/router-for-me/CLIProxyAPI/v6/internal/registry" "github.com/router-for-me/CLIProxyAPI/v6/internal/runtime/executor" _ "github.com/router-for-me/CLIProxyAPI/v6/internal/usage" @@ -23,6 +22,7 @@ import ( sdkAuth "github.com/router-for-me/CLIProxyAPI/v6/sdk/auth" coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth" "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/usage" + "github.com/router-for-me/CLIProxyAPI/v6/sdk/config" log "github.com/sirupsen/logrus" ) diff --git a/sdk/cliproxy/types.go b/sdk/cliproxy/types.go index 42c7c488..1521dffe 100644 --- a/sdk/cliproxy/types.go +++ b/sdk/cliproxy/types.go @@ -6,9 +6,9 @@ package cliproxy import ( "context" - "github.com/router-for-me/CLIProxyAPI/v6/internal/config" "github.com/router-for-me/CLIProxyAPI/v6/internal/watcher" coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth" + "github.com/router-for-me/CLIProxyAPI/v6/sdk/config" ) // TokenClientProvider loads clients backed by stored authentication tokens. diff --git a/sdk/cliproxy/watcher.go b/sdk/cliproxy/watcher.go index 921e2068..caeadf19 100644 --- a/sdk/cliproxy/watcher.go +++ b/sdk/cliproxy/watcher.go @@ -3,9 +3,9 @@ package cliproxy import ( "context" - "github.com/router-for-me/CLIProxyAPI/v6/internal/config" "github.com/router-for-me/CLIProxyAPI/v6/internal/watcher" coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth" + "github.com/router-for-me/CLIProxyAPI/v6/sdk/config" ) func defaultWatcherFactory(configPath, authDir string, reload func(*config.Config)) (*WatcherWrapper, error) { diff --git a/sdk/config/config.go b/sdk/config/config.go index f6f20d5c..6e4efad5 100644 --- a/sdk/config/config.go +++ b/sdk/config/config.go @@ -1,87 +1,59 @@ -// Package config provides configuration management for the CLI Proxy API server. -// It handles loading and parsing YAML configuration files, and provides structured -// access to application settings including server port, authentication directory, -// debug settings, proxy configuration, and API keys. +// Package config provides the public SDK configuration API. +// +// It re-exports the server configuration types and helpers so external projects can +// embed CLIProxyAPI without importing internal packages. package config -// SDKConfig represents the application's configuration, loaded from a YAML file. -type SDKConfig struct { - // ProxyURL is the URL of an optional proxy server to use for outbound requests. - ProxyURL string `yaml:"proxy-url" json:"proxy-url"` +import internalconfig "github.com/router-for-me/CLIProxyAPI/v6/internal/config" - // ForceModelPrefix requires explicit model prefixes (e.g., "teamA/gemini-3-pro-preview") - // to target prefixed credentials. When false, unprefixed model requests may use prefixed - // credentials as well. - ForceModelPrefix bool `yaml:"force-model-prefix" json:"force-model-prefix"` +type SDKConfig = internalconfig.SDKConfig +type AccessConfig = internalconfig.AccessConfig +type AccessProvider = internalconfig.AccessProvider - // RequestLog enables or disables detailed request logging functionality. - RequestLog bool `yaml:"request-log" json:"request-log"` +type Config = internalconfig.Config - // APIKeys is a list of keys for authenticating clients to this proxy server. - APIKeys []string `yaml:"api-keys" json:"api-keys"` +type TLSConfig = internalconfig.TLSConfig +type RemoteManagement = internalconfig.RemoteManagement +type AmpCode = internalconfig.AmpCode +type PayloadConfig = internalconfig.PayloadConfig +type PayloadRule = internalconfig.PayloadRule +type PayloadModelRule = internalconfig.PayloadModelRule - // Access holds request authentication provider configuration. - Access AccessConfig `yaml:"auth,omitempty" json:"auth,omitempty"` -} +type GeminiKey = internalconfig.GeminiKey +type CodexKey = internalconfig.CodexKey +type ClaudeKey = internalconfig.ClaudeKey +type VertexCompatKey = internalconfig.VertexCompatKey +type VertexCompatModel = internalconfig.VertexCompatModel +type OpenAICompatibility = internalconfig.OpenAICompatibility +type OpenAICompatibilityAPIKey = internalconfig.OpenAICompatibilityAPIKey +type OpenAICompatibilityModel = internalconfig.OpenAICompatibilityModel -// AccessConfig groups request authentication providers. -type AccessConfig struct { - // Providers lists configured authentication providers. - Providers []AccessProvider `yaml:"providers,omitempty" json:"providers,omitempty"` -} - -// AccessProvider describes a request authentication provider entry. -type AccessProvider struct { - // Name is the instance identifier for the provider. - Name string `yaml:"name" json:"name"` - - // Type selects the provider implementation registered via the SDK. - Type string `yaml:"type" json:"type"` - - // SDK optionally names a third-party SDK module providing this provider. - SDK string `yaml:"sdk,omitempty" json:"sdk,omitempty"` - - // APIKeys lists inline keys for providers that require them. - APIKeys []string `yaml:"api-keys,omitempty" json:"api-keys,omitempty"` - - // Config passes provider-specific options to the implementation. - Config map[string]any `yaml:"config,omitempty" json:"config,omitempty"` -} +type TLS = internalconfig.TLSConfig const ( - // AccessProviderTypeConfigAPIKey is the built-in provider validating inline API keys. - AccessProviderTypeConfigAPIKey = "config-api-key" - - // DefaultAccessProviderName is applied when no provider name is supplied. - DefaultAccessProviderName = "config-inline" + AccessProviderTypeConfigAPIKey = internalconfig.AccessProviderTypeConfigAPIKey + DefaultAccessProviderName = internalconfig.DefaultAccessProviderName + DefaultPanelGitHubRepository = internalconfig.DefaultPanelGitHubRepository ) -// ConfigAPIKeyProvider returns the first inline API key provider if present. -func (c *SDKConfig) ConfigAPIKeyProvider() *AccessProvider { - if c == nil { - return nil - } - for i := range c.Access.Providers { - if c.Access.Providers[i].Type == AccessProviderTypeConfigAPIKey { - if c.Access.Providers[i].Name == "" { - c.Access.Providers[i].Name = DefaultAccessProviderName - } - return &c.Access.Providers[i] - } - } - return nil +func MakeInlineAPIKeyProvider(keys []string) *AccessProvider { + return internalconfig.MakeInlineAPIKeyProvider(keys) } -// MakeInlineAPIKeyProvider constructs an inline API key provider configuration. -// It returns nil when no keys are supplied. -func MakeInlineAPIKeyProvider(keys []string) *AccessProvider { - if len(keys) == 0 { - return nil - } - provider := &AccessProvider{ - Name: DefaultAccessProviderName, - Type: AccessProviderTypeConfigAPIKey, - APIKeys: append([]string(nil), keys...), - } - return provider +func LoadConfig(configFile string) (*Config, error) { return internalconfig.LoadConfig(configFile) } + +func LoadConfigOptional(configFile string, optional bool) (*Config, error) { + return internalconfig.LoadConfigOptional(configFile, optional) +} + +func SaveConfigPreserveComments(configFile string, cfg *Config) error { + return internalconfig.SaveConfigPreserveComments(configFile, cfg) +} + +func SaveConfigPreserveCommentsUpdateNestedScalar(configFile string, path []string, value string) error { + return internalconfig.SaveConfigPreserveCommentsUpdateNestedScalar(configFile, path, value) +} + +func NormalizeCommentIndentation(data []byte) []byte { + return internalconfig.NormalizeCommentIndentation(data) } diff --git a/sdk/logging/request_logger.go b/sdk/logging/request_logger.go new file mode 100644 index 00000000..39ff5ba8 --- /dev/null +++ b/sdk/logging/request_logger.go @@ -0,0 +1,18 @@ +// Package logging re-exports request logging primitives for SDK consumers. +package logging + +import internallogging "github.com/router-for-me/CLIProxyAPI/v6/internal/logging" + +// RequestLogger defines the interface for logging HTTP requests and responses. +type RequestLogger = internallogging.RequestLogger + +// StreamingLogWriter handles real-time logging of streaming response chunks. +type StreamingLogWriter = internallogging.StreamingLogWriter + +// FileRequestLogger implements RequestLogger using file-based storage. +type FileRequestLogger = internallogging.FileRequestLogger + +// NewFileRequestLogger creates a new file-based request logger. +func NewFileRequestLogger(enabled bool, logsDir string, configDir string) *FileRequestLogger { + return internallogging.NewFileRequestLogger(enabled, logsDir, configDir) +}