@codexapi/codexclaude 2.0.36 → 2.0.39

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.36`
9
- - Bundled official runtime dependency: `@openai/codex@0.152.0-alpha.6`
8
+ - Local launcher version: `2.0.39`
9
+ - Bundled official runtime dependency: `@openai/codex@0.154.0`
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.36.tgz`
11
+ - Hosted fallback archive: `https://codexapi.pro/downloads/codexapi-codexclaude-2.0.39.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.36` 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,17 +48,17 @@ 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-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`
55
55
  - Sandbox mode: `danger-full-access`
56
56
  - Display wording for permissions: `Full Access` in launcher-owned copy
57
- - Local model catalog: `~/.codexclaude/model_catalog.json`; this keeps the native `/model` picker limited to the supported choices and prevents fallback metadata warnings for ChatGPT Daybreak Blue, GPT-5.6 Terra, SOL, SOL Max, SOL Ultra, Opus 5, Opus 4.8, Sonnet 5, and Fable 5.
57
+ - Local model catalog: `~/.codexclaude/model_catalog.json`; this keeps the native `/model` picker limited to the supported choices and prevents fallback metadata warnings for ChatGPT-6 Astra, ChatGPT Daybreak Blue, GPT-5.6 Terra, SOL, SOL Max, SOL Ultra, Opus 5, Opus 4.8, Sonnet 5, and Fable 5.
58
58
  - Service tier: omitted by default; `/fast` remains user-controlled and billed server-side when active.
59
59
  - TUI status line: `model-with-reasoning`, `fast-mode`, `current-dir`
60
60
  - Fable 5 stays visible to the user while the backend applies its server-managed route and billing policy.
61
- - Premium model billing remains server-managed and is not rendered in the customer model picker or model descriptions.
61
+ - Premium model billing remains server-managed. ChatGPT-6 Astra is the explicit exception: its native signed-in 2x usage notice is printed when the user selects it, without injecting billing text into a model prompt or assistant output.
62
62
 
63
63
  ## Wallet Display
64
64
 
@@ -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.36 · 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.39 · GPT-5.6 SOL Extra High · ~/project · Wallet balance $20.00
71
+ Fable 5.1 is now available in 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:
@@ -88,7 +90,7 @@ When updating the bundled runtime:
88
90
  11. Keep update checks limited to `/api/v1/codexclaude/version` and `@codexapi/codexclaude` npm metadata.
89
91
  12. Keep the interactive `Upgrade Now` flow installing `@codexapi/codexclaude@latest` without a shell and relaunching automatically.
90
92
  13. Keep `codexclaude` as the only manifest-owned command and preserve the conditional, non-overwriting `codex` and `claude` postinstall aliases.
91
- 14. Keep the managed `MODELS` map and local model catalog, including Terra, SOL Max, and SOL Ultra.
93
+ 14. Keep the managed `MODELS` map and local model catalog, including Astra, Terra, SOL Max, and SOL Ultra.
92
94
  15. Keep bearer username auth and the `X-CodexAPI-*` headers.
93
95
  16. Update `README.md`, `CUSTOMIZATIONS.md`, and `codexclaude.md`.
94
96
  17. Run syntax checks, temporary-home setup, alias-preservation tests, `npm pack --dry-run`, and endpoint validation before publishing.
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 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.36` bundles the exact Codex runtime dependency `@openai/codex@0.152.0-alpha.6` and makes GPT-5.6 SOL with visible Extra High reasoning the default for new or invalid configurations. Existing valid model selections remain unchanged. It preserves the collision-safe updater, platform-native runtime launch, managed account and wallet integration, Full Access defaults, upstream update suppression, and complete model metadata.
5
+ Version `2.0.39` bundles the exact Codex runtime dependency `@openai/codex@0.154.0`. 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.36` 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 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.
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,18 +80,21 @@ 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.36 · 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.39 · GPT-5.6 SOL Extra High · ~/project · Wallet balance $20.00
85
+ Fable 5.1 is now available in 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 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.
94
- - A local `~/.codexclaude/model_catalog.json` file so the native `/model` picker lists only the supported MagnetAPI.org Codex CLI choices, including Terra, SOL Max, and SOL Ultra.
97
+ - A local `~/.codexclaude/model_catalog.json` file so the native `/model` picker lists only the supported MagnetAPI.org Codex CLI choices, including Astra, Terra, SOL Max, and SOL Ultra.
95
98
  - No default fast service tier. Fast mode is billed only when the user enables it inside the client.
96
99
  - Token Wallet balance lookup before launch without exposing separate `status` or `usage` launcher commands.
97
100
  - Authenticated `topup` and `/topup` commands using fixed server-authoritative packages, idempotent Stripe fulfillment, and post-payment wallet refresh.
@@ -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.36";
16
- const CODEX_VERSION = "0.152.0-alpha.6";
18
+ const APP_VERSION = "2.0.39";
19
+ const CODEX_VERSION = "0.154.0";
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 is now available in codexclaude.";
40
43
  const ANSI_GREEN = "\x1b[32m";
41
44
  const ANSI_RESET = "\x1b[0m";
42
45
  const SERVICE_CATALOG = [
@@ -91,70 +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
126
  label: "ChatGPT Daybreak Blue",
108
- aliases: ["3", "daybreak", "daybreak-blue", "chatgpt-daybreak-blue", "gpt-daybreak-blue-latest"],
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
171
  };
157
172
 
173
+ const PICKER_MODELS = Object.values(MODELS).filter((model) => !model.legacy);
174
+
158
175
  async function main() {
159
176
  ensureExecutable();
160
177
  ensureSafeCommandAliases();
@@ -182,7 +199,7 @@ async function main() {
182
199
  logout();
183
200
  return;
184
201
  }
185
- if (command === "model") {
202
+ if (["model", "models", "/model", "/models"].includes(command)) {
186
203
  await modelFlow();
187
204
  return;
188
205
  }
@@ -216,7 +233,7 @@ ${APP_DISPLAY_NAME} v${APP_VERSION}
216
233
 
217
234
  Commands:
218
235
  codexclaude login Login with dashboard username/email and password
219
- codexclaude model Select 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
220
237
  codexclaude changelog Show the latest CodexAPI.pro launcher changes
221
238
  codexclaude setup Rewrite ~/.codex/config.toml with Full Access defaults
222
239
  codexclaude topup Select an API Coding Credit package and securely complete payment
@@ -554,20 +571,33 @@ async function ensureReady() {
554
571
  async function askModel(rl, current) {
555
572
  const currentLabel = MODELS[current]?.label || MODELS[DEFAULT_MODEL_ID].label;
556
573
  console.log("\nChoose your coding model:");
557
- Object.values(MODELS).forEach((model, index) => {
574
+ PICKER_MODELS.forEach((model, index) => {
558
575
  console.log(` ${index + 1}. ${model.label}`);
559
576
  });
560
577
  const answer = (await rl.question(`Model [${currentLabel}]: `)).trim().toLowerCase();
561
578
  if (!answer) {
562
- return MODELS[current] ? current : DEFAULT_MODEL_ID;
579
+ const selected = MODELS[current] ? current : DEFAULT_MODEL_ID;
580
+ printModelSelectionWarning(selected);
581
+ return selected;
563
582
  }
564
- 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));
565
584
  if (!match) {
566
585
  throw new Error("Unknown model choice. Run codexclaude model and choose one of the numbered options.");
567
586
  }
587
+ printModelSelectionWarning(match.id);
568
588
  return match.id;
569
589
  }
570
590
 
591
+ function printModelSelectionWarning(modelId) {
592
+ if (modelId !== "gpt-6-astra") {
593
+ return;
594
+ }
595
+ console.log(
596
+ "\nBilling notice: Native ChatGPT-6 Astra usage is charged at 2x the normal Codex rate. " +
597
+ "When the managed cross-model route serves the request, that route's normal billing applies.",
598
+ );
599
+ }
600
+
571
601
  async function postJson(pathname, body, origin = API_ORIGIN) {
572
602
  const response = await fetch(`${String(origin || API_ORIGIN).replace(/\/+$/, "")}${pathname}`, {
573
603
  method: "POST",
@@ -618,7 +648,6 @@ async function enforceLauncherVersion() {
618
648
  async function runMandatoryUpgrade(metadata) {
619
649
  const latest = metadata?.latestVersion || "latest";
620
650
  const minimum = metadata?.minimumVersion || latest;
621
- const hostedTarballUrl = metadata?.hostedTarballUrl || HOSTED_TARBALL_URL;
622
651
  const changes = Array.isArray(metadata?.changes) ? metadata.changes.slice(0, 4) : [];
623
652
  console.error(`\n✨ Update now available · ${APP_DISPLAY_NAME} v${latest}`);
624
653
  console.error(`${APP_DISPLAY_NAME} v${latest} is required before your next coding session.`);
@@ -648,28 +677,21 @@ async function runMandatoryUpgrade(metadata) {
648
677
  }
649
678
 
650
679
  console.log(`\nDownloading and installing ${NPM_PACKAGE_NAME}@latest...`);
651
- const npmCommand = process.platform === "win32" ? "npm.cmd" : "npm";
652
- let exitCode = await spawnAndWait(npmCommand, ["install", "-g", `${NPM_PACKAGE_NAME}@latest`]);
653
- const installedAfterRegistry = exitCode === 0 ? installedLauncherVersion() : null;
654
- if (exitCode !== 0 || isOlderVersion(installedAfterRegistry, minimum)) {
655
- if (exitCode === 0) {
656
- console.log(`\nThe npm registry returned v${installedAfterRegistry || "unknown"}, below required v${minimum}.`);
657
- }
658
- console.log("\nThe npm registry install did not complete. Trying the hosted release automatically...");
659
- exitCode = await spawnAndWait(npmCommand, ["install", "-g", hostedTarballUrl]);
660
- }
661
- const installedVersion = exitCode === 0 ? installedLauncherVersion() : null;
662
- if (exitCode !== 0 || isOlderVersion(installedVersion, minimum)) {
663
- throw new Error(
664
- `Automatic upgrade failed with exit code ${exitCode} and installed version ${installedVersion || "unknown"}. Run ${UPDATE_COMMAND}. ` +
665
- `If npm is unavailable, use: npm install -g ${hostedTarballUrl}`,
666
- );
667
- }
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
+ });
668
691
  console.log(`\nUpgrade complete. Restarting ${APP_DISPLAY_NAME} now...`);
669
692
  const relaunchArgs = process.argv.slice(2);
670
- const relaunchCommand = process.platform === "win32" ? "codexclaude.cmd" : "codexclaude";
671
693
  try {
672
- const relaunchExitCode = await spawnAndWait(relaunchCommand, relaunchArgs);
694
+ const relaunchExitCode = await spawnAndWait(process.execPath, [installed.entrypoint, ...relaunchArgs]);
673
695
  if (relaunchExitCode !== 0) {
674
696
  throw new Error(`updated client exited with code ${relaunchExitCode}`);
675
697
  }
@@ -681,24 +703,6 @@ async function runMandatoryUpgrade(metadata) {
681
703
  }
682
704
  }
683
705
 
684
- function installedLauncherVersion() {
685
- const command = process.platform === "win32" ? "codexclaude.cmd" : "codexclaude";
686
- const result = spawnSync(command, ["--version"], {
687
- encoding: "utf8",
688
- env: {
689
- ...process.env,
690
- CODEXCLAUDE_DISABLE_UPDATE_CHECK: "1",
691
- CODEXCLAUDE_SKIP_COMMAND_ALIASES: "1",
692
- },
693
- shell: false,
694
- });
695
- if (result.error || result.status !== 0) {
696
- return null;
697
- }
698
- const match = String(result.stdout || "").match(/\bv(\d+(?:\.\d+){2})\b/i);
699
- return match?.[1] || null;
700
- }
701
-
702
706
  async function spawnAndWait(command, args) {
703
707
  return await new Promise((resolve, reject) => {
704
708
  const child = spawn(command, args, { stdio: "inherit", env: process.env, shell: false });
@@ -922,6 +926,8 @@ function isTruthy(value) {
922
926
 
923
927
  function writeCodexConfig(config) {
924
928
  mkdirSync(CODEX_DIR, { recursive: true });
929
+ const existing = existsSync(CODEX_CONFIG_FILE)
930
+ ? TOML.parse(readFileSync(CODEX_CONFIG_FILE, "utf8").replace(/^\uFEFF/, "")) : {};
925
931
  if (existsSync(CODEX_CONFIG_FILE)) {
926
932
  const backup = `${CODEX_CONFIG_FILE}.backup.${timestamp()}`;
927
933
  copyFileSync(CODEX_CONFIG_FILE, backup);
@@ -958,7 +964,16 @@ function writeCodexConfig(config) {
958
964
  'terminal_title = ["project-name", "model-with-reasoning", "run-state"]',
959
965
  "",
960
966
  ].join("\n");
961
- 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);
962
977
  }
963
978
 
964
979
  function clearUpstreamUpdateCache() {
@@ -974,7 +989,8 @@ function clearUpstreamUpdateCache() {
974
989
  function writeModelCatalog() {
975
990
  mkdirSync(CONFIG_DIR, { recursive: true });
976
991
  const catalog = {
977
- 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)),
978
994
  };
979
995
  writeFileSync(MODEL_CATALOG_FILE, `${JSON.stringify(catalog, null, 2)}\n`, { encoding: "utf8", mode: 0o600 });
980
996
  return MODEL_CATALOG_FILE;
@@ -991,8 +1007,9 @@ function modelCatalogEntry(model, index) {
991
1007
  supports_image_detail_original: true,
992
1008
  truncation_policy: { mode: "tokens", limit: 10000 },
993
1009
  supports_parallel_tool_calls: true,
994
- context_window: MODEL_CATALOG_CONTEXT_WINDOW,
995
- max_context_window: MODEL_CATALOG_CONTEXT_WINDOW,
1010
+ context_window: model.contextWindow || MODEL_CATALOG_CONTEXT_WINDOW,
1011
+ max_context_window: model.contextWindow || MODEL_CATALOG_CONTEXT_WINDOW,
1012
+ max_output_tokens: model.maxOutputTokens || null,
996
1013
  auto_compact_token_limit: null,
997
1014
  reasoning_summary_format: "experimental",
998
1015
  default_reasoning_summary: "none",
@@ -1000,9 +1017,9 @@ function modelCatalogEntry(model, index) {
1000
1017
  display_name: model.label,
1001
1018
  description: modelCatalogDescription(model.id),
1002
1019
  default_reasoning_level: reasoningEffortForModel(model.id),
1003
- supported_reasoning_levels: SUPPORTED_REASONING_LEVELS,
1020
+ supported_reasoning_levels: model.supportedReasoningLevels || SUPPORTED_REASONING_LEVELS,
1004
1021
  shell_type: "shell_command",
1005
- visibility: "list",
1022
+ visibility: model.legacy ? "hide" : "list",
1006
1023
  minimal_client_version: "0.124.0",
1007
1024
  supported_in_api: true,
1008
1025
  availability_nux: null,
@@ -1019,6 +1036,9 @@ function modelCatalogEntry(model, index) {
1019
1036
  }
1020
1037
 
1021
1038
  function modelCatalogDescription(modelId) {
1039
+ if (modelId === "gpt-6-astra") {
1040
+ return "ChatGPT-6-Astra: coding tools, configurable reasoning, and image input.";
1041
+ }
1022
1042
  if (modelId === "gpt-5.6") {
1023
1043
  return "GPT-5.6 coding route with local coding tools and wallet billing.";
1024
1044
  }
@@ -1047,7 +1067,7 @@ function modelCatalogDescription(modelId) {
1047
1067
  return "Sonnet 5 coding route with local coding tools and wallet billing.";
1048
1068
  }
1049
1069
  if (modelId === "claude-fable-5") {
1050
- 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.";
1051
1071
  }
1052
1072
  return "ChatGPT-5.5 coding route with local coding tools and wallet billing.";
1053
1073
  }
@@ -1080,17 +1100,28 @@ function logout() {
1080
1100
 
1081
1101
  async function runCodex(args) {
1082
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
+ }
1083
1113
  let wallet = null;
1084
1114
  try {
1085
1115
  wallet = await walletSnapshot(config);
1086
1116
  } catch {
1087
1117
  // Wallet lookup should never block the user from starting a local coding session.
1088
1118
  }
1089
- if (!isTruthy(process.env.CODEXCLAUDE_QUIET_STATUS)) {
1119
+ if (interactive) {
1090
1120
  console.log(formatStatusLine(config, wallet, process.cwd()));
1091
- 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`);
1092
1124
  }
