mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-06-10 00:13:42 +00:00
30708ae79d
* feat(auth): support named chatgpt oauth providers - add provider-scoped ChatGPT OAuth routes and CLI support - persist refresh tokens per provider and reject provider-type collisions - wire provider OAuth setup flows in the dashboard and setup UI Refs #448 * feat(agent): add chatgpt oauth account routing - add agent other_config routing for manual and round-robin selection - reuse routed provider resolution across resolver and pending loaders - add router, parser, and agent advanced dialog coverage for multi-account use Refs #448 * docs(api): describe chatgpt oauth routing - document named-provider ChatGPT OAuth auth routes - describe agent-side account routing and round-robin behavior - update OpenAPI agent config schema and provider type enum Refs #448 * fix(store): add missing agent key context helpers * feat(ui): clarify chatgpt oauth account setup and routing * docs(providers): align chatgpt oauth alias examples * feat(agent): add codex pool activity dashboard * fix(providers): harden codex oauth alias setup * feat(codex-pool): improve routing dashboard UX - redesign the Codex/OpenAI pool page around saved-pool checkpoints and live evidence - add clearer selection, attention, and recent-proof states for pool members - make the lower panels fill the remaining desktop viewport while staying responsive * fix(store): resolve context helper merge duplication * feat(oauth): add codex pool quota and observation APIs - add quota inspection and observation endpoints for ChatGPT Subscription (OAuth) providers - teach codex routing to surface pool activity, observation metadata, and quota-aware readiness - extend tests and HTTP docs/OpenAPI for the new pool monitoring flows * feat(web): add codex pool quota monitor and controls - add provider quota fetching, readiness badges, and live routing evidence on the account pool page - redesign pool setup and activity panels for multi-account management with localized copy updates - keep the live monitor internally scrollable and compact the account cards for better viewport fit * fix(web): clarify pool routing labels - rename the recent request badge from Direct to Selected - restore compact quota bars in the live pool cards * feat(codex-pool): add runtime health dashboard - derive per-provider success and failure health from routed Codex traces - surface routing, quota, and recent request evidence in the pool UI - align provider alias guidance and owner access with the dashboard role model * docs(auth): document tenant scoping and key roles * fix(auth): harden tenant and codex pool access control * fix(providers): align codex pool runtime defaults * feat(ui): tighten codex pool responsive layout * feat(chatgpt-oauth): refine codex pool management UX * feat(chatgpt-oauth): surface quota bars on provider pages - add compact quota bars to Codex provider rows and provider detail - fetch quota only for ready visible provider rows and ready detail aliases - fix managed-member detail visibility and tighten provider locale copy
134 lines
3.6 KiB
Go
134 lines
3.6 KiB
Go
package cmd
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"net/url"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/nextlevelbuilder/goclaw/internal/oauth"
|
|
"github.com/spf13/cobra"
|
|
)
|
|
|
|
func authCmd() *cobra.Command {
|
|
cmd := &cobra.Command{
|
|
Use: "auth",
|
|
Short: "Authenticate named ChatGPT OAuth accounts",
|
|
Long: "Manage ChatGPT OAuth authentication via the running gateway. Requires the gateway to be running.",
|
|
}
|
|
cmd.AddCommand(authStatusCmd())
|
|
cmd.AddCommand(authLogoutCmd())
|
|
return cmd
|
|
}
|
|
|
|
// gatewayURL returns the base URL for the running gateway.
|
|
func gatewayURL() string {
|
|
if u := os.Getenv("GOCLAW_GATEWAY_URL"); u != "" {
|
|
return strings.TrimRight(u, "/")
|
|
}
|
|
host := os.Getenv("GOCLAW_HOST")
|
|
if host == "" {
|
|
host = "127.0.0.1"
|
|
}
|
|
port := os.Getenv("GOCLAW_PORT")
|
|
if port == "" {
|
|
port = "3577"
|
|
}
|
|
return fmt.Sprintf("http://%s:%s", host, port)
|
|
}
|
|
|
|
// gatewayRequest sends an authenticated request to the running gateway.
|
|
func gatewayRequest(method, path string) (map[string]any, error) {
|
|
url := gatewayURL() + path
|
|
req, err := http.NewRequest(method, url, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if token := os.Getenv("GOCLAW_TOKEN"); token != "" {
|
|
req.Header.Set("Authorization", "Bearer "+token)
|
|
}
|
|
|
|
resp, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("cannot reach gateway at %s: %w", gatewayURL(), err)
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
body, _ := io.ReadAll(io.LimitReader(resp.Body, 1<<20))
|
|
var result map[string]any
|
|
if err := json.Unmarshal(body, &result); err != nil {
|
|
return nil, fmt.Errorf("invalid response from gateway: %s", string(body))
|
|
}
|
|
|
|
if resp.StatusCode >= 400 {
|
|
if msg, ok := result["error"].(string); ok {
|
|
return nil, fmt.Errorf("gateway error: %s", msg)
|
|
}
|
|
return nil, fmt.Errorf("gateway returned status %d", resp.StatusCode)
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func authStatusCmd() *cobra.Command {
|
|
return &cobra.Command{
|
|
Use: "status [provider]",
|
|
Short: "Show OAuth authentication status",
|
|
Long: "Check if a named ChatGPT OAuth account is authenticated on the running gateway.",
|
|
Args: cobra.MaximumNArgs(1),
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
provider := resolveOAuthProviderArg(args)
|
|
result, err := gatewayRequest("GET", fmt.Sprintf("/v1/auth/chatgpt/%s/status", url.PathEscape(provider)))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if auth, _ := result["authenticated"].(bool); auth {
|
|
name, _ := result["provider_name"].(string)
|
|
if name == "" {
|
|
name = provider
|
|
}
|
|
fmt.Printf("ChatGPT OAuth account: active (alias: %s)\n", name)
|
|
fmt.Printf("Use model prefix '%s/' in agent config (e.g. %s/gpt-5.4).\n", name, name)
|
|
} else {
|
|
fmt.Printf("No ChatGPT OAuth tokens found for alias '%s'.\n", provider)
|
|
fmt.Println("Use the web UI to authenticate this ChatGPT OAuth account.")
|
|
}
|
|
return nil
|
|
},
|
|
}
|
|
}
|
|
|
|
func authLogoutCmd() *cobra.Command {
|
|
return &cobra.Command{
|
|
Use: "logout [provider]",
|
|
Short: "Disconnect stored ChatGPT OAuth tokens",
|
|
Args: cobra.MaximumNArgs(1),
|
|
RunE: func(cmd *cobra.Command, args []string) error {
|
|
provider := resolveOAuthProviderArg(args)
|
|
_, err := gatewayRequest("POST", fmt.Sprintf("/v1/auth/chatgpt/%s/logout", url.PathEscape(provider)))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
fmt.Printf("ChatGPT OAuth account disconnected for alias '%s'.\n", provider)
|
|
return nil
|
|
},
|
|
}
|
|
}
|
|
|
|
func resolveOAuthProviderArg(args []string) string {
|
|
if len(args) == 0 {
|
|
return oauth.DefaultProviderName
|
|
}
|
|
provider := strings.TrimSpace(args[0])
|
|
if provider == "" || provider == "openai" {
|
|
return oauth.DefaultProviderName
|
|
}
|
|
return provider
|
|
}
|