-
Notifications
You must be signed in to change notification settings - Fork 0
Use Gemini subscription OAuth accounts only #29
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -64,58 +64,43 @@ func runGeminiCommand(ctx context.Context, args []string, stdout io.Writer, stde | |
| if err != nil { | ||
| return err | ||
| } | ||
| aiStudioAccounts, err := client.List(ctx, "gemini-ai") | ||
| accounts, err := client.List(ctx, "gemini") | ||
| if err != nil { | ||
| return err | ||
| } | ||
| codeAssistAccounts, err := client.List(ctx, "gemini") | ||
| selectedAccount, err := selectGeminiAccount(account, accounts) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| provider, selectedAccount, err := selectGeminiAccount(account, aiStudioAccounts, codeAssistAccounts) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| return runGemini(ctx, client.BaseURL, client.Token, provider, selectedAccount, withDefaultGeminiModel(passthrough), os.Stdin, stdout, stderr) | ||
| return runGemini(ctx, client.BaseURL, client.Token, selectedAccount, withDefaultGeminiModel(passthrough), os.Stdin, stdout, stderr) | ||
| } | ||
|
|
||
| func selectGeminiAccount(selector string, aiStudioAccounts []api.Credential, codeAssistAccounts []api.Credential) (string, string, error) { | ||
| func selectGeminiAccount(selector string, accounts []api.Credential) (string, error) { | ||
| if selector != "" { | ||
| matches := make([]struct{ provider, id string }, 0, 2) | ||
| for _, group := range []struct { | ||
| provider string | ||
| accounts []api.Credential | ||
| }{{"gemini-ai", aiStudioAccounts}, {"gemini", codeAssistAccounts}} { | ||
| for _, account := range group.accounts { | ||
| if account.ID == selector || account.Name == selector { | ||
| matches = append(matches, struct{ provider, id string }{group.provider, account.ID}) | ||
| } | ||
| matches := make([]string, 0, 1) | ||
| for _, account := range accounts { | ||
| if account.ID == selector || account.Name == selector { | ||
| matches = append(matches, account.ID) | ||
| } | ||
| } | ||
| if len(matches) == 0 { | ||
| return "", "", fmt.Errorf("Gemini account %q is not registered", selector) | ||
| return "", fmt.Errorf("Gemini subscription account %q is not registered", selector) | ||
| } | ||
| if len(matches) > 1 { | ||
| return "", "", fmt.Errorf("Gemini account %q is ambiguous; use its credential ID", selector) | ||
| return "", fmt.Errorf("Gemini subscription account %q is ambiguous; use its credential ID", selector) | ||
| } | ||
| return matches[0].provider, matches[0].id, nil | ||
| return matches[0], nil | ||
| } | ||
| provider, accounts := "gemini-ai", aiStudioAccounts | ||
| if len(accounts) == 0 { | ||
| provider, accounts = "gemini", codeAssistAccounts | ||
| return "", errors.New("no Gemini subscription accounts are registered; run 'prism gemini auth login'") | ||
| } | ||
| if len(accounts) == 0 { | ||
| return "", "", errors.New("no Gemini accounts are registered; run 'prism gemini-ai auth add'") | ||
| } | ||
| account, err := rotateProviderAccount(provider, accounts) | ||
| return provider, account, err | ||
| return rotateProviderAccount("gemini", accounts) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Suggestion: Default execution now invokes the shared file-backed rotation routine for every Gemini launch. That routine performs an unlocked read, increment, and rename, so concurrent Severity Level: Major
|
||
| } | ||
|
|
||
| func runGemini( | ||
| ctx context.Context, | ||
| prismURL string, | ||
| prismCredential string, | ||
| provider string, | ||
| account string, | ||
| args []string, | ||
| stdin io.Reader, | ||
|
|
@@ -126,7 +111,7 @@ func runGemini( | |
| if err != nil { | ||
| return err | ||
| } | ||
| bridge, err := startGeminiBridge(prismURL, prismCredential, provider, account, stderr) | ||
| bridge, err := startGeminiBridge(prismURL, prismCredential, account, stderr) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
@@ -191,7 +176,7 @@ func withDefaultGeminiModel(args []string) []string { | |
| return append([]string{"--model", defaultGeminiModel}, args...) | ||
| } | ||
|
|
||
| func startGeminiBridge(prismURL string, prismCredential string, provider string, account string, stderr io.Writer) (*geminiBridge, error) { | ||
| func startGeminiBridge(prismURL string, prismCredential string, account string, stderr io.Writer) (*geminiBridge, error) { | ||
| target, err := url.Parse(prismURL) | ||
| if err != nil || (target.Scheme != "https" && target.Scheme != "http") || target.Host == "" { | ||
| return nil, errors.New("Prism URL is invalid") | ||
|
|
@@ -202,9 +187,6 @@ func startGeminiBridge(prismURL string, prismCredential string, provider string, | |
| if strings.TrimSpace(account) == "" || strings.ContainsAny(account, "\r\n") { | ||
| return nil, errors.New("Gemini account selector is invalid") | ||
| } | ||
| if provider != "gemini-ai" && provider != "gemini" { | ||
| return nil, errors.New("Gemini account provider is invalid") | ||
| } | ||
| credentialBytes := make([]byte, 32) | ||
| if _, err := rand.Read(credentialBytes); err != nil { | ||
| return nil, errors.New("could not create a local Gemini credential") | ||
|
|
@@ -223,7 +205,6 @@ func startGeminiBridge(prismURL string, prismCredential string, provider string, | |
| request.Header.Del("X-Prism-Gemini-Provider") | ||
| request.Header.Set("Authorization", "Bearer "+prismCredential) | ||
| request.Header.Set("X-Prism-Gemini-Account", "b64:"+base64.RawURLEncoding.EncodeToString([]byte(account))) | ||
| request.Header.Set("X-Prism-Gemini-Provider", provider) | ||
| } | ||
| proxy.ErrorLog = log.New(stderr, "prism: ", 0) | ||
| proxy.ErrorHandler = func(response http.ResponseWriter, _ *http.Request, _ error) { | ||
|
|
@@ -297,8 +278,8 @@ func printGeminiHelp(output io.Writer) { | |
| prism gemini [--account <alias-or-id>] [Gemini CLI arguments...] | ||
|
|
||
| Runs the official Gemini CLI through Prism's Vault-backed Google accounts. | ||
| AI Studio accounts are preferred and selected in balanced rotation. A valid | ||
| Code Assist account can still be selected explicitly with --account. | ||
| Only fixed-price Gemini subscription and Code Assist OAuth accounts are used; | ||
| AI Studio API keys are intentionally unsupported to prevent usage-based charges. | ||
| The default model is gemini-3.7-flash; use --model gemini-3.1-pro-preview for | ||
| hard software-engineering and multi-step tool-use work. | ||
| Run 'gemini --help' for Gemini CLI options.`) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: Commands that do not need an account, such as
prism gemini --version, now perform a network-backed credential listing before the Gemini CLI is invoked. This makes local CLI metadata commands fail when Prism is unavailable or credentials are invalid; handle local-only CLI commands before listing accounts or only list accounts when an authenticated execution is required. [api mismatch]Severity Level: Major⚠️
Prompt for AI Agent 🤖