@codexapi/codexclaude 2.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CUSTOMIZATIONS.md +54 -0
- package/README.md +84 -0
- package/bin/codexclaude.js +660 -0
- package/codexclaude.md +56 -0
- package/package.json +38 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# CodexAPI.pro Customizations
|
|
2
|
+
|
|
3
|
+
`codexclaude` intentionally tracks the official OpenAI Codex CLI while keeping a thin CodexAPI.pro launcher layer.
|
|
4
|
+
|
|
5
|
+
## Upstream Runtime
|
|
6
|
+
|
|
7
|
+
- Official package: `@openai/codex`
|
|
8
|
+
- Pinned runtime version: `0.134.0`
|
|
9
|
+
- Local launcher version: `2.0.4`
|
|
10
|
+
|
|
11
|
+
## Launcher Behavior
|
|
12
|
+
|
|
13
|
+
- Authenticates customers against `https://codexapi.pro/api/v1/auth/login`.
|
|
14
|
+
- Saves the returned API username in `~/.codexclaude/config.json`.
|
|
15
|
+
- Rewrites `~/.codex/config.toml` before every launch.
|
|
16
|
+
- Starts official Codex with `--search` by default.
|
|
17
|
+
- Passes all unknown commands directly to the official Codex CLI.
|
|
18
|
+
- Provides `codexclaude changelog` so users can see forced-update changes from the terminal.
|
|
19
|
+
- Checks npm latest at startup and blocks coding with an Update Now command when a newer launcher is available.
|
|
20
|
+
|
|
21
|
+
## Provider Config
|
|
22
|
+
|
|
23
|
+
The launcher writes a Codex model provider named `codexapi`:
|
|
24
|
+
|
|
25
|
+
- `base_url = "https://codexapi.pro/v1"`
|
|
26
|
+
- `wire_api = "responses"`
|
|
27
|
+
- `supports_websockets = false`
|
|
28
|
+
- `experimental_bearer_token` set to the customer API username
|
|
29
|
+
- `http_headers` identify launcher traffic to CodexAPI.pro for server-side plan enforcement
|
|
30
|
+
|
|
31
|
+
## Runtime Defaults
|
|
32
|
+
|
|
33
|
+
- Model: selected by the user, defaulting to `gpt-5.5`; supported launcher choices are `gpt-5.5`, `claude-opus-4-7`, and `deepseek-v4-pro`.
|
|
34
|
+
- Reasoning effort: `high`
|
|
35
|
+
- Service tier: `fast`
|
|
36
|
+
- Approval policy: `never`
|
|
37
|
+
- Sandbox mode: `danger-full-access`
|
|
38
|
+
- TUI status line: `model-with-reasoning`, `fast-mode`, `current-dir`
|
|
39
|
+
- Opus 4.7 Responses traffic is routed through CodexAPI.pro's working Claude tool-call bridge.
|
|
40
|
+
- Deepseek v4 Budget Responses traffic is routed through CodexAPI.pro's Deepseek-compatible gateway.
|
|
41
|
+
|
|
42
|
+
## Wallet Display
|
|
43
|
+
|
|
44
|
+
Before launching Codex, the launcher calls `/api/v1/me` using the saved API username as a bearer token and prints a compact status line:
|
|
45
|
+
|
|
46
|
+
```text
|
|
47
|
+
gpt-5.5 high fast · ~/project · Wallet $20.00
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The same wallet lookup is available through:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
codexclaude status
|
|
54
|
+
```
|
package/README.md
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# codexclaude
|
|
2
|
+
|
|
3
|
+
`codexclaude` is the CodexAPI.pro launcher for customers who want one coding CLI that can use ChatGPT-5.5, Opus 4.7, or Deepseek v4 Budget against the same CodexAPI.pro Token Wallet.
|
|
4
|
+
|
|
5
|
+
Version `2.0.4` launches the official OpenAI Codex CLI `0.134.0` package and applies CodexAPI.pro account, wallet, model, and Full Access defaults before Codex starts.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install -g https://codexapi.pro/downloads/codexapi-codexclaude-2.0.4.tgz
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
This installs the current `codexclaude` launcher from npm.
|
|
14
|
+
|
|
15
|
+
## Required updates
|
|
16
|
+
|
|
17
|
+
CodexAPI.pro checks the CodexAPI.pro API and npm at startup. If a newer required launcher is available, the app stops before coding starts and shows the exact **Update Now** command:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install -g https://codexapi.pro/downloads/codexapi-codexclaude-2.0.4.tgz
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
This does not delete your local Codex session history. The update only replaces the `codexclaude` launcher and refreshes the CodexAPI.pro provider config.
|
|
24
|
+
|
|
25
|
+
## First Login
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
codexclaude login
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Enter the dashboard username or email address and password. The launcher saves the API username locally, writes `~/.codex/config.toml`, sets Full Access defaults, and lets you choose the coding model.
|
|
32
|
+
|
|
33
|
+
## Start Coding
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
codexclaude start
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Resume the latest project:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
codexclaude resume
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Switch model:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
codexclaude model
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Available model labels are `ChatGPT-5.5`, `Opus 4.7`, and `Deepseek v4 Budget`. All three use the CodexAPI.pro Responses-compatible endpoint and the same customer Token Wallet.
|
|
52
|
+
|
|
53
|
+
Show current launcher changes:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
codexclaude changelog
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Wallet Status
|
|
60
|
+
|
|
61
|
+
Before launching Codex, the app prints a compact status line like:
|
|
62
|
+
|
|
63
|
+
```text
|
|
64
|
+
gpt-5.5 high fast · ~/codexapi · Wallet $20.00
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
You can also check this without starting Codex:
|
|
68
|
+
|
|
69
|
+
```bash
|
|
70
|
+
codexclaude status
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Preserved CodexAPI.pro Customizations
|
|
74
|
+
|
|
75
|
+
- Dashboard username or email plus password login.
|
|
76
|
+
- ChatGPT-5.5, Opus 4.7, or Deepseek v4 Budget model selection.
|
|
77
|
+
- CodexAPI.pro Responses-compatible provider at `https://codexapi.pro/v1`.
|
|
78
|
+
- Full Access defaults: approval policy `never` and sandbox mode `danger-full-access`.
|
|
79
|
+
- High reasoning and fast service tier defaults.
|
|
80
|
+
- Official Codex `0.134.0` runtime with the current profile-compatible config enabled.
|
|
81
|
+
- Token Wallet balance lookup before launch and in `codexclaude status`.
|
|
82
|
+
- Opus 4.7 tool-call compatibility through the CodexAPI.pro Responses gateway.
|
|
83
|
+
- Backend-identifying request headers so CodexAPI.pro can enforce subscription plan rules server-side.
|
|
84
|
+
- npm latest-version checks before coding starts, with an explicit update command when a newer required launcher exists.
|
|
@@ -0,0 +1,660 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { spawn } from "node:child_process";
|
|
4
|
+
import { createRequire } from "node:module";
|
|
5
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, copyFileSync, chmodSync } from "node:fs";
|
|
6
|
+
import { homedir } from "node:os";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import readline from "node:readline/promises";
|
|
9
|
+
import { stdin as input, stdout as output } from "node:process";
|
|
10
|
+
|
|
11
|
+
const require = createRequire(import.meta.url);
|
|
12
|
+
|
|
13
|
+
const APP_VERSION = "2.0.4";
|
|
14
|
+
const CODEX_VERSION = "0.134.0";
|
|
15
|
+
const CODEX_REASONING_EFFORT = "high";
|
|
16
|
+
const CODEX_SERVICE_TIER = "fast";
|
|
17
|
+
const API_ORIGIN = (process.env.CODEXCLAUDE_API_ORIGIN || "https://codexapi.pro").replace(/\/+$/, "");
|
|
18
|
+
const API_BASE_URL = `${API_ORIGIN}/v1`;
|
|
19
|
+
const VERSION_CHECK_PATH = "/api/v1/codexclaude/version";
|
|
20
|
+
const NPM_PACKAGE_NAME = "@codexapi/codexclaude";
|
|
21
|
+
const NPM_LATEST_URL = "https://registry.npmjs.org/@codexapi%2Fcodexclaude/latest";
|
|
22
|
+
const UPDATE_COMMAND = `npm install -g ${NPM_PACKAGE_NAME}@latest`;
|
|
23
|
+
const CONFIG_DIR = path.join(homedir(), ".codexclaude");
|
|
24
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
|
|
25
|
+
const CODEX_DIR = path.join(homedir(), ".codex");
|
|
26
|
+
const CODEX_CONFIG_FILE = path.join(CODEX_DIR, "config.toml");
|
|
27
|
+
const PROVIDER_ID = "codexapi";
|
|
28
|
+
|
|
29
|
+
const MODELS = {
|
|
30
|
+
"gpt-5.5": {
|
|
31
|
+
id: "gpt-5.5",
|
|
32
|
+
label: "ChatGPT-5.5",
|
|
33
|
+
aliases: ["1", "gpt", "gpt-5.5", "chatgpt", "chatgpt-5.5"],
|
|
34
|
+
},
|
|
35
|
+
"claude-opus-4-7": {
|
|
36
|
+
id: "claude-opus-4-7",
|
|
37
|
+
label: "Opus 4.7",
|
|
38
|
+
aliases: ["2", "opus", "opus-4.7", "claude", "claude-opus-4-7"],
|
|
39
|
+
},
|
|
40
|
+
"deepseek-v4-pro": {
|
|
41
|
+
id: "deepseek-v4-pro",
|
|
42
|
+
label: "Deepseek v4 Budget",
|
|
43
|
+
aliases: ["3", "deepseek", "deepseek-v4", "deepseek-v4-pro", "budget"],
|
|
44
|
+
budget: true,
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
async function main() {
|
|
49
|
+
ensureExecutable();
|
|
50
|
+
const [command = "start", ...args] = process.argv.slice(2);
|
|
51
|
+
if (["-v", "--version", "version"].includes(command)) {
|
|
52
|
+
console.log(`codexclaude ${APP_VERSION}`);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (["-h", "--help", "help"].includes(command)) {
|
|
56
|
+
printHelp();
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
if (["changelog", "changes", "whats-new"].includes(command)) {
|
|
60
|
+
await printChangelog();
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
await enforceLauncherVersion();
|
|
64
|
+
if (command === "login") {
|
|
65
|
+
await loginFlow();
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
if (command === "logout") {
|
|
69
|
+
logout();
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
if (command === "status") {
|
|
73
|
+
await status();
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
if (command === "model") {
|
|
77
|
+
await modelFlow();
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (command === "setup") {
|
|
81
|
+
await setupFlow();
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
if (command === "resume") {
|
|
85
|
+
await ensureReady();
|
|
86
|
+
await runCodex(["resume", "--search", ...args]);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
if (command === "start") {
|
|
90
|
+
await ensureReady();
|
|
91
|
+
await runCodex(args.length ? args : ["--search"]);
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
await ensureReady();
|
|
96
|
+
await runCodex([command, ...args]);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function printHelp() {
|
|
100
|
+
console.log(`
|
|
101
|
+
codexclaude ${APP_VERSION}
|
|
102
|
+
|
|
103
|
+
Commands:
|
|
104
|
+
codexclaude login Login with CodexAPI.pro username/email and password
|
|
105
|
+
codexclaude model Select ChatGPT-5.5, Opus 4.7, or Deepseek v4 Budget
|
|
106
|
+
codexclaude changelog Show the latest CodexAPI.pro launcher changes
|
|
107
|
+
codexclaude setup Rewrite ~/.codex/config.toml with Full Access defaults
|
|
108
|
+
codexclaude start Start Codex with --search
|
|
109
|
+
codexclaude resume Resume with codex resume --search
|
|
110
|
+
codexclaude status Show saved account, selected model, and wallet balance
|
|
111
|
+
codexclaude logout Remove saved codexclaude account state
|
|
112
|
+
|
|
113
|
+
Any other command is passed through to official Codex CLI ${CODEX_VERSION} after config is applied.
|
|
114
|
+
`);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async function printChangelog() {
|
|
118
|
+
const metadata = await getJson(`${VERSION_CHECK_PATH}?current=${encodeURIComponent(APP_VERSION)}`);
|
|
119
|
+
console.log(`codexclaude ${metadata?.latestVersion || APP_VERSION}`);
|
|
120
|
+
console.log(`Required minimum: ${metadata?.minimumVersion || APP_VERSION}`);
|
|
121
|
+
const changes = Array.isArray(metadata?.changes) ? metadata.changes : [];
|
|
122
|
+
if (changes.length) {
|
|
123
|
+
console.log("\nChanges:");
|
|
124
|
+
for (const change of changes) {
|
|
125
|
+
console.log(`- ${change}`);
|
|
126
|
+
}
|
|
127
|
+
} else if (metadata?.changelog) {
|
|
128
|
+
console.log(`\n${metadata.changelog}`);
|
|
129
|
+
}
|
|
130
|
+
if (metadata?.installCommand) {
|
|
131
|
+
console.log(`\nUpdate command: ${metadata.installCommand}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async function loginFlow() {
|
|
136
|
+
const rl = readline.createInterface({ input, output });
|
|
137
|
+
try {
|
|
138
|
+
const username = (await rl.question("CodexAPI.pro username or email: ")).trim();
|
|
139
|
+
const password = await rl.question("Dashboard password: ");
|
|
140
|
+
if (!username || !password) {
|
|
141
|
+
throw new Error("Username/email and password are required.");
|
|
142
|
+
}
|
|
143
|
+
const response = await postJson("/api/v1/auth/login", {
|
|
144
|
+
username,
|
|
145
|
+
password,
|
|
146
|
+
deviceName: `codexclaude ${APP_VERSION}`,
|
|
147
|
+
});
|
|
148
|
+
const apiUsername = response?.cliCredentials?.username || response?.user?.username;
|
|
149
|
+
if (!apiUsername) {
|
|
150
|
+
throw new Error("Login succeeded but no API username was returned.");
|
|
151
|
+
}
|
|
152
|
+
const existing = readConfig();
|
|
153
|
+
const selectedModel = await askModel(rl, existing?.model || "gpt-5.5");
|
|
154
|
+
const config = {
|
|
155
|
+
version: APP_VERSION,
|
|
156
|
+
apiOrigin: API_ORIGIN,
|
|
157
|
+
username: apiUsername,
|
|
158
|
+
dashboardEmail: response?.user?.email || null,
|
|
159
|
+
model: selectedModel,
|
|
160
|
+
loggedInAt: new Date().toISOString(),
|
|
161
|
+
};
|
|
162
|
+
saveConfig(config);
|
|
163
|
+
writeCodexConfig(config);
|
|
164
|
+
console.log(`\nLogged in as ${apiUsername}. Model: ${MODELS[selectedModel].label}.`);
|
|
165
|
+
console.log("Full Access permissions, high reasoning, fast mode, and CodexAPI.pro provider config were written.");
|
|
166
|
+
} finally {
|
|
167
|
+
rl.close();
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
async function modelFlow() {
|
|
172
|
+
const config = requireConfig();
|
|
173
|
+
const rl = readline.createInterface({ input, output });
|
|
174
|
+
try {
|
|
175
|
+
config.model = await askModel(rl, config.model || "gpt-5.5");
|
|
176
|
+
saveConfig(config);
|
|
177
|
+
writeCodexConfig(config);
|
|
178
|
+
console.log(`Model set to ${MODELS[config.model].label}.`);
|
|
179
|
+
} finally {
|
|
180
|
+
rl.close();
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
async function setupFlow() {
|
|
185
|
+
const config = requireConfig();
|
|
186
|
+
writeCodexConfig(config);
|
|
187
|
+
console.log(`Configured ${CODEX_CONFIG_FILE} for ${MODELS[config.model].label} with Full Access, high reasoning, and fast mode.`);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
async function ensureReady() {
|
|
191
|
+
let config = readConfig();
|
|
192
|
+
if (!config?.username) {
|
|
193
|
+
await loginFlow();
|
|
194
|
+
config = requireConfig();
|
|
195
|
+
}
|
|
196
|
+
writeCodexConfig(config);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
async function askModel(rl, current) {
|
|
200
|
+
const currentLabel = MODELS[current]?.label || MODELS["gpt-5.5"].label;
|
|
201
|
+
console.log("\nChoose your coding model:");
|
|
202
|
+
console.log(" 1. ChatGPT-5.5");
|
|
203
|
+
console.log(" 2. Opus 4.7");
|
|
204
|
+
console.log(" 3. Deepseek v4 Budget");
|
|
205
|
+
const answer = (await rl.question(`Model [${currentLabel}]: `)).trim().toLowerCase();
|
|
206
|
+
if (!answer) {
|
|
207
|
+
return MODELS[current] ? current : "gpt-5.5";
|
|
208
|
+
}
|
|
209
|
+
const match = Object.values(MODELS).find((model) => model.aliases.includes(answer));
|
|
210
|
+
if (!match) {
|
|
211
|
+
throw new Error("Unknown model choice. Enter 1 for ChatGPT-5.5, 2 for Opus 4.7, or 3 for Deepseek v4 Budget.");
|
|
212
|
+
}
|
|
213
|
+
return match.id;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
async function postJson(pathname, body) {
|
|
217
|
+
const response = await fetch(`${API_ORIGIN}${pathname}`, {
|
|
218
|
+
method: "POST",
|
|
219
|
+
headers: {
|
|
220
|
+
"content-type": "application/json",
|
|
221
|
+
"user-agent": `codexclaude/${APP_VERSION}`,
|
|
222
|
+
},
|
|
223
|
+
body: JSON.stringify(body),
|
|
224
|
+
});
|
|
225
|
+
const text = await response.text();
|
|
226
|
+
let payload = null;
|
|
227
|
+
try {
|
|
228
|
+
payload = text ? JSON.parse(text) : null;
|
|
229
|
+
} catch {
|
|
230
|
+
payload = { detail: text };
|
|
231
|
+
}
|
|
232
|
+
if (!response.ok) {
|
|
233
|
+
if (response.status === 426) {
|
|
234
|
+
throw new Error(updateRequiredMessage(payload));
|
|
235
|
+
}
|
|
236
|
+
throw new Error(payload?.detail || `Request failed with HTTP ${response.status}.`);
|
|
237
|
+
}
|
|
238
|
+
return payload;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
async function enforceLauncherVersion() {
|
|
242
|
+
if (isTruthy(process.env.CODEXCLAUDE_DISABLE_UPDATE_CHECK)) {
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
const params = new URLSearchParams({ current: APP_VERSION });
|
|
246
|
+
let metadata = null;
|
|
247
|
+
try {
|
|
248
|
+
metadata = await getJson(`${VERSION_CHECK_PATH}?${params.toString()}`);
|
|
249
|
+
} catch {
|
|
250
|
+
metadata = null;
|
|
251
|
+
}
|
|
252
|
+
if (metadata?.updateRequired || isOlderVersion(APP_VERSION, metadata?.minimumVersion)) {
|
|
253
|
+
throw new Error(updateRequiredMessage(metadata));
|
|
254
|
+
}
|
|
255
|
+
const npmMetadata = await npmLatestMetadata();
|
|
256
|
+
if (npmMetadata?.version && isOlderVersion(APP_VERSION, npmMetadata.version)) {
|
|
257
|
+
throw new Error(
|
|
258
|
+
updateRequiredMessage({
|
|
259
|
+
latestVersion: npmMetadata.version,
|
|
260
|
+
minimumVersion: npmMetadata.version,
|
|
261
|
+
installCommand: UPDATE_COMMAND,
|
|
262
|
+
registryInstallCommand: UPDATE_COMMAND,
|
|
263
|
+
changes: [
|
|
264
|
+
`npm latest is ${npmMetadata.version}; update codexclaude before starting a coding session.`,
|
|
265
|
+
],
|
|
266
|
+
}),
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
async function getJson(pathname) {
|
|
272
|
+
const response = await fetch(`${API_ORIGIN}${pathname}`, {
|
|
273
|
+
headers: {
|
|
274
|
+
"accept": "application/json",
|
|
275
|
+
"user-agent": `codexclaude/${APP_VERSION}`,
|
|
276
|
+
},
|
|
277
|
+
});
|
|
278
|
+
const text = await response.text();
|
|
279
|
+
let payload = null;
|
|
280
|
+
try {
|
|
281
|
+
payload = text ? JSON.parse(text) : null;
|
|
282
|
+
} catch {
|
|
283
|
+
payload = { detail: text };
|
|
284
|
+
}
|
|
285
|
+
if (!response.ok) {
|
|
286
|
+
throw new Error(payload?.detail || `Request failed with HTTP ${response.status}.`);
|
|
287
|
+
}
|
|
288
|
+
return payload;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
async function npmLatestMetadata() {
|
|
292
|
+
const controller = new AbortController();
|
|
293
|
+
const timer = setTimeout(() => controller.abort(), 3500);
|
|
294
|
+
timer.unref?.();
|
|
295
|
+
try {
|
|
296
|
+
const response = await fetch(NPM_LATEST_URL, {
|
|
297
|
+
headers: {
|
|
298
|
+
"accept": "application/json",
|
|
299
|
+
"user-agent": `codexclaude/${APP_VERSION}`,
|
|
300
|
+
},
|
|
301
|
+
signal: controller.signal,
|
|
302
|
+
});
|
|
303
|
+
if (!response.ok) {
|
|
304
|
+
return null;
|
|
305
|
+
}
|
|
306
|
+
return await response.json();
|
|
307
|
+
} catch {
|
|
308
|
+
return null;
|
|
309
|
+
} finally {
|
|
310
|
+
clearTimeout(timer);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
async function getJsonAuth(pathname, config, options = {}) {
|
|
315
|
+
const controller = new AbortController();
|
|
316
|
+
const timeoutMs = Number(options.timeoutMs || 5000);
|
|
317
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
318
|
+
timer.unref?.();
|
|
319
|
+
try {
|
|
320
|
+
const response = await fetch(`${config.apiOrigin || API_ORIGIN}${pathname}`, {
|
|
321
|
+
headers: {
|
|
322
|
+
"accept": "application/json",
|
|
323
|
+
"authorization": `Bearer ${config.username}`,
|
|
324
|
+
"user-agent": `codexclaude/${APP_VERSION}`,
|
|
325
|
+
},
|
|
326
|
+
signal: controller.signal,
|
|
327
|
+
});
|
|
328
|
+
const text = await response.text();
|
|
329
|
+
let payload = null;
|
|
330
|
+
try {
|
|
331
|
+
payload = text ? JSON.parse(text) : null;
|
|
332
|
+
} catch {
|
|
333
|
+
payload = { detail: text };
|
|
334
|
+
}
|
|
335
|
+
if (!response.ok) {
|
|
336
|
+
throw new Error(payload?.detail || `Request failed with HTTP ${response.status}.`);
|
|
337
|
+
}
|
|
338
|
+
return payload;
|
|
339
|
+
} finally {
|
|
340
|
+
clearTimeout(timer);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
function updateRequiredMessage(metadata) {
|
|
345
|
+
if (typeof metadata === "string") {
|
|
346
|
+
return metadata;
|
|
347
|
+
}
|
|
348
|
+
if (typeof metadata?.detail === "string" && !metadata.latestVersion && !metadata.minimumVersion) {
|
|
349
|
+
return metadata.detail;
|
|
350
|
+
}
|
|
351
|
+
const installCommand =
|
|
352
|
+
metadata?.installCommand ||
|
|
353
|
+
metadata?.registryInstallCommand ||
|
|
354
|
+
UPDATE_COMMAND;
|
|
355
|
+
const latest = metadata?.latestVersion || "latest";
|
|
356
|
+
const minimum = metadata?.minimumVersion || latest;
|
|
357
|
+
return [
|
|
358
|
+
`Update Now: CodexAPI.pro has updated to codexclaude ${latest}. Update your agent now to continue.`,
|
|
359
|
+
`Installed codexclaude: ${APP_VERSION}. Required: ${minimum}. Latest: ${latest}.`,
|
|
360
|
+
`Run: ${installCommand}`,
|
|
361
|
+
"Your Codex session history will not be deleted. The update only replaces the codexclaude launcher and rewrites provider config backups.",
|
|
362
|
+
].join("\n");
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function isOlderVersion(current, required) {
|
|
366
|
+
if (!required) {
|
|
367
|
+
return false;
|
|
368
|
+
}
|
|
369
|
+
const currentParts = semverParts(current);
|
|
370
|
+
const requiredParts = semverParts(required);
|
|
371
|
+
for (let index = 0; index < 3; index += 1) {
|
|
372
|
+
if (currentParts[index] < requiredParts[index]) {
|
|
373
|
+
return true;
|
|
374
|
+
}
|
|
375
|
+
if (currentParts[index] > requiredParts[index]) {
|
|
376
|
+
return false;
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
return false;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
function semverParts(value) {
|
|
383
|
+
const parts = String(value || "")
|
|
384
|
+
.match(/\d+/g)
|
|
385
|
+
?.slice(0, 3)
|
|
386
|
+
.map((part) => Number(part)) || [];
|
|
387
|
+
while (parts.length < 3) {
|
|
388
|
+
parts.push(0);
|
|
389
|
+
}
|
|
390
|
+
return parts;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
function isTruthy(value) {
|
|
394
|
+
return ["1", "true", "yes", "on"].includes(String(value || "").trim().toLowerCase());
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
function writeCodexConfig(config) {
|
|
398
|
+
mkdirSync(CODEX_DIR, { recursive: true });
|
|
399
|
+
if (existsSync(CODEX_CONFIG_FILE)) {
|
|
400
|
+
const backup = `${CODEX_CONFIG_FILE}.backup.${timestamp()}`;
|
|
401
|
+
copyFileSync(CODEX_CONFIG_FILE, backup);
|
|
402
|
+
}
|
|
403
|
+
const model = MODELS[config.model] ? config.model : "gpt-5.5";
|
|
404
|
+
const toml = [
|
|
405
|
+
`model = ${tomlString(model)}`,
|
|
406
|
+
'preferred_auth_method = "apikey"',
|
|
407
|
+
`model_provider = ${tomlString(PROVIDER_ID)}`,
|
|
408
|
+
`model_reasoning_effort = ${tomlString(CODEX_REASONING_EFFORT)}`,
|
|
409
|
+
`service_tier = ${tomlString(CODEX_SERVICE_TIER)}`,
|
|
410
|
+
'approval_policy = "never"',
|
|
411
|
+
'sandbox_mode = "danger-full-access"',
|
|
412
|
+
"",
|
|
413
|
+
`[model_providers.${PROVIDER_ID}]`,
|
|
414
|
+
'name = "CodexAPI.pro"',
|
|
415
|
+
`base_url = ${tomlString(API_BASE_URL)}`,
|
|
416
|
+
`experimental_bearer_token = ${tomlString(config.username)}`,
|
|
417
|
+
'wire_api = "responses"',
|
|
418
|
+
"supports_websockets = false",
|
|
419
|
+
`http_headers = { "X-CodexAPI-Client" = "codexclaude", "X-CodexAPI-Client-Version" = ${tomlString(APP_VERSION)}, "X-CodexAPI-Selected-Model" = ${tomlString(model)} }`,
|
|
420
|
+
"",
|
|
421
|
+
"[tui]",
|
|
422
|
+
'status_line = ["model-with-reasoning", "fast-mode", "current-dir"]',
|
|
423
|
+
"status_line_use_colors = true",
|
|
424
|
+
'terminal_title = ["project-name", "model-with-reasoning", "run-state"]',
|
|
425
|
+
"",
|
|
426
|
+
].join("\n");
|
|
427
|
+
writeFileSync(CODEX_CONFIG_FILE, toml, { encoding: "utf8", mode: 0o600 });
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
function readConfig() {
|
|
431
|
+
try {
|
|
432
|
+
return JSON.parse(readFileSync(CONFIG_FILE, "utf8"));
|
|
433
|
+
} catch {
|
|
434
|
+
return null;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
function requireConfig() {
|
|
439
|
+
const config = readConfig();
|
|
440
|
+
if (!config?.username) {
|
|
441
|
+
throw new Error("Not logged in. Run: codexclaude login");
|
|
442
|
+
}
|
|
443
|
+
return config;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
function saveConfig(config) {
|
|
447
|
+
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
448
|
+
writeFileSync(CONFIG_FILE, `${JSON.stringify(config, null, 2)}\n`, { encoding: "utf8", mode: 0o600 });
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
function logout() {
|
|
452
|
+
saveConfig({ version: APP_VERSION, loggedOutAt: new Date().toISOString() });
|
|
453
|
+
console.log("codexclaude login state removed. Codex config was left untouched.");
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
async function status() {
|
|
457
|
+
const config = readConfig();
|
|
458
|
+
if (!config?.username) {
|
|
459
|
+
console.log("Not logged in. Run: codexclaude login");
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
console.log(`Logged in as: ${config.username}`);
|
|
463
|
+
console.log(`Model: ${MODELS[config.model]?.label || config.model}`);
|
|
464
|
+
console.log(`Codex runtime: @openai/codex ${CODEX_VERSION}`);
|
|
465
|
+
console.log(`Reasoning: ${CODEX_REASONING_EFFORT}`);
|
|
466
|
+
console.log(`Service tier: ${CODEX_SERVICE_TIER}`);
|
|
467
|
+
console.log(`API: ${config.apiOrigin || API_ORIGIN}`);
|
|
468
|
+
console.log(`Codex config: ${CODEX_CONFIG_FILE}`);
|
|
469
|
+
try {
|
|
470
|
+
const wallet = await walletSnapshot(config);
|
|
471
|
+
console.log(`Token Wallet: ${formatUsd(wallet.balance)}`);
|
|
472
|
+
console.log(formatStatusLine(config, wallet, process.cwd()));
|
|
473
|
+
} catch (error) {
|
|
474
|
+
console.log(`Token Wallet: unavailable (${error.message})`);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
async function runCodex(args) {
|
|
479
|
+
const config = requireConfig();
|
|
480
|
+
let wallet = null;
|
|
481
|
+
try {
|
|
482
|
+
wallet = await walletSnapshot(config);
|
|
483
|
+
} catch {
|
|
484
|
+
// Wallet lookup should never block the user from starting a local coding session.
|
|
485
|
+
}
|
|
486
|
+
if (!isTruthy(process.env.CODEXCLAUDE_QUIET_STATUS)) {
|
|
487
|
+
console.log(formatStatusLine(config, wallet, process.cwd()));
|
|
488
|
+
}
|
|
489
|
+
const launch = resolveCodexLaunch();
|
|
490
|
+
const env = {
|
|
491
|
+
...process.env,
|
|
492
|
+
CODEXAPI_CODEX_API_KEY: config.username,
|
|
493
|
+
CODEXCLAUDE_CODEX_VERSION: CODEX_VERSION,
|
|
494
|
+
CODEXCLAUDE_MODEL_ID: config.model,
|
|
495
|
+
CODEXCLAUDE_MODEL_LABEL: MODELS[config.model]?.label || config.model,
|
|
496
|
+
CODEXCLAUDE_VERSION: APP_VERSION,
|
|
497
|
+
};
|
|
498
|
+
if (wallet) {
|
|
499
|
+
env.CODEXCLAUDE_WALLET_BALANCE_USD = String(wallet.balance);
|
|
500
|
+
}
|
|
501
|
+
const child = spawn(launch.command, [...launch.argsPrefix, ...args], { stdio: "inherit", env });
|
|
502
|
+
child.on("error", (error) => {
|
|
503
|
+
console.error(error.message);
|
|
504
|
+
process.exit(1);
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
const forwardSignal = (signal) => {
|
|
508
|
+
if (child.killed) {
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
try {
|
|
512
|
+
child.kill(signal);
|
|
513
|
+
} catch {
|
|
514
|
+
// The child may have exited between the signal and this handler.
|
|
515
|
+
}
|
|
516
|
+
};
|
|
517
|
+
["SIGINT", "SIGTERM", "SIGHUP"].forEach((signal) => {
|
|
518
|
+
process.on(signal, () => forwardSignal(signal));
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
const childResult = await new Promise((resolve) => {
|
|
522
|
+
child.on("exit", (code, signal) => {
|
|
523
|
+
if (signal) {
|
|
524
|
+
resolve({ type: "signal", signal });
|
|
525
|
+
} else {
|
|
526
|
+
resolve({ type: "code", exitCode: code ?? 1 });
|
|
527
|
+
}
|
|
528
|
+
});
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
if (childResult.type === "signal") {
|
|
532
|
+
process.kill(process.pid, childResult.signal);
|
|
533
|
+
} else {
|
|
534
|
+
process.exit(childResult.exitCode);
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
function resolveCodexLaunch() {
|
|
539
|
+
const bundled = resolveBundledCodexLaunch();
|
|
540
|
+
if (bundled) {
|
|
541
|
+
return bundled;
|
|
542
|
+
}
|
|
543
|
+
return {
|
|
544
|
+
command: commandName("npx"),
|
|
545
|
+
argsPrefix: ["-y", `@openai/codex@${CODEX_VERSION}`],
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
function resolveBundledCodexLaunch() {
|
|
550
|
+
try {
|
|
551
|
+
const packageJsonPath = require.resolve("@openai/codex/package.json");
|
|
552
|
+
const packageRoot = path.dirname(packageJsonPath);
|
|
553
|
+
const binPath = path.join(packageRoot, "bin", "codex.js");
|
|
554
|
+
if (existsSync(binPath)) {
|
|
555
|
+
return {
|
|
556
|
+
command: process.execPath,
|
|
557
|
+
argsPrefix: [binPath],
|
|
558
|
+
};
|
|
559
|
+
}
|
|
560
|
+
} catch {
|
|
561
|
+
return null;
|
|
562
|
+
}
|
|
563
|
+
return null;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
async function walletSnapshot(config) {
|
|
567
|
+
let payload = null;
|
|
568
|
+
try {
|
|
569
|
+
payload = await getJsonAuth("/api/v1/wallet/status", config, { timeoutMs: 10000 });
|
|
570
|
+
} catch {
|
|
571
|
+
payload = await getJsonAuth("/api/v1/me", config, { timeoutMs: 20000 });
|
|
572
|
+
}
|
|
573
|
+
const user = payload?.user || {};
|
|
574
|
+
const balance = numericWalletValue(
|
|
575
|
+
payload?.tokenWalletBalancePreciseUsd ??
|
|
576
|
+
payload?.walletBalancePreciseUsd ??
|
|
577
|
+
payload?.tokenWalletBalanceUsd ??
|
|
578
|
+
payload?.walletBalanceUsd ??
|
|
579
|
+
payload?.wallet_balance_usd ??
|
|
580
|
+
user?.tokenWalletBalancePreciseUsd ??
|
|
581
|
+
user?.walletBalancePreciseUsd ??
|
|
582
|
+
user?.tokenWalletBalanceUsd ??
|
|
583
|
+
user?.walletBalanceUsd,
|
|
584
|
+
);
|
|
585
|
+
return {
|
|
586
|
+
balance,
|
|
587
|
+
raw: payload,
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
function numericWalletValue(value) {
|
|
592
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
593
|
+
return value;
|
|
594
|
+
}
|
|
595
|
+
const normalized = String(value ?? "0").replace(/[^0-9.-]+/g, "");
|
|
596
|
+
const parsed = Number(normalized);
|
|
597
|
+
return Number.isFinite(parsed) ? parsed : 0;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
function formatStatusLine(config, wallet, cwd) {
|
|
601
|
+
const model = modelStatusName(config.model);
|
|
602
|
+
const walletText = wallet ? `Wallet ${formatUsd(wallet.balance)}` : "Wallet unavailable";
|
|
603
|
+
return `${model} ${CODEX_REASONING_EFFORT} ${CODEX_SERVICE_TIER} · ${formatCwd(cwd)} · ${walletText}`;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
function modelStatusName(model) {
|
|
607
|
+
if (model === "claude-opus-4-7") {
|
|
608
|
+
return "opus-4.7";
|
|
609
|
+
}
|
|
610
|
+
if (model === "deepseek-v4-pro") {
|
|
611
|
+
return "deepseek-v4 budget";
|
|
612
|
+
}
|
|
613
|
+
return "gpt-5.5";
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
function formatUsd(value) {
|
|
617
|
+
return `$${Number(value || 0).toLocaleString("en-US", {
|
|
618
|
+
minimumFractionDigits: 2,
|
|
619
|
+
maximumFractionDigits: 2,
|
|
620
|
+
})}`;
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
function formatCwd(cwd) {
|
|
624
|
+
const home = homedir();
|
|
625
|
+
if (cwd === home) {
|
|
626
|
+
return "~";
|
|
627
|
+
}
|
|
628
|
+
if (cwd.startsWith(`${home}${path.sep}`)) {
|
|
629
|
+
return `~${cwd.slice(home.length)}`;
|
|
630
|
+
}
|
|
631
|
+
return cwd;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
function commandName(command) {
|
|
635
|
+
if (process.platform !== "win32") {
|
|
636
|
+
return command;
|
|
637
|
+
}
|
|
638
|
+
return command === "codex" || command === "npm" || command === "npx" ? `${command}.cmd` : command;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
function tomlString(value) {
|
|
642
|
+
return `"${String(value ?? "").replaceAll("\\", "\\\\").replaceAll('"', '\\"')}"`;
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
function timestamp() {
|
|
646
|
+
return new Date().toISOString().replace(/[-:TZ.]/g, "").slice(0, 14);
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
function ensureExecutable() {
|
|
650
|
+
try {
|
|
651
|
+
chmodSync(new URL(import.meta.url), 0o755);
|
|
652
|
+
} catch {
|
|
653
|
+
// The file may already be executable or installed on a filesystem that ignores chmod.
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
main().catch((error) => {
|
|
658
|
+
console.error(`codexclaude: ${error.message}`);
|
|
659
|
+
process.exit(1);
|
|
660
|
+
});
|
package/codexclaude.md
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# codexclaude Customization Ledger
|
|
2
|
+
|
|
3
|
+
This file tracks the CodexAPI.pro-specific layer that must be re-applied after pulling a newer official OpenAI Codex CLI release.
|
|
4
|
+
|
|
5
|
+
## Current Release
|
|
6
|
+
|
|
7
|
+
- Launcher package: `@codexapi/codexclaude`
|
|
8
|
+
- Launcher version: `2.0.4`
|
|
9
|
+
- Official Codex runtime dependency: `@openai/codex@0.134.0`
|
|
10
|
+
- Public install command: `npm install -g https://codexapi.pro/downloads/codexapi-codexclaude-2.0.4.tgz`
|
|
11
|
+
- Default API origin: `https://codexapi.pro`
|
|
12
|
+
- Responses base URL: `https://codexapi.pro/v1`
|
|
13
|
+
|
|
14
|
+
## Reapply Checklist After Upstream Codex Updates
|
|
15
|
+
|
|
16
|
+
- Keep `bin/codexclaude.js` as the thin launcher and avoid patching upstream Codex internals unless absolutely necessary.
|
|
17
|
+
- Update `CODEX_VERSION` and `package.json` dependency together.
|
|
18
|
+
- Keep login against `/api/v1/auth/login`.
|
|
19
|
+
- Keep saved launcher state in `~/.codexclaude/config.json`.
|
|
20
|
+
- Keep generated Codex config at `~/.codex/config.toml`.
|
|
21
|
+
- Preserve `model_provider = "codexapi"`, `wire_api = "responses"`, and `supports_websockets = false`.
|
|
22
|
+
- Preserve bearer authentication with the customer API username as `experimental_bearer_token`.
|
|
23
|
+
- Preserve request headers: `X-CodexAPI-Client`, `X-CodexAPI-Client-Version`, and `X-CodexAPI-Selected-Model`.
|
|
24
|
+
- Preserve Full Access defaults: `approval_policy = "never"` and `sandbox_mode = "danger-full-access"`.
|
|
25
|
+
- Preserve `model_reasoning_effort = "high"` and `service_tier = "fast"` unless the business rule changes.
|
|
26
|
+
- Keep `codexclaude start` launching Codex with `--search` by default.
|
|
27
|
+
- Keep `codexclaude resume` launching `codex resume --search`.
|
|
28
|
+
- Keep all unknown commands passed through to official Codex.
|
|
29
|
+
- Keep wallet status lookup through `/api/v1/wallet/status` with `/api/v1/me` fallback.
|
|
30
|
+
- Keep startup update enforcement through both `/api/v1/codexclaude/version` and npm latest metadata.
|
|
31
|
+
- Keep the user-facing update message with the exact npm update command.
|
|
32
|
+
|
|
33
|
+
## Model Choices
|
|
34
|
+
|
|
35
|
+
- `gpt-5.5`: default ChatGPT-5.5 coding route.
|
|
36
|
+
- `claude-opus-4-7`: Opus 4.7 coding route through CodexAPI.pro Responses compatibility.
|
|
37
|
+
- `deepseek-v4-pro`: Deepseek v4 Budget coding route through CodexAPI.pro Responses compatibility.
|
|
38
|
+
|
|
39
|
+
The selected model must be written into `~/.codex/config.toml` before the Codex process starts. The backend snapshots the route at request start; the launcher must not change the model mid-generation.
|
|
40
|
+
|
|
41
|
+
## Version 2.0.4 Changes
|
|
42
|
+
|
|
43
|
+
- Added Deepseek v4 Budget as a third model choice.
|
|
44
|
+
- Added `X-CodexAPI-Selected-Model` to generated provider headers.
|
|
45
|
+
- Added `CODEXCLAUDE_MODEL_ID` to the spawned Codex environment.
|
|
46
|
+
- Added npm latest-version detection before coding sessions start.
|
|
47
|
+
- Updated the forced update message so users see the exact npm command.
|
|
48
|
+
- Added this customization ledger for future upstream Codex merges.
|
|
49
|
+
|
|
50
|
+
## Validation Commands
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
node --check bin/codexclaude.js
|
|
54
|
+
npm pack
|
|
55
|
+
CODEXCLAUDE_DISABLE_UPDATE_CHECK=1 codexclaude --version
|
|
56
|
+
```
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@codexapi/codexclaude",
|
|
3
|
+
"version": "2.0.4",
|
|
4
|
+
"description": "CodexAPI.pro coding CLI launcher for ChatGPT-5.5, Opus 4.7, and Deepseek v4 Budget, powered by official Codex CLI 0.134.0.",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"codexclaude": "bin/codexclaude.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"bin",
|
|
12
|
+
"README.md",
|
|
13
|
+
"CUSTOMIZATIONS.md",
|
|
14
|
+
"codexclaude.md"
|
|
15
|
+
],
|
|
16
|
+
"engines": {
|
|
17
|
+
"node": ">=20"
|
|
18
|
+
},
|
|
19
|
+
"dependencies": {
|
|
20
|
+
"@openai/codex": "0.134.0"
|
|
21
|
+
},
|
|
22
|
+
"publishConfig": {
|
|
23
|
+
"access": "public"
|
|
24
|
+
},
|
|
25
|
+
"keywords": [
|
|
26
|
+
"codex",
|
|
27
|
+
"claude",
|
|
28
|
+
"opus",
|
|
29
|
+
"deepseek",
|
|
30
|
+
"budget",
|
|
31
|
+
"codexapi",
|
|
32
|
+
"coding-cli"
|
|
33
|
+
],
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "https://codexapi.pro"
|
|
37
|
+
}
|
|
38
|
+
}
|