@codexapi/codexclaude 2.0.37 → 2.0.40

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 CHANGED
@@ -5,10 +5,10 @@
5
5
  ## Runtime
6
6
 
7
7
  - Customer-facing app name: `MagnetAPI.org Codex CLI`
8
- - Local launcher version: `2.0.37`
9
- - Bundled official runtime dependency: `@openai/codex@0.153.4`
8
+ - Local launcher version: `2.0.40`
9
+ - Bundled official runtime dependency: `@openai/codex@0.157.0-alpha.7`
10
10
  - Required install command: `npm install -g @codexapi/codexclaude@latest`
11
- - Hosted fallback: `npm install -g https://codexapi.pro/downloads/codexapi-codexclaude-2.0.37.tgz`
11
+ - Hosted fallback archive: `https://codexapi.pro/downloads/codexapi-codexclaude-2.0.40.tgz`, with its SHA-256 sidecar. The updater installs a verified local copy, not an npm remote URL.
12
12
  - Bundled runtime version is managed internally and must not be used for customer update prompts.
13
13
  - Launcher update checks may call the CodexAPI.pro version endpoint and `@codexapi/codexclaude` npm metadata only.
14
14
  - The launcher must never check `@openai/codex` npm metadata or OpenAI GitHub releases for update notices.
@@ -25,8 +25,8 @@
25
25
  - Passes unknown commands directly to the bundled runtime after config is applied.
26
26
  - Provides `codexclaude changelog` using the managed version endpoint.
27
27
  - Does not expose separate `codexclaude status` or `codexclaude usage` commands; wallet information remains a compact pre-launch line.
28
- - Version `2.0.37` is forced by the backend minimum version and older clients must show an update-required message.
29
- - Mandatory updates present a press-Enter `Upgrade Now` action in an interactive terminal, invoke npm with an argument array and no shell interpolation, verify the installed launcher version, use the hosted release if the registry fails or returns a stale version, then relaunch the requested action automatically.
28
+ - The backend minimum controls enforcement. Never require an unpublished or unverified archive.
29
+ - Mandatory updates present a press-Enter `Upgrade Now` action, install the exact managed version through npm-cli.js on Windows, verify npm's actual global package, fall back to a checksum-verified local archive and relaunch its verified entrypoint through Node. Do not weaken npm policies or rely on stale PATH aliases.
30
30
  - The npm package manifest exposes only `codexclaude`, preventing npm from colliding with a native `codex` or `claude` command before postinstall runs.
31
31
  - Global installation creates optional `codex` and `claude` aliases only when no existing command is present in the npm prefix or anywhere on `PATH`; native clients must never be overwritten.
32
32
  - `codexclaude topup` and `codexclaude /topup` authenticate with the saved System Token, map the six fixed packages to the correct site-specific plan ids, and call only `/api/v1/wallet/stripe/checkout` and `/api/v1/wallet/stripe/confirm`.
@@ -48,7 +48,7 @@ The launcher writes a model provider named `codexapi`:
48
48
  ## Runtime Defaults
49
49
 
50
50
  - Default model: `gpt-5.6-sol` displayed as `GPT-5.6 SOL`
51
- - Supported launcher choices: `gpt-6-astra`, `gpt-daybreak-blue-latest`, `gpt-5.6`, `gpt-5.6-sol-max`, `gpt-5.6-sol-ultra`, `gpt-5.6-sol`, `gpt-5.5`, `claude-opus-5`, `claude-opus-4-8`, `claude-sonnet-5`, `claude-fable-5`, and `gpt-5.6-terra`
51
+ - Picker order: `gpt-6-astra`, `claude-fable-5` (Fable 5.1), `claude-opus-5`, followed by current GPT-5.6, Daybreak and Sonnet variants. GPT-5.5 and Opus 4.8 are hidden compatibility entries, not deleted API identities. Numeric choices are derived from visible order, never stored as stale aliases.
52
52
  - Default reasoning effort displayed locally: `Extra High`, stored as `xhigh`; SOL Max and SOL Ultra retain their explicit premium labels.
53
53
  - Reasoning hint written to managed runtime config: model-specific
54
54
  - Approval policy: `never`
@@ -67,10 +67,12 @@ Before launching the runtime, the launcher calls `/api/v1/wallet/status`, then `
67
67
  Expected status line:
68
68
 
69
69
  ```text
70
- MagnetAPI.org Codex CLI v2.0.37 · GPT-5.6 SOL Extra High · ~/project · Wallet balance $20.00
71
- Fable 5 is now available in codexclaude, exclusively.
70
+ MagnetAPI.org Codex CLI v2.0.40 · GPT-5.6 SOL Extra High · ~/project · Wallet balance $20.00
71
+ Fable 5.1, ChatGPT-6-Astra and Daybreak Blue are now available. Save 10% on API coding usage with codexclaude.
72
72
  ```
73
73
 
74
+ `bin/splash.js` owns the terminal-only binary graphic and launch classification. Preserve `codexclaude, powered by https://magnetapi.org`, own app version, responsive widths, NO_COLOR, TERM=dumb and explicit opt-outs. No sleeps, clearing, network calls or decorations in headless/JSON/server output. `test/frontier.test.js` checks ordering, legacy visibility, numeric selection and splash behavior. `test/update.test.js` covers local verified fallback and Windows npm resolution.
75
+
74
76
  ## Upgrade Checklist
75
77
 
76
78
  When updating the bundled runtime:
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # codexclaude
2
2
 
3
- `codexclaude` installs the MagnetAPI.org Codex CLI for customers who want one coding CLI that can use ChatGPT-6 Astra, ChatGPT Daybreak Blue, GPT-5.6, GPT-5.6 Terra, GPT-5.6 SOL, GPT-5.6 SOL Max, GPT-5.6 SOL Ultra, ChatGPT-5.5, Opus 5, Opus 4.8, Sonnet 5, or Fable 5 against the same Token Wallet.
3
+ `codexclaude` installs the MagnetAPI.org Codex CLI for customers who want one coding client for ChatGPT-6-Astra, Fable 5.1, Opus 5, Sonnet 5, Daybreak Blue and GPT-5.6 models, using the same Token Wallet.
4
4
 
