superlean-codex 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 SuperleanAI
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,139 @@
1
+ # Superlean Codex
2
+
3
+ Run Codex through SuperleanAI while keeping your existing ChatGPT login.
4
+
5
+ ## Install
6
+
7
+ Install the launcher globally, then run it from any project:
8
+
9
+ ```sh
10
+ npm install -g superlean-codex
11
+ superlean-codex
12
+ ```
13
+
14
+ Or install and launch it in one command:
15
+
16
+ ```sh
17
+ npx --yes superlean-codex@latest
18
+ ```
19
+
20
+ Requires Node.js 22+ with npm on macOS, Linux, or WSL.
21
+
22
+ ## First run
23
+
24
+ The launcher:
25
+
26
+ 1. Checks that Codex is already signed in with ChatGPT.
27
+ 2. Opens the Superlean dashboard in your browser.
28
+ 3. Asks you to approve **Connect Codex** using the code shown in the terminal.
29
+ 4. Saves your Superlean identity key and starts Codex in the current project.
30
+
31
+ If the first check fails, run `codex login` directly and choose **Sign in with
32
+ ChatGPT**, then run `superlean-codex` again. Superlean does not read, copy, or
33
+ store the ChatGPT tokens in Codex's credential store.
34
+
35
+ After setup, use the launcher just like Codex:
36
+
37
+ ```sh
38
+ superlean-codex
39
+ superlean-codex --resume
40
+ superlean-codex exec "Explain this repository"
41
+ ```
42
+
43
+ Reconnect to Superlean after a key expires or is revoked:
44
+
45
+ ```sh
46
+ superlean-codex login
47
+ ```
48
+
49
+ Reconnects reuse the existing usable Codex key entry. A new entry is created
50
+ only when none can be reused.
51
+
52
+ ## What it installs
53
+
54
+ The first run writes the shell launcher to `~/.superlean/bin`, adds that
55
+ directory to Bash or Zsh startup files, and stores the Superlean identity key
56
+ in `~/.superlean/codex.env.sh` with owner-only permissions. Personal overrides
57
+ in `~/.superlean/codex-user.env.sh` are preserved across reinstalls.
58
+
59
+ An existing Codex installation is used when available. Otherwise, npm runs the
60
+ official `@openai/codex` package from its cache. The launcher preserves your
61
+ current directory and forwards all Codex arguments.
62
+
63
+ The launcher applies these settings only to the Codex process it starts; it
64
+ does not rewrite your normal Codex configuration:
65
+
66
+ ```toml
67
+ model_provider = "superlean"
68
+
69
+ [model_providers.superlean]
70
+ name = "SuperleanAI"
71
+ base_url = "https://api.superleanai.com/codex/v1"
72
+ wire_api = "responses"
73
+ requires_openai_auth = true
74
+ env_http_headers = { "X-SuperleanAI-Key" = "SUPERLEANAI_CODEX_KEY" }
75
+ ```
76
+
77
+ `requires_openai_auth` keeps your ChatGPT authentication in the Authorization
78
+ header. The separate `X-SuperleanAI-Key` header identifies your Superlean
79
+ account and is removed by the gateway before the request is forwarded.
80
+
81
+ ## Other install options
82
+
83
+ Set up the launcher without starting Codex:
84
+
85
+ ```sh
86
+ npx --yes superlean-codex@latest install
87
+ ```
88
+
89
+ Use `--no-profile` to leave shell startup files unchanged. If the installer
90
+ does not edit a profile, it prints the PATH command to add manually.
91
+
92
+ Pass Codex arguments through the npm entry point after `--`:
93
+
94
+ ```sh
95
+ npx --yes superlean-codex@latest -- --help
96
+ ```
97
+
98
+ ## Development
99
+
100
+ ```sh
101
+ npm test
102
+ npm run check
103
+ npm pack --dry-run
104
+ node bin/superlean.js --help
105
+ ```
106
+
107
+ The browser login protocol is documented in
108
+ [docs/service-login.md](docs/service-login.md).
109
+
110
+ ## Releasing
111
+
112
+ CI (`.github/workflows/ci.yml`) runs tests and static checks on Node 22 and 24
113
+ for every push to `main` and every pull request. CD
114
+ (`.github/workflows/publish.yml`) publishes to npm when a GitHub Release is
115
+ published, using [npm trusted publishing](https://docs.npmjs.com/trusted-publishers)
116
+ (OIDC) with provenance. No npm token is stored in GitHub.
117
+
118
+ One-time setup:
119
+
120
+ 1. If the package does not exist on npm yet, publish the first version manually
121
+ by running `npm publish` from a local checkout.
122
+ 2. On npmjs.com, open the package page → **Settings** → **Trusted Publisher**
123
+ and add a GitHub Actions publisher with organization `superleanai`,
124
+ repository `superlean-codex`, and workflow filename `publish.yml`.
125
+
126
+ To cut a release:
127
+
128
+ ```sh
129
+ npm version patch # or minor / major
130
+ git push --follow-tags
131
+ gh release create v0.1.1 --generate-notes
132
+ ```
133
+
134
+ The release tag must match the version in `package.json`, and `prepublishOnly`
135
+ runs the test suite and static checks before the package is uploaded.
136
+
137
+ ## License
138
+
139
+ MIT © SuperleanAI. See [LICENSE](LICENSE).
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env node
2
+ import { spawn } from 'node:child_process';
3
+ import { homedir } from 'node:os';
4
+ import { install, shellQuote } from '../lib/install.js';
5
+
6
+ const help = `Usage:
7
+ npx superlean-codex@latest Set up and launch Codex
8
+ npx superlean-codex@latest install Set up without launching
9
+ npx superlean-codex@latest login Sign in to Superlean again
10
+ npx superlean-codex@latest --no-profile Leave shell profiles unchanged
11
+ npx superlean-codex@latest -- [args] Pass arguments to Codex
12
+
13
+ After setup, use superlean-codex in a new terminal.
14
+ Requires macOS, Linux, or WSL and Node.js 22+ with npm.`;
15
+
16
+ async function main() {
17
+ const args = process.argv.slice(2);
18
+ let launch = true;
19
+ let profile = true;
20
+ while (args.length) {
21
+ const arg = args[0];
22
+ if (arg === '--') { args.shift(); break; }
23
+ if (arg === '--help' || arg === '-h') { console.log(help); return; }
24
+ if (arg === '--no-profile') { profile = false; args.shift(); continue; }
25
+ if (arg === 'install') { launch = false; args.shift(); continue; }
26
+ break;
27
+ }
28
+ if (process.platform === 'win32') throw new Error('Use WSL for the Superlean shell launcher.');
29
+ const result = await install({ home: homedir(), shell: process.env.SHELL, profile });
30
+ console.log(`Superlean installed. Environment: ${result.envPath}`);
31
+ if (result.profiles.length) console.log('Next time, open a new terminal and run superlean-codex.');
32
+ else console.log(`Add the launcher to this shell: export PATH=${shellQuote(result.binDir)}:"$PATH"`);
33
+ if (!launch) return;
34
+ console.log('Checking your Codex login before applying Superlean settings.');
35
+ const child = spawn(result.launcherPath, args, { stdio: 'inherit' });
36
+ const onInterrupt = () => {};
37
+ const onTerminate = () => child.kill('SIGTERM');
38
+ process.on('SIGINT', onInterrupt);
39
+ process.on('SIGTERM', onTerminate);
40
+ const { code, signal } = await new Promise((resolve, reject) => {
41
+ child.once('error', reject);
42
+ child.once('exit', (code, signal) => resolve({ code, signal }));
43
+ });
44
+ process.removeListener('SIGINT', onInterrupt);
45
+ process.removeListener('SIGTERM', onTerminate);
46
+ process.exitCode = code ?? (signal === 'SIGINT' ? 130 : 143);
47
+ }
48
+ main().catch((error) => { console.error(`Superlean: ${error.message}`); process.exitCode = 1; });
@@ -0,0 +1,21 @@
1
+ # Browser-approved CLI login
2
+
3
+ The launcher creates a random verifier and sends its SHA-256 challenge to
4
+ `POST /api/cli/login` with `provider: "codex"`. The dashboard returns a
5
+ short-lived flow ID and browser approval URL. After the signed-in user approves
6
+ the matching code, only the terminal holding the verifier can redeem the flow.
7
+
8
+ The redemption response contains a Codex-scoped Superlean JWT. The launcher
9
+ writes it to `~/.superlean/codex.env.sh` and exposes it to Codex as
10
+ `SUPERLEANAI_CODEX_KEY`. Codex reads that value through `env_http_headers` and
11
+ sends it as `X-SuperleanAI-Key`; its own ChatGPT bearer token remains in the
12
+ Authorization header.
13
+
14
+ The gateway accepts this combination only on the `/codex` profile, verifies
15
+ that the Superlean key has a live `proxy:codex` database row, strips the
16
+ identity header, and forwards the user's ChatGPT credential. Reconnecting
17
+ reuses the most recent usable Codex key entry.
18
+
19
+ The flow stores neither the verifier nor either bearer credential. Flow IDs
20
+ are hashed in the database, approvals expire after ten minutes, and successful
21
+ redemption deletes the flow.
@@ -0,0 +1,9 @@
1
+ let input = '';
2
+ process.stdin.setEncoding('utf8');
3
+ process.stdin.on('data', (chunk) => {
4
+ input += chunk;
5
+ if (input.length > 64 * 1024) process.exit(1);
6
+ });
7
+ process.stdin.on('end', () => {
8
+ process.exit(/^Logged in using ChatGPT\s*$/m.test(input) ? 0 : 1);
9
+ });
@@ -0,0 +1,25 @@
1
+ import { chmod, mkdir, writeFile } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { shellQuote } from './shell-quote.js';
4
+
5
+ function safe(value, name, { nonempty = true } = {}) {
6
+ if (typeof value !== 'string' || (nonempty && !value) || /[\r\n\0]/.test(value)) {
7
+ throw new Error(`${name} must be ${nonempty ? 'a nonempty string' : 'a string'} without newlines.`);
8
+ }
9
+ return value;
10
+ }
11
+
12
+ export async function configureCodex({ directory, baseUrl = 'https://api.superleanai.com/codex/v1', userKey }) {
13
+ safe(directory, 'directory');
14
+ safe(userKey, 'user key');
15
+ const parsed = new URL(safe(baseUrl, 'base URL'));
16
+ if (!['https:', 'http:'].includes(parsed.protocol) || parsed.username || parsed.password) {
17
+ throw new Error('base URL must be an HTTP(S) URL without embedded credentials.');
18
+ }
19
+ await mkdir(directory, { recursive: true, mode: 0o700 });
20
+ const path = join(directory, 'codex.env.sh');
21
+ const contents = `# Generated by Superlean. Reconnect with: superlean-codex login\nunset OPENAI_API_KEY\nexport SUPERLEANAI_CODEX_KEY=${shellQuote(userKey)}\n`;
22
+ await writeFile(path, contents, { mode: 0o600 });
23
+ await chmod(path, 0o600);
24
+ return path;
25
+ }
package/lib/install.js ADDED
@@ -0,0 +1,68 @@
1
+ import { chmod, mkdir, readFile, writeFile } from 'node:fs/promises';
2
+ import { basename, join } from 'node:path';
3
+ import { shellQuote } from './shell-quote.js';
4
+ export { shellQuote } from './shell-quote.js';
5
+
6
+ const marker = '# >>> Superlean >>>';
7
+ const endMarker = '# <<< Superlean <<<';
8
+
9
+ async function readOptional(path) {
10
+ try { return await readFile(path, 'utf8'); }
11
+ catch (error) { if (error.code === 'ENOENT') return null; throw error; }
12
+ }
13
+
14
+ async function updateProfile(path, binDir) {
15
+ const current = (await readOptional(path)) ?? '';
16
+ const block = `${marker}\ncase ":$PATH:" in\n *:${shellQuote(binDir)}:*) ;;\n *) export PATH=${shellQuote(binDir)}:"$PATH" ;;\nesac\n${endMarker}`;
17
+ const start = current.indexOf(marker);
18
+ let updated;
19
+ if (start === -1) updated = `${current}${current.endsWith('\n') || !current ? '' : '\n'}\n${block}\n`;
20
+ else {
21
+ const end = current.indexOf(endMarker, start);
22
+ if (end === -1) throw new Error(`Incomplete Superlean block in ${path}; repair it and retry.`);
23
+ updated = current.slice(0, start) + block + current.slice(end + endMarker.length);
24
+ }
25
+ if (updated !== current) await writeFile(path, updated, { mode: 0o600 });
26
+ }
27
+
28
+ export async function install({ home, shell = '', profile = true }) {
29
+ if (!home || /[\r\n]/.test(home)) throw new Error('A home directory without newlines is required.');
30
+ const directory = join(home, '.superlean');
31
+ const binDir = join(directory, 'bin');
32
+ const libDir = join(directory, 'lib-codex');
33
+ await mkdir(binDir, { recursive: true, mode: 0o700 });
34
+ await chmod(directory, 0o700);
35
+ await mkdir(libDir, { recursive: true, mode: 0o700 });
36
+ await writeFile(join(libDir, 'package.json'), '{"type":"module"}\n', { mode: 0o600 });
37
+ for (const name of ['service-login.js', 'configure-codex.js', 'shell-quote.js', 'check-codex-auth.js']) {
38
+ await writeFile(join(libDir, name), await readFile(new URL(name, import.meta.url), 'utf8'), { mode: 0o600 });
39
+ }
40
+ await writeFile(join(directory, 'codex-defaults.sh'), await readFile(new URL('../shell/env.sh', import.meta.url), 'utf8'), { mode: 0o600 });
41
+ const envPath = join(directory, 'codex-user.env.sh');
42
+ try { await writeFile(envPath, '# Personal Codex overrides. Preserved when Superlean is reinstalled.\n', { flag: 'wx', mode: 0o600 }); }
43
+ catch (error) { if (error.code !== 'EEXIST') throw error; }
44
+ const launcherPath = join(binDir, 'superlean-codex');
45
+ const script = (await readFile(new URL('../shell/launcher.sh', import.meta.url), 'utf8'))
46
+ .replaceAll('__SUPERLEAN_DIR__', shellQuote(directory))
47
+ .replaceAll('__SUPERLEAN_LIB_DIR__', shellQuote(libDir));
48
+ await writeFile(launcherPath, script, { mode: 0o700 });
49
+ await chmod(launcherPath, 0o700);
50
+ const profiles = [];
51
+ if (profile) {
52
+ switch (basename(shell)) {
53
+ case 'zsh': profiles.push(join(home, '.zshrc')); break;
54
+ case 'bash': {
55
+ profiles.push(join(home, '.bashrc'));
56
+ const loginFiles = ['.bash_profile', '.bash_login', '.profile'];
57
+ let loginFile = '.profile';
58
+ for (const file of loginFiles) if (await readOptional(join(home, file)) !== null) { loginFile = file; break; }
59
+ profiles.push(join(home, loginFile));
60
+ break;
61
+ }
62
+ case 'sh': case 'dash': case '': profiles.push(join(home, '.profile')); break;
63
+ default: break;
64
+ }
65
+ for (const path of profiles) await updateProfile(path, binDir);
66
+ }
67
+ return { directory, binDir, envPath, launcherPath, profiles };
68
+ }
@@ -0,0 +1,85 @@
1
+ import { randomBytes, createHash } from 'node:crypto';
2
+ import { spawn } from 'node:child_process';
3
+ import { setTimeout as delay } from 'node:timers/promises';
4
+ import { pathToFileURL } from 'node:url';
5
+ import { configureCodex } from './configure-codex.js';
6
+
7
+ export async function openBrowser(url) {
8
+ const command = process.platform === 'darwin' ? 'open' : 'xdg-open';
9
+ await new Promise((resolve, reject) => {
10
+ const child = spawn(command, [url], { stdio: 'ignore', detached: true });
11
+ child.unref();
12
+ child.once('error', reject);
13
+ child.once('exit', (code) => code === 0 ? resolve() : reject(new Error('Browser could not open.')));
14
+ });
15
+ }
16
+
17
+ export async function serviceLogin({
18
+ dashboard = 'https://dashboard.superleanai.com', fetchFn = fetch,
19
+ openUrl = openBrowser, wait = delay, output = console.log, signal,
20
+ } = {}) {
21
+ const origin = new URL(dashboard);
22
+ if (origin.protocol !== 'https:' && !(origin.protocol === 'http:' && ['localhost', '127.0.0.1', '[::1]'].includes(origin.hostname))) {
23
+ throw new Error('Superlean sign-in requires HTTPS, except on localhost.');
24
+ }
25
+ async function post(path, body) {
26
+ const response = await fetchFn(new URL(path, origin), {
27
+ method: 'POST', headers: { 'Content-Type': 'application/json' },
28
+ body: JSON.stringify(body), redirect: 'error',
29
+ signal: signal ? AbortSignal.any([signal, AbortSignal.timeout(15000)]) : AbortSignal.timeout(15000),
30
+ });
31
+ let data;
32
+ try { data = await response.json(); } catch {
33
+ throw new Error('Superlean CLI sign-in is unavailable. The dashboard must include the CLI login endpoints.');
34
+ }
35
+ return { response, data };
36
+ }
37
+ const verifier = randomBytes(32).toString('base64url');
38
+ const challenge = createHash('sha256').update(verifier).digest('base64url');
39
+ const { response: started, data: flow } = await post('/api/cli/login', { code_challenge: challenge, provider: 'codex' });
40
+ if (started.status !== 201 || !/^[A-Za-z0-9_-]{43}$/.test(flow.flow_id ?? '')
41
+ || typeof flow.verification_uri !== 'string' || !Number.isFinite(flow.expires_in)
42
+ || flow.expires_in <= 0 || flow.expires_in > 600 || !Number.isFinite(flow.interval)
43
+ || flow.interval < 1 || flow.interval > 30) {
44
+ throw new Error('Superlean could not start CLI sign-in. Check that the updated dashboard is deployed.');
45
+ }
46
+ const verificationUrl = new URL(flow.verification_uri, origin);
47
+ if (verificationUrl.origin !== origin.origin || verificationUrl.pathname !== '/cli/authorize'
48
+ || verificationUrl.searchParams.get('flow') !== flow.flow_id) {
49
+ throw new Error('The dashboard returned an invalid sign-in URL.');
50
+ }
51
+ const userCode = `${flow.flow_id.slice(0, 4)}-${flow.flow_id.slice(4, 8)}`;
52
+ const displayCode = process.stdout.isTTY ? `\u001b[1m${userCode}\u001b[22m` : userCode;
53
+ output(`\n\nSign in to Superlean and approve code ${displayCode}:\n\n`);
54
+ output(verificationUrl.href);
55
+ const deadline = Date.now() + flow.expires_in * 1000;
56
+ // A missing browser (for example over SSH) leaves the printed URL usable.
57
+ Promise.resolve().then(() => openUrl(verificationUrl.href)).catch(() => {});
58
+ while (Date.now() < deadline) {
59
+ await wait(Math.min(flow.interval * 1000, Math.max(0, deadline - Date.now())), undefined, { signal });
60
+ if (Date.now() >= deadline) break;
61
+ const { response, data } = await post('/api/cli/login/token', { flow_id: flow.flow_id, code_verifier: verifier });
62
+ if (response.status === 202 && data.error === 'authorization_pending') continue;
63
+ if (response.status === 200 && typeof data.user_key === 'string' && data.user_key.trim()) {
64
+ return data.user_key;
65
+ }
66
+ if (response.status === 402) throw new Error('An active Superlean subscription is required. Visit https://dashboard.superleanai.com/subscription, then retry.');
67
+ if (response.status === 410) throw new Error('Superlean sign-in expired or was already used. Run the launcher again.');
68
+ throw new Error('Superlean could not complete sign-in. Run the launcher again.');
69
+ }
70
+ throw new Error('Superlean sign-in expired. Run the launcher again.');
71
+ }
72
+
73
+ if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
74
+ const controller = new AbortController();
75
+ process.once('SIGINT', () => controller.abort());
76
+ process.once('SIGTERM', () => controller.abort());
77
+ try {
78
+ const userKey = await serviceLogin({ signal: controller.signal });
79
+ await configureCodex({ directory: process.argv[2], userKey });
80
+ console.log('Superlean connected. Starting Codex.');
81
+ } catch (error) {
82
+ console.error(controller.signal.aborted ? 'Superlean sign-in cancelled.' : `Superlean: ${error.message}`);
83
+ process.exitCode = controller.signal.aborted ? 130 : 1;
84
+ }
85
+ }
@@ -0,0 +1,3 @@
1
+ export function shellQuote(value) {
2
+ return `'${value.replaceAll("'", "'\\''")}'`;
3
+ }
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "superlean-codex",
3
+ "version": "0.1.0",
4
+ "description": "One-command setup and shell launcher for Codex through SuperleanAI.",
5
+ "license": "MIT",
6
+ "author": "SuperleanAI",
7
+ "homepage": "https://github.com/superleanai/superlean-codex#readme",
8
+ "bugs": {
9
+ "url": "https://github.com/superleanai/superlean-codex/issues"
10
+ },
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "git+https://github.com/superleanai/superlean-codex.git"
14
+ },
15
+ "keywords": [
16
+ "codex",
17
+ "openai",
18
+ "superleanai",
19
+ "cli"
20
+ ],
21
+ "type": "module",
22
+ "bin": {
23
+ "superlean-codex": "bin/superlean.js"
24
+ },
25
+ "files": [
26
+ "bin/",
27
+ "lib/",
28
+ "shell/",
29
+ "docs/",
30
+ "README.md",
31
+ "LICENSE"
32
+ ],
33
+ "engines": {
34
+ "node": ">=22"
35
+ },
36
+ "scripts": {
37
+ "test": "node --test",
38
+ "check": "node --check bin/superlean.js && node --check lib/install.js && node --check lib/configure-codex.js && node --check lib/service-login.js && node --check lib/check-codex-auth.js && sh -n shell/launcher.sh && sh -n shell/env.sh",
39
+ "prepublishOnly": "npm test && npm run check"
40
+ },
41
+ "publishConfig": {
42
+ "access": "public"
43
+ }
44
+ }
package/shell/env.sh ADDED
@@ -0,0 +1,3 @@
1
+ # Superlean defaults. Add environment variables shipped to all users here.
2
+ # This file is sourced only by the Superlean Codex launcher.
3
+ # Keep user credentials out of this package.
@@ -0,0 +1,54 @@
1
+ #!/bin/sh
2
+ set -eu
3
+
4
+ superlean_dir=__SUPERLEAN_DIR__
5
+ superlean_lib_dir=__SUPERLEAN_LIB_DIR__
6
+
7
+ if ! command -v node >/dev/null 2>&1; then
8
+ echo 'Superlean: install Node.js 22+ (with npm) and try again.' >&2
9
+ exit 127
10
+ fi
11
+ if (
12
+ unset OPENAI_API_KEY SUPERLEANAI_CODEX_KEY
13
+ if command -v codex >/dev/null 2>&1; then
14
+ superlean_auth=$(codex login status 2>&1) || exit "$?"
15
+ elif command -v npx >/dev/null 2>&1; then
16
+ superlean_auth=$(npx --yes --package=@openai/codex@latest -- codex login status 2>&1) || exit "$?"
17
+ else
18
+ echo 'Superlean: install Node.js 22+ (with npm), or install Codex and try again.' >&2
19
+ exit 127
20
+ fi
21
+ printf '%s' "$superlean_auth" | node "$superlean_lib_dir/check-codex-auth.js"
22
+ ); then
23
+ :
24
+ else
25
+ superlean_status=$?
26
+ echo 'Superlean: could not confirm your ChatGPT login. Run codex login without Superlean settings, then retry.' >&2
27
+ exit "$superlean_status"
28
+ fi
29
+
30
+ . "$superlean_dir/codex-defaults.sh"
31
+ . "$superlean_dir/codex-user.env.sh"
32
+
33
+ superlean_login=false
34
+ if [ "${1-}" = login ]; then superlean_login=true; shift; fi
35
+ if [ "$superlean_login" = true ] || [ ! -f "$superlean_dir/codex.env.sh" ]; then
36
+ node "$superlean_lib_dir/service-login.js" "$superlean_dir"
37
+ fi
38
+ . "$superlean_dir/codex.env.sh"
39
+
40
+ set -- \
41
+ -c 'model_provider="superlean"' \
42
+ -c 'model_providers.superlean.name="SuperleanAI"' \
43
+ -c 'model_providers.superlean.base_url="https://api.superleanai.com/codex/v1"' \
44
+ -c 'model_providers.superlean.wire_api="responses"' \
45
+ -c 'model_providers.superlean.requires_openai_auth=true' \
46
+ -c 'model_providers.superlean.env_http_headers={"X-SuperleanAI-Key"="SUPERLEANAI_CODEX_KEY"}' \
47
+ "$@"
48
+
49
+ if command -v codex >/dev/null 2>&1; then exec codex "$@"; fi
50
+ if ! command -v npx >/dev/null 2>&1; then
51
+ echo 'Superlean: install Node.js 22+ (with npm), or install Codex and try again.' >&2
52
+ exit 127
53
+ fi
54
+ exec npx --yes --package=@openai/codex@latest -- codex "$@"