mirror of
https://github.com/coder/coder.git
synced 2026-10-31 02:43:21 +08:00
## Problem Junie CLI works with AI Gateway, but every Junie session is recorded as `Unknown`. Junie sends `User-Agent: Junie:SNAPSHOT` (observed directly from the CLI), which `GuessClient` does not match, so usage, spend, and audit views cannot attribute Junie traffic. There were also no docs for pointing Junie at AI Gateway. ## Fix - Detect the observed `junie:` user-agent prefix and add the `Junie` client value. - Add the Junie icon so sessions render with a logo instead of the fallback question mark. - Add a Junie client page and list it in the compatibility table. - Add `Roo Code` and `Charm Crush` to the documented `client` filter values. Both were already implemented but missing from the list. ## Supported configurations | Provider | API type | Endpoint | |-----------|-------------------|--------------------------| | OpenAI | `OpenAIResponses` | `/openai/v1/responses` | | Anthropic | `Anthropic` | `/anthropic/v1/messages` | BYOK works on both through `extraHeaders` with `X-Coder-AI-Governance-Token`. Preview: https://coder.com/docs/@matifali-junie-ai-gateway-client/ai-coder/ai-gateway/clients/junie > 🤖 This PR was created with the help of Coder Agents, and needs a human review. 🧑💻
67 lines
2.5 KiB
Go
67 lines
2.5 KiB
Go
package aibridge
|
|
|
|
import (
|
|
"net/http"
|
|
"strings"
|
|
)
|
|
|
|
type Client string
|
|
|
|
const (
|
|
// Possible values for the "client" field in interception records.
|
|
// Must be kept in sync with documentation: https://github.com/coder/coder/blob/3cf867f84aa32d2febf7a26dc7e52be6beb8a2ac/docs/ai-coder/ai-gateway/monitoring.md?plain=1#L47-L57
|
|
ClientClaudeCode Client = "Claude Code"
|
|
ClientCodex Client = "Codex"
|
|
ClientZed Client = "Zed"
|
|
ClientCopilotVSC Client = "GitHub Copilot (VS Code)"
|
|
ClientCopilotCLI Client = "GitHub Copilot (CLI)"
|
|
ClientKilo Client = "Kilo Code"
|
|
ClientCoderAgents Client = "Coder Agents"
|
|
ClientCrush Client = "Charm Crush"
|
|
ClientMux Client = "Mux"
|
|
ClientRoo Client = "Roo Code"
|
|
ClientCursor Client = "Cursor"
|
|
ClientOpenCode Client = "OpenCode"
|
|
ClientJunie Client = "Junie"
|
|
ClientUnknown Client = "Unknown"
|
|
)
|
|
|
|
// GuessClient attempts to guess the client application from the request headers.
|
|
// Not all clients set proper user agent headers, so this is a best-effort approach.
|
|
// Based on https://github.com/coder/aibridge/issues/20#issuecomment-3769444101.
|
|
func GuessClient(r *http.Request) Client {
|
|
userAgent := strings.ToLower(r.UserAgent())
|
|
originator := r.Header.Get("originator")
|
|
|
|
// Must be kept in sync with documentation: https://github.com/coder/coder/blob/3cf867f84aa32d2febf7a26dc7e52be6beb8a2ac/docs/ai-coder/ai-gateway/monitoring.md?plain=1#L47-L57
|
|
switch {
|
|
case strings.HasPrefix(userAgent, "mux/"):
|
|
return ClientMux
|
|
case strings.HasPrefix(userAgent, "claude"):
|
|
return ClientClaudeCode
|
|
case strings.HasPrefix(userAgent, "codex"):
|
|
return ClientCodex
|
|
case strings.HasPrefix(userAgent, "zed/"):
|
|
return ClientZed
|
|
case strings.HasPrefix(userAgent, "githubcopilotchat/"):
|
|
return ClientCopilotVSC
|
|
case strings.HasPrefix(userAgent, "copilot/"):
|
|
return ClientCopilotCLI
|
|
case strings.HasPrefix(userAgent, "kilo-code/") || originator == "kilo-code":
|
|
return ClientKilo
|
|
case strings.HasPrefix(userAgent, "roo-code/") || originator == "roo-code":
|
|
return ClientRoo
|
|
case strings.HasPrefix(userAgent, "coder-agents/"):
|
|
return ClientCoderAgents
|
|
case strings.HasPrefix(userAgent, "charm crush/") || strings.HasPrefix(userAgent, "charm-crush/"):
|
|
return ClientCrush
|
|
case r.Header.Get("x-cursor-client-version") != "":
|
|
return ClientCursor
|
|
case strings.HasPrefix(userAgent, "opencode/"):
|
|
return ClientOpenCode
|
|
case strings.HasPrefix(userAgent, "junie:"):
|
|
return ClientJunie
|
|
}
|
|
return ClientUnknown
|
|
}
|