| 511 | } |
| 512 | |
| 513 | func parseShellRC(path, modelOverride string) (ResolvedEndpoint, bool, error) { |
| 514 | data, err := os.ReadFile(path) |
| 515 | if err != nil { |
| 516 | return ResolvedEndpoint{}, false, nil |
| 517 | } |
| 518 | |
| 519 | var baseURL, token, model string |
| 520 | for _, line := range strings.Split(string(data), "\n") { |
| 521 | line = strings.TrimSpace(line) |
| 522 | matches := exportRe.FindStringSubmatch(line) |
| 523 | if matches == nil { |
| 524 | continue |
| 525 | } |
| 526 | key := matches[1] |
| 527 | value := matches[2] |
| 528 | if value == "" { |
| 529 | value = matches[3] |
| 530 | } |
| 531 | if value == "" { |
| 532 | value = matches[4] |
| 533 | } |
| 534 | value = strings.TrimSpace(value) |
| 535 | |
| 536 | switch key { |
| 537 | case "ANTHROPIC_BASE_URL": |
| 538 | baseURL = value |
| 539 | case "ANTHROPIC_AUTH_TOKEN": |
| 540 | token = value |
| 541 | case "ANTHROPIC_MODEL": |
| 542 | model = value |
| 543 | } |
| 544 | } |
| 545 | if modelOverride != "" { |
| 546 | model = modelOverride |
| 547 | } |
| 548 | |
| 549 | if baseURL == "" || token == "" || model == "" { |
| 550 | return ResolvedEndpoint{}, false, nil |
| 551 | } |
| 552 | |
| 553 | url := ensureMessagesSuffix(baseURL) |
| 554 | |
| 555 | // Claude Code shell rc tokens are OAuth/Bearer-style credentials. |
| 556 | return ResolvedEndpoint{URL: url, Token: token, Model: model, Protocol: ProtocolAnthropic, AuthHeader: "authorization", Source: "Shell rc file"}, true, nil |
| 557 | } |
| 558 | |
| 559 | func defaultAuthHeader(protocol string) string { |
| 560 | // auth_header is Anthropic-only; OpenAI-compatible clients keep API key auth. |