diff --git a/aibridge/client.go b/aibridge/client.go index f5ff608a32..836c09c4b0 100644 --- a/aibridge/client.go +++ b/aibridge/client.go @@ -22,6 +22,7 @@ const ( ClientRoo Client = "Roo Code" ClientCursor Client = "Cursor" ClientOpenCode Client = "OpenCode" + ClientJunie Client = "Junie" ClientUnknown Client = "Unknown" ) @@ -58,6 +59,8 @@ func GuessClient(r *http.Request) Client { return ClientCursor case strings.HasPrefix(userAgent, "opencode/"): return ClientOpenCode + case strings.HasPrefix(userAgent, "junie:"): + return ClientJunie } return ClientUnknown } diff --git a/aibridge/client_test.go b/aibridge/client_test.go index 253a374a69..cde72029a1 100644 --- a/aibridge/client_test.go +++ b/aibridge/client_test.go @@ -104,6 +104,11 @@ func TestGuessClient(t *testing.T) { userAgent: "opencode/1.16.0 ai-sdk/provider-utils/4.0.23 runtime/bun/1.3.14", wantClient: aibridge.ClientOpenCode, }, + { + name: "junie_cli", + userAgent: "Junie:SNAPSHOT", + wantClient: aibridge.ClientJunie, + }, { name: "unknown_client", userAgent: "ccclaude-cli/calude-with-wrong-prefix", diff --git a/docs/ai-coder/ai-gateway/clients/index.md b/docs/ai-coder/ai-gateway/clients/index.md index f0d78aae31..a4bddc4cda 100644 --- a/docs/ai-coder/ai-gateway/clients/index.md +++ b/docs/ai-coder/ai-gateway/clients/index.md @@ -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. | diff --git a/docs/ai-coder/ai-gateway/clients/junie.md b/docs/ai-coder/ai-gateway/clients/junie.md new file mode 100644 index 0000000000..9d9fd5d752 --- /dev/null +++ b/docs/ai-coder/ai-gateway/clients/junie.md @@ -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 Coder API token, used for authentication with AI Gateway. +export 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) diff --git a/docs/ai-coder/ai-gateway/monitoring.md b/docs/ai-coder/ai-gateway/monitoring.md index 5f888c5961..b44d75758d 100644 --- a/docs/ai-coder/ai-gateway/monitoring.md +++ b/docs/ai-coder/ai-gateway/monitoring.md @@ -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` diff --git a/docs/manifest.json b/docs/manifest.json index 84732dc56a..221ef88347 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -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.", diff --git a/site/src/pages/AIBridgePage/icons/AIBridgeClientIcon.tsx b/site/src/pages/AIBridgePage/icons/AIBridgeClientIcon.tsx index 9bfd291483..575e586b81 100644 --- a/site/src/pages/AIBridgePage/icons/AIBridgeClientIcon.tsx +++ b/site/src/pages/AIBridgePage/icons/AIBridgeClientIcon.tsx @@ -84,6 +84,13 @@ export const AIBridgeClientIcon = ({ className={cn(iconClassName, className)} /> ); + case "Junie": + return ( + + ); case "GitHub Copilot (VS Code)": case "GitHub Copilot (CLI)": return ( diff --git a/site/src/theme/icons.json b/site/src/theme/icons.json index dcf8a01942..402ffcb6e9 100644 --- a/site/src/theme/icons.json +++ b/site/src/theme/icons.json @@ -79,6 +79,7 @@ "jetbrains-toolbox.svg", "jetbrains.svg", "jfrog.svg", + "junie.svg", "jupyter.svg", "k8s.png", "k8s.svg", diff --git a/site/static/icon/junie.svg b/site/static/icon/junie.svg new file mode 100644 index 0000000000..537d1cd663 --- /dev/null +++ b/site/static/icon/junie.svg @@ -0,0 +1,5 @@ + + + + +