mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-19 07:07:27 +00:00
feat(extensions): add OpenAI Codex CLI auth provider
Adds a new authentication provider that reads OAuth tokens from the OpenAI Codex CLI (~/.codex/auth.json) to authenticate with OpenAI's API. This allows ChatGPT Plus/Pro subscribers to use OpenAI models in OpenClaw without needing a separate API key - just authenticate with 'codex login' first, then enable this plugin. Features: - Reads existing Codex CLI credentials from ~/.codex/auth.json - Supports all Codex-available models (gpt-4.1, gpt-4o, o1, o3, etc.) - Automatic token expiry detection from JWT - Clear setup instructions and troubleshooting docs Usage: openclaw plugins enable openai-codex-auth openclaw models auth login --provider openai-codex --set-default
This commit is contained in:
82
extensions/openai-codex-auth/README.md
Normal file
82
extensions/openai-codex-auth/README.md
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
# OpenAI Codex CLI Auth (OpenClaw plugin)
|
||||||
|
|
||||||
|
Use OpenAI models with your **ChatGPT Plus/Pro subscription** via the Codex CLI OAuth tokens.
|
||||||
|
|
||||||
|
This plugin reads authentication from the [OpenAI Codex CLI](https://github.com/openai/codex) and uses those OAuth credentials to access OpenAI models — no separate API key required.
|
||||||
|
|
||||||
|
## Enable
|
||||||
|
|
||||||
|
Bundled plugins are disabled by default. Enable this one:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
openclaw plugins enable openai-codex-auth
|
||||||
|
```
|
||||||
|
|
||||||
|
Restart the Gateway after enabling.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
1. **ChatGPT Plus or Pro subscription** — required for Codex CLI access
|
||||||
|
2. **Codex CLI installed and authenticated**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install Codex CLI
|
||||||
|
npm install -g @openai/codex
|
||||||
|
|
||||||
|
# Authenticate (opens browser for OAuth)
|
||||||
|
codex login
|
||||||
|
```
|
||||||
|
|
||||||
|
This creates `~/.codex/auth.json` with your OAuth tokens.
|
||||||
|
|
||||||
|
## Authenticate with OpenClaw
|
||||||
|
|
||||||
|
After Codex CLI is authenticated:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
openclaw models auth login --provider openai-codex --set-default
|
||||||
|
```
|
||||||
|
|
||||||
|
## Available Models
|
||||||
|
|
||||||
|
The following models are available through Codex CLI authentication:
|
||||||
|
|
||||||
|
- `openai/gpt-4.1`, `openai/gpt-4.1-mini`, `openai/gpt-4.1-nano`
|
||||||
|
- `openai/gpt-4o`, `openai/gpt-4o-mini`
|
||||||
|
- `openai/o1`, `openai/o1-mini`, `openai/o1-pro`
|
||||||
|
- `openai/o3`, `openai/o3-mini`
|
||||||
|
- `openai/o4-mini`
|
||||||
|
|
||||||
|
Default model: `openai/o3`
|
||||||
|
|
||||||
|
## How It Works
|
||||||
|
|
||||||
|
1. The plugin reads `~/.codex/auth.json` created by `codex login`
|
||||||
|
2. OAuth tokens from your ChatGPT subscription are extracted
|
||||||
|
3. OpenClaw uses these tokens to authenticate with OpenAI's API
|
||||||
|
4. Tokens auto-refresh when needed (handled by OpenClaw's credential system)
|
||||||
|
|
||||||
|
## Why Use This?
|
||||||
|
|
||||||
|
- **No separate API key** — use your existing ChatGPT Plus/Pro subscription
|
||||||
|
- **No usage-based billing** — covered by your subscription
|
||||||
|
- **Access to latest models** — same models available in ChatGPT
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### "No Codex auth found"
|
||||||
|
|
||||||
|
Run `codex login` to authenticate the Codex CLI first.
|
||||||
|
|
||||||
|
### Tokens expired
|
||||||
|
|
||||||
|
Re-run `codex login` to refresh your tokens, then re-authenticate:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
codex login
|
||||||
|
openclaw models auth login --provider openai-codex --set-default
|
||||||
|
```
|
||||||
|
|
||||||
|
### Model not available
|
||||||
|
|
||||||
|
Some models may require specific subscription tiers (e.g., o1-pro requires ChatGPT Pro).
|
||||||
160
extensions/openai-codex-auth/index.ts
Normal file
160
extensions/openai-codex-auth/index.ts
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
import {
|
||||||
|
emptyPluginConfigSchema,
|
||||||
|
type OpenClawPluginApi,
|
||||||
|
type ProviderAuthContext,
|
||||||
|
type ProviderAuthResult,
|
||||||
|
} from "openclaw/plugin-sdk";
|
||||||
|
import * as fs from "node:fs";
|
||||||
|
import * as path from "node:path";
|
||||||
|
import * as os from "node:os";
|
||||||
|
|
||||||
|
const PROVIDER_ID = "openai-codex";
|
||||||
|
const PROVIDER_LABEL = "OpenAI Codex CLI";
|
||||||
|
const AUTH_FILE = path.join(os.homedir(), ".codex", "auth.json");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OpenAI Codex models available via ChatGPT Plus/Pro subscription.
|
||||||
|
* These are the models exposed through the Codex CLI OAuth tokens.
|
||||||
|
*/
|
||||||
|
const CODEX_MODELS = [
|
||||||
|
"openai/gpt-4.1",
|
||||||
|
"openai/gpt-4.1-mini",
|
||||||
|
"openai/gpt-4.1-nano",
|
||||||
|
"openai/gpt-4o",
|
||||||
|
"openai/gpt-4o-mini",
|
||||||
|
"openai/o1",
|
||||||
|
"openai/o1-mini",
|
||||||
|
"openai/o1-pro",
|
||||||
|
"openai/o3",
|
||||||
|
"openai/o3-mini",
|
||||||
|
"openai/o4-mini",
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
const DEFAULT_MODEL = "openai/o3";
|
||||||
|
|
||||||
|
interface CodexAuthTokens {
|
||||||
|
access_token: string;
|
||||||
|
refresh_token?: string;
|
||||||
|
account_id?: string;
|
||||||
|
expires_at?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CodexAuthFile {
|
||||||
|
tokens?: CodexAuthTokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read the Codex CLI auth.json file from ~/.codex/auth.json
|
||||||
|
*/
|
||||||
|
function readCodexAuth(): CodexAuthFile | null {
|
||||||
|
try {
|
||||||
|
if (!fs.existsSync(AUTH_FILE)) return null;
|
||||||
|
const content = fs.readFileSync(AUTH_FILE, "utf-8");
|
||||||
|
return JSON.parse(content) as CodexAuthFile;
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decode JWT expiry timestamp from access token
|
||||||
|
*/
|
||||||
|
function decodeJwtExpiry(token: string): number | undefined {
|
||||||
|
try {
|
||||||
|
const payload = token.split(".")[1];
|
||||||
|
if (!payload) return undefined;
|
||||||
|
const decoded = JSON.parse(Buffer.from(payload, "base64").toString()) as { exp?: number };
|
||||||
|
return decoded.exp ? decoded.exp * 1000 : undefined;
|
||||||
|
} catch {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const openaiCodexPlugin = {
|
||||||
|
id: "openai-codex-auth",
|
||||||
|
name: "OpenAI Codex Auth",
|
||||||
|
description: "Use OpenAI models via Codex CLI authentication (ChatGPT Plus/Pro)",
|
||||||
|
configSchema: emptyPluginConfigSchema(),
|
||||||
|
|
||||||
|
register(api: OpenClawPluginApi) {
|
||||||
|
api.registerProvider({
|
||||||
|
id: PROVIDER_ID,
|
||||||
|
label: PROVIDER_LABEL,
|
||||||
|
docsPath: "/providers/models",
|
||||||
|
aliases: ["codex", "chatgpt"],
|
||||||
|
|
||||||
|
auth: [
|
||||||
|
{
|
||||||
|
id: "codex-cli",
|
||||||
|
label: "Codex CLI Auth",
|
||||||
|
hint: "Use existing Codex CLI authentication from ~/.codex/auth.json",
|
||||||
|
kind: "custom",
|
||||||
|
|
||||||
|
run: async (ctx: ProviderAuthContext): Promise<ProviderAuthResult> => {
|
||||||
|
const spin = ctx.prompter.progress("Reading Codex CLI auth…");
|
||||||
|
|
||||||
|
try {
|
||||||
|
const auth = readCodexAuth();
|
||||||
|
|
||||||
|
if (!auth?.tokens?.access_token) {
|
||||||
|
spin.stop("No Codex auth found");
|
||||||
|
await ctx.prompter.note(
|
||||||
|
"Run 'codex login' first to authenticate with OpenAI.\n\n" +
|
||||||
|
"Install Codex CLI: npm install -g @openai/codex\n" +
|
||||||
|
"Then run: codex login",
|
||||||
|
"Setup required",
|
||||||
|
);
|
||||||
|
throw new Error("Codex CLI not authenticated. Run: codex login");
|
||||||
|
}
|
||||||
|
|
||||||
|
spin.stop("Codex auth loaded");
|
||||||
|
|
||||||
|
const profileId = `openai-codex:${auth.tokens.account_id ?? "default"}`;
|
||||||
|
const expires = auth.tokens.expires_at
|
||||||
|
? auth.tokens.expires_at * 1000
|
||||||
|
: decodeJwtExpiry(auth.tokens.access_token);
|
||||||
|
|
||||||
|
const modelsConfig: Record<string, object> = {};
|
||||||
|
for (const model of CODEX_MODELS) {
|
||||||
|
modelsConfig[model] = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
profiles: [
|
||||||
|
{
|
||||||
|
profileId,
|
||||||
|
credential: {
|
||||||
|
type: "oauth",
|
||||||
|
provider: PROVIDER_ID,
|
||||||
|
access: auth.tokens.access_token,
|
||||||
|
refresh: auth.tokens.refresh_token ?? "",
|
||||||
|
expires: expires ?? Date.now() + 3600000,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
configPatch: {
|
||||||
|
agents: {
|
||||||
|
defaults: {
|
||||||
|
models: modelsConfig,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultModel: DEFAULT_MODEL,
|
||||||
|
notes: [
|
||||||
|
"Using Codex CLI auth from ~/.codex/auth.json",
|
||||||
|
`Available models: ${CODEX_MODELS.join(", ")}`,
|
||||||
|
"Tokens auto-refresh when needed.",
|
||||||
|
],
|
||||||
|
};
|
||||||
|
} catch (err) {
|
||||||
|
spin.stop("Failed to load Codex auth");
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default openaiCodexPlugin;
|
||||||
9
extensions/openai-codex-auth/openclaw.plugin.json
Normal file
9
extensions/openai-codex-auth/openclaw.plugin.json
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"id": "openai-codex-auth",
|
||||||
|
"providers": ["openai-codex"],
|
||||||
|
"configSchema": {
|
||||||
|
"type": "object",
|
||||||
|
"additionalProperties": false,
|
||||||
|
"properties": {}
|
||||||
|
}
|
||||||
|
}
|
||||||
15
extensions/openai-codex-auth/package.json
Normal file
15
extensions/openai-codex-auth/package.json
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"name": "@openclaw/openai-codex-auth",
|
||||||
|
"version": "2026.2.16",
|
||||||
|
"private": true,
|
||||||
|
"description": "OpenAI Codex CLI auth provider plugin - use ChatGPT Plus/Pro subscription for OpenAI models",
|
||||||
|
"type": "module",
|
||||||
|
"devDependencies": {
|
||||||
|
"openclaw": "workspace:*"
|
||||||
|
},
|
||||||
|
"openclaw": {
|
||||||
|
"extensions": [
|
||||||
|
"./index.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user