From 2a23a79bd200f377b4b2a9287db8515bb0302844 Mon Sep 17 00:00:00 2001 From: YG Park Date: Mon, 24 Aug 2026 20:47:57 +0900 Subject: [PATCH] fix(gemini): enable external gateway auth (#25) --- internal/cli/gemini.go | 26 +++++++++++++++++++++----- internal/cli/gemini_test.go | 4 ++-- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/internal/cli/gemini.go b/internal/cli/gemini.go index ad832d6..014c355 100644 --- a/internal/cli/gemini.go +++ b/internal/cli/gemini.go @@ -104,7 +104,7 @@ func runGemini( } defer os.RemoveAll(settingsDirectory) settingsPath := filepath.Join(settingsDirectory, "settings.json") - if err := os.WriteFile(settingsPath, []byte(`{"security":{"auth":{"selectedType":"gateway"}}}`), 0o600); err != nil { + if err := os.WriteFile(settingsPath, []byte(`{"security":{"auth":{"selectedType":"gateway","useExternal":true}}}`), 0o600); err != nil { return errors.New("could not create Gemini CLI gateway settings") } @@ -113,7 +113,7 @@ func runGemini( command.Stdin = stdin command.Stdout = stdout command.Stderr = stderr - command.Env = geminiEnvironment(os.Environ(), bridge.url, bridge.headerName+": "+bridge.headerValue, settingsPath) + command.Env = geminiEnvironment(os.Environ(), bridge.url, bridge.headerName+": "+bridge.headerValue, settingsPath, isAutomatedGeminiPrompt(args)) if err := command.Run(); err != nil { var exitError *exec.ExitError if errors.As(err, &exitError) { @@ -217,8 +217,17 @@ func (bridge *geminiBridge) close() { _ = bridge.server.Shutdown(ctx) } -func geminiEnvironment(environment []string, baseURL string, customHeaders string, settingsPath string) []string { - filtered := make([]string, 0, len(environment)+3) +func isAutomatedGeminiPrompt(args []string) bool { + for _, argument := range args { + if argument == "-p" || argument == "--prompt" || strings.HasPrefix(argument, "--prompt=") { + return true + } + } + return false +} + +func geminiEnvironment(environment []string, baseURL string, customHeaders string, settingsPath string, trustWorkspace bool) []string { + filtered := make([]string, 0, len(environment)+4) for _, entry := range environment { name, _, _ := strings.Cut(entry, "=") switch strings.ToUpper(name) { @@ -227,13 +236,20 @@ func geminiEnvironment(environment []string, baseURL string, customHeaders strin "GEMINI_CLI_SYSTEM_SETTINGS_PATH": continue } + if trustWorkspace && strings.EqualFold(name, "GEMINI_CLI_TRUST_WORKSPACE") { + continue + } filtered = append(filtered, entry) } - return append(filtered, + filtered = append(filtered, "GOOGLE_GEMINI_BASE_URL="+baseURL, "GEMINI_CLI_CUSTOM_HEADERS="+customHeaders, "GEMINI_CLI_SYSTEM_SETTINGS_PATH="+settingsPath, ) + if trustWorkspace { + filtered = append(filtered, "GEMINI_CLI_TRUST_WORKSPACE=true") + } + return filtered } func printGeminiHelp(output io.Writer) { diff --git a/internal/cli/gemini_test.go b/internal/cli/gemini_test.go index dc94a6d..33a5c35 100644 --- a/internal/cli/gemini_test.go +++ b/internal/cli/gemini_test.go @@ -91,7 +91,7 @@ func TestRunGeminiUsesOfficialCLIWithGatewayEnvironment(t *testing.T) { defer func() { findGeminiCLIExecutable = original }() directory := t.TempDir() executable := filepath.Join(directory, "gemini") - if err := os.WriteFile(executable, []byte("#!/bin/sh\nprintf '%s\\n' \"$@\"\nprintf 'base=%s\\nheaders=%s\\nsettings=%s\\n' \"$GOOGLE_GEMINI_BASE_URL\" \"$GEMINI_CLI_CUSTOM_HEADERS\" \"$GEMINI_CLI_SYSTEM_SETTINGS_PATH\"\n"), 0o755); err != nil { + if err := os.WriteFile(executable, []byte("#!/bin/sh\nprintf '%s\\n' \"$@\"\nprintf 'base=%s\\nheaders=%s\\nsettings=%s\\ntrust=%s\\n' \"$GOOGLE_GEMINI_BASE_URL\" \"$GEMINI_CLI_CUSTOM_HEADERS\" \"$GEMINI_CLI_SYSTEM_SETTINGS_PATH\" \"$GEMINI_CLI_TRUST_WORKSPACE\"\ncat \"$GEMINI_CLI_SYSTEM_SETTINGS_PATH\"\n"), 0o755); err != nil { t.Fatal(err) } findGeminiCLIExecutable = func() (geminiCLIExecutable, error) { return geminiCLIExecutable{path: executable}, nil } @@ -101,7 +101,7 @@ func TestRunGeminiUsesOfficialCLIWithGatewayEnvironment(t *testing.T) { if err := runGemini(context.Background(), upstream.URL, "circles-secret", "person@example.com", withDefaultGeminiModel([]string{"-p", "hello"}), strings.NewReader(""), &output, io.Discard); err != nil { t.Fatal(err) } - for _, value := range []string{"--model\ngemini-3.7-flash\n-p\nhello", "base=http://127.0.0.1:", "headers=X-Prism-Gemini-Bridge:", "settings=/"} { + for _, value := range []string{"--model\ngemini-3.7-flash\n-p\nhello", "base=http://127.0.0.1:", "headers=X-Prism-Gemini-Bridge:", "settings=/", "trust=true", `"selectedType":"gateway"`, `"useExternal":true`} { if !strings.Contains(output.String(), value) { t.Fatalf("output omitted %q: %s", value, output.String()) }