1093
- process.stdout.write(`\x1b]0;${APP_DISPLAY_NAME} v${APP_VERSION}\x07`);
1094
1125
  const launch = resolveCodexLaunch();
1095
1126
  const env = {
1096
1127
  ...process.env,
@@ -1255,6 +1286,9 @@ function formatStatusLine(config, wallet, cwd) {
1255
1286
  }
1256
1287
 
1257
1288
  function modelStatusName(model) {
1289
+ if (model === "gpt-6-astra") {
1290
+ return "ChatGPT-6 Astra";
1291
+ }
1258
1292
  if (model === "gpt-5.6") {
1259
1293
  return "GPT-5.6";
1260
1294
  }
@@ -1320,7 +1354,7 @@ function normalizeConfig(config) {
1320
1354
  if (["claude-sonnet-4.6", "claude-sonnet-4-6", "sonnet-4.6", "sonnet-4-6", "sonnet-5"].includes(normalized.model)) {
1321
1355
  normalized.model = "claude-sonnet-5";
1322
1356
  }
1323
- 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)) {
1324
1358
  normalized.model = "claude-fable-5";
1325
1359
  }
1326
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,13 +4,72 @@ This file is the durable handoff for the `@codexapi/codexclaude` package in `/ro
4
4
 
5
5
  ## Current Release
6
6
 
7
+ ### 2026-09-10 Frontier Picker And Binary Splash: 2.0.39
8
+
9
+ This section supersedes older release snapshots below. Source/package version
10
+ is `2.0.39`, with exact `@openai/codex@0.154.0`. Release deployment state and npm
11
+ verification are recorded in `/root/codexapi/STATE.md`; never infer registry
12
+ publication or minimum enforcement from the source version alone.
13
+
14
+ The first three choices in the launcher selector and native catalog are
15
+ `ChatGPT-6-Astra` (`gpt-6-astra`), `Fable 5.1` (`claude-fable-5`) and `Opus 5`
16
+ (`claude-opus-5`). Remaining current choices retain their API ids. GPT-5.5 and
17
+ Opus 4.8 are compatibility-only: hidden from new selection, with full metadata
18
+ retained so upgrades do not change existing customer configurations or history.
19
+ Do not invent `claude-fable-5.1` upstream. A display alias normalizes to the stable
20
+ Fable id. Numeric choices are calculated from visible order to avoid selecting
21
+ the wrong model after reordering. `model`, `models`, `/model` and `/models` are
22
+ launcher aliases; the native interactive command remains `/model`.
23
+
24
+ Default model remains `gpt-5.6-sol`, Extra High. Full Access, four-site login,
25
+ customer wallet display, top-up, reasoning metadata, tool/image transport,
26
+ client headers and native-safe command aliases are preserved. Setup parses TOML,
27
+ backs up and atomically merges only owned fields. No runtime routing, billing,
28
+ provider credentials or regional native Codex installation is changed by this
29
+ launcher release.
30
+
31
+ `bin/splash.js` renders gold/cyan binary artwork and the exact line
32
+ `codexclaude, powered by https://magnetapi.org`, with app version 2.0.39.
33
+ It adapts to narrow terminals, has no animation delay and honors NO_COLOR,
34
+ TERM=dumb, CODEXCLAUDE_NO_SPLASH and CODEXCLAUDE_QUIET_STATUS. Launcher notices,
35
+ wallet text and terminal-title escapes are now excluded from exec/JSON, server
36
+ protocols, completions and pipes. Never pollute machine-readable stdout.
37
+
38
+ `bin/update.js` ports the tested claudecodex updater: exact registry target,
39
+ checksum-verified temporary local archive on fallback, bounded HTTPS downloads,
40
+ Windows npm-cli.js through Node, actual global package verification and direct
41
+ relaunch of that installed entrypoint. No npm --force/remote-policy override.
42
+ Only CodexAPI and our npm package may provide update metadata. Publish and verify
43
+ archives, SHA-256 sidecars, registry and dashboard scripts before raising the
44
+ minimum version. Preserve native binaries, local tools, providers and projects.
45
+
46
+ Run `npm test` for config preservation, frontier metadata, aliases, splash,
47
+ wallet/top-up and updater behavior. Then test the packed installation with actual
48
+ 0.154.0 exec JSON, native model picker and bounded live test-account calls.
49
+ All four websites, VIP setup and generated wizard checksums must match the final
50
+ archive; do not repack changed files after publishing its hash.
51
+
52
+ ### 2026-09-09 Configuration Safety Build
53
+
54
+ `2.0.38` is a hosted safety build for the MagnetAPI v2 Setup Wizard. npm `latest`
55
+ and the global mandatory minimum remain `2.0.37`; npm authentication must be
56
+ renewed before publishing `2.0.38` to the registry. Do not force a registry update
57
+ until that publication is verified. The wizard downloads the exact hosted archive,
58
+ verifies SHA-256, and installs the local file, avoiding npm remote-fetch restrictions.
59
+ The runtime remains Codex `0.153.4`; model, routing, authentication and billing
60
+ behavior are unchanged. `writeCodexConfig` now parses TOML with pinned
61
+ `@iarna/toml@2.2.5`, merges only managed root fields, the owned provider and owned
62
+ TUI fields, preserves other providers/MCP/projects, backs up the original, and
63
+ replaces the file atomically. Invalid existing TOML stops setup without changing it.
64
+ Run `npm test`, including the real-launcher configuration-preservation tests.
65
+
7
66
  - Package name: `@codexapi/codexclaude`
8
- - Launcher version: `2.0.36`
67
+ - Launcher version: `2.0.37`
9
68
  - Customer-facing app name: `MagnetAPI.org Codex CLI`
10
- - Bundled runtime dependency: `@openai/codex@0.152.0-alpha.6`
69
+ - Bundled runtime dependency: `@openai/codex@0.153.4`
11
70
  - Required install command: `npm install -g @codexapi/codexclaude@latest`
12
- - Hosted fallback: `npm install -g https://codexapi.pro/downloads/codexapi-codexclaude-2.0.36.tgz`
13
- - Backend minimum version: `2.0.36`
71
+ - Hosted fallback: `npm install -g https://codexapi.pro/downloads/codexapi-codexclaude-2.0.37.tgz`
72
+ - Backend minimum version: `2.0.37`
14
73
  - Default visible model: `GPT-5.6 SOL`
15
74
  - Default model slug: `gpt-5.6-sol`
16
75
  - Default visible reasoning effort: `Extra High`, stored as `xhigh`; premium SOL Max and SOL Ultra retain their explicit `max` and `ultra` labels.
@@ -25,13 +84,13 @@ This file is the durable handoff for the `@codexapi/codexclaude` package in `/ro
25
84
  The launcher must present itself as:
26
85
 
27
86
  ```text
28
- MagnetAPI.org Codex CLI v2.0.36
87
+ MagnetAPI.org Codex CLI v2.0.37
29
88
  ```
30
89
 
31
90
  Normal pre-launch status should look like:
32
91
 
33
92
  ```text
34
- MagnetAPI.org Codex CLI v2.0.36 · GPT-5.6 SOL Extra High · ~/project · Wallet balance $20.00
93
+ MagnetAPI.org Codex CLI v2.0.37 · GPT-5.6 SOL Extra High · ~/project · Wallet balance $20.00
35
94
  Fable 5 is now available in codexclaude, exclusively.
36
95
  ```
37
96
 
@@ -50,6 +109,9 @@ The launcher command `codexclaude model` must list these customer-visible models
50
109
  - `claude-sonnet-5` displayed as `Sonnet 5`
51
110
  - `claude-fable-5` displayed as `Fable 5`
52
111
  - `gpt-5.6-terra` displayed as `GPT-5.6 Terra`
112
+ - `gpt-6-astra` displayed as `ChatGPT-6 Astra`
113
+
114
+ ChatGPT-6 Astra supports `low`, `medium`, `high`, `xhigh`, and `max` reasoning in the managed catalog, with a 1,050,000-token context window and 128,000 maximum output tokens. The launcher displays a billing notice when Astra is selected. Native signed-in Astra usage is charged at 2x the normal Codex rate; when the dedicated Astra route follows Global Cross-Model Routing, normal billing for that route applies.
53
115
 
54
116
  GPT-5.6 SOL must be the default when a new config is created or a legacy config contains an unsupported model value. It is displayed as Extra High while the generated runtime config stores `xhigh`. A valid existing customer selection must remain unchanged across upgrades. The selected model slug must stay in `~/.codex/config.toml`; do not rewrite customer-facing model selections locally. `gpt-5.6-sol-max` and `gpt-5.6-sol-ultra` remain distinct for customer choice, usage display, and wallet billing.
55
117
 
@@ -60,7 +122,7 @@ GPT-5.6 SOL must be the default when a new config is created or a legacy config
60
122
  - `https://codexapi.pro/api/v1/codexclaude/version`
61
123
  - `https://registry.npmjs.org/@codexapi%2Fcodexclaude/latest`
62
124
 
63
- It must not check `@openai/codex` npm metadata, OpenAI GitHub releases, or any upstream OpenAI update endpoint. Version `2.0.36` is both the enforced minimum and current latest release. Compatible older launchers offer an interactive `Upgrade Now` action; pressing Enter invokes `npm install -g @codexapi/codexclaude@latest` without shell interpolation. If that registry install fails, the launcher automatically tries the hosted tarball. Non-interactive terminals print the exact update command and exit instead of waiting for input. After a successful interactive update, the launcher starts the requested action again automatically.
125
+ It must not check `@openai/codex` npm metadata, OpenAI GitHub releases, or any upstream OpenAI update endpoint. Version `2.0.37` is both the enforced minimum and current latest release. Compatible older launchers offer an interactive `Upgrade Now` action; pressing Enter invokes `npm install -g @codexapi/codexclaude@latest` without shell interpolation. If that registry install fails, the launcher automatically tries the hosted tarball. Non-interactive terminals print the exact update command and exit instead of waiting for input. After a successful interactive update, the launcher starts the requested action again automatically.
64
126
 
65
127
  The npm package manifest owns only `codexclaude`. This is deliberate: npm creates manifest binaries before postinstall, so claiming `codex` there causes an `EEXIST` upgrade failure on machines with native Codex CLI. During a global installation, postinstall may create optional `codex` and `claude` aliases only when neither the target npm prefix nor any directory on `PATH` already contains the command. Existing native clients are preserved and no install path requires `--force`.
66
128
 
@@ -96,7 +158,7 @@ base_url = "<service Responses URL>"
96
158
  experimental_bearer_token = "<customer System Token/API username>"
97
159
  wire_api = "responses"
98
160
  supports_websockets = false
99
- http_headers = { "X-CodexAPI-Client" = "codexclaude", "X-CodexAPI-Client-Version" = "2.0.36", "X-CodexAPI-Selected-Model" = "<selected model>" }
161
+ http_headers = { "X-CodexAPI-Client" = "codexclaude", "X-CodexAPI-Client-Version" = "2.0.37", "X-CodexAPI-Selected-Model" = "<selected model>" }
100
162
 
101
163
  [tui]
102
164
  status_line = ["model-with-reasoning", "fast-mode", "current-dir"]
@@ -104,7 +166,7 @@ status_line_use_colors = true
104
166
  terminal_title = ["project-name", "model-with-reasoning", "run-state"]
105
167
  ```
106
168
 
107
- The generated model catalog must contain full metadata for every supported slug, including `gpt-daybreak-blue-latest`, `gpt-5.6`, `gpt-5.6-terra`, `gpt-5.6-sol-max`, and `gpt-5.6-sol-ultra`. This prevents fallback metadata warnings such as:
169
+ The generated model catalog must contain full metadata for every supported slug, including `gpt-6-astra`, `gpt-daybreak-blue-latest`, `gpt-5.6`, `gpt-5.6-terra`, `gpt-5.6-sol-max`, and `gpt-5.6-sol-ultra`. This prevents fallback metadata warnings such as:
108
170
 
109
171
  ```text
110
172
  Model metadata for `claude-opus-4-8` not found. Defaulting to fallback metadata; this can degrade performance and cause issues.
@@ -183,7 +245,7 @@ tmp_home="$(mktemp -d)"
183
245
  mkdir -p "$tmp_home/.codexclaude"
184
246
  cat > "$tmp_home/.codexclaude/config.json" <<'JSON'
185
247
  {
186
- "version": "2.0.36",
248
+ "version": "2.0.37",
187
249
  "serviceId": "codexapi",
188
250
  "serviceLabel": "CodexAPI.pro",
189
251
  "apiOrigin": "https://codexapi.pro",
@@ -222,10 +284,18 @@ After packaging, copy the tarball to the public downloads directories, restart t
222
284
 
223
285
  ```bash
224
286
  curl -fsS "https://codexapi.pro/api/v1/codexclaude/version?current=2.0.17"
225
- curl -fsSI "https://codexapi.pro/downloads/codexapi-codexclaude-2.0.36.tgz"
287
+ curl -fsSI "https://codexapi.pro/downloads/codexapi-codexclaude-2.0.37.tgz"
226
288
  ```
227
289
 
228
- The version endpoint must report `latestVersion = 2.0.36`, `minimumVersion = 2.0.36`, and `updateRequired = true` for every older client.
290
+ The version endpoint must report `latestVersion = 2.0.37`, `minimumVersion = 2.0.37`, and `updateRequired = true` for every older client.
291
+
292
+ ## Version 2.0.37 Changes
293
+
294
+ - Updated the bundled coding runtime to exact `@openai/codex@0.153.4`.
295
+ - Added ChatGPT-6 Astra with complete context, output, image, search, tool, and reasoning metadata.
296
+ - Added a model-selection warning for Astra's native signed-in 2x usage rate while keeping cross-model billing route-aware.
297
+ - Preserved all MagnetAPI account login, wallet, top-up, Full Access, safe-alias, update, and model-selection customizations.
298
+ - Made `2.0.37` mandatory after npm and the hosted fallback package become available.
229
299
 
230
300
  ## Version 2.0.36 Changes
231
301
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@codexapi/codexclaude",
3
- "version": "2.0.36",
4
- "description": "MagnetAPI.org Codex CLI for ChatGPT Daybreak Blue, GPT-5.6, Opus 5, Sonnet 5, and Fable 5.",
3
+ "version": "2.0.39",
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.152.0-alpha.6"
25
+ "@iarna/toml": "2.2.5",
26
+ "@openai/codex": "0.154.0"
26
27
  },
27
28
  "publishConfig": {
28
29
  "access": "public"