Add claude code multiplexer service
This commit is contained in:
116
service/ccm/credential_darwin.go
Normal file
116
service/ccm/credential_darwin.go
Normal file
@@ -0,0 +1,116 @@
|
||||
//go:build darwin && cgo
|
||||
|
||||
package ccm
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
E "github.com/sagernet/sing/common/exceptions"
|
||||
|
||||
"github.com/keybase/go-keychain"
|
||||
)
|
||||
|
||||
func getKeychainServiceName() string {
|
||||
configDirectory := os.Getenv("CLAUDE_CONFIG_DIR")
|
||||
if configDirectory == "" {
|
||||
return "Claude Code-credentials"
|
||||
}
|
||||
|
||||
userInfo, err := getRealUser()
|
||||
if err != nil {
|
||||
return "Claude Code-credentials"
|
||||
}
|
||||
defaultConfigDirectory := filepath.Join(userInfo.HomeDir, ".claude")
|
||||
if configDirectory == defaultConfigDirectory {
|
||||
return "Claude Code-credentials"
|
||||
}
|
||||
|
||||
hash := sha256.Sum256([]byte(configDirectory))
|
||||
return "Claude Code-credentials-" + hex.EncodeToString(hash[:])[:8]
|
||||
}
|
||||
|
||||
func platformReadCredentials(customPath string) (*oauthCredentials, error) {
|
||||
if customPath != "" {
|
||||
return readCredentialsFromFile(customPath)
|
||||
}
|
||||
|
||||
userInfo, err := getRealUser()
|
||||
if err == nil {
|
||||
query := keychain.NewItem()
|
||||
query.SetSecClass(keychain.SecClassGenericPassword)
|
||||
query.SetService(getKeychainServiceName())
|
||||
query.SetAccount(userInfo.Username)
|
||||
query.SetMatchLimit(keychain.MatchLimitOne)
|
||||
query.SetReturnData(true)
|
||||
|
||||
results, err := keychain.QueryItem(query)
|
||||
if err == nil && len(results) == 1 {
|
||||
var container struct {
|
||||
ClaudeAIAuth *oauthCredentials `json:"claudeAiOauth,omitempty"`
|
||||
}
|
||||
unmarshalErr := json.Unmarshal(results[0].Data, &container)
|
||||
if unmarshalErr == nil && container.ClaudeAIAuth != nil {
|
||||
return container.ClaudeAIAuth, nil
|
||||
}
|
||||
}
|
||||
if err != nil && err != keychain.ErrorItemNotFound {
|
||||
return nil, E.Cause(err, "query keychain")
|
||||
}
|
||||
}
|
||||
|
||||
defaultPath, err := getDefaultCredentialsPath()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return readCredentialsFromFile(defaultPath)
|
||||
}
|
||||
|
||||
func platformWriteCredentials(oauthCredentials *oauthCredentials, customPath string) error {
|
||||
if customPath != "" {
|
||||
return writeCredentialsToFile(oauthCredentials, customPath)
|
||||
}
|
||||
|
||||
userInfo, err := getRealUser()
|
||||
if err == nil {
|
||||
data, err := json.Marshal(map[string]any{"claudeAiOauth": oauthCredentials})
|
||||
if err == nil {
|
||||
serviceName := getKeychainServiceName()
|
||||
item := keychain.NewItem()
|
||||
item.SetSecClass(keychain.SecClassGenericPassword)
|
||||
item.SetService(serviceName)
|
||||
item.SetAccount(userInfo.Username)
|
||||
item.SetData(data)
|
||||
item.SetAccessible(keychain.AccessibleWhenUnlocked)
|
||||
|
||||
err = keychain.AddItem(item)
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err == keychain.ErrorDuplicateItem {
|
||||
query := keychain.NewItem()
|
||||
query.SetSecClass(keychain.SecClassGenericPassword)
|
||||
query.SetService(serviceName)
|
||||
query.SetAccount(userInfo.Username)
|
||||
|
||||
updateItem := keychain.NewItem()
|
||||
updateItem.SetData(data)
|
||||
|
||||
updateErr := keychain.UpdateItem(query, updateItem)
|
||||
if updateErr == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
defaultPath, err := getDefaultCredentialsPath()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return writeCredentialsToFile(oauthCredentials, defaultPath)
|
||||
}
|
||||
Reference in New Issue
Block a user