echo "This page is instructions for an AI coding agent to read, not a script to run. Nothing was run."; exit 1
<!-- Line 1 stops any shell this page is piped into, and the '<' that starts this line stops PowerShell. -->
# Runbook: SolCard Inference in Codex, next to the user's ChatGPT models

Read this with your shell (`curl -fsSL https://api.solcardinference.ai/install/codex`): the commands below must be exact. It's about 28 KB: if your tool shows only part of it, read the rest before you start. On native Windows, follow `https://api.solcardinference.ai/install/codex/windows` instead.

You're Codex, in the Codex CLI or in the ChatGPT app, and the user asked you to add SolCard Inference to their models. When you're done, Codex's model picker lists the user's own ChatGPT models exactly as before, on their own ChatGPT plan and their own login, and SolCard Inference's models below them.

A small local proxy does the routing. It's [CLIProxyAPI](https://github.com/router-for-me/CLIProxyAPI) (open source), in SolCard's build of version 8.0.46-solcard.2, which adds one thing: it passes the account's own model list through unchanged.
- **Where it runs:** on `127.0.0.1` only, as a login service, behind a key only this machine has.
- **ChatGPT requests:** they go on to ChatGPT unchanged, with the login Codex already has. The proxy never holds, copies or refreshes that login.
- **SolCard requests:** they go to SolCard with the user's SolCard key.
- **What the user does:** approves SolCard Inference once, in the browser.

How to follow this runbook:
- Do the steps in order. Each one says what to run and what you should see. If you see something else, follow that step's **If not** line. If none fits, tell the user what you saw, in one or two sentences, and stop.
- Run each command exactly as written, in your shell tool. Replace only the parts in `<angle brackets>`.
- From step 2 on, the commands use the network and write outside this project. Run each of them with escalated permissions (outside the sandbox), with a short reason. The user approves each one.
- Never print a key or token, and never open `~/.codex/auth.json`. The commands here keep keys in files.
- Write files only with the shell commands here, and change only what they name. Everything SolCard adds lives in `~/.solcard-inference/codex` and in two settings in `~/.codex/config.toml`, which Codex itself writes in step 8.
- If a step fails halfway, run it again from its first command. Every step is safe to repeat.

## Step 0. Check where you're running

```sh
CODEX="$(readlink -f "$(command -v apply_patch)" 2>/dev/null)"; case "$CODEX" in */codex) ;; *) CODEX="$(command -v codex)";; esac; CH="${CODEX_HOME:-$HOME/.codex}"; case "$CODEX" in *[Rr]esources/codex-cli/*) S=app;; *) S=cli;; esac; echo "os=$(uname -s) arch=$(uname -m) codex=${CODEX:-none} surface=$S"; [ -n "$CODEX" ] && "$CODEX" login status 2>&1 | tail -1; grep -nE '^[[:space:]]*(model_provider|openai_base_url|model_catalog_json)[[:space:]]*=' "$CH/config.toml" 2>/dev/null || echo "no provider settings"
```

- **If `os=` is not `Linux` or `Darwin`, or your shell is PowerShell** (native Windows): stop following this page, and follow `https://api.solcardinference.ai/install/codex/windows` instead. Read it with `curl.exe -fsSL`.
- **If the login line says an API key, or "Not logged in"**: this setup keeps a ChatGPT subscription's own models. Tell the user it works with Codex signed in with ChatGPT, and stop.
- **If a line shows `model_provider` set to anything but `"solcard-inference"`, or `openai_base_url` or `model_catalog_json`**: Codex already uses another model provider. Tell the user what it is, say this setup would replace it while SolCard is installed, and stop unless they say to go on.
- **`codex=none`:** Codex isn't reachable from your shell. Tell the user this setup couldn't find Codex on this computer, and stop.
- **`surface=`** says where you're running: `cli` for the Codex CLI, `app` for Codex in the ChatGPT app. Step 9 uses it. If the user has said where they are, that wins.

## Step 1. Ask once

Ask the user this question word for word, without adding to it or summarizing it, and wait for the answer. If you give a reason for asking, give this one: the setup adds a small background service on this computer, and Codex will send its model requests to it.

