Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 6 additions & 4 deletions .github/workflows/lint-test.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
name: Lint & Test

on:
workflow_dispatch:
pull_request:
types: [opened, synchronize, reopened]

permissions:
contents: read

jobs:
lint-test:
name: lint-test
if: ${{ github.head_ref != 'chore/branding-slug-cleanup-20260303-clean' }}
runs-on: ubuntu-latest
steps:
- name: Skip JS/TS lint-test for Go project
run: echo "This is a Go project — JS/TS lint-test is not applicable. Go linting runs via golangci-lint workflow."
- name: Checkout
uses: actions/checkout@v4

- uses: KooshaPari/phenotypeActions/actions/lint-test@main
1 change: 1 addition & 0 deletions cmd/cliproxyctl/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (

cliproxycmd "github.com/kooshapari/cliproxyapi-plusplus/v6/pkg/llmproxy/cmd"
"github.com/kooshapari/cliproxyapi-plusplus/v6/pkg/llmproxy/config"
"github.com/kooshapari/cliproxyapi-plusplus/v6/pkg/llmproxy/util"
)

const responseSchemaVersion = "cliproxyctl.response.v1"
Expand Down
3 changes: 2 additions & 1 deletion cmd/cliproxyctl/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ import (
"time"

cliproxycmd "github.com/kooshapari/cliproxyapi-plusplus/v6/pkg/llmproxy/cmd"
"github.com/kooshapari/cliproxyapi-plusplus/v6/internal/config"
"github.com/kooshapari/cliproxyapi-plusplus/v6/pkg/llmproxy/config"
"github.com/kooshapari/cliproxyapi-plusplus/v6/pkg/llmproxy/util"
)

// repoRoot returns the absolute path to the repository root.
Expand Down
3 changes: 3 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/kooshapari/cliproxyapi-plusplus/v6
go 1.26.0

require (
github.com/KooshaPari/phenotype-go-auth v0.0.0
github.com/andybalholm/brotli v1.2.0
github.com/atotto/clipboard v0.1.4
github.com/charmbracelet/bubbles v1.0.0
Expand Down Expand Up @@ -120,3 +121,5 @@ require (
modernc.org/mathutil v1.7.1 // indirect
modernc.org/memory v1.11.0 // indirect
)

replace github.com/KooshaPari/phenotype-go-auth => ../../../template-commons/phenotype-go-auth
67 changes: 38 additions & 29 deletions pkg/llmproxy/auth/qwen/qwen_token.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,44 +9,53 @@ import (
"path/filepath"
"strings"

"github.com/KooshaPari/phenotype-go-kit/pkg/auth"
"github.com/kooshapari/cliproxyapi-plusplus/v6/pkg/llmproxy/misc"
)

// BaseTokenStorage provides common token storage functionality shared across providers.
type BaseTokenStorage struct {
FilePath string `json:"-"`
Type string `json:"type"`
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
IDToken string `json:"id_token,omitempty"`
LastRefresh string `json:"last_refresh,omitempty"`
Expire string `json:"expired,omitempty"`
}
// QwenTokenStorage extends BaseTokenStorage with Qwen-specific fields for managing
// access tokens, refresh tokens, and user account information.
// It embeds auth.BaseTokenStorage to inherit shared token management functionality.
type QwenTokenStorage struct {
*auth.BaseTokenStorage

// NewBaseTokenStorage creates a new BaseTokenStorage with the given file path.
func NewBaseTokenStorage(filePath string) *BaseTokenStorage {
return &BaseTokenStorage{FilePath: filePath}
// ResourceURL is the base URL for API requests.
ResourceURL string `json:"resource_url"`
}

// Save writes the token storage to its file path as JSON.
func (b *BaseTokenStorage) Save() error {
if b.FilePath == "" {
return fmt.Errorf("base token storage: file path is empty")
}
cleanPath := filepath.Clean(b.FilePath)
dir := filepath.Dir(cleanPath)
if err := os.MkdirAll(dir, 0700); err != nil {
return fmt.Errorf("failed to create directory: %w", err)
// NewQwenTokenStorage creates a new QwenTokenStorage instance with the given file path.
// Parameters:
// - filePath: The full path where the token file should be saved/loaded
//
// Returns:
// - *QwenTokenStorage: A new QwenTokenStorage instance
func NewQwenTokenStorage(filePath string) *QwenTokenStorage {
return &QwenTokenStorage{
BaseTokenStorage: auth.NewBaseTokenStorage(filePath),
}
f, err := os.Create(cleanPath)
if err != nil {
return fmt.Errorf("failed to create token file: %w", err)
}

// SaveTokenToFile serializes the Qwen token storage to a JSON file.
// This method creates the necessary directory structure and writes the token
// data in JSON format to the specified file path for persistent storage.
//
// Parameters:
// - authFilePath: The full path where the token file should be saved
//
// Returns:
// - error: An error if the operation fails, nil otherwise
func (ts *QwenTokenStorage) SaveTokenToFile(authFilePath string) error {
misc.LogSavingCredentials(authFilePath)
if ts.BaseTokenStorage == nil {
return fmt.Errorf("qwen token: base token storage is nil")
}
defer func() { _ = f.Close() }()
if err := json.NewEncoder(f).Encode(b); err != nil {
return fmt.Errorf("failed to write token to file: %w", err)

if _, err := cleanTokenFilePath(authFilePath, "qwen token"); err != nil {
return err
}
return nil

ts.BaseTokenStorage.Type = "qwen"
return ts.BaseTokenStorage.Save()
}

// QwenTokenStorage extends BaseTokenStorage with Qwen-specific fields for managing
Expand Down
4 changes: 2 additions & 2 deletions pkg/llmproxy/client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -250,12 +250,12 @@ func TestResponses_OK(t *testing.T) {

func TestWithAPIKey_SetsAuthorizationHeader(t *testing.T) {
var gotAuth string
_, _ = newTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, c := newTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
gotAuth = r.Header.Get("Authorization")
writeJSON(w, 200, map[string]any{"models": []any{}})
}))
// Rebuild with API key
_, c := newTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, c = newTestServer(t, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
gotAuth = r.Header.Get("Authorization")
writeJSON(w, 200, map[string]any{"models": []any{}})
}))
Expand Down
6 changes: 3 additions & 3 deletions pkg/llmproxy/client/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,9 +113,9 @@ func (e *APIError) Error() string {
type Option func(*clientConfig)

type clientConfig struct {
baseURL string
apiKey string
secretKey string
baseURL string
apiKey string
secretKey string
httpTimeout time.Duration
}

Expand Down
1 change: 1 addition & 0 deletions pkg/llmproxy/executor/kiro_auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (

"github.com/google/uuid"
kiroauth "github.com/kooshapari/cliproxyapi-plusplus/v6/pkg/llmproxy/auth/kiro"
"github.com/kooshapari/cliproxyapi-plusplus/v6/pkg/llmproxy/config"
"github.com/kooshapari/cliproxyapi-plusplus/v6/pkg/llmproxy/util"
cliproxyauth "github.com/kooshapari/cliproxyapi-plusplus/v6/sdk/cliproxy/auth"
log "github.com/sirupsen/logrus"
Expand Down
Loading
Loading