Merge remote-tracking branch 'origin/main' into pr-11
2 files · +40 -2
M packages/agent/build.go +9 -2
| @@ -317,8 +317,15 @@ func Resolve(args Args, requireCred bool) (Resolved, error) { | |||
| 317 | 317 | // never shows a "not logged in" banner. | |
| 318 | 318 | userPickedProvider := args.Provider != "" | |
| 319 | 319 | if credErr != nil && !userPickedProvider && provName != "ollama" { | |
| 320 | - | for _, other := range []string{"anthropic", "openai", "openai-codex", "kimi", "deepseek", "google"} { | |
| 321 | - | if other == provName { | |
| 320 | + | // Scan every known provider (not a hardcoded subset) so any | |
| 321 | + | // env-based credential is discovered, e.g. an env-only | |
| 322 | + | // amazon-bedrock setup (AWS_BEARER_TOKEN_BEDROCK / AWS_PROFILE / | |
| 323 | + | // IAM keys) when no config.json pins the provider, such as after | |
| 324 | + | // pointing ZOT_HOME at a fresh home dir. Iteration order of | |
| 325 | + | // knownProviders defines fallback priority. ollama is skipped: | |
| 326 | + | // it has no credential and would always "match". | |
| 327 | + | for _, other := range knownProviders { | |
| 328 | + | if other == provName || other == "ollama" { | |
| 322 | 329 | continue | |
| 323 | 330 | } | |
| 324 | 331 | if c, m, a, err := ResolveCredentialFull(other, args.APIKey); err == nil { | |
| @@ -317,8 +317,15 @@ func Resolve(args Args, requireCred bool) (Resolved, error) { | |||||
| 317 | // never shows a "not logged in" banner. | 317 | // never shows a "not logged in" banner. | ||
| 318 | userPickedProvider := args.Provider != "" | 318 | userPickedProvider := args.Provider != "" | ||
| 319 | if credErr != nil && !userPickedProvider && provName != "ollama" { | 319 | if credErr != nil && !userPickedProvider && provName != "ollama" { | ||
| 320 | - | for _, other := range []string{"anthropic", "openai", "openai-codex", "kimi", "deepseek", "google"} { | 320 | + | // Scan every known provider (not a hardcoded subset) so any |
| 321 | - | if other == provName { | 321 | + | // env-based credential is discovered, e.g. an env-only |
| 322 | + | // amazon-bedrock setup (AWS_BEARER_TOKEN_BEDROCK / AWS_PROFILE / | |||
| 323 | + | // IAM keys) when no config.json pins the provider, such as after | |||
| 324 | + | // pointing ZOT_HOME at a fresh home dir. Iteration order of | |||
| 325 | + | // knownProviders defines fallback priority. ollama is skipped: | |||
| 326 | + | // it has no credential and would always "match". | |||
| 327 | + | for _, other := range knownProviders { | |||
| 328 | + | if other == provName || other == "ollama" { | |||
| 322 | continue | 329 | continue | ||
| 323 | } | 330 | } | ||
| 324 | if c, m, a, err := ResolveCredentialFull(other, args.APIKey); err == nil { | 331 | if c, m, a, err := ResolveCredentialFull(other, args.APIKey); err == nil { | ||
M packages/agent/build_test.go +31 -0
| @@ -111,6 +111,37 @@ func TestResolveExplicitFlagStaleDoesNotRepairConfig(t *testing.T) { | |||
| 111 | 111 | } | |
| 112 | 112 | } | |
| 113 | 113 | ||
| 114 | + | // TestResolveEnvOnlyBedrockDiscoveredWithoutConfig reproduces issue | |
| 115 | + | // #15: pointing ZOT_HOME at a fresh dir drops the persisted | |
| 116 | + | // config.json (which pinned provider=amazon-bedrock). Resolve must | |
| 117 | + | // still discover bedrock from the AWS env vars instead of falling back | |
| 118 | + | // to anthropic and reporting "not logged in". | |
| 119 | + | func TestResolveEnvOnlyBedrockDiscoveredWithoutConfig(t *testing.T) { | |
| 120 | + | t.Setenv("ZOT_HOME", t.TempDir()) // fresh home: no config.json | |
| 121 | + | // Disable the Kimi CLI token fallback so a developer machine with a | |
| 122 | + | // real Kimi CLI login doesn't pre-empt bedrock in the scan. | |
| 123 | + | if err := SetKimiCLIFallbackDisabled(true); err != nil { | |
| 124 | + | t.Fatal(err) | |
| 125 | + | } | |
| 126 | + | t.Setenv("AWS_BEARER_TOKEN_BEDROCK", "test-bedrock-token") | |
| 127 | + | t.Setenv("AWS_REGION", "us-east-1") | |
| 128 | + | // Make sure no other provider's env credential pre-empts bedrock. | |
| 129 | + | for _, k := range []string{"ANTHROPIC_API_KEY", "ANTHROPIC_OAUTH_TOKEN", "OPENAI_API_KEY", "GEMINI_API_KEY", "GOOGLE_API_KEY", "DEEPSEEK_API_KEY", "KIMI_API_KEY", "MOONSHOT_API_KEY"} { | |
| 130 | + | t.Setenv(k, "") | |
| 131 | + | } | |
| 132 | + | ||
| 133 | + | r, err := Resolve(Args{}, true) | |
| 134 | + | if err != nil { | |
| 135 | + | t.Fatalf("Resolve errored with env-only bedrock: %v", err) | |
| 136 | + | } | |
| 137 | + | if r.Provider != "amazon-bedrock" { | |
| 138 | + | t.Fatalf("provider = %q, want amazon-bedrock", r.Provider) | |
| 139 | + | } | |
| 140 | + | if !r.HasCredential() { | |
| 141 | + | t.Fatalf("bedrock credential not resolved from env") | |
| 142 | + | } | |
| 143 | + | } | |
| 144 | + | ||
| 114 | 145 | func TestResolveOllamaUsesModelBaseURLBeforeDefault(t *testing.T) { | |
| 115 | 146 | t.Setenv("ZOT_HOME", t.TempDir()) | |
| 116 | 147 | provider.SetLiveModels(nil) | |
| @@ -111,6 +111,37 @@ func TestResolveExplicitFlagStaleDoesNotRepairConfig(t *testing.T) { | |||||
| 111 | } | 111 | } | ||
| 112 | } | 112 | } | ||
| 113 | 113 | ||||
| 114 | + | // TestResolveEnvOnlyBedrockDiscoveredWithoutConfig reproduces issue | |||
| 115 | + | // #15: pointing ZOT_HOME at a fresh dir drops the persisted | |||
| 116 | + | // config.json (which pinned provider=amazon-bedrock). Resolve must | |||
| 117 | + | // still discover bedrock from the AWS env vars instead of falling back | |||
| 118 | + | // to anthropic and reporting "not logged in". | |||
| 119 | + | func TestResolveEnvOnlyBedrockDiscoveredWithoutConfig(t *testing.T) { | |||
| 120 | + | t.Setenv("ZOT_HOME", t.TempDir()) // fresh home: no config.json | |||
| 121 | + | // Disable the Kimi CLI token fallback so a developer machine with a | |||
| 122 | + | // real Kimi CLI login doesn't pre-empt bedrock in the scan. | |||
| 123 | + | if err := SetKimiCLIFallbackDisabled(true); err != nil { | |||
| 124 | + | t.Fatal(err) | |||
| 125 | + | } | |||
| 126 | + | t.Setenv("AWS_BEARER_TOKEN_BEDROCK", "test-bedrock-token") | |||
| 127 | + | t.Setenv("AWS_REGION", "us-east-1") | |||
| 128 | + | // Make sure no other provider's env credential pre-empts bedrock. | |||
| 129 | + | for _, k := range []string{"ANTHROPIC_API_KEY", "ANTHROPIC_OAUTH_TOKEN", "OPENAI_API_KEY", "GEMINI_API_KEY", "GOOGLE_API_KEY", "DEEPSEEK_API_KEY", "KIMI_API_KEY", "MOONSHOT_API_KEY"} { | |||
| 130 | + | t.Setenv(k, "") | |||
| 131 | + | } | |||
| 132 | + | ||||
| 133 | + | r, err := Resolve(Args{}, true) | |||
| 134 | + | if err != nil { | |||
| 135 | + | t.Fatalf("Resolve errored with env-only bedrock: %v", err) | |||
| 136 | + | } | |||
| 137 | + | if r.Provider != "amazon-bedrock" { | |||
| 138 | + | t.Fatalf("provider = %q, want amazon-bedrock", r.Provider) | |||
| 139 | + | } | |||
| 140 | + | if !r.HasCredential() { | |||
| 141 | + | t.Fatalf("bedrock credential not resolved from env") | |||
| 142 | + | } | |||
| 143 | + | } | |||
| 144 | + | ||||
| 114 | func TestResolveOllamaUsesModelBaseURLBeforeDefault(t *testing.T) { | 145 | func TestResolveOllamaUsesModelBaseURLBeforeDefault(t *testing.T) { | ||
| 115 | t.Setenv("ZOT_HOME", t.TempDir()) | 146 | t.Setenv("ZOT_HOME", t.TempDir()) | ||
| 116 | provider.SetLiveModels(nil) | 147 | provider.SetLiveModels(nil) | ||