mirror of
https://github.com/coder/coder.git
synced 2026-09-21 12:44:32 +08:00
feat: track Junie as an AI Gateway client (#28266)
## 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. 🧑💻
This commit is contained in:
@@ -50,6 +50,7 @@ The table below shows tested AI clients and their compatibility with AI Gateway.
|
||||
| [Kilo Code](./kilo-code.md) | ✅ | ✅ | ❌ | |
|
||||
| [VS Code](./vscode.md) | ✅ | ✅ | ❌ | VS Code 1.122+ via Custom Endpoint provider. GitHub sign-in not required. Inline suggestions still require GitHub Copilot. |
|
||||
| [JetBrains IDEs](./jetbrains.md) | ✅ | ❌ | ❌ | Works in Chat mode via [third-party model configuration](https://www.jetbrains.com/help/ai-assistant/use-custom-models.html#provide-your-own-api-key). |
|
||||
| [Junie](./junie.md) | ✅ | ✅ | ✅ | Junie CLI via custom model profiles. |
|
||||
| [Zed](./zed.md) | ✅ | ✅ | ❌ | |
|
||||
| [GitHub Copilot](./copilot.md) | ⚙️ | - | - | Requires [AI Gateway Proxy](../ai-gateway-proxy/index.md). Uses per-user GitHub tokens. |
|
||||
| Devin Desktop (formerly Windsurf) | ❌ | ❌ | ❌ | No option to override base URL. |
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
# Junie
|
||||
|
||||
> [!NOTE]
|
||||
> AI Gateway is part of [AI Governance](../../ai-governance.md), which is
|
||||
> included with a Premium license.
|
||||
|
||||
[Junie CLI](https://junie.jetbrains.com/docs/junie-cli.html) supports AI Gateway
|
||||
through [custom model profiles](https://junie.jetbrains.com/docs/custom-llm-models.html#using-custom-models).
|
||||
|
||||
| Provider | API type | Endpoint |
|
||||
|-----------|-------------------|--------------------------|
|
||||
| OpenAI | `OpenAIResponses` | `/openai/v1/responses` |
|
||||
| Anthropic | `Anthropic` | `/anthropic/v1/messages` |
|
||||
|
||||
For Junie inside JetBrains IDEs, refer to [JetBrains IDEs](./jetbrains.md).
|
||||
|
||||
## Prerequisites
|
||||
|
||||
* **Junie CLI**: Installed with `curl -fsSL https://junie.jetbrains.com/install.sh | bash`.
|
||||
* **Authentication**: Your **[Coder API token](../../../admin/users/sessions-tokens.md#generate-a-long-lived-api-token-on-behalf-of-yourself)**.
|
||||
|
||||
## Centralized API Key
|
||||
|
||||
Junie discovers custom model profiles from JSON files in two locations:
|
||||
|
||||
| Scope | Location |
|
||||
|---------|--------------------------|
|
||||
| User | `~/.junie/models/*.json` |
|
||||
| Project | `.junie/models/*.json` |
|
||||
|
||||
The file name without the `.json` extension is the profile ID.
|
||||
|
||||
Create `~/.junie/models/ai-gateway.json` for the OpenAI endpoint:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "gpt-5.5",
|
||||
"baseUrl": "https://coder.example.com/api/v2/ai-gateway/openai/v1/responses",
|
||||
"apiType": "OpenAIResponses",
|
||||
"apiKey": "${CODER_API_TOKEN}",
|
||||
"fasterModel": { "id": "gpt-5.4-mini" }
|
||||
}
|
||||
```
|
||||
|
||||
Or for the Anthropic endpoint:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "claude-sonnet-5",
|
||||
"baseUrl": "https://coder.example.com/api/v2/ai-gateway/anthropic/v1/messages",
|
||||
"apiType": "Anthropic",
|
||||
"apiKey": "${CODER_API_TOKEN}",
|
||||
"fasterModel": { "id": "claude-haiku-4-5-20251001" }
|
||||
}
|
||||
```
|
||||
|
||||
*Replace `coder.example.com` with your Coder deployment URL.*
|
||||
|
||||
> [!NOTE]
|
||||
> `baseUrl` is the complete endpoint URL. Junie does not append a path to it, so
|
||||
> include `/v1/responses` for `OpenAIResponses` and `/v1/messages` for
|
||||
> `Anthropic`.
|
||||
|
||||
Junie resolves `${VAR}` references in `apiKey` and `extraHeaders` from the
|
||||
environment, so the token never has to be written to disk:
|
||||
|
||||
```sh
|
||||
export CODER_API_TOKEN=$(coder login token)
|
||||
```
|
||||
|
||||
Run Junie with the profile:
|
||||
|
||||
```sh
|
||||
junie --model custom:ai-gateway
|
||||
```
|
||||
|
||||
Custom profiles also appear in the model selection list of the interactive TUI
|
||||
and in the `/model` slash command.
|
||||
|
||||
## BYOK (Personal API Key)
|
||||
|
||||
Junie supports custom headers, so BYOK works by sending your personal provider
|
||||
key as the API key and the Coder API token as the governance token:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "gpt-5.5",
|
||||
"baseUrl": "https://coder.example.com/api/v2/ai-gateway/openai/v1/responses",
|
||||
"apiType": "OpenAIResponses",
|
||||
"apiKey": "${OPENAI_API_KEY}",
|
||||
"extraHeaders": { "X-Coder-AI-Governance-Token": "${CODER_API_TOKEN}" }
|
||||
}
|
||||
```
|
||||
|
||||
Set both environment variables:
|
||||
|
||||
```sh
|
||||
# Your personal provider API key, forwarded to the upstream provider.
|
||||
export OPENAI_API_KEY="<your-openai-api-key>"
|
||||
|
||||
# Your Coder API token, used for authentication with AI Gateway.
|
||||
export CODER_API_TOKEN="<your-coder-api-token>"
|
||||
```
|
||||
|
||||
## Pre-configuring in Templates
|
||||
|
||||
Commit the profile to your repository at `.junie/models/ai-gateway.json` and
|
||||
inject the token from the template, so users get a working configuration without
|
||||
manual setup:
|
||||
|
||||
```tf
|
||||
data "coder_workspace_owner" "me" {}
|
||||
|
||||
data "coder_workspace" "me" {}
|
||||
|
||||
resource "coder_env" "coder_api_token" {
|
||||
agent_id = coder_agent.main.id
|
||||
name = "CODER_API_TOKEN"
|
||||
value = data.coder_workspace_owner.me.session_token
|
||||
}
|
||||
```
|
||||
|
||||
Reference the deployment URL in the profile with
|
||||
`${data.coder_workspace.me.access_url}/api/v2/ai-gateway/openai/v1/responses`
|
||||
if you generate the file from the template instead of committing it.
|
||||
|
||||
**References:** [Junie custom LLMs](https://junie.jetbrains.com/docs/custom-llm-models.html#using-custom-models)
|
||||
@@ -212,10 +212,13 @@ Available query filters:
|
||||
- `GitHub Copilot (VS Code)`
|
||||
- `GitHub Copilot (CLI)`
|
||||
- `Kilo Code`
|
||||
- `Roo Code`
|
||||
- `Coder Agents`
|
||||
- `Mux`
|
||||
- `Cursor`
|
||||
- `OpenCode`
|
||||
- `Charm Crush`
|
||||
- `Junie`
|
||||
- `Unknown`
|
||||
|
||||
</details>
|
||||
|
||||
@@ -1267,6 +1267,12 @@
|
||||
"path": "./ai-coder/ai-gateway/clients/jetbrains.md",
|
||||
"state": ["premium"]
|
||||
},
|
||||
{
|
||||
"title": "Junie",
|
||||
"description": "Configure Junie CLI to route requests through AI Gateway with a custom model profile.",
|
||||
"path": "./ai-coder/ai-gateway/clients/junie.md",
|
||||
"state": ["premium"]
|
||||
},
|
||||
{
|
||||
"title": "Zed",
|
||||
"description": "Configure the Zed editor's language models to route requests through AI Gateway.",
|
||||
|
||||
Reference in New Issue
Block a user