> To add SolCard Inference next to your ChatGPT models, I'll download CLIProxyAPI, an open-source proxy, and run it as a small background service that only this computer can reach. Codex uses one model provider at a time, so it will send its requests to this proxy, which passes your ChatGPT requests on to ChatGPT unchanged, with your ChatGPT login, and SolCard's to SolCard. Your ChatGPT models stay exactly as they are, on your own plan, and SolCard's models are added below them. You'll approve SolCard Inference once in your browser. OK to go ahead?

If step 0 showed `surface=app`, add this before the last sentence:

> While it's set up, Apps (connectors) are off in the ChatGPT app; removing SolCard turns them back on.

- **If they say no:** stop.
- **If they ask something:** answer it from this runbook, then ask again. If they ask about cost or their ChatGPT plan, these are the facts:
  - Their ChatGPT plan isn't changed or charged by this: their ChatGPT models keep running on it as before.
  - SolCard Inference is prepaid: requests use credit they add, and nothing is charged automatically unless they turn on auto-reload or subscribe to a plan. New accounts get $1 of free credit.
- **If they ask whether the proxy sees their ChatGPT login:** yes. Each ChatGPT request passes through it with the login Codex puts on it, on to ChatGPT unchanged. The proxy runs on this computer only, stores no login and logs no requests.
- **If they ask for a way without the proxy:** there isn't one that keeps their ChatGPT models. Codex uses one model provider at a time, and the proxy is what lets both sets of models share the picker.
- **If they decline:** stop. Don't set SolCard Inference up another way, for example as Codex's only provider or with settings you write yourself: their ChatGPT models would leave the picker.

## Step 2. Download CLIProxyAPI 8.0.46-solcard.2 and check it

Pick the file for this machine from step 0's `os=` and `arch=`:

| `os=` | `arch=` | FILE | SHA256 |
| --- | --- | --- | --- |
| `Darwin` | `arm64` | `CLIProxyAPI_8.0.46-solcard.2_darwin_aarch64.tar.gz` | `c574ee4d4ed269e7292a7c66eaa99cc524e300047861690011c0801b815d94b4` |
| `Darwin` | `x86_64` | `CLIProxyAPI_8.0.46-solcard.2_darwin_amd64.tar.gz` | `77dbc3dc3ae82b94b5d3fd8bff17541a3b5585572aaa9e3ef3c62d567f33ac7e` |
| `Linux` | `x86_64` | `CLIProxyAPI_8.0.46-solcard.2_linux_amd64.tar.gz` | `64d0c7d74705fb943b4a50235263df8173a4e9ef0433ab9c884835e51a6fce31` |
| `Linux` | `aarch64` | `CLIProxyAPI_8.0.46-solcard.2_linux_aarch64.tar.gz` | `356eb5d8e33f838ed3de2ccd1e407e987076e33e174cdaee76e3ec52e2fa15e4` |

Replace `<FILE>` and `<SHA256>` with that row:

```sh
mkdir -p ~/.solcard-inference/codex/auth && chmod 700 ~/.solcard-inference ~/.solcard-inference/codex ~/.solcard-inference/codex/auth && cd ~/.solcard-inference/codex && curl -fsSL -o cpa.tar.gz "https://github.com/SolCardDev/CLIProxyAPI/releases/download/v8.0.46-solcard.2/<FILE>" && got=$({ sha256sum cpa.tar.gz 2>/dev/null || shasum -a 256 cpa.tar.gz; } | cut -d' ' -f1) && if [ "$got" = "<SHA256>" ]; then tar -xzf cpa.tar.gz cli-proxy-api && rm cpa.tar.gz && chmod 755 cli-proxy-api && echo "checksum ok"; else rm cpa.tar.gz; echo "CHECKSUM MISMATCH: $got"; fi
```

- **You should see:** `checksum ok`.
- **If you see `CHECKSUM MISMATCH`:** stop, and tell the user the download didn't match the published checksum. Don't run anything you downloaded.

## Step 3. A port and a local key

```sh
cd ~/.solcard-inference/codex && if [ ! -s port ]; then for p in 18321 18322 18323 18324; do curl -s -o /dev/null --connect-timeout 1 "http://127.0.0.1:$p/"; if [ $? -eq 7 ]; then echo "$p" > port; break; fi; done; fi; [ -s local.key ] || (umask 077; od -An -tx1 -N24 /dev/urandom | tr -d ' \n' > local.key); echo "port $(cat port)"
```

- **You should see:** `port ` and a number.
- **If no number is printed:** the ports 18321 18322 18323 18324 are all busy. Tell the user, and stop.

