mirror of
https://github.com/sipeed/picoclaw.git
synced 2026-06-12 18:08:54 +00:00
87 lines
2.4 KiB
Go
87 lines
2.4 KiB
Go
package cliprovider
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"time"
|
|
)
|
|
|
|
// CodexHomeEnvVar is the environment variable that overrides the Codex CLI
|
|
// home directory when resolving the codex auth.json credentials file.
|
|
// Default: ~/.codex
|
|
const CodexHomeEnvVar = "CODEX_HOME"
|
|
|
|
// CodexCliAuth represents the ~/.codex/auth.json file structure.
|
|
type CodexCliAuth struct {
|
|
Tokens struct {
|
|
AccessToken string `json:"access_token"`
|
|
RefreshToken string `json:"refresh_token"`
|
|
AccountID string `json:"account_id"`
|
|
} `json:"tokens"`
|
|
}
|
|
|
|
// ReadCodexCliCredentials reads OAuth tokens from the Codex CLI's auth.json file.
|
|
// Expiry is estimated as file modification time + 1 hour (same approach as moltbot).
|
|
func ReadCodexCliCredentials() (accessToken, accountID string, expiresAt time.Time, err error) {
|
|
authPath, err := resolveCodexAuthPath()
|
|
if err != nil {
|
|
return "", "", time.Time{}, err
|
|
}
|
|
|
|
data, err := os.ReadFile(authPath)
|
|
if err != nil {
|
|
return "", "", time.Time{}, fmt.Errorf("reading %s: %w", authPath, err)
|
|
}
|
|
|
|
var auth CodexCliAuth
|
|
if err = json.Unmarshal(data, &auth); err != nil {
|
|
return "", "", time.Time{}, fmt.Errorf("parsing %s: %w", authPath, err)
|
|
}
|
|
|
|
if auth.Tokens.AccessToken == "" {
|
|
return "", "", time.Time{}, fmt.Errorf("no access_token in %s", authPath)
|
|
}
|
|
|
|
stat, err := os.Stat(authPath)
|
|
if err != nil {
|
|
expiresAt = time.Now().Add(time.Hour)
|
|
} else {
|
|
expiresAt = stat.ModTime().Add(time.Hour)
|
|
}
|
|
|
|
return auth.Tokens.AccessToken, auth.Tokens.AccountID, expiresAt, nil
|
|
}
|
|
|
|
// CreateCodexCliTokenSource creates a token source that reads from ~/.codex/auth.json.
|
|
// This allows the existing CodexProvider to reuse Codex CLI credentials.
|
|
func CreateCodexCliTokenSource() func() (string, string, error) {
|
|
return func() (string, string, error) {
|
|
token, accountID, expiresAt, err := ReadCodexCliCredentials()
|
|
if err != nil {
|
|
return "", "", fmt.Errorf("reading codex cli credentials: %w", err)
|
|
}
|
|
|
|
if time.Now().After(expiresAt) {
|
|
return "", "", fmt.Errorf(
|
|
"codex cli credentials expired (auth.json last modified > 1h ago). Run: codex login",
|
|
)
|
|
}
|
|
|
|
return token, accountID, nil
|
|
}
|
|
}
|
|
|
|
func resolveCodexAuthPath() (string, error) {
|
|
codexHome := os.Getenv(CodexHomeEnvVar)
|
|
if codexHome == "" {
|
|
home, err := os.UserHomeDir()
|
|
if err != nil {
|
|
return "", fmt.Errorf("getting home dir: %w", err)
|
|
}
|
|
codexHome = filepath.Join(home, ".codex")
|
|
}
|
|
return filepath.Join(codexHome, "auth.json"), nil
|
|
}
|