| 542 | } |
| 543 | |
| 544 | func parseShellRC(path, modelOverride string) (ResolvedEndpoint, bool, error) { |
| 545 | data, err := os.ReadFile(path) |
| 546 | if err != nil { |
| 547 | return ResolvedEndpoint{}, false, nil |
| 548 | } |
| 549 | |
| 550 | var baseURL, token, model string |
| 551 | for _, line := range strings.Split(string(data), "\n") { |
| 552 | line = strings.TrimSpace(line) |
| 553 | matches := exportRe.FindStringSubmatch(line) |
| 554 | if matches == nil { |
| 555 | continue |
| 556 | } |
| 557 | key := matches[1] |
| 558 | value := matches[2] |
| 559 | if value == "" { |
| 560 | value = matches[3] |
| 561 | } |
| 562 | if value == "" { |
| 563 | value = matches[4] |
| 564 | } |
| 565 | value = strings.TrimSpace(value) |
| 566 | |
| 567 | switch key { |
| 568 | case "ANTHROPIC_BASE_URL": |
| 569 | baseURL = value |
| 570 | case "ANTHROPIC_AUTH_TOKEN": |
| 571 | token = value |
| 572 | case "ANTHROPIC_MODEL": |
| 573 | model = value |
| 574 | } |
| 575 | } |
| 576 | if modelOverride != "" { |
| 577 | model = modelOverride |
| 578 | } |
| 579 | |
| 580 | if baseURL == "" || token == "" || model == "" { |
| 581 | return ResolvedEndpoint{}, false, nil |
| 582 | } |
| 583 | |
| 584 | url := ensureMessagesSuffix(baseURL) |
| 585 | |
| 586 | // Claude Code shell rc tokens are OAuth/Bearer-style credentials. |
| 587 | return ResolvedEndpoint{URL: url, Token: token, Model: model, Protocol: ProtocolAnthropic, AuthHeader: "authorization", Source: "Shell rc file"}, true, nil |
| 588 | } |
| 589 | |
| 590 | func defaultAuthHeader(protocol string) string { |
| 591 | // auth_header is Anthropic-only; OpenAI-compatible clients keep API key auth. |