diff --git a/internal/plugins/ai/gemini_openai/gemini.go b/internal/plugins/ai/gemini_openai/gemini.go deleted file mode 100644 index d8bf89f9..00000000 --- a/internal/plugins/ai/gemini_openai/gemini.go +++ /dev/null @@ -1,15 +0,0 @@ -package gemini_openai - -import ( - "github.com/danielmiessler/fabric/internal/plugins/ai/openai" -) - -func NewClient() (ret *Client) { - ret = &Client{} - ret.Client = openai.NewClientCompatible("GeminiOpenAI", "https://generativelanguage.googleapis.com/v1beta", nil) - return -} - -type Client struct { - *openai.Client -} diff --git a/internal/util/oauth_storage.go b/internal/util/oauth_storage.go deleted file mode 100644 index 5a4033e8..00000000 --- a/internal/util/oauth_storage.go +++ /dev/null @@ -1,124 +0,0 @@ -package util - -import ( - "encoding/json" - "fmt" - "os" - "path/filepath" - "time" -) - -// OAuthToken represents stored OAuth token information -type OAuthToken struct { - AccessToken string `json:"access_token"` - RefreshToken string `json:"refresh_token"` - ExpiresAt int64 `json:"expires_at"` - TokenType string `json:"token_type"` - Scope string `json:"scope"` -} - -// IsExpired checks if the token is expired or will expire within the buffer time -func (t *OAuthToken) IsExpired(bufferMinutes int) bool { - if t.ExpiresAt == 0 { - return true - } - bufferTime := time.Duration(bufferMinutes) * time.Minute - return time.Now().Add(bufferTime).Unix() >= t.ExpiresAt -} - -// OAuthStorage handles persistent storage of OAuth tokens -type OAuthStorage struct { - configDir string -} - -// NewOAuthStorage creates a new OAuth storage instance -func NewOAuthStorage() (*OAuthStorage, error) { - homeDir, err := os.UserHomeDir() - if err != nil { - return nil, fmt.Errorf("failed to get user home directory: %w", err) - } - - configDir := filepath.Join(homeDir, ".config", "fabric") - - // Ensure config directory exists - if err := os.MkdirAll(configDir, 0755); err != nil { - return nil, fmt.Errorf("failed to create config directory: %w", err) - } - - return &OAuthStorage{configDir: configDir}, nil -} - -// GetTokenPath returns the file path for a provider's OAuth token -func (s *OAuthStorage) GetTokenPath(provider string) string { - return filepath.Join(s.configDir, fmt.Sprintf(".%s_oauth", provider)) -} - -// SaveToken saves an OAuth token to disk with proper permissions -func (s *OAuthStorage) SaveToken(provider string, token *OAuthToken) error { - tokenPath := s.GetTokenPath(provider) - - // Marshal token to JSON - data, err := json.MarshalIndent(token, "", " ") - if err != nil { - return fmt.Errorf("failed to marshal token: %w", err) - } - - // Write to temporary file first for atomic operation - tempPath := tokenPath + ".tmp" - if err := os.WriteFile(tempPath, data, 0600); err != nil { - return fmt.Errorf("failed to write token file: %w", err) - } - - // Atomic rename - if err := os.Rename(tempPath, tokenPath); err != nil { - os.Remove(tempPath) // Clean up temp file - return fmt.Errorf("failed to save token file: %w", err) - } - - return nil -} - -// LoadToken loads an OAuth token from disk -func (s *OAuthStorage) LoadToken(provider string) (*OAuthToken, error) { - tokenPath := s.GetTokenPath(provider) - - // Check if file exists - if _, err := os.Stat(tokenPath); os.IsNotExist(err) { - return nil, nil // No token stored - } - - // Read token file - data, err := os.ReadFile(tokenPath) - if err != nil { - return nil, fmt.Errorf("failed to read token file: %w", err) - } - - // Unmarshal token - var token OAuthToken - if err := json.Unmarshal(data, &token); err != nil { - return nil, fmt.Errorf("failed to parse token file: %w", err) - } - - return &token, nil -} - -// DeleteToken removes a stored OAuth token -func (s *OAuthStorage) DeleteToken(provider string) error { - tokenPath := s.GetTokenPath(provider) - - if err := os.Remove(tokenPath); err != nil && !os.IsNotExist(err) { - return fmt.Errorf("failed to delete token file: %w", err) - } - - return nil -} - -// HasValidToken checks if a valid (non-expired) token exists for a provider -func (s *OAuthStorage) HasValidToken(provider string, bufferMinutes int) bool { - token, err := s.LoadToken(provider) - if err != nil || token == nil { - return false - } - - return !token.IsExpired(bufferMinutes) -} diff --git a/internal/util/oauth_storage_test.go b/internal/util/oauth_storage_test.go deleted file mode 100644 index 054e1683..00000000 --- a/internal/util/oauth_storage_test.go +++ /dev/null @@ -1,232 +0,0 @@ -package util - -import ( - "os" - "path/filepath" - "testing" - "time" -) - -func TestOAuthToken_IsExpired(t *testing.T) { - tests := []struct { - name string - expiresAt int64 - bufferMinutes int - expected bool - }{ - { - name: "token not expired", - expiresAt: time.Now().Unix() + 3600, // 1 hour from now - bufferMinutes: 5, - expected: false, - }, - { - name: "token expired", - expiresAt: time.Now().Unix() - 3600, // 1 hour ago - bufferMinutes: 5, - expected: true, - }, - { - name: "token expires within buffer", - expiresAt: time.Now().Unix() + 120, // 2 minutes from now - bufferMinutes: 5, - expected: true, - }, - { - name: "zero expiry time", - expiresAt: 0, - bufferMinutes: 5, - expected: true, - }, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - token := &OAuthToken{ExpiresAt: tt.expiresAt} - if got := token.IsExpired(tt.bufferMinutes); got != tt.expected { - t.Errorf("IsExpired() = %v, want %v", got, tt.expected) - } - }) - } -} - -func TestOAuthStorage_SaveAndLoadToken(t *testing.T) { - // Create temporary directory for testing - tempDir, err := os.MkdirTemp("", "fabric_oauth_test") - if err != nil { - t.Fatalf("Failed to create temp dir: %v", err) - } - defer os.RemoveAll(tempDir) - - // Create storage with custom config dir - storage := &OAuthStorage{configDir: tempDir} - - // Test token - token := &OAuthToken{ - AccessToken: "test_access_token", - RefreshToken: "test_refresh_token", - ExpiresAt: time.Now().Unix() + 3600, - TokenType: "Bearer", - Scope: "test_scope", - } - - // Test saving token - err = storage.SaveToken("test_provider", token) - if err != nil { - t.Fatalf("Failed to save token: %v", err) - } - - // Verify file exists and has correct permissions - tokenPath := storage.GetTokenPath("test_provider") - info, err := os.Stat(tokenPath) - if err != nil { - t.Fatalf("Token file not created: %v", err) - } - if info.Mode().Perm() != 0600 { - t.Errorf("Token file has wrong permissions: %v, want 0600", info.Mode().Perm()) - } - - // Test loading token - loadedToken, err := storage.LoadToken("test_provider") - if err != nil { - t.Fatalf("Failed to load token: %v", err) - } - if loadedToken == nil { - t.Fatal("Loaded token is nil") - } - - // Verify token data - if loadedToken.AccessToken != token.AccessToken { - t.Errorf("AccessToken mismatch: got %v, want %v", loadedToken.AccessToken, token.AccessToken) - } - if loadedToken.RefreshToken != token.RefreshToken { - t.Errorf("RefreshToken mismatch: got %v, want %v", loadedToken.RefreshToken, token.RefreshToken) - } - if loadedToken.ExpiresAt != token.ExpiresAt { - t.Errorf("ExpiresAt mismatch: got %v, want %v", loadedToken.ExpiresAt, token.ExpiresAt) - } -} - -func TestOAuthStorage_LoadNonExistentToken(t *testing.T) { - // Create temporary directory for testing - tempDir, err := os.MkdirTemp("", "fabric_oauth_test") - if err != nil { - t.Fatalf("Failed to create temp dir: %v", err) - } - defer os.RemoveAll(tempDir) - - storage := &OAuthStorage{configDir: tempDir} - - // Try to load non-existent token - token, err := storage.LoadToken("nonexistent") - if err != nil { - t.Fatalf("Unexpected error loading non-existent token: %v", err) - } - if token != nil { - t.Error("Expected nil token for non-existent provider") - } -} - -func TestOAuthStorage_DeleteToken(t *testing.T) { - // Create temporary directory for testing - tempDir, err := os.MkdirTemp("", "fabric_oauth_test") - if err != nil { - t.Fatalf("Failed to create temp dir: %v", err) - } - defer os.RemoveAll(tempDir) - - storage := &OAuthStorage{configDir: tempDir} - - // Create and save a token - token := &OAuthToken{ - AccessToken: "test_token", - RefreshToken: "test_refresh", - ExpiresAt: time.Now().Unix() + 3600, - } - err = storage.SaveToken("test_provider", token) - if err != nil { - t.Fatalf("Failed to save token: %v", err) - } - - // Verify token exists - tokenPath := storage.GetTokenPath("test_provider") - if _, err := os.Stat(tokenPath); os.IsNotExist(err) { - t.Fatal("Token file should exist before deletion") - } - - // Delete token - err = storage.DeleteToken("test_provider") - if err != nil { - t.Fatalf("Failed to delete token: %v", err) - } - - // Verify token is deleted - if _, err := os.Stat(tokenPath); !os.IsNotExist(err) { - t.Error("Token file should not exist after deletion") - } - - // Test deleting non-existent token (should not error) - err = storage.DeleteToken("nonexistent") - if err != nil { - t.Errorf("Deleting non-existent token should not error: %v", err) - } -} - -func TestOAuthStorage_HasValidToken(t *testing.T) { - // Create temporary directory for testing - tempDir, err := os.MkdirTemp("", "fabric_oauth_test") - if err != nil { - t.Fatalf("Failed to create temp dir: %v", err) - } - defer os.RemoveAll(tempDir) - - storage := &OAuthStorage{configDir: tempDir} - - // Test with no token - if storage.HasValidToken("test_provider", 5) { - t.Error("Should return false when no token exists") - } - - // Save valid token - validToken := &OAuthToken{ - AccessToken: "valid_token", - RefreshToken: "refresh_token", - ExpiresAt: time.Now().Unix() + 3600, // 1 hour from now - } - err = storage.SaveToken("test_provider", validToken) - if err != nil { - t.Fatalf("Failed to save valid token: %v", err) - } - - // Test with valid token - if !storage.HasValidToken("test_provider", 5) { - t.Error("Should return true for valid token") - } - - // Save expired token - expiredToken := &OAuthToken{ - AccessToken: "expired_token", - RefreshToken: "refresh_token", - ExpiresAt: time.Now().Unix() - 3600, // 1 hour ago - } - err = storage.SaveToken("expired_provider", expiredToken) - if err != nil { - t.Fatalf("Failed to save expired token: %v", err) - } - - // Test with expired token - if storage.HasValidToken("expired_provider", 5) { - t.Error("Should return false for expired token") - } -} - -func TestOAuthStorage_GetTokenPath(t *testing.T) { - storage := &OAuthStorage{configDir: "/test/config"} - - expected := filepath.Join("/test/config", ".test_provider_oauth") - actual := storage.GetTokenPath("test_provider") - - if actual != expected { - t.Errorf("GetTokenPath() = %v, want %v", actual, expected) - } -}