fix(cli): brand xAI OAuth as Kilocode

This commit is contained in:
Josh Lambert
2026-05-22 00:27:53 -04:00
parent 3d64f562be
commit 6a41985d5a
3 changed files with 15 additions and 13 deletions
+1 -1
View File
@@ -19,7 +19,7 @@ import { gitlabAuthPlugin as GitlabAuthPlugin } from "opencode-gitlab-auth"
import { PoeAuthPlugin } from "opencode-poe-auth"
import { CloudflareAIGatewayAuthPlugin, CloudflareWorkersAuthPlugin } from "./cloudflare"
import { AzureAuthPlugin } from "./azure"
import { XaiAuthPlugin } from "./xai"
import { XaiAuthPlugin } from "./xai" // kilocode_change
import { Effect, Layer, Context, Stream } from "effect"
import { EffectBridge } from "@/effect/bridge"
import { InstanceState } from "@/effect/instance-state"
+9 -8
View File
@@ -1,3 +1,4 @@
// kilocode_change - new file
import type { Hooks, PluginInput } from "@kilocode/plugin" // kilocode_change
import * as Log from "@opencode-ai/core/util/log"
import { OAUTH_DUMMY_KEY } from "../auth"
@@ -109,7 +110,7 @@ function authHeaders() {
return {
"Content-Type": "application/x-www-form-urlencoded",
Accept: "application/json",
"User-Agent": `opencode/${InstallationVersion}`,
"User-Agent": `kilocode/${InstallationVersion}`,
}
}
@@ -141,7 +142,7 @@ export function buildAuthorizeUrl(
): string {
// `plan=generic` opts the consent screen into xAI's generic OAuth plan tier;
// without it, accounts.x.ai rejects loopback OAuth from non-allowlisted
// clients. `referrer=opencode` lets xAI attribute opencode-originated
// clients. `referrer=kilocode` lets xAI attribute kilocode-originated
// logins in their OAuth server logs (best-effort attribution while we
// continue to reuse the Grok-CLI client_id).
const params = new URLSearchParams({
@@ -154,7 +155,7 @@ export function buildAuthorizeUrl(
state,
nonce,
plan: "generic",
referrer: "opencode",
referrer: "kilocode",
})
return `${options.authorizeUrl ?? AUTHORIZE_URL}?${params.toString()}`
}
@@ -306,7 +307,7 @@ export async function pollDeviceCodeToken(
const HTML_SUCCESS = `<!doctype html>
<html>
<head>
<title>Kilo - xAI Authorization Successful</title>
<title>Kilocode - xAI Authorization Successful</title>
<style>
body {
font-family:
@@ -337,7 +338,7 @@ const HTML_SUCCESS = `<!doctype html>
<body>
<div class="container">
<h1>Authorization Successful</h1>
<p>You can close this window and return to Kilo.</p>
<p>You can close this window and return to Kilocode.</p>
</div>
<script>
setTimeout(() => window.close(), 2000)
@@ -348,7 +349,7 @@ const HTML_SUCCESS = `<!doctype html>
const HTML_ERROR = (error: string) => `<!doctype html>
<html>
<head>
<title>Kilo - xAI Authorization Failed</title>
<title>Kilocode - xAI Authorization Failed</title>
<style>
body {
font-family:
@@ -504,7 +505,7 @@ async function startOAuthServer(): Promise<{ port: number; redirectUri: string }
// After listen() succeeds, install a permanent log-only listener so
// that subsequent server errors (e.g. accept() failures, socket-level
// errors) don't trip Node's default "unhandled error event = throw"
// behavior and crash the entire opencode process. Matches the silent-
// behavior and crash the entire Kilocode process. Matches the silent-
// swallow behavior the Codex plugin gets from its permanent
// `oauthServer!.on("error", reject)`.
server.on("error", (err) => log.warn("xai oauth server error", { error: err }))
@@ -652,7 +653,7 @@ export async function XaiAuthPlugin(input: PluginInput, options: XaiAuthPluginOp
}
}
headers.set("authorization", `Bearer ${currentAuth.access}`)
headers.set("User-Agent", `opencode/${InstallationVersion}`)
headers.set("User-Agent", `kilocode/${InstallationVersion}`)
return fetch(requestInput, { ...init, headers })
},
+5 -4
View File
@@ -1,3 +1,4 @@
// kilocode_change - new file
import { describe, expect, test } from "bun:test"
import { accessTokenIsExpiring, buildAuthorizeUrl, escapeHtml, pollDeviceCodeToken, requestDeviceCode, XaiAuthPlugin } from "../../src/plugin/xai"
import { OAUTH_DUMMY_KEY } from "../../src/auth"
@@ -87,7 +88,7 @@ describe("plugin.xai", () => {
expect(params.get("state")).toBe("state-abc")
expect(params.get("nonce")).toBe("nonce-xyz")
expect(params.get("plan")).toBe("generic")
expect(params.get("referrer")).toBe("opencode")
expect(params.get("referrer")).toBe("kilocode")
})
test("supports endpoint override for local integration tests", () => {
@@ -142,7 +143,7 @@ describe("plugin.xai", () => {
expect(captured[0].get("authorization")).toBe("Bearer live-token")
expect(captured[0].get("x-keep")).toBe("yes")
expect(captured[0].get("user-agent")).toMatch(/^opencode\//)
expect(captured[0].get("user-agent")).toMatch(/^kilocode\//)
})
test("does not mutate caller headers and supports HeadersInit shapes", async () => {
@@ -173,7 +174,7 @@ describe("plugin.xai", () => {
expect(captured.map((headers) => headers.get("x-trace"))).toEqual(["plain-object", "tuple-array", "headers-instance"])
for (const headers of captured) {
expect(headers.get("authorization")).toBe("Bearer tok")
expect(headers.get("user-agent")).toMatch(/^opencode\//)
expect(headers.get("user-agent")).toMatch(/^kilocode\//)
}
})
@@ -425,7 +426,7 @@ describe("plugin.xai", () => {
expect(request.method).toBe("POST")
expect(request.headers.get("content-type")).toBe("application/x-www-form-urlencoded")
expect(request.headers.get("accept")).toBe("application/json")
expect(request.headers.get("user-agent")).toMatch(/^opencode\//)
expect(request.headers.get("user-agent")).toMatch(/^kilocode\//)
capturedBody = await request.text()
return Response.json({ device_code: "DC", user_code: "UC", verification_uri: "https://x.ai/device" })
})