## Step 4. The SolCard key

```sh
cd ~/.solcard-inference/codex && if [ -s solcard.key ]; then echo "key present"; elif [ -s ../solcard.key ]; then (umask 077; cp ../solcard.key solcard.key) && echo "key reused"; else echo "no key"; fi
```

- **`key present` or `key reused`** (the user already set SolCard up, here or for Claude Code): go to step 6.
- **`no key`, and the user's message included a SolCard Inference key** (it starts with `sk-si-`): save it with this command, putting the key between the quotes, and go to step 6:

  ```sh
  cd ~/.solcard-inference/codex && (umask 077; printf '%s' '<the key>' > solcard.key) && echo "key saved"
  ```

- **`no key` otherwise:** start the approval:

  ```sh
  cd ~/.solcard-inference/codex && curl -fsS -X POST "https://api.solcardinference.ai/v1/connect" -H 'content-type: application/json' -d "{\"client\":\"codex\",\"device\":\"$(hostname | cut -c1-40)\"}" -o connect.json && chmod 600 connect.json && echo "link $(sed -n 's/.*"verification_uri_complete":"\([^"]*\)".*/\1/p' connect.json)" && echo "code $(sed -n 's/.*"user_code":"\([^"]*\)".*/\1/p' connect.json)"
  ```

  - **You should see:** `link ` and a link starting `https://app.solcardinference.ai/connect?code=`, then `code ` and the code.
  - **If not:** show the user the error line, and stop.

  Tell the user, with the real link and code, the link in full on its own line:

  > Approve SolCard Inference in your browser: `<the link>`
  > Sign up or sign in, check the code is `<the code>`, and click Approve. Tell me when it's done.

  Then wait for the user.

## Step 5. Collect the SolCard key

Only if step 4 started an approval, and only after the user says they approved. It waits up to 45 seconds; if your tool returns before it finishes, wait for that same command instead of starting it again:

```sh
cd ~/.solcard-inference/codex && if [ -s solcard.key ]; then echo "solcard ok"; else (umask 077; curl -s -X POST "https://api.solcardinference.ai/v1/connect/token" -H 'content-type: application/json' -d "{\"device_code\":\"$(sed -n 's/.*"device_code":"\([^"]*\)".*/\1/p' connect.json)\",\"wait\":45}" > token.json); if grep -q '"api_key"' token.json; then (umask 077; sed -n 's/.*"api_key":"\([^"]*\)".*/\1/p' token.json > solcard.key); rm token.json connect.json; echo "solcard ok"; else cat token.json; rm token.json; fi; fi
```

- **You should see:** `solcard ok`.
- **If you see `authorization_pending`:** the user hasn't approved yet. Ask them to finish the SolCard page, then run step 5 again.
- **If you see `expired_token` or `access_denied`:** run step 4's approval command again, give the user the new link and code, and when they say done, run step 5 again.

## Step 6. Run the proxy at login

This writes the proxy's config and its ChatGPT entry. Where each request goes, so you can check it in these two files:
- **`account-catalog: true`:** Codex's model list request is answered with the list ChatGPT returns for the user's own login, entries unchanged, followed by SolCard's rows.
- **`api-keys: codex:`:** it has one entry, SolCard. It serves only the `solcard-inference-*` names listed in it, with the SolCard key. `disable-cooling` keeps one failed SolCard request from pausing the next ones.
- **Every other model:** it goes to `auth/codex-login.json`, which sends it to ChatGPT (`https://chatgpt.com/backend-api/codex`) with the `Authorization` and `ChatGPT-Account-ID` headers Codex put on that request.
  - That file holds no login: its `access_token` is a placeholder.
  - Its error rules return ChatGPT's answer to Codex without blocking later requests. Codex then refreshes its own login as it always has.
- **Logging:** request logging is off.

```sh
cd ~/.solcard-inference/codex && umask 077 && cat > config.yaml <<EOF
config-version: 8
server:
  host: "127.0.0.1"
  port: $(cat port)
management:
  allow-remote: false
  secret-key: ""
  disable-control-panel: true
access:
  api-keys:
    - "$(cat local.key)"
oauth:
  auth-dir: "$HOME/.solcard-inference/codex/auth"
client:
  codex:
    account-catalog: true
upstream:
  codex:
    disable-codex-cloaking: true
multimedia:
  disable-image-generation: passthrough
api-keys:
  codex:
    - name: solcard-inference
      base-url: "https://api.solcardinference.ai/v1"
      disable-cooling: true
      models:
        - name: "solcard-inference-large"
          alias: "solcard-inference-large"
          thinking:
            levels: ["low", "medium", "high"]
        - name: "solcard-inference-fast"
          alias: "solcard-inference-fast"
          thinking:
            levels: ["low", "medium", "high"]
        - name: "solcard-inference-base"
          alias: "solcard-inference-base"
          thinking:
            levels: ["low", "medium", "high"]
        - name: "solcard-inference-large-legacy"
          alias: "solcard-inference-large-legacy"
          thinking:
            levels: ["low", "medium", "high"]
        - name: "solcard-inference-pro"
          alias: "solcard-inference-pro"
          thinking:
            levels: ["low", "medium", "high"]
        - name: "solcard-inference-lite"
          alias: "solcard-inference-lite"
          thinking:
            levels: ["low", "medium", "high"]
        - name: "solcard-inference-hy4"
          alias: "solcard-inference-hy4"
          thinking:
            levels: ["low", "medium", "high"]
      keys:
        - api-key: "$(cat solcard.key)"
EOF
chmod 600 config.yaml && printf '{\n  "type": "codex",\n  "email": "codex-login@this-machine",\n  "access_token": "uses-the-callers-own-chatgpt-login",\n  "plan_type": "pro",\n  "disable_cooling": true,\n  "request_scoped_errors": [\n    {"status": 401, "match-regexr": ["(?s).*"], "action": "stop"},\n    {"status": 403, "match-regexr": ["(?s).*"], "action": "stop"},\n    {"status": 429, "match-regexr": ["(?s).*"], "action": "stop"}\n  ],\n  "headers": { "Authorization": "$Authorization", "Chatgpt-Account-Id": "$Chatgpt-Account-Id" }\n}\n' > auth/codex-login.json && chmod 600 auth/codex-login.json && cat > start.sh <<'START' && chmod 700 start.sh
#!/bin/sh
# Starts the SolCard Inference proxy for Codex if nothing answers on its port, and waits up to 5 seconds for it.
cd ~/.solcard-inference/codex 2>/dev/null || exit 0
up() { curl -s -o /dev/null --connect-timeout 1 "http://127.0.0.1:$(cat port)/"; [ $? -ne 7 ]; }
up && exit 0
(setsid nohup ./cli-proxy-api -config config.yaml >> proxy.log 2>&1 < /dev/null &)
for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25; do up && exit 0; sleep 0.2; done
exit 0
START
printf 'SolCard Inference for Codex: the local proxy that puts SolCard Inference models next to your ChatGPT models.\nTo remove it, follow "To undo" in https://api.solcardinference.ai/install/codex (read it with curl). It removes the two Codex settings first, then this folder and its login service.\n' > README.txt && echo "config ok"
```

Then start it at login. Run only the block for this machine.

**Linux:**

```sh
export XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}"; mkdir -p ~/.config/systemd/user && printf '[Unit]\nDescription=SolCard Inference proxy for Codex (CLIProxyAPI 8.0.46-solcard.2; to remove: ~/.solcard-inference/codex/README.txt)\n[Service]\nExecStart=%%h/.solcard-inference/codex/cli-proxy-api -config %%h/.solcard-inference/codex/config.yaml\nRestart=on-failure\nRestartSec=2\n[Install]\nWantedBy=default.target\n' > ~/.config/systemd/user/solcard-inference-codex.service && systemctl --user daemon-reload && systemctl --user enable solcard-inference-codex.service && systemctl --user restart solcard-inference-codex.service && sleep 3 && systemctl --user is-active solcard-inference-codex.service
```

- **You should see:** `active`.
- **If `systemctl` says it can't connect to a bus** (WSL and some containers have no user services): start it now and from every new terminal:

  ```sh
  sh ~/.solcard-inference/codex/start.sh; for f in ~/.bashrc ~/.zshrc; do case "$f:$SHELL" in *bashrc:*bash|*zshrc:*zsh) touch "$f";; esac; if [ -f "$f" ]; then grep -q 'solcard-inference/codex/start.sh' "$f" || printf '\n[ -x ~/.solcard-inference/codex/start.sh ] && sh ~/.solcard-inference/codex/start.sh  # SolCard Inference\n' >> "$f"; echo "set in $f"; fi; done
  ```

**macOS:**

```sh
mkdir -p ~/Library/LaunchAgents && P=~/Library/LaunchAgents/ai.solcardinference.codex.plist && printf '<?xml version="1.0" encoding="UTF-8"?>\n<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">\n<plist version="1.0"><dict><key>Label</key><string>ai.solcardinference.codex</string><key>ProgramArguments</key><array><string>%s/.solcard-inference/codex/cli-proxy-api</string><string>-config</string><string>%s/.solcard-inference/codex/config.yaml</string></array><key>RunAtLoad</key><true/><key>KeepAlive</key><true/><key>StandardOutPath</key><string>%s/.solcard-inference/codex/proxy.log</string><key>StandardErrorPath</key><string>%s/.solcard-inference/codex/proxy.log</string></dict></plist>\n' "$HOME" "$HOME" "$HOME" "$HOME" > "$P" && (launchctl bootout "gui/$(id -u)/ai.solcardinference.codex" 2>/dev/null; true) && launchctl bootstrap "gui/$(id -u)" "$P" && sleep 3 && launchctl print "gui/$(id -u)/ai.solcardinference.codex" | grep -m1 'state ='
```

- **You should see:** `state = running`.

## Step 7. Check the proxy

```sh
cd ~/.solcard-inference/codex && curl -s -m 20 "http://127.0.0.1:$(cat port)/v1/models" -H "Authorization: Bearer $(cat local.key)" | grep -o '"id":"solcard-inference[^"]*"' | wc -l | tr -d ' '; printf 'solcard-inference-fast: '; curl -s -o /dev/null -w '%{http_code}\n' -m 90 "http://127.0.0.1:$(cat port)/v1/responses" -H "Authorization: Bearer $(cat local.key)" -H 'content-type: application/json' -d '{"model":"solcard-inference-fast","stream":false,"input":"Reply with: ok"}'
```

- **You should see:** `7`, then `solcard-inference-fast: 200`. ChatGPT models can't be tried from here: they answer with the user's ChatGPT login, which only Codex sends.
- **If the first line is `0`, or the second shows `000`:** the proxy isn't up yet. Wait five seconds and run it again.
- **If you see `402`:** the SolCard account has no credit left. Carry on with step 8, and in step 9 add, in these words:
  > Your SolCard Inference account has no credit yet. The easiest way to use SolCard's models is a plan: https://app.solcardinference.ai/plans. Or add credit instead: https://app.solcardinference.ai/top-up.
- **Any other code:** tell the user, and stop.

## Step 8. Point Codex at the proxy

Codex writes its own settings here. When you ask to run this command, give this reason: point Codex at the local proxy the user agreed to in step 1. Its config writer keeps everything else in `~/.codex/config.toml` exactly as it is, comments included. The command first saves a copy of the file. It uses the Codex you're running in (Codex puts its own `apply_patch` helper on your PATH, which points to it). The settings request is in single quotes, and the shell fills in its two `%s` with the port and the local key: copy the command exactly as it is.

```sh
CODEX="$(readlink -f "$(command -v apply_patch)" 2>/dev/null)"; case "$CODEX" in */codex) ;; *) CODEX="$(command -v codex)";; esac; CH="${CODEX_HOME:-$HOME/.codex}"; cd ~/.solcard-inference/codex && mkdir -p "$CH" && { [ -f "$CH/config.toml.before-solcard" ] || cp "$CH/config.toml" "$CH/config.toml.before-solcard" 2>/dev/null || true; } && { printf '%s\n' '{"id":1,"method":"initialize","params":{"clientInfo":{"name":"solcard-inference-install","version":"1"}}}' '{"method":"initialized"}'; printf '{"id":2,"method":"config/batchWrite","params":{"edits":[{"keyPath":"model_provider","value":"solcard-inference","mergeStrategy":"replace"},{"keyPath":"model_providers.solcard-inference","value":{"name":"OpenAI","base_url":"http://127.0.0.1:%s/backend-api/codex","wire_api":"responses","requires_openai_auth":true,"supports_websockets":true,"http_headers":{"X-Api-Key":"%s"}},"mergeStrategy":"replace"}]}}\n' "$(cat port)" "$(cat local.key)"; sleep 3; } | "$CODEX" app-server 2>/dev/null | grep -o '"id":2,"result":{"status":"[a-zA-Z]*"'; chmod 600 "$CH/config.toml"
```

- **You should see:** `"id":2,"result":{"status":"ok"`.
- **If the user declines this command:** don't write a different provider or edit `config.toml` yourself. Tell them SolCard Inference isn't set up, and stop.
- **If nothing is printed:** Codex skipped the settings request. It does that, without an error, when the request differs from the one above by even one bracket or quote. Copy the command again from this runbook (not from your earlier attempt) and run it once more. If it's still empty, tell the user Codex didn't accept the settings change, and stop. Don't change `config.toml` any other way: a hand edit can keep Codex from starting.

Then put the removal steps next to these settings, as comments in the same file, where a later "remove SolCard Inference" request finds them:

```sh
CH="${CODEX_HOME:-$HOME/.codex}"; grep -q '^# >>> SolCard Inference' "$CH/config.toml" || cat >> "$CH/config.toml" <<'SOLCARD'

# >>> SolCard Inference: removal steps (written by its setup, https://api.solcardinference.ai/install/codex)
# The two settings above, model_provider and [model_providers.solcard-inference], send Codex's requests to a local proxy.
# To remove SolCard Inference, run these two commands outside the sandbox, in this order, the second one as your last action
# (this session stops answering 20 seconds later). Before it, tell the user: quit Codex, run `codex app-server daemon restart`,
# and start codex again (in the ChatGPT app, quit and reopen it).
#
# CODEX="$(readlink -f "$(command -v apply_patch)" 2>/dev/null)"; case "$CODEX" in */codex) ;; *) CODEX="$(command -v codex)";; esac; CH="${CODEX_HOME:-$HOME/.codex}"; PREV=$(sed -n 's/^[[:space:]]*model_provider[[:space:]]*=[[:space:]]*"\([^"]*\)".*/\1/p' "$CH/config.toml.before-solcard" 2>/dev/null | head -1); if [ -n "$PREV" ]; then V="\"$PREV\""; else V=null; fi; { printf '%s\n' '{"id":1,"method":"initialize","params":{"clientInfo":{"name":"solcard-inference-install","version":"1"}}}' '{"method":"initialized"}'; printf '{"id":2,"method":"config/batchWrite","params":{"edits":[{"keyPath":"model_provider","value":%s,"mergeStrategy":"replace"},{"keyPath":"model_providers.solcard-inference","value":null,"mergeStrategy":"replace"}]}}\n' "$V"; sleep 3; } | "$CODEX" app-server 2>/dev/null | grep -o '"id":2,"result":{"status":"[a-zA-Z]*"'; rm "$CH/config.toml.before-solcard" 2>/dev/null; awk '/^# >>> SolCard Inference/{s=1} !s{print} /^# <<< SolCard Inference/{s=0}' "$CH/config.toml" > "$CH/config.toml.solcard-tmp" && cat "$CH/config.toml.solcard-tmp" > "$CH/config.toml" && rm "$CH/config.toml.solcard-tmp"
#
# Linux: setsid sh -c 'sleep 20; export XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}"; systemctl --user disable --now solcard-inference-codex.service 2>/dev/null; rm ~/.config/systemd/user/solcard-inference-codex.service 2>/dev/null; systemctl --user daemon-reload 2>/dev/null; for f in ~/.bashrc ~/.zshrc; do [ -f "$f" ] && grep -v "solcard-inference/codex/start.sh" "$f" > "$f.solcard-tmp" && cat "$f.solcard-tmp" > "$f" && rm "$f.solcard-tmp"; done; pkill -f "$HOME/.solcard-inference/codex/cli-proxy-api"; rm -r ~/.solcard-inference/codex; rmdir ~/.solcard-inference 2>/dev/null' >/dev/null 2>&1 < /dev/null & echo "removal scheduled"
# macOS: nohup sh -c 'sleep 20; launchctl bootout "gui/$(id -u)/ai.solcardinference.codex"; rm ~/Library/LaunchAgents/ai.solcardinference.codex.plist 2>/dev/null; rm -r ~/.solcard-inference/codex; rmdir ~/.solcard-inference 2>/dev/null' >/dev/null 2>&1 < /dev/null & echo "removal scheduled"
# <<< SolCard Inference
SOLCARD
grep -c '^# >>> SolCard Inference' "$CH/config.toml"
```

- **You should see:** `1`.

The provider's `name` is `OpenAI` on purpose. It tells Codex to treat these requests exactly as it treats its own ChatGPT ones, which they are; only the address differs.

## Step 9. Tell the user

Say, in these words:

> SolCard Inference is set up. Restart Codex to see it: in a terminal, quit Codex, run `codex app-server daemon restart` (Codex keeps its settings in a background service until it restarts), then start `codex` again; in the ChatGPT app, quit the app and open it again. The model picker lists your ChatGPT models as before, and below them SolCard Inference Large, Fast, Base and Large (legacy).

If step 0 showed `surface=cli`, add, in these words:

> Above the list, Codex notes that the OpenAI base URL is overridden: that's this setup sending your ChatGPT requests through the local proxy, and it's expected.

The change applies on the next start, so don't test it from this session, and don't restart Codex yourself.

## To undo

When the user asks to "remove SolCard Inference from my models":

1. Let Codex remove its two settings. This also puts back any model provider the user had before, and removes the removal steps this setup left as comments in `config.toml`:

   ```sh
   CODEX="$(readlink -f "$(command -v apply_patch)" 2>/dev/null)"; case "$CODEX" in */codex) ;; *) CODEX="$(command -v codex)";; esac; CH="${CODEX_HOME:-$HOME/.codex}"; PREV=$(sed -n 's/^[[:space:]]*model_provider[[:space:]]*=[[:space:]]*"\([^"]*\)".*/\1/p' "$CH/config.toml.before-solcard" 2>/dev/null | head -1); if [ -n "$PREV" ]; then V="\"$PREV\""; else V=null; fi; { printf '%s\n' '{"id":1,"method":"initialize","params":{"clientInfo":{"name":"solcard-inference-install","version":"1"}}}' '{"method":"initialized"}'; printf '{"id":2,"method":"config/batchWrite","params":{"edits":[{"keyPath":"model_provider","value":%s,"mergeStrategy":"replace"},{"keyPath":"model_providers.solcard-inference","value":null,"mergeStrategy":"replace"}]}}\n' "$V"; sleep 3; } | "$CODEX" app-server 2>/dev/null | grep -o '"id":2,"result":{"status":"[a-zA-Z]*"'; rm "$CH/config.toml.before-solcard" 2>/dev/null; awk '/^# >>> SolCard Inference/{s=1} !s{print} /^# <<< SolCard Inference/{s=0}' "$CH/config.toml" > "$CH/config.toml.solcard-tmp" && cat "$CH/config.toml.solcard-tmp" > "$CH/config.toml" && rm "$CH/config.toml.solcard-tmp"
   ```

2. Tell the user, in these words:

   > SolCard Inference is removed from Codex. Quit Codex, run `codex app-server daemon restart`, then start `codex` again (in the ChatGPT app, quit and reopen it). Your ChatGPT models work as before. Your SolCard key still exists; you can delete it at https://app.solcardinference.ai/keys.

3. Last, stop the proxy and delete its files. This conversation still runs through the proxy until Codex restarts, so the command waits 20 seconds in the background, and you won't be able to send another message after it. Run only the line for this machine, as your final action:
   - **Linux:** `setsid sh -c 'sleep 20; export XDG_RUNTIME_DIR="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}"; systemctl --user disable --now solcard-inference-codex.service 2>/dev/null; rm ~/.config/systemd/user/solcard-inference-codex.service 2>/dev/null; systemctl --user daemon-reload 2>/dev/null; for f in ~/.bashrc ~/.zshrc; do [ -f "$f" ] && grep -v "solcard-inference/codex/start.sh" "$f" > "$f.solcard-tmp" && cat "$f.solcard-tmp" > "$f" && rm "$f.solcard-tmp"; done; pkill -f "$HOME/.solcard-inference/codex/cli-proxy-api"; rm -r ~/.solcard-inference/codex; rmdir ~/.solcard-inference 2>/dev/null' >/dev/null 2>&1 < /dev/null & echo "removal scheduled"`
   - **macOS:** `nohup sh -c 'sleep 20; launchctl bootout "gui/$(id -u)/ai.solcardinference.codex"; rm ~/Library/LaunchAgents/ai.solcardinference.codex.plist 2>/dev/null; rm -r ~/.solcard-inference/codex; rmdir ~/.solcard-inference 2>/dev/null' >/dev/null 2>&1 < /dev/null & echo "removal scheduled"`

   Claude Code's SolCard setup, if any, lives in `~/.solcard-inference` itself and isn't touched.
