xAI Grok for @intx/inference: the grok CLI OAuth client config and token mapping for @corbits/oauth-core, and a Responses API adapter for xAI's CLI chat proxy built on @corbits/openai-responses. An inference provider for Corbits and Interchange agents that also works in any host that runs @intx/inference.
- Grok on a grok CLI login. grok CLI OAuth tokens are rejected by
api.x.aiand only work against the CLI chat proxy. This package supplies the PKCE login config, the code exchange and refresh, and the proxy endpoint. - The exact request the proxy accepts. The
x-grok-*client headers, the model-override header, a string-shaped leadingsystemmessage anddetailedreasoning summaries are fixed in the adapter.max_output_tokensandtemperatureare never sent. - No hub required for inference.
xaiUserIdFromAccessTokenreads the user id header value from the access token's JWTsubclaim, so a sidecar needs only the token.
bun add @corbits/xai-provider @corbits/oauth-core@^0.2.0 @corbits/openai-responses@^0.2.0 @intx/inference@^0.4.0 @intx/types@^0.4.0Runs on Bun >= 1.2 or Node >= 24.
Needs XAI_ACCESS_TOKEN from a completed grok CLI login.
import { createDependencies, runInference } from "@intx/inference";
import {
createXaiResponsesAdapter,
XAI_DEFAULT_MODELS,
XAI_OAUTH_PROXY_BASE_URL,
XAI_PROVIDER,
XAI_USER_ID_OPTION,
xaiUserIdFromAccessToken,
} from "@corbits/xai-provider";
const token = process.env["XAI_ACCESS_TOKEN"];
if (token === undefined) throw new Error("XAI_ACCESS_TOKEN is not set");
const userId = xaiUserIdFromAccessToken(token);
if (userId === undefined) throw new Error("XAI_ACCESS_TOKEN has no sub claim");
const deps = createDependencies({
has: (provider) => provider === XAI_PROVIDER,
resolve: createXaiResponsesAdapter,
});
let seq = 0;
for await (const event of runInference({
deps,
source: {
id: "xai",
provider: XAI_PROVIDER,
baseURL: XAI_OAUTH_PROXY_BASE_URL,
credentialId: "xai",
model: XAI_DEFAULT_MODELS[0],
},
turns: [
{
role: "user",
timestamp: Date.now(),
content: [{ type: "text", text: "Say hello." }],
},
],
inferenceOptions: {
providerOptions: { [XAI_USER_ID_OPTION]: userId },
},
nextSeq: () => seq++,
readMaterial: () => ({ secret: token }),
})) {
if (event.type === "inference.text.delta")
process.stdout.write(event.data.token);
if (event.type === "inference.error")
throw new Error(event.data.error.message);
}
process.stdout.write("\n");Interchange runs AI agents as principals (accounts that hold their own identity, permissions and credentials). Corbits packages add what an agent product needs around it.
- Runs in: the agent sidecar (the runtime next to each agent) for inference, and the hub (the multi-tenant control plane) for login and token refresh.
- Plugs into: the
@intx/inferenceadapter registry, as the factory for thexaiprovider id. - Pairs with:
@corbits/oauth-corefor login and refresh, and@corbits/openai-responses, the wire protocol underneath.
| Export | Description |
|---|---|
createXaiResponsesAdapter |
AdapterFactory for the CLI chat proxy. Per-source quirks are ignored. |
xaiResponsesQuirks |
The ResponsesQuirks the adapter is built from. |
xaiOAuthConfig |
OAuthClientConfig for @corbits/oauth-core: client id, endpoints, loopback redirect, scopes. |
exchangeXaiCode(code, verifier, now, fetch?) |
Redeems an authorization code for XaiTokens. |
refreshXaiTokens(refresh, now, fetch?) |
Refreshes XaiTokens, keeping the old refresh token when the response omits one. |
xaiTokensFromResponse(response, now, previousRefresh?) |
Maps a token endpoint response to XaiTokens, carrying id_token as idToken. |
xaiUserIdFromAccessToken(access) |
Reads the JWT sub claim. Returns undefined on malformed input; does not verify signatures. |
XaiTokens |
Type. |
XAI_PROVIDER |
The "xai" provider id. |
XAI_OAUTH_PROXY_BASE_URL |
https://cli-chat-proxy.grok.com/v1, the base URL for OAuth credentials. |
XAI_API_KEY_BASE_URL |
https://api.x.ai/v1, the base URL for API key credentials. |
XAI_DEFAULT_MODELS |
The model ids the CLI chat proxy serves. |
XAI_USER_ID_OPTION |
providerOptions key sent as the x-grok-user-id header. |
XAI_SESSION_ID_OPTION |
providerOptions key sent as prompt_cache_key. |
XAI_REASONING_EFFORT_OPTION |
providerOptions key sent as reasoning.effort. |
XAI_REFRESH_SKEW_MS |
Refresh this long before expiry. |
The OAuth and client-identity constants behind xaiOAuthConfig and the adapter (XAI_CLIENT_ID, XAI_AUTHORIZE_URL, XAI_TOKEN_URL, XAI_REDIRECT_URI, XAI_SCOPES, XAI_TOKEN_TIMEOUT_MS, XAI_RESPONSES_PATH, XAI_CLIENT_IDENTIFIER, XAI_CLIENT_VERSION, XAI_USER_AGENT) are exported too.
Register the login on the hub. @corbits/oauth-core/hub's mountOAuthLogin and createOAuthTokenRefresher take a map of providers; this package supplies the xAI entry:
import type { OAuthLoginProviders } from "@corbits/oauth-core/hub";
import {
exchangeXaiCode,
refreshXaiTokens,
XAI_PROVIDER,
xaiOAuthConfig,
} from "@corbits/xai-provider";
export const providers: OAuthLoginProviders = {
[XAI_PROVIDER]: {
oauthConfig: xaiOAuthConfig,
exchange: (code, verifier, now) => exchangeXaiCode(code, verifier, now),
refresh: (refreshSecret, now) => refreshXaiTokens(refreshSecret, now),
},
};Load the adapter in the sidecar from an operator-configured AdapterManifest:
import { createDependencies, type AdapterManifest } from "@intx/inference";
import { loadAdapterRegistry } from "@intx/inference/providers";
const manifest: AdapterManifest = [
{
provider: "xai",
specifier: "@corbits/xai-provider",
export: "createXaiResponsesAdapter",
},
];
export const deps = createDependencies(await loadAdapterRegistry(manifest));Each xAI source points at XAI_OAUTH_PROXY_BASE_URL, uses the OAuth access token as its credential, and picks a model from XAI_DEFAULT_MODELS. Pass xaiUserIdFromAccessToken(token) as providerOptions[XAI_USER_ID_OPTION].