devel/crush: OpenBSD patches

This commit is contained in:
c0dev0id
2026-07-08 11:31:31 +02:00
parent a6b68a0b99
commit 844067ee41
9 changed files with 68 additions and 32 deletions

View File

@@ -0,0 +1,17 @@
Index: internal/agent/coordinator.go
--- internal/agent/coordinator.go.orig
+++ internal/agent/coordinator.go
@@ -823,11 +823,11 @@ func (c *coordinator) buildAnthropicProvider(baseURL,
switch {
case strings.HasPrefix(apiKey, "Bearer "):
// NOTE: Prevent the SDK from picking up the API key from env.
- os.Setenv("ANTHROPIC_API_KEY", "")
+ os.Unsetenv("ANTHROPIC_API_KEY")
headers["Authorization"] = apiKey
case providerID == string(catwalk.InferenceProviderMiniMax) || providerID == string(catwalk.InferenceProviderMiniMaxChina):
// NOTE: Prevent the SDK from picking up the API key from env.
- os.Setenv("ANTHROPIC_API_KEY", "")
+ os.Unsetenv("ANTHROPIC_API_KEY")
headers["Authorization"] = "Bearer " + apiKey
case apiKey != "":
// X-Api-Key header

View File

@@ -1,6 +1,7 @@
--- internal/cmd/login.go Index: internal/cmd/login.go
--- internal/cmd/login.go.orig
+++ internal/cmd/login.go +++ internal/cmd/login.go
@@ -6,12 +6,14 @@ @@ -6,12 +6,14 @@ import (
"fmt" "fmt"
"os" "os"
"os/signal" "os/signal"
@@ -15,7 +16,7 @@
"github.com/charmbracelet/crush/internal/oauth/copilot" "github.com/charmbracelet/crush/internal/oauth/copilot"
"github.com/charmbracelet/crush/internal/oauth/hyper" "github.com/charmbracelet/crush/internal/oauth/hyper"
"github.com/charmbracelet/x/ansi" "github.com/charmbracelet/x/ansi"
@@ -25,11 +27,14 @@ @@ -25,11 +27,14 @@ var loginCmd = &cobra.Command{
Short: "Login Crush to a platform", Short: "Login Crush to a platform",
Long: `Login Crush to a specified platform. Long: `Login Crush to a specified platform.
The platform should be provided as an argument. The platform should be provided as an argument.
@@ -31,7 +32,7 @@
# Authenticate with GitHub Copilot # Authenticate with GitHub Copilot
crush login copilot crush login copilot
@@ -38,6 +43,8 @@ @@ -38,6 +43,8 @@ crush login -f copilot
`, `,
ValidArgs: []cobra.Completion{ ValidArgs: []cobra.Completion{
"hyper", "hyper",
@@ -40,7 +41,7 @@
"copilot", "copilot",
"github", "github",
"github-copilot", "github-copilot",
@@ -64,6 +71,8 @@ @@ -64,6 +71,8 @@ crush login -f copilot
switch provider { switch provider {
case "hyper": case "hyper":
return loginHyper(c, ws.ID, force) return loginHyper(c, ws.ID, force)
@@ -49,7 +50,7 @@
case "copilot", "github", "github-copilot": case "copilot", "github", "github-copilot":
return loginCopilot(c, ws.ID, force) return loginCopilot(c, ws.ID, force)
default: default:
@@ -140,6 +149,68 @@ @@ -140,6 +149,68 @@ func loginHyper(c *client.Client, wsID string, force b
fmt.Println() fmt.Println()
fmt.Println("You're now authenticated with Hyper!") fmt.Println("You're now authenticated with Hyper!")

View File

@@ -1,9 +1,10 @@
--- internal/config/config.go Index: internal/config/config.go
--- internal/config/config.go.orig
+++ internal/config/config.go +++ internal/config/config.go
@@ -177,6 +177,22 @@ func (c *ProviderConfig) SetupGitHubCopilot() { @@ -178,6 +178,26 @@ func (c *ProviderConfig) SetupGitHubCopilot() {
maps.Copy(c.ExtraHeaders, copilot.Headers()) maps.Copy(c.ExtraHeaders, copilot.Headers())
} }
+func (c *ProviderConfig) SetupClaudeCode() { +func (c *ProviderConfig) SetupClaudeCode() {
+ c.FlatRate = true + c.FlatRate = true
+ c.SystemPromptPrefix = "You are Claude Code, Anthropic's official CLI for Claude." + c.SystemPromptPrefix = "You are Claude Code, Anthropic's official CLI for Claude."
@@ -11,6 +12,7 @@
+ c.ExtraHeaders = make(map[string]string) + c.ExtraHeaders = make(map[string]string)
+ } + }
+ c.ExtraHeaders["anthropic-version"] = "2023-06-01" + c.ExtraHeaders["anthropic-version"] = "2023-06-01"
+ c.ExtraHeaders["User-Agent"] = "claude-code/2.1.112"
+ const want = "oauth-2025-04-20" + const want = "oauth-2025-04-20"
+ if v := c.ExtraHeaders["anthropic-beta"]; !strings.Contains(v, want) { + if v := c.ExtraHeaders["anthropic-beta"]; !strings.Contains(v, want) {
+ if v != "" { + if v != "" {
@@ -18,6 +20,11 @@
+ } + }
+ c.ExtraHeaders["anthropic-beta"] = v + want + c.ExtraHeaders["anthropic-beta"] = v + want
+ } + }
+ if c.APIKey != "" && !strings.HasPrefix(c.APIKey, "Bearer ") {
+ c.APIKey = "Bearer " + c.APIKey
+ }
+} +}
+ +
type MCPType string type MCPType string
const (

View File

@@ -1,6 +1,7 @@
--- internal/config/load.go Index: internal/config/load.go
--- internal/config/load.go.orig
+++ internal/config/load.go +++ internal/config/load.go
@@ -287,14 +287,7 @@ @@ -287,14 +287,7 @@ func (c *Config) configureProviders(ctx context.Contex
switch { switch {
case p.ID == catwalk.InferenceProviderAnthropic && config.OAuthToken != nil: case p.ID == catwalk.InferenceProviderAnthropic && config.OAuthToken != nil:

View File

@@ -1,6 +1,7 @@
--- internal/config/store.go Index: internal/config/store.go
--- internal/config/store.go.orig
+++ internal/config/store.go +++ internal/config/store.go
@@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@ import (
"github.com/charmbracelet/crush/internal/env" "github.com/charmbracelet/crush/internal/env"
"github.com/charmbracelet/crush/internal/lock" "github.com/charmbracelet/crush/internal/lock"
"github.com/charmbracelet/crush/internal/oauth" "github.com/charmbracelet/crush/internal/oauth"
@@ -8,7 +9,7 @@
"github.com/charmbracelet/crush/internal/oauth/copilot" "github.com/charmbracelet/crush/internal/oauth/copilot"
"github.com/charmbracelet/crush/internal/oauth/hyper" "github.com/charmbracelet/crush/internal/oauth/hyper"
"github.com/tidwall/gjson" "github.com/tidwall/gjson"
@@ -470,6 +471,8 @@ @@ -470,6 +471,8 @@ func (s *ConfigStore) SetProviderAPIKey(scope Scope, p
providerConfig.APIKey = v.AccessToken providerConfig.APIKey = v.AccessToken
providerConfig.OAuthToken = v providerConfig.OAuthToken = v
switch providerID { switch providerID {
@@ -17,7 +18,7 @@
case string(catwalk.InferenceProviderCopilot): case string(catwalk.InferenceProviderCopilot):
providerConfig.SetupGitHubCopilot() providerConfig.SetupGitHubCopilot()
} }
@@ -596,7 +599,10 @@ @@ -596,7 +599,10 @@ func (s *ConfigStore) refreshOAuthTokenLocked(ctx cont
slog.Info("Successfully refreshed OAuth token", "provider", providerID) slog.Info("Successfully refreshed OAuth token", "provider", providerID)
providerConfig.OAuthToken = refreshedToken providerConfig.OAuthToken = refreshedToken
providerConfig.APIKey = refreshedToken.AccessToken providerConfig.APIKey = refreshedToken.AccessToken
@@ -29,7 +30,7 @@
providerConfig.SetupGitHubCopilot() providerConfig.SetupGitHubCopilot()
} }
cfg.Providers.Set(providerID, providerConfig) cfg.Providers.Set(providerID, providerConfig)
@@ -638,6 +644,8 @@ @@ -638,6 +644,8 @@ func (s *ConfigStore) exchange(ctx context.Context, pr
return s.exchangeToken(ctx, providerID, refreshToken) return s.exchangeToken(ctx, providerID, refreshToken)
} }
switch providerID { switch providerID {
@@ -38,7 +39,7 @@
case string(catwalk.InferenceProviderCopilot): case string(catwalk.InferenceProviderCopilot):
return copilot.RefreshToken(ctx, refreshToken) return copilot.RefreshToken(ctx, refreshToken)
case hyperp.Name: case hyperp.Name:
@@ -662,7 +670,10 @@ @@ -662,7 +670,10 @@ func (s *ConfigStore) refreshLockPath(providerID strin
func (s *ConfigStore) applyToken(providerConfig ProviderConfig, token *oauth.Token, providerID string) error { func (s *ConfigStore) applyToken(providerConfig ProviderConfig, token *oauth.Token, providerID string) error {
providerConfig.OAuthToken = token providerConfig.OAuthToken = token
providerConfig.APIKey = token.AccessToken providerConfig.APIKey = token.AccessToken

View File

@@ -1,4 +1,5 @@
--- /dev/null Index: internal/oauth/claude/challenge.go
--- internal/oauth/claude/challenge.go.orig
+++ internal/oauth/claude/challenge.go +++ internal/oauth/claude/challenge.go
@@ -0,0 +1,28 @@ @@ -0,0 +1,28 @@
+package claude +package claude

View File

@@ -1,6 +1,7 @@
--- /dev/null Index: internal/oauth/claude/oauth.go
--- internal/oauth/claude/oauth.go.orig
+++ internal/oauth/claude/oauth.go +++ internal/oauth/claude/oauth.go
@@ -0,0 +1,126 @@ @@ -0,0 +1,131 @@
+package claude +package claude
+ +
+import ( +import (
@@ -17,19 +18,24 @@
+ "github.com/charmbracelet/crush/internal/oauth" + "github.com/charmbracelet/crush/internal/oauth"
+) +)
+ +
+const clientId = "9d1c250a-e61b-44d9-88ed-5944d1962f5e" +const (
+ clientId = "9d1c250a-e61b-44d9-88ed-5944d1962f5e"
+ tokenURL = "https://platform.claude.com/v1/oauth/token"
+ redirectURI = "https://platform.claude.com/oauth/code/callback"
+)
+ +
+// AuthorizeURL returns the Claude Code Max OAuth2 authorization URL. +// AuthorizeURL returns the Claude Code Max OAuth2 authorization URL.
+func AuthorizeURL(verifier, challenge string) (string, error) { +func AuthorizeURL(verifier, challenge string) (string, error) {
+ u, err := url.Parse("https://claude.ai/oauth/authorize") + u, err := url.Parse("https://platform.claude.com/oauth/authorize")
+ if err != nil { + if err != nil {
+ return "", err + return "", err
+ } + }
+ q := u.Query() + q := u.Query()
+ q.Set("response_type", "code") + q.Set("code", "true")
+ q.Set("client_id", clientId) + q.Set("client_id", clientId)
+ q.Set("redirect_uri", "https://console.anthropic.com/oauth/code/callback") + q.Set("response_type", "code")
+ q.Set("scope", "org:create_api_key user:profile user:inference") + q.Set("redirect_uri", redirectURI)
+ q.Set("scope", "org:create_api_key user:profile user:inference user:sessions:claude_code user:mcp_servers user:file_upload")
+ q.Set("code_challenge", challenge) + q.Set("code_challenge", challenge)
+ q.Set("code_challenge_method", "S256") + q.Set("code_challenge_method", "S256")
+ q.Set("state", verifier) + q.Set("state", verifier)
@@ -52,11 +58,11 @@
+ "state": state, + "state": state,
+ "grant_type": "authorization_code", + "grant_type": "authorization_code",
+ "client_id": clientId, + "client_id": clientId,
+ "redirect_uri": "https://console.anthropic.com/oauth/code/callback", + "redirect_uri": redirectURI,
+ "code_verifier": verifier, + "code_verifier": verifier,
+ } + }
+ +
+ resp, err := request(ctx, "POST", "https://console.anthropic.com/v1/oauth/token", reqBody) + resp, err := request(ctx, "POST", tokenURL, reqBody)
+ if err != nil { + if err != nil {
+ return nil, err + return nil, err
+ } + }
@@ -87,7 +93,7 @@
+ "client_id": clientId, + "client_id": clientId,
+ } + }
+ +
+ resp, err := request(ctx, "POST", "https://console.anthropic.com/v1/oauth/token", reqBody) + resp, err := request(ctx, "POST", tokenURL, reqBody)
+ if err != nil { + if err != nil {
+ return nil, err + return nil, err
+ } + }
@@ -122,7 +128,7 @@
+ } + }
+ +
+ req.Header.Set("Content-Type", "application/json") + req.Header.Set("Content-Type", "application/json")
+ req.Header.Set("User-Agent", "anthropic") + req.Header.Set("User-Agent", "claude-code/2.1.112")
+ +
+ client := &http.Client{Timeout: 30 * time.Second} + client := &http.Client{Timeout: 30 * time.Second}
+ return client.Do(req) + return client.Do(req)

View File

@@ -1,4 +1,5 @@
--- /dev/null Index: internal/ui/dialog/oauth_claude.go
--- internal/ui/dialog/oauth_claude.go.orig
+++ internal/ui/dialog/oauth_claude.go +++ internal/ui/dialog/oauth_claude.go
@@ -0,0 +1,241 @@ @@ -0,0 +1,241 @@
+package dialog +package dialog

View File

@@ -1,6 +1,7 @@
--- internal/ui/model/ui.go Index: internal/ui/model/ui.go
--- internal/ui/model/ui.go.orig
+++ internal/ui/model/ui.go +++ internal/ui/model/ui.go
@@ -1952,6 +1952,8 @@ func (m *UI) openAuthenticationDialog(provider catwalk.Provider, model config.Se @@ -1952,6 +1952,8 @@ func (m *UI) openAuthenticationDialog(provider catwalk
switch provider.ID { switch provider.ID {
case "hyper": case "hyper":
dlg, cmd = dialog.NewOAuthHyper(m.com, isOnboarding, provider, model, modelType) dlg, cmd = dialog.NewOAuthHyper(m.com, isOnboarding, provider, model, modelType)