5
- Version `2.0.37` bundles the exact Codex runtime dependency `@openai/codex@0.153.4` and adds ChatGPT-6 Astra with its supported reasoning levels and complete model metadata. GPT-5.6 SOL with visible Extra High reasoning remains the default for new or invalid configurations, and existing valid model selections remain unchanged. The release preserves the collision-safe updater, platform-native runtime launch, managed account and wallet integration, Full Access defaults, upstream update suppression, and every MagnetAPI customization.
5
+ Version `2.0.40` bundles the exact Codex runtime dependency `@openai/codex@0.157.0-alpha.7`. The model picker starts with ChatGPT-6-Astra, Fable 5.1 and Opus 5. GPT-5.6 SOL with Extra High reasoning remains the default; existing valid selections are preserved. The release retains four-site authentication, wallet integration, Full Access defaults, local tools, safe configuration merging and managed updates.
6
6
 
7
7
  ## Install Or Update
8
8
 
@@ -16,7 +16,7 @@ This does not delete local session history. The update replaces the `codexclaude
16
16
 
17
17
  The launcher checks CodexAPI.pro's managed version endpoint and the `@codexapi/codexclaude` npm package metadata only. It must never check `@openai/codex` npm metadata or OpenAI GitHub releases for customer update prompts.
18
18
 
19
- Version `2.0.37` is enforced as the minimum supported version. A compatible older launcher presents `✨ Update now available`; pressing Enter installs `@codexapi/codexclaude@latest` and relaunches the requested action automatically. If the npm registry install fails, the updater tries the hosted release before asking the user to intervene. Non-interactive terminals print the same npm command instead of hanging for input.
19
+ The service's release metadata determines the minimum supported version. When an update is required, pressing Enter at `Upgrade Now` installs the exact managed release and restarts the requested action. If npm is unavailable or stale, this release downloads and verifies the hosted package before installing it as a local file. Native `codex` and `claude` commands are never overwritten. Non-interactive commands fail clearly rather than waiting for input.
20
20
 
21
21
  ## First Login
22
22
 
@@ -44,7 +44,7 @@ Switch model:
44
44
  codexclaude model
45
45
  ```
46
46
 
47
- Available model labels are `ChatGPT-6 Astra`, `ChatGPT Daybreak Blue`, `GPT-5.6`, `GPT-5.6 SOL Max`, `GPT-5.6 SOL Ultra`, `GPT-5.6 SOL`, `ChatGPT-5.5`, `Opus 5`, `Opus 4.8`, `Sonnet 5`, `Fable 5`, and `GPT-5.6 Terra`. All models use a Responses-compatible endpoint and the same customer Token Wallet. Selecting ChatGPT-6 Astra displays its billing notice before the choice is saved; native signed-in Astra usage is charged at 2x the normal Codex rate, while managed cross-model execution follows that route's normal billing.
47
+ The first three choices are `ChatGPT-6-Astra`, `Fable 5.1` and `Opus 5`, followed by GPT-5.6, Daybreak Blue, SOL Max, SOL Ultra, SOL, Sonnet 5 and Terra. `codexclaude model`, `models`, `/model` and `/models` open the same launcher selector; the native in-session command remains `/model`. Legacy GPT-5.5 and Opus 4.8 metadata is hidden from new selection but retained for existing configurations. Fable 5.1 uses the compatible API id `claude-fable-5`; billing remains server-managed.
48
48
 
49
49
  ## Launch Commands
50
50
 
@@ -80,14 +80,17 @@ Card numbers, CVCs, and bank authentication challenges are entered only on Strip
80
80
  Before launching the coding runtime, the app prints a compact status line like:
81
81
 
82
82
  ```text
83
- MagnetAPI.org Codex CLI v2.0.37 · GPT-5.6 SOL Extra High · ~/project · Wallet balance $20.00
84
- Fable 5 is now available in codexclaude, exclusively.
83
+ codexclaude, powered by https://magnetapi.org
84
+ MagnetAPI.org Codex CLI v2.0.40 · GPT-5.6 SOL Extra High · ~/project · Wallet balance $20.00
85
+ Fable 5.1, ChatGPT-6-Astra and Daybreak Blue are now available. Save 10% on API coding usage with codexclaude.
85
86
  ```
86
87
 
88
+ Interactive startup includes a gold-and-cyan binary graphic. Narrow terminals use a compact design. `NO_COLOR` and `TERM=dumb` disable color; `CODEXCLAUDE_NO_SPLASH=1` hides the graphic, and `CODEXCLAUDE_QUIET_STATUS=1` hides all launcher startup decoration. Exec/JSON, completion, server protocols, pipes and version/help output never receive splash or wallet text.
89
+
87
90
  ## Preserved Customizations
88
91
 
89
92
  - Dashboard Username or Email Address plus password login for CodexAPI.pro, ChatPlus.chat, MyAPI.world, and MagnetAPI.org.
90
- - ChatGPT-6 Astra, ChatGPT Daybreak Blue, Opus 5, GPT-5.6, GPT-5.6 Terra, GPT-5.6 SOL Max, GPT-5.6 SOL Ultra, GPT-5.6 SOL, ChatGPT-5.5, Opus 4.8, Sonnet 5, or Fable 5 model selection, with GPT-5.6 SOL/Extra High as the default.
93
+ - Frontier-first model selection, with GPT-5.6 SOL/Extra High as the default and legacy configuration compatibility.
91
94
  - Service-specific Responses-compatible provider for the dashboard account that logged in.
92
95
  - Full Access defaults: approval policy `never` and sandbox mode `danger-full-access`.
93
96
  - Model-specific visible reasoning in the local UI while the API server keeps upstream runtime effort server-managed.
@@ -2,18 +2,21 @@
2
2
 
3
3
  import { spawn, spawnSync } from "node:child_process";
4
4
  import { createRequire } from "node:module";
5
- import { existsSync, mkdirSync, readFileSync, writeFileSync, copyFileSync, chmodSync, unlinkSync } from "node:fs";
5
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, copyFileSync, chmodSync, unlinkSync, renameSync } from "node:fs";
6
+ import TOML from "@iarna/toml";
6
7
  import { homedir } from "node:os";
7
8
  import path from "node:path";
8
9
  import readline from "node:readline/promises";
9
10
  import { stdin as input, stdout as output } from "node:process";
10
11
  import { fileURLToPath } from "node:url";
11
12
  import { installSafeCommandAliases } from "../scripts/install-safe-aliases.js";
13
+ import { renderSplash, isInteractiveLaunch } from "./splash.js";
14
+ import { installedPackage, npmInvocation, performUpdate, releaseTarget } from "./update.js";
12
15
 
13
16
  const require = createRequire(import.meta.url);
14
17
 
15
- const APP_VERSION = "2.0.37";
16
- const CODEX_VERSION = "0.153.4";
18
+ const APP_VERSION = "2.0.40";
19
+ const CODEX_VERSION = "0.157.0-alpha.7";
17
20
  const APP_DISPLAY_NAME = "MagnetAPI.org Codex CLI";
18
21
  const DEFAULT_MODEL_ID = "gpt-5.6-sol";
19
22
  const DEFAULT_DISPLAY_REASONING_EFFORT = "xhigh";
@@ -36,7 +39,7 @@ const CODEX_CONFIG_FILE = path.join(CODEX_DIR, "config.toml");
36
39
  const CODEX_UPDATE_CACHE_FILE = path.join(CODEX_DIR, "version.json");
37
40
  const PROVIDER_ID = "codexapi";
38
41
  const MODEL_CATALOG_CONTEXT_WINDOW = 272000;
39
- const FABLE_ANNOUNCEMENT = "Fable 5 is now available in codexclaude, exclusively.";
42
+ const FABLE_ANNOUNCEMENT = "Fable 5.1, ChatGPT-6-Astra and Daybreak Blue are now available. Save 10% on API coding usage with codexclaude.";
40
43
  const ANSI_GREEN = "\x1b[32m";
41
44
  const ANSI_RESET = "\x1b[0m";
42
45
  const SERVICE_CATALOG = [
@@ -91,80 +94,84 @@ const TOPUP_POLL_INTERVAL_MS = positiveInteger(process.env.CODEXCLAUDE_TOPUP_POL
91
94
  const TOPUP_TIMEOUT_MS = positiveInteger(process.env.CODEXCLAUDE_TOPUP_TIMEOUT_MS, 30 * 60 * 1000);
92
95
 
93
96
  const MODELS = {
97
+ "gpt-6-astra": {
98
+ id: "gpt-6-astra",
99
+ label: "ChatGPT-6-Astra",
100
+ aliases: ["astra", "gpt-6", "gpt-6-astra", "gpt6-astra", "chatgpt-6-astra"],
101
+ reasoningEffort: "xhigh",
102
+ contextWindow: 1050000,
103
+ maxOutputTokens: 128000,
104
+ premium: true,
105
+ supportedReasoningLevels: SUPPORTED_REASONING_LEVELS.filter(({ effort }) => effort !== "ultra"),
106
+ },
107
+ "claude-fable-5": {
108
+ id: "claude-fable-5",
109
+ label: "Fable 5.1",
110
+ aliases: ["fable", "fable-5.1", "claude-fable-5.1", "fable-5", "fabel", "fabel-5", "claude-fable-5", "claude-fabel-5"],
111
+ premium: true,
112
+ },
94
113
  "claude-opus-5": {
95
114
  id: "claude-opus-5",
96
115
  label: "Opus 5",
97
- aliases: ["1", "opus", "opus-5", "claude", "claude-opus-5"],
116
+ aliases: ["opus", "opus-5", "claude", "claude-opus-5"],
98
117
  },
99
118
  "gpt-5.6": {
100
119
  id: "gpt-5.6",
101
120
  label: "GPT-5.6",
102
- aliases: ["2", "gpt", "gpt-5.6", "chatgpt", "chatgpt-5.6", "gpt56"],
121
+ aliases: ["gpt", "gpt-5.6", "chatgpt", "chatgpt-5.6", "gpt56"],
103
122
  reasoningEffort: "xhigh",
104
123
  },
105
124
  "gpt-daybreak-blue-latest": {
106
125
  id: "gpt-daybreak-blue-latest",
107
- label: "ChatGPT Daybreak Blue",
108
- aliases: ["3", "daybreak", "daybreak-blue", "chatgpt-daybreak-blue", "gpt-daybreak-blue-latest"],
126
+ label: "Daybreak Blue - Advanced Security",
127
+ aliases: ["daybreak", "daybreak-blue", "chatgpt-daybreak-blue", "gpt-daybreak-blue-latest"],
109
128
  reasoningEffort: "xhigh",
110
129
  },
111
130
  "gpt-5.6-sol-max": {
112
131
  id: "gpt-5.6-sol-max",
113
132
  label: "GPT-5.6 SOL Max",
114
- aliases: ["4", "gpt-5.6-sol-max", "gpt-5.6-max", "gpt56-max", "sol-max", "max"],
133
+ aliases: ["gpt-5.6-sol-max", "gpt-5.6-max", "gpt56-max", "sol-max", "max"],
115
134
  reasoningEffort: "max",
116
135
  },
117
136
  "gpt-5.6-sol-ultra": {
118
137
  id: "gpt-5.6-sol-ultra",
119
138
  label: "GPT-5.6 SOL Ultra",
120
- aliases: ["5", "gpt-5.6-sol-ultra", "gpt-5.6-ultra", "gpt56-ultra", "sol-ultra", "ultra"],
139
+ aliases: ["gpt-5.6-sol-ultra", "gpt-5.6-ultra", "gpt56-ultra", "sol-ultra", "ultra"],
121
140
  reasoningEffort: "ultra",
122
141
  },
123
142
  "gpt-5.6-sol": {
124
143
  id: "gpt-5.6-sol",
125
144
  label: "GPT-5.6 SOL",
126
- aliases: ["6", "gpt-5.6-sol", "sol"],
145
+ aliases: ["gpt-5.6-sol", "sol"],
127
146
  reasoningEffort: "xhigh",
128
147
  },
129
148
  "gpt-5.5": {
130
149
  id: "gpt-5.5",
131
150
  label: "ChatGPT-5.5",
132
- aliases: ["7", "gpt-5.5", "chatgpt-5.5"],
151
+ aliases: ["gpt-5.5", "chatgpt-5.5"],
152
+ legacy: true,
133
153
  },
134
154
  "claude-opus-4-8": {
135
155
  id: "claude-opus-4-8",
136
156
  label: "Opus 4.8",
137
- aliases: ["8", "opus-4.8", "opus-4.7", "claude-opus-4-8", "claude-opus-4-7"],
157
+ aliases: ["opus-4.8", "opus-4.7", "claude-opus-4-8", "claude-opus-4-7"],
158
+ legacy: true,
138
159
  },
139
160
  "claude-sonnet-5": {
140
161
  id: "claude-sonnet-5",
141
162
  label: "Sonnet 5",
142
- aliases: ["9", "sonnet", "sonnet-5", "sonnet-4.6", "sonnet-4-6", "claude-sonnet-5", "claude-sonnet-4-6", "claude-sonnet-4.6"],
143
- },
144
- "claude-fable-5": {
145
- id: "claude-fable-5",
146
- label: "Fable 5",
147
- aliases: ["10", "fable", "fable-5", "fabel", "fabel-5", "claude-fable-5", "claude-fabel-5"],
148
- premium: true,
163
+ aliases: ["sonnet", "sonnet-5", "sonnet-4.6", "sonnet-4-6", "claude-sonnet-5", "claude-sonnet-4-6", "claude-sonnet-4.6"],
149
164
  },
150
165
  "gpt-5.6-terra": {
151
166
  id: "gpt-5.6-terra",
152
167
  label: "GPT-5.6 Terra",
153
- aliases: ["11", "terra", "gpt-5.6-terra", "gpt56-terra"],
168
+ aliases: ["terra", "gpt-5.6-terra", "gpt56-terra"],
154
169
  reasoningEffort: "xhigh",
155
170
  },
156
- "gpt-6-astra": {
157
- id: "gpt-6-astra",
158
- label: "ChatGPT-6 Astra",
159
- aliases: ["12", "astra", "gpt-6", "gpt-6-astra", "gpt6-astra", "chatgpt-6-astra"],
160
- reasoningEffort: "xhigh",
161
- contextWindow: 1050000,
162
- maxOutputTokens: 128000,
163
- premium: true,
164
- supportedReasoningLevels: SUPPORTED_REASONING_LEVELS.filter(({ effort }) => effort !== "ultra"),
165
- },
166
171
  };
167
172
 
173
+ const PICKER_MODELS = Object.values(MODELS).filter((model) => !model.legacy);
174
+
168
175
  async function main() {
169
176
  ensureExecutable();
170
177
  ensureSafeCommandAliases();
@@ -192,7 +199,7 @@ async function main() {
192
199
  logout();
193
200
  return;
194
201
  }
195
- if (command === "model") {
202
+ if (["model", "models", "/model", "/models"].includes(command)) {
196
203
  await modelFlow();
197
204
  return;
198
205
  }
@@ -226,7 +233,7 @@ ${APP_DISPLAY_NAME} v${APP_VERSION}
226
233
 
227
234
  Commands:
228
235
  codexclaude login Login with dashboard username/email and password
229
- codexclaude model Select GPT-6 Astra, Daybreak Blue, GPT-5.6, Opus 5, Terra, SOL modes, Opus 4.8, Sonnet 5, or Fable 5
236
+ codexclaude models Select ChatGPT-6-Astra, Fable 5.1, Opus 5, or another current model
230
237
  codexclaude changelog Show the latest CodexAPI.pro launcher changes
231
238
  codexclaude setup Rewrite ~/.codex/config.toml with Full Access defaults
232
239
  codexclaude topup Select an API Coding Credit package and securely complete payment
@@ -564,7 +571,7 @@ async function ensureReady() {
564
571
  async function askModel(rl, current) {
565
572
  const currentLabel = MODELS[current]?.label || MODELS[DEFAULT_MODEL_ID].label;
566
573
  console.log("\nChoose your coding model:");
567
- Object.values(MODELS).forEach((model, index) => {
574
+ PICKER_MODELS.forEach((model, index) => {
568
575
  console.log(` ${index + 1}. ${model.label}`);
569
576
  });
570
577
  const answer = (await rl.question(`Model [${currentLabel}]: `)).trim().toLowerCase();
@@ -573,7 +580,7 @@ async function askModel(rl, current) {
573
580
  printModelSelectionWarning(selected);
574
581
  return selected;
575
582
  }
576
- const match = Object.values(MODELS).find((model) => model.aliases.includes(answer));
583
+ const match = PICKER_MODELS.find((model, index) => String(index + 1) === answer || model.aliases.includes(answer));
577
584
  if (!match) {
578
585
  throw new Error("Unknown model choice. Run codexclaude model and choose one of the numbered options.");
579
586
  }
@@ -641,7 +648,6 @@ async function enforceLauncherVersion() {
641
648
  async function runMandatoryUpgrade(metadata) {
642
649
  const latest = metadata?.latestVersion || "latest";
643
650
  const minimum = metadata?.minimumVersion || latest;
644
- const hostedTarballUrl = metadata?.hostedTarballUrl || HOSTED_TARBALL_URL;
645
651
  const changes = Array.isArray(metadata?.changes) ? metadata.changes.slice(0, 4) : [];
646
652
  console.error(`\n✨ Update now available · ${APP_DISPLAY_NAME} v${latest}`);
647
653
  console.error(`${APP_DISPLAY_NAME} v${latest} is required before your next coding session.`);
@@ -671,28 +677,21 @@ async function runMandatoryUpgrade(metadata) {
671
677
  }
672
678
 
673
679
  console.log(`\nDownloading and installing ${NPM_PACKAGE_NAME}@latest...`);
674
- const npmCommand = process.platform === "win32" ? "npm.cmd" : "npm";
675
- let exitCode = await spawnAndWait(npmCommand, ["install", "-g", `${NPM_PACKAGE_NAME}@latest`]);
676
- const installedAfterRegistry = exitCode === 0 ? installedLauncherVersion() : null;
677
- if (exitCode !== 0 || isOlderVersion(installedAfterRegistry, minimum)) {
678
- if (exitCode === 0) {
679
- console.log(`\nThe npm registry returned v${installedAfterRegistry || "unknown"}, below required v${minimum}.`);
680
- }
681
- console.log("\nThe npm registry install did not complete. Trying the hosted release automatically...");
682
- exitCode = await spawnAndWait(npmCommand, ["install", "-g", hostedTarballUrl]);
683
- }
684
- const installedVersion = exitCode === 0 ? installedLauncherVersion() : null;
685
- if (exitCode !== 0 || isOlderVersion(installedVersion, minimum)) {
686
- throw new Error(
687
- `Automatic upgrade failed with exit code ${exitCode} and installed version ${installedVersion || "unknown"}. Run ${UPDATE_COMMAND}. ` +
688
- `If npm is unavailable, use: npm install -g ${hostedTarballUrl}`,
689
- );
690
- }
680
+ const npm = npmInvocation();
681
+ const installed = await performUpdate({
682
+ release: releaseTarget(metadata, null),
683
+ runNpm: args => spawnAndWait(npm.command, [...npm.args, ...args]),
684
+ readInstalled: () => {
685
+ const result = spawnSync(npm.command, [...npm.args, "root", "-g"], {
686
+ encoding: "utf8", timeout: 30000, shell: false,
687
+ });
688
+ return result.status === 0 ? installedPackage(String(result.stdout || "")) : null;
689
+ },
690
+ });
691
691
  console.log(`\nUpgrade complete. Restarting ${APP_DISPLAY_NAME} now...`);
692
692
  const relaunchArgs = process.argv.slice(2);
693
- const relaunchCommand = process.platform === "win32" ? "codexclaude.cmd" : "codexclaude";
694
693
  try {
695
- const relaunchExitCode = await spawnAndWait(relaunchCommand, relaunchArgs);
694
+ const relaunchExitCode = await spawnAndWait(process.execPath, [installed.entrypoint, ...relaunchArgs]);
696
695
  if (relaunchExitCode !== 0) {
697
696
  throw new Error(`updated client exited with code ${relaunchExitCode}`);
698
697
  }
@@ -704,24 +703,6 @@ async function runMandatoryUpgrade(metadata) {
704
703
  }
705
704
  }
706
705
 
707
- function installedLauncherVersion() {
708
- const command = process.platform === "win32" ? "codexclaude.cmd" : "codexclaude";
709
- const result = spawnSync(command, ["--version"], {
710
- encoding: "utf8",
711
- env: {
712
- ...process.env,
713
- CODEXCLAUDE_DISABLE_UPDATE_CHECK: "1",
714
- CODEXCLAUDE_SKIP_COMMAND_ALIASES: "1",
715
- },
716
- shell: false,
717
- });
718
- if (result.error || result.status !== 0) {
719
- return null;
720
- }
721
- const match = String(result.stdout || "").match(/\bv(\d+(?:\.\d+){2})\b/i);
722
- return match?.[1] || null;
723
- }
724
-
725
706
  async function spawnAndWait(command, args) {
726
707
  return await new Promise((resolve, reject) => {
727
708
  const child = spawn(command, args, { stdio: "inherit", env: process.env, shell: false });
@@ -945,6 +926,8 @@ function isTruthy(value) {
945
926
 
946
927
  function writeCodexConfig(config) {
947
928
  mkdirSync(CODEX_DIR, { recursive: true });
929
+ const existing = existsSync(CODEX_CONFIG_FILE)
930
+ ? TOML.parse(readFileSync(CODEX_CONFIG_FILE, "utf8").replace(/^\uFEFF/, "")) : {};
948
931
  if (existsSync(CODEX_CONFIG_FILE)) {
949
932
  const backup = `${CODEX_CONFIG_FILE}.backup.${timestamp()}`;
950
933
  copyFileSync(CODEX_CONFIG_FILE, backup);
@@ -981,7 +964,16 @@ function writeCodexConfig(config) {
981
964
  'terminal_title = ["project-name", "model-with-reasoning", "run-state"]',
982
965
  "",
983
966
  ].join("\n");
984
- writeFileSync(CODEX_CONFIG_FILE, toml, { encoding: "utf8", mode: 0o600 });
967
+ const managed = TOML.parse(toml);
968
+ const merged = {
969
+ ...existing,
970
+ ...managed,
971
+ model_providers: { ...existing.model_providers, ...managed.model_providers },
972
+ tui: { ...existing.tui, ...managed.tui },
973
+ };
974
+ const temporary = `${CODEX_CONFIG_FILE}.tmp.${process.pid}`;
975
+ writeFileSync(temporary, TOML.stringify(merged), { encoding: "utf8", mode: 0o600 });
976
+ renameSync(temporary, CODEX_CONFIG_FILE);
985
977
  }
986
978
 
987
979
  function clearUpstreamUpdateCache() {
@@ -997,7 +989,8 @@ function clearUpstreamUpdateCache() {
997
989
  function writeModelCatalog() {
998
990
  mkdirSync(CONFIG_DIR, { recursive: true });
999
991
  const catalog = {
1000
- models: Object.values(MODELS).map((model, index) => modelCatalogEntry(model, index)),
992
+ models: [...PICKER_MODELS, ...Object.values(MODELS).filter((model) => model.legacy)]
993
+ .map((model, index) => modelCatalogEntry(model, index)),
1001
994
  };
1002
995
  writeFileSync(MODEL_CATALOG_FILE, `${JSON.stringify(catalog, null, 2)}\n`, { encoding: "utf8", mode: 0o600 });
1003
996
  return MODEL_CATALOG_FILE;
@@ -1026,7 +1019,7 @@ function modelCatalogEntry(model, index) {
1026
1019
  default_reasoning_level: reasoningEffortForModel(model.id),
1027
1020
  supported_reasoning_levels: model.supportedReasoningLevels || SUPPORTED_REASONING_LEVELS,
1028
1021
  shell_type: "shell_command",
1029
- visibility: "list",
1022
+ visibility: model.legacy ? "hide" : "list",
1030
1023
  minimal_client_version: "0.124.0",
1031
1024
  supported_in_api: true,
1032
1025
  availability_nux: null,
@@ -1044,7 +1037,7 @@ function modelCatalogEntry(model, index) {
1044
1037
 
1045
1038
  function modelCatalogDescription(modelId) {
1046
1039
  if (modelId === "gpt-6-astra") {
1047
- return "ChatGPT-6 Astra coding route with local coding tools, configurable reasoning, and wallet billing.";
1040
+ return "ChatGPT-6-Astra: coding tools, configurable reasoning, and image input.";
1048
1041
  }
1049
1042
  if (modelId === "gpt-5.6") {
1050
1043
  return "GPT-5.6 coding route with local coding tools and wallet billing.";
@@ -1074,7 +1067,7 @@ function modelCatalogDescription(modelId) {
1074
1067
  return "Sonnet 5 coding route with local coding tools and wallet billing.";
1075
1068
  }
1076
1069
  if (modelId === "claude-fable-5") {
1077
- return "Fable 5 premium coding route with local coding tools and wallet billing.";
1070
+ return "Fable 5.1: coding tools and image input, using the compatible Fable 5 API id.";
1078
1071
  }
1079
1072
  return "ChatGPT-5.5 coding route with local coding tools and wallet billing.";
1080
1073
  }
@@ -1107,17 +1100,28 @@ function logout() {
1107
1100
 
1108
1101
  async function runCodex(args) {
1109
1102
  const config = requireConfig();
1103
+ const interactive = isInteractiveLaunch(args, {
1104
+ stdinTTY: input.isTTY, stdoutTTY: output.isTTY,
1105
+ quiet: isTruthy(process.env.CODEXCLAUDE_QUIET_STATUS),
1106
+ });
1107
+ if (interactive && !isTruthy(process.env.CODEXCLAUDE_NO_SPLASH)) {
1108
+ process.stderr.write(renderSplash({
1109
+ version: APP_VERSION, columns: output.columns,
1110
+ color: process.env.NO_COLOR === undefined && process.env.TERM !== "dumb",
1111
+ }));
1112
+ }
1110
1113
  let wallet = null;
1111
1114
  try {
1112
1115
  wallet = await walletSnapshot(config);
1113
1116
  } catch {
1114
1117
  // Wallet lookup should never block the user from starting a local coding session.
1115
1118
  }
1116
- if (!isTruthy(process.env.CODEXCLAUDE_QUIET_STATUS)) {
1119
+ if (interactive) {
1117
1120
  console.log(formatStatusLine(config, wallet, process.cwd()));
1118
- console.log(`${ANSI_GREEN}${FABLE_ANNOUNCEMENT}${ANSI_RESET}`);
1121
+ const color = process.env.NO_COLOR === undefined && process.env.TERM !== "dumb";
1122
+ console.log(color ? `${ANSI_GREEN}${FABLE_ANNOUNCEMENT}${ANSI_RESET}` : FABLE_ANNOUNCEMENT);
1123
+ if (color) process.stdout.write(`\x1b]0;${APP_DISPLAY_NAME} v${APP_VERSION}\x07`);
1119
1124
  }
1120
- process.stdout.write(`\x1b]0;${APP_DISPLAY_NAME} v${APP_VERSION}\x07`);
1121
1125
  const launch = resolveCodexLaunch();
1122
1126
  const env = {
1123
1127
  ...process.env,
@@ -1289,7 +1293,7 @@ function modelStatusName(model) {
1289
1293
  return "GPT-5.6";
1290
1294
  }
1291
1295
  if (model === "gpt-daybreak-blue-latest") {
1292
- return "ChatGPT Daybreak Blue";
1296
+ return "Daybreak Blue - Advanced Security";
1293
1297
  }
1294
1298
  if (model === "gpt-5.6-sol-max") {
1295
1299
  return "gpt-5.6-sol max";
@@ -1350,7 +1354,7 @@ function normalizeConfig(config) {
1350
1354
  if (["claude-sonnet-4.6", "claude-sonnet-4-6", "sonnet-4.6", "sonnet-4-6", "sonnet-5"].includes(normalized.model)) {
1351
1355
  normalized.model = "claude-sonnet-5";
1352
1356
  }
1353
- if (["claude-fabel-5", "fable-5", "fabel-5"].includes(normalized.model)) {
1357
+ if (["claude-fabel-5", "fable-5", "fabel-5", "fable-5.1", "claude-fable-5.1"].includes(normalized.model)) {
1354
1358
  normalized.model = "claude-fable-5";
1355
1359
  }
1356
1360
  if (["gpt-5.6-sol-max", "gpt-5.6-max", "gpt56-max", "sol-max", "max"].includes(normalized.model)) {
package/bin/splash.js ADDED
@@ -0,0 +1,36 @@
1
+ // Terminal-only artwork. Never delay startup or mix it into machine-readable output.
2
+ export function isInteractiveLaunch(args, { stdinTTY, stdoutTTY, quiet = false }) {
3
+ if (!stdinTTY || !stdoutTTY || quiet) return false;
4
+ if (args.some((arg) => /^(?:--json(?:=|$)|--help$|-h$|--version$|-V$)/.test(arg))) return false;
5
+ return !["exec", "e", "review", "app-server", "mcp-server", "completion", "mcp", "debug", "features"].includes(args[0]);
6
+ }
7
+
8
+ export function renderSplash({ version, columns = 80, color = true }) {
9
+ const width = Math.max(1, Math.min(Number(columns) || 80, 74));
10
+ const gold = color ? "\x1b[93m" : "";
11
+ const cyan = color ? "\x1b[36m" : "";
12
+ const bold = color ? "\x1b[1m" : "";
13
+ const reset = color ? "\x1b[0m" : "";
14
+ const lines = [];
15
+ const add = (value, style = "") => {
16
+ for (let offset = 0; offset < value.length; offset += width) {
17
+ lines.push(style + value.slice(offset, offset + width) + reset);
18
+ }
19
+ };
20
+ add("-".repeat(width), cyan);
21
+ if (width >= 40) {
22
+ [
23
+ " 011110 011110 010 101 001",
24
+ " 01 01 101 010 110",
25
+ " 10 10 010 101 001",
26
+ " 01 01 101 010 110",
27
+ " 100111 100111 010 101 001",
28
+ ].forEach((line) => add(line, gold));
29
+ } else {
30
+ add("0101 CODE + CREATE 1010", gold);
31
+ }
32
+ add(`codexclaude v${version}`, bold + gold);
33
+ add("codexclaude, powered by https://magnetapi.org", cyan);
34
+ add("-".repeat(width), cyan);
35
+ return "\n" + lines.join("\n") + "\n\n";
36
+ }
package/bin/update.js ADDED
@@ -0,0 +1,94 @@
1
+ import { createHash } from "node:crypto";
2
+ import { mkdtemp, writeFile, rm } from "node:fs/promises";
3
+ import { existsSync, readFileSync, realpathSync } from "node:fs";
4
+ import { tmpdir } from "node:os";
5
+ import path from "node:path";
6
+
7
+ export const PACKAGE = "@codexapi/codexclaude";
8
+ const VERSION = /^\d+\.\d+\.\d+$/;
9
+
10
+ export function atLeast(actual, required) {
11
+ if (!VERSION.test(actual || "") || !VERSION.test(required || "")) return false;
12
+ const a = actual.split(".").map(Number), b = required.split(".").map(Number);
13
+ for (let i = 0; i < 3; i++) if (a[i] !== b[i]) return a[i] > b[i];
14
+ return true;
15
+ }
16
+
17
+ export function releaseTarget(service, registry) {
18
+ const versions = [service?.latestVersion, service?.minimumVersion, registry?.version].filter(v => VERSION.test(v || ""));
19
+ const version = versions.reduce((a, b) => atLeast(a, b) ? a : b, null);
20
+ if (!version) throw new Error("No valid managed release version is available. Please retry shortly.");
21
+ const hostedTarballUrl = `https://codexapi.pro/downloads/codexapi-codexclaude-${version}.tgz`;
22
+ return { version, hostedTarballUrl, sha256: service?.latestVersion === version ? service.hostedTarballSha256 : null };
23
+ }
24
+
25
+ export async function downloadVerified(release, fetchImpl = fetch) {
26
+ const expectedUrl = `https://codexapi.pro/downloads/codexapi-codexclaude-${release.version}.tgz`;
27
+ if (!VERSION.test(release.version) || release.hostedTarballUrl !== expectedUrl) throw new Error("Untrusted managed update location.");
28
+ const request = async url => {
29
+ const response = await fetchImpl(url, { redirect: "error", signal: AbortSignal.timeout(60000) });
30
+ if (!response.ok) throw new Error(`Managed update download failed (HTTP ${response.status}).`);
31
+ return response;
32
+ };
33
+ const hash = release.sha256 || (await (await request(`${expectedUrl}.sha256`)).text()).trim().split(/\s+/)[0];
34
+ if (!/^[a-f0-9]{64}$/i.test(hash || "")) throw new Error("Managed update checksum is unavailable. No package was installed.");
35
+ const response = await request(expectedUrl);
36
+ const chunks = [];
37
+ let size = 0;
38
+ for await (const chunk of response.body) {
39
+ size += chunk.length;
40
+ if (size > 25 * 1024 * 1024) throw new Error("Managed update exceeds the download size limit.");
41
+ chunks.push(chunk);
42
+ }
43
+ const contents = Buffer.concat(chunks);
44
+ if (createHash("sha256").update(contents).digest("hex") !== hash.toLowerCase()) throw new Error("Managed update checksum mismatch. No package was installed.");
45
+ const directory = await mkdtemp(path.join(tmpdir(), "codexclaude-update-"));
46
+ const file = path.join(directory, `codexclaude-${release.version}.tgz`);
47
+ try { await writeFile(file, contents, { mode: 0o600, flag: "wx" }); }
48
+ catch (error) { await rm(directory, { recursive: true, force: true }); throw error; }
49
+ return { file, cleanup: () => rm(directory, { recursive: true, force: true }) };
50
+ }
51
+
52
+ export function npmInvocation(env = process.env, platform = process.platform, executable = process.execPath) {
53
+ const candidates = [env.npm_execpath, path.join(path.dirname(executable), "node_modules/npm/bin/npm-cli.js")].filter(Boolean);
54
+ if (platform === "win32" && env.APPDATA) candidates.push(path.join(env.APPDATA, "npm/node_modules/npm/bin/npm-cli.js"));
55
+ for (const directory of (env.PATH || env.Path || "").split(platform === "win32" ? ";" : ":")) {
56
+ if (!directory) continue;
57
+ candidates.push(path.join(directory, "node_modules/npm/bin/npm-cli.js"));
58
+ if (platform !== "win32") candidates.push(path.join(directory, "npm"));
59
+ }
60
+ for (const candidate of candidates) {
61
+ try {
62
+ const real = realpathSync(candidate);
63
+ if (path.basename(real) === "npm-cli.js") return { command: executable, args: [real] };
64
+ } catch {}
65
+ }
66
+ if (platform === "win32") throw new Error("npm could not be located. Repair your Node.js installation, then retry the update.");
67
+ return { command: "npm", args: [] };
68
+ }
69
+
70
+ export function installedPackage(globalRoot) {
71
+ try {
72
+ const root = path.join(globalRoot.trim(), "@codexapi", "codexclaude");
73
+ const pkg = JSON.parse(readFileSync(path.join(root, "package.json"), "utf8"));
74
+ const entrypoint = path.join(root, "bin/codexclaude.js");
75
+ if (pkg.name !== PACKAGE || !VERSION.test(pkg.version) || !existsSync(entrypoint)) return null;
76
+ return { version: pkg.version, entrypoint };
77
+ } catch { return null; }
78
+ }
79
+
80
+ export async function performUpdate({ release, runNpm, readInstalled, download = downloadVerified }) {
81
+ let code = await runNpm(["install", "-g", `${PACKAGE}@${release.version}`]);
82
+ let installed = code === 0 ? await readInstalled() : null;
83
+ if (!installed || !atLeast(installed.version, release.version)) {
84
+ const artifact = await download(release);
85
+ try {
86
+ code = await runNpm(["install", "-g", artifact.file]);
87
+ installed = code === 0 ? await readInstalled() : null;
88
+ } finally { await artifact.cleanup(); }
89
+ }
90
+ if (code !== 0 || !installed || !atLeast(installed.version, release.version)) {
91
+ throw new Error(`Automatic update did not install v${release.version}. Run: npm install -g ${PACKAGE}@${release.version}`);
92
+ }
93
+ return installed;
94
+ }
package/codexclaude.md CHANGED
@@ -4,6 +4,87 @@ This file is the durable handoff for the `@codexapi/codexclaude` package in `/ro
4
4
 
5
5
  ## Current Release
6
6
 
7
+ ### 2026-09-22 Daybreak And Runtime Update: 2.0.40
8
+
9
+ Version 2.0.40 bundles exact `@openai/codex@0.157.0-alpha.7`. The launcher model
10
+ catalogue names `gpt-daybreak-blue-latest` **Daybreak Blue - Advanced Security**.
11
+ Startup announces Fable 5.1, ChatGPT-6-Astra, Daybreak Blue and the existing 10%
12
+ managed-client API usage saving. Service-owned output uses MagnetAPI.org;
13
+ upstream package identifiers, SDK field names and license notices stay intact.
14
+ Version and minimum are advanced only after the checked hosted archive exists.
15
+ The launcher and upstream runtime have separate version numbers. Preserve the
16
+ same safe config merge, model defaults, local tools and native command ownership.
17
+ See STATE.md for actual publication and deployment evidence.
18
+
19
+ ## Previous Release
20
+
21
+ ### 2026-09-10 Frontier Picker And Binary Splash: 2.0.39
22
+
23
+ This section supersedes older release snapshots below. Source/package version
24
+ is `2.0.39`, with exact `@openai/codex@0.154.0`. Release deployment state and npm
25
+ verification are recorded in `/root/codexapi/STATE.md`; never infer registry
26
+ publication or minimum enforcement from the source version alone.
27
+
28
+ Publication completed on 2026-09-10 after separate npm login and publish
29
+ approvals. npm latest and the London/Chicago/Frankfurt mandatory minimum are
30
+ 2.0.39. All four sites' legacy/VIP labels and four-step wizards agree. The real
31
+ 2.0.37-to-2.0.39 registry upgrade and automatic relaunch passed in an isolated
32
+ home/prefix, preserving unrelated configuration. The hosted archive and npm
33
+ archive are identical. Never repack this published version after documentation
34
+ edits. Historical 2.0.37/2.0.38 statements below are superseded by this section.
35
+
36
+ The first three choices in the launcher selector and native catalog are
37
+ `ChatGPT-6-Astra` (`gpt-6-astra`), `Fable 5.1` (`claude-fable-5`) and `Opus 5`
38
+ (`claude-opus-5`). Remaining current choices retain their API ids. GPT-5.5 and
39
+ Opus 4.8 are compatibility-only: hidden from new selection, with full metadata
40
+ retained so upgrades do not change existing customer configurations or history.
41
+ Do not invent `claude-fable-5.1` upstream. A display alias normalizes to the stable
42
+ Fable id. Numeric choices are calculated from visible order to avoid selecting
43
+ the wrong model after reordering. `model`, `models`, `/model` and `/models` are
44
+ launcher aliases; the native interactive command remains `/model`.
45
+
46
+ Default model remains `gpt-5.6-sol`, Extra High. Full Access, four-site login,
47
+ customer wallet display, top-up, reasoning metadata, tool/image transport,
48
+ client headers and native-safe command aliases are preserved. Setup parses TOML,
49
+ backs up and atomically merges only owned fields. No runtime routing, billing,
50
+ provider credentials or regional native Codex installation is changed by this
51
+ launcher release.
52
+
53
+ `bin/splash.js` renders gold/cyan binary artwork and the exact line
54
+ `codexclaude, powered by https://magnetapi.org`, with app version 2.0.39.
55
+ It adapts to narrow terminals, has no animation delay and honors NO_COLOR,
56
+ TERM=dumb, CODEXCLAUDE_NO_SPLASH and CODEXCLAUDE_QUIET_STATUS. Launcher notices,
57
+ wallet text and terminal-title escapes are now excluded from exec/JSON, server
58
+ protocols, completions and pipes. Never pollute machine-readable stdout.
59
+
60
+ `bin/update.js` ports the tested claudecodex updater: exact registry target,
61
+ checksum-verified temporary local archive on fallback, bounded HTTPS downloads,
62
+ Windows npm-cli.js through Node, actual global package verification and direct
63
+ relaunch of that installed entrypoint. No npm --force/remote-policy override.
64
+ Only CodexAPI and our npm package may provide update metadata. Publish and verify
65
+ archives, SHA-256 sidecars, registry and dashboard scripts before raising the
66
+ minimum version. Preserve native binaries, local tools, providers and projects.
67
+
68
+ Run `npm test` for config preservation, frontier metadata, aliases, splash,
69
+ wallet/top-up and updater behavior. Then test the packed installation with actual
70
+ 0.154.0 exec JSON, native model picker and bounded live test-account calls.
71
+ All four websites, VIP setup and generated wizard checksums must match the final
72
+ archive; do not repack changed files after publishing its hash.
73
+
74
+ ### 2026-09-09 Configuration Safety Build
75
+
76
+ `2.0.38` is a hosted safety build for the MagnetAPI v2 Setup Wizard. npm `latest`
77
+ and the global mandatory minimum remain `2.0.37`; npm authentication must be
78
+ renewed before publishing `2.0.38` to the registry. Do not force a registry update
79
+ until that publication is verified. The wizard downloads the exact hosted archive,
80
+ verifies SHA-256, and installs the local file, avoiding npm remote-fetch restrictions.
81
+ The runtime remains Codex `0.153.4`; model, routing, authentication and billing
82
+ behavior are unchanged. `writeCodexConfig` now parses TOML with pinned
83
+ `@iarna/toml@2.2.5`, merges only managed root fields, the owned provider and owned
84
+ TUI fields, preserves other providers/MCP/projects, backs up the original, and
85
+ replaces the file atomically. Invalid existing TOML stops setup without changing it.
86
+ Run `npm test`, including the real-launcher configuration-preservation tests.
87
+
7
88
  - Package name: `@codexapi/codexclaude`
8
89
  - Launcher version: `2.0.37`
9
90
  - Customer-facing app name: `MagnetAPI.org Codex CLI`
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@codexapi/codexclaude",
3
- "version": "2.0.37",
4
- "description": "MagnetAPI.org Codex CLI for ChatGPT-6 Astra, ChatGPT Daybreak Blue, GPT-5.6, Opus 5, Sonnet 5, and Fable 5.",
3
+ "version": "2.0.40",
4
+ "description": "MagnetAPI.org Codex CLI for ChatGPT-6-Astra, Fable 5.1, Opus 5, Sonnet 5, Daybreak Blue, and GPT-5.6.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
7
7
  "bin": {
@@ -22,7 +22,8 @@
22
22
  "node": ">=20"
23
23
  },
24
24
  "dependencies": {
25
- "@openai/codex": "0.153.4"
25
+ "@iarna/toml": "2.2.5",
26
+ "@openai/codex": "0.157.0-alpha.7"
26
27
  },
27
28
  "publishConfig": {
28
29
  "access": "public"