Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
c6fbf77
feat: ChatGPT desktop send-unblock intercept (opt-in)
lcxhh521 Sep 24, 2026
f211849
feat: launch watcher for ChatGPT desktop send-unblock
lcxhh521 Sep 24, 2026
dea3099
fix(chatgpt-unblock): working launch path, WebSocket relay and review…
lcxhh521 Sep 26, 2026
72b5a83
fix(chatgpt-unblock): cap fragmented WS messages by bytes and honor h…
lcxhh521 Sep 26, 2026
12697f3
fix(chatgpt-unblock): keep an error listener on the WS tunnel until a…
lcxhh521 Sep 26, 2026
59fd784
feat(chatgpt-unblock): PAC-fallback mode so traffic survives opencode…
lcxhh521 Sep 26, 2026
249cffd
fix(chatgpt-unblock): review fixes for the PAC fallback
lcxhh521 Sep 26, 2026
14eac24
fix(chatgpt-unblock): backpressure-safe splice and a working head dea…
lcxhh521 Sep 26, 2026
40739db
fix(chatgpt-unblock): honour a system PAC and release listeners on fa…
lcxhh521 Sep 26, 2026
d38137a
docs(chatgpt-desktop): document the PAC fallback mode
lcxhh521 Sep 26, 2026
cd3265a
fix(chatgpt-unblock): restore undoes either launch switch
lcxhh521 Sep 26, 2026
70a7d73
fix(chatgpt-unblock): RFC 1929 auth for the SOCKS5 WebSocket dial
lcxhh521 Sep 27, 2026
bbb26cb
fix(chatgpt-unblock): reject a malformed chatgptDesktop block at the …
lcxhh521 Sep 29, 2026
345f42f
feat(chatgpt-unblock): route the bundled app-server's account reads t…
lcxhh521 Sep 29, 2026
37ac1d7
fix(chatgpt-unblock): also drop a plain-quota rate_limit_reached_type…
lcxhh521 Sep 29, 2026
2073a01
fix(chatgpt-unblock): pass the PAC to the app inline as a data: URL
lcxhh521 Sep 29, 2026
41ce732
fix(chatgpt-unblock): address review findings on the app-server path
lcxhh521 Sep 29, 2026
c362202
fix(chatgpt-unblock): recognise the injected chatgpt_base_url by valu…
lcxhh521 Sep 29, 2026
6b1b3c4
fix(chatgpt-unblock): serve the app-server route over HTTPS and make …
lcxhh521 Sep 29, 2026
4239992
feat(chatgpt-unblock): open the quota gate the app-server reports ove…
lcxhh521 Sep 29, 2026
235afd5
fix(chatgpt-unblock): exec the real app-server so the app keeps trust…
lcxhh521 Sep 29, 2026
2eb49af
test(chatgpt-unblock): cover the 5-hour-only exhaustion reported in #…
lcxhh521 Sep 29, 2026
c7ef34d
fix(chatgpt-unblock): scope the gate rewrites and clean up on launche…
lcxhh521 Sep 29, 2026
dc653eb
fix(chatgpt-unblock): keep the rate-limit flags closed beside a non-q…
lcxhh521 Sep 29, 2026
61947c0
fix(chatgpt-unblock): look for non-quota reasons across the whole pay…
lcxhh521 Sep 29, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,10 @@ Sum of the table: **1061**. Zero leftover.

`phase100-native-parity.test.ts`

#### `tests/chatgpt-unblock/` (1)

`rewrite.test.ts`

## 3. Cross-cutting coupling

### 3.A tests/helpers imported by how many tests (unique files)
Expand Down
1 change: 1 addition & 0 deletions docs-site/astro.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ export default defineConfig({
{ label: "Native Context Compatibility", translations: { ko: "네이티브 컨텍스트 호환성", fr: "Compatibilité du contexte natif", "zh-CN": "原生上下文兼容性", "zh-TW": "原生脈絡相容性", ru: "Совместимость с нативным контекстом", ja: "ネイティブコンテキストの互換性", tr: "Yerel bağlam uyumluluğu" }, slug: "guides/codex-native-context" },
{ label: "macOS Menu Bar App", translations: { fr: "Application barre de menus macOS", ko: "macOS 메뉴바 앱", "zh-CN": "macOS 菜单栏应用", "zh-TW": "macOS 選單列 App", ru: "Приложение в строке меню macOS", ja: "macOS メニューバーアプリ", tr: "macOS Menü Çubuğu Uygulaması" }, slug: "guides/macos-menu-bar" },
{ label: "Desktop App", translations: { fr: "Application de bureau", ko: "데스크톱 앱", "zh-CN": "桌面应用", "zh-TW": "桌面 App", ru: "Настольное приложение", ja: "デスクトップアプリ", tr: "Masaüstü Uygulaması" }, slug: "guides/desktop-app" },
{ label: "ChatGPT Desktop Send Unblock", translations: { fr: "Déblocage de l'envoi dans ChatGPT Desktop", ko: "ChatGPT 데스크톱 전송 잠금 해제", "zh-CN": "ChatGPT 桌面版发送键解锁", "zh-TW": "ChatGPT 桌面版傳送鍵解鎖", ru: "Разблокировка отправки в ChatGPT Desktop", ja: "ChatGPT デスクトップの送信ロック解除", tr: "ChatGPT Masaüstü Gönderme Kilidini Açma" }, slug: "guides/chatgpt-desktop" },
{ label: "Model Ordering", translations: { fr: "Ordre des modèles", ko: "모델 정렬에 관하여", "zh-CN": "模型排序", "zh-TW": "模型排序", ru: "Сортировка моделей", ja: "モデルの並び順", tr: "Model Sıralaması" }, slug: "guides/model-ordering" },
{ label: "Combos", translations: { fr: "Combinaisons", ko: "콤보", "zh-CN": "组合", "zh-TW": "組合", ru: "Комбо", ja: "コンボ", tr: "Kombolar" }, slug: "guides/combos" },
{ label: "Protocol Paths", translations: { fr: "Chemins de protocole", ko: "프로토콜 경로", "zh-CN": "协议路径", "zh-TW": "協定路徑", ru: "Пути протоколов", ja: "プロトコル経路", tr: "Protokol Yolları" }, slug: "guides/protocol-paths" },
Expand Down
146 changes: 146 additions & 0 deletions docs-site/src/content/docs/fr/guides/chatgpt-desktop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
---
title: Déblocage de l'envoi dans ChatGPT Desktop
description: Garder utilisable la zone de saisie de l'application ChatGPT de bureau quand le quota d'utilisation du compte est épuisé (macOS, sur activation).
---

Quand le compte ChatGPT connecté épuise son quota d'utilisation, l'application de bureau ChatGPT
grise son bouton d'envoi, même pour les conversations dont opencodex route les appels de modèle vers
d'autres fournisseurs. Cette intégration macOS, à activer explicitement, garde la zone de saisie
utilisable. Elle est désactivée par défaut.

## Ce qu'elle modifie

opencodex exécute un écouteur TLS local pour `chatgpt.com`. L'application est lancée avec une option
Chromium qui envoie `chatgpt.com` vers cet écouteur ; tous les autres hôtes, sous-domaines compris,
gardent leur route habituelle. Les requêtes sont relayées vers le vrai `chatgpt.com` avec les
identifiants de l'application, et les WebSockets (comme la dictée vocale) sont relayés aussi. Rien
n'est journalisé ni stocké.

Les réponses passent sans modification, sauf pour deux points de terminaison :

- les métadonnées de conversation (`/backend-api/conversation/init` et le flux de conversation) : les
verrous d'envoi dus au quota d'utilisation sont retirés ;
- l'instantané d'utilisation (`/backend-api/wham/usage`) : la barrière « limite atteinte » est ouverte.

Les verrous d'envoi ayant une autre raison, comme un abonnement requis, sont conservés et listés par
`ocx chatgpt status`. L'utilisation affichée (pourcentages, heures de réinitialisation, bannières)
n'est jamais modifiée, et les serveurs d'OpenAI appliquent toujours toutes les limites à leurs propres
requêtes.

## Configuration

1. Activez la fonctionnalité dans `~/.opencodex/config.json` puis redémarrez opencodex :

```json
{ "chatgptDesktop": { "unblockSend": true } }
```

L'écouteur utilise le port du proxy plus 200 (`10300` par défaut). Définissez
`chatgptDesktop.port` pour choisir un autre port.

2. Faites confiance une fois à l'autorité de certification locale. La commande demande votre mot de
passe de session ; exécutez-la vous-même :

```bash
security add-trusted-cert -r trustRoot -p ssl \
-k ~/Library/Keychains/login.keychain-db ~/.opencodex/claude-intercept/ca.pem
```

Sans cette confiance, l'application ne peut pas charger les pages de compte, d'utilisation ni de
réglages. Si vous utilisez un répertoire opencodex personnalisé, `ocx chatgpt status` affiche la
commande exacte pour votre configuration.

3. Lancez l'application via opencodex :

```bash
ocx chatgpt launch
```

4. Facultatif : faire utiliser la route aussi aux lancements normaux depuis le Dock ou Spotlight :

```bash
ocx chatgpt install-watcher
```

Le surveillant s'exécute à chaque démarrage de l'application. Si l'application a été ouverte
normalement pendant qu'opencodex tourne, il la quitte juste après son lancement et la rouvre avec
la route. Il n'agit jamais sur une application déjà en cours d'utilisation et ne fait rien quand
opencodex ne tourne pas. La commande demande une confirmation ; `--yes` confirme sans interaction.

## Configurations réseau

Aucune règle de VPN ou de proxy n'est nécessaire. En mode par défaut, les arguments de lancement sont
choisis d'après le proxy système à chaque démarrage de l'application :

| Configuration | Arguments de lancement de l'application |
|---|---|
| Sans proxy | La route `chatgpt.com` seule. |
| VPN en mode proxy système | La route, le proxy système avec repli direct, et un contournement pour `chatgpt.com` seulement. |
| VPN en mode TUN | La route seule ; le trafic de boucle locale n'entre jamais dans le tunnel. |
| Fichier PAC | La route seule. Le fichier PAC peut laisser `chatgpt.com` sur le proxy, la zone de saisie peut donc rester verrouillée, mais rien d'autre ne casse. |

opencodex joint le vrai `chatgpt.com` via son propre réglage `proxy`, comme tout son autre trafic
sortant.

## Garder l'application utilisable quand opencodex s'arrête

En mode par défaut, une application routée dépend de l'écouteur : tant qu'opencodex est arrêté, ses
requêtes vers `chatgpt.com` échouent. Le repli PAC lance plutôt l'application avec un fichier PAC
généré, pour qu'elle se replie d'elle-même :

```json
{ "chatgptDesktop": { "unblockSend": true, "pacFallback": true } }
```

`pacFallback` n'a d'effet qu'avec `unblockSend`. opencodex écoute alors aussi sur le port de
l'écouteur plus un (`10301` par défaut) et réécrit `chatgpt-unblock.pac` dans son répertoire à chaque
démarrage. Le PAC envoie `chatgpt.com` d'abord vers opencodex, et tous les autres hôtes selon le
routage du système :

| Configuration | Autres hôtes, et `chatgpt.com` tant qu'opencodex est arrêté |
|---|---|
| Aucun proxy, ou VPN en mode TUN | Direct. |
| VPN en mode proxy système | Le proxy système, puis direct. |
| Fichier PAC | Le PAC système, intégré au fichier généré. |

Quand opencodex s'arrête, l'application continue de fonctionner par ce chemin, sans redémarrage ;
seul le déblocage de l'envoi est suspendu jusqu'au retour d'opencodex. Le routage est capturé au
démarrage d'opencodex : après un changement de mode du VPN, redémarrez opencodex et lancez
`ocx chatgpt launch`. Si un PAC système est configuré mais illisible à ce moment, les autres hôtes
passent en direct et opencodex affiche un avertissement.

Après avoir activé ou désactivé `pacFallback`, redémarrez opencodex, lancez `ocx chatgpt launch`, et
relancez `ocx chatgpt install-watcher` si vous utilisez le surveillant.

## Vérifier l'état

```bash
ocx chatgpt status
```

La commande indique si la fonctionnalité est active, si l'écouteur du port est celui d'opencodex, si
le certificat est de confiance, l'état du surveillant, si l'application en cours porte la route, et
les verrous d'envoi conservés volontairement.

## Désactiver

```bash
ocx chatgpt uninstall-watcher
ocx chatgpt restore
```

`restore` rouvre une application routée avec le réseau natif. Réglez ensuite
`chatgptDesktop.unblockSend` sur `false` et redémarrez opencodex. L'autorité de certification est
partagée avec les intégrations Claude d'opencodex ; ne retirez sa confiance que si vous n'utilisez
ni l'une ni l'autre.

## Dépannage

- **Les pages de compte, d'utilisation ou de réglages ne se chargent pas :** le certificat n'est pas
de confiance. Refaites l'étape 2 ; `ocx chatgpt status` affiche l'état de confiance.
- **Le bouton d'envoi reste grisé :** consultez `ocx chatgpt status`. L'application tourne peut-être
sans la route (lancez `ocx chatgpt launch`), ou le verrou a une raison autre que le quota
d'utilisation, listée sous « send blocks kept ».
- **L'application ne charge plus rien après l'arrêt d'opencodex :** en mode par défaut, une
application routée dépend de l'écouteur. Redémarrez opencodex ou lancez `ocx chatgpt restore`, ou
activez le repli PAC pour que l'application se replie d'elle-même.
156 changes: 156 additions & 0 deletions docs-site/src/content/docs/guides/chatgpt-desktop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
---
title: ChatGPT Desktop Send Unblock
description: Keep the ChatGPT desktop app's composer usable when the account's usage quota runs out (macOS, opt-in).
---

When the logged-in ChatGPT account runs out of usage quota, the ChatGPT desktop app greys out
its send button, even for conversations whose model calls opencodex routes to other providers.
This opt-in macOS integration keeps the composer usable. It is off by default.

## What it changes

opencodex runs a local TLS listener for `chatgpt.com`. The app is launched with a Chromium
switch that sends `chatgpt.com` to that listener; every other host, including its subdomains,
keeps its normal route. Requests are relayed to the real `chatgpt.com` with the app's own
credentials, and WebSockets (such as voice dictation) are relayed as well. Nothing is logged or
stored.

Responses are passed through unchanged except for two endpoints:

- conversation metadata (`/backend-api/conversation/init` and the conversation stream): send
locks caused by usage quota are removed;
- the usage snapshot (`/backend-api/wham/usage`): the "limit reached" gate is opened.

Send locks with any other reason, such as a subscription requirement, are kept, and
`ocx chatgpt status` lists them. Displayed usage (percentages, reset times, banners) is never
changed, and OpenAI's servers still enforce every limit on their own requests.

On some builds the send button follows what the app's built-in Codex server reports about your
account, and that server fetches it with its own HTTP client, which neither the resolver rule nor a
PAC file reaches. For those, set `chatgptDesktop.appServerShim` to `true` (together with
`unblockSend`) and launch the app with `ocx chatgpt launch`: the app then starts its Codex server
through a small stdio shim that opens only the subscription-quota part of the rate-limit answer.
The shim touches nothing else: no environment variable, address or config key is changed, the
server's own child processes are untouched, and it does not depend on opencodex running. If it
cannot start, the launcher falls through to the real binary. Everything that is not a plain-quota
lock (workspace or credit limits, spend controls) is passed through as the server sent it.

## Setup

1. Enable the feature in `~/.opencodex/config.json` and restart opencodex:

```json
{ "chatgptDesktop": { "unblockSend": true } }
```

The listener uses the proxy port plus 200 (`10300` by default). Set
`chatgptDesktop.port` to choose another port.

2. Trust the local certificate authority once. The command asks for your login password, so
run it yourself:

```bash
security add-trusted-cert -r trustRoot -p ssl \
-k ~/Library/Keychains/login.keychain-db ~/.opencodex/claude-intercept/ca.pem
```

Without this trust the app cannot load account, usage or settings pages. If you use a
custom opencodex home, `ocx chatgpt status` prints the exact command for your setup.

3. Launch the app through opencodex:

```bash
ocx chatgpt launch
```

4. Optional: make normal Dock and Spotlight launches use the route too:

```bash
ocx chatgpt install-watcher
```

The watcher runs each time the app starts. If the app was opened normally while opencodex
is running, it quits the app right after launch and reopens it with the route. It never acts
on an app that is already in use, and does nothing while opencodex is not running. The
command asks for confirmation; `--yes` confirms non-interactively.

## Network setups

No VPN or proxy rules are needed. In the default mode the launch arguments are chosen from the
system proxy each time the app starts:

| Setup | What the app is launched with |
|---|---|
| No proxy | The `chatgpt.com` route only. |
| VPN in system-proxy mode | The route, the system proxy with a direct fallback, and a bypass for `chatgpt.com` only. |
| VPN in TUN mode | The route only; loopback traffic never enters the tunnel. |
| PAC file | The route only. The PAC file may keep `chatgpt.com` on the proxy, so the composer can stay locked, but nothing else breaks. |

opencodex reaches the real `chatgpt.com` through its own `proxy` setting, like all its other
outbound traffic.

## Keep the app working when opencodex stops

In the default mode a routed app depends on the listener: while opencodex is stopped, its
`chatgpt.com` requests fail. PAC fallback launches the app with a generated PAC script instead,
so the app falls back on its own. The script is passed inline (a `data:` URL) because the app
ignores a `file://` PAC and an `http://` one would need opencodex running to be fetched:

```json
{ "chatgptDesktop": { "unblockSend": true, "pacFallback": true } }
```

`pacFallback` only takes effect together with `unblockSend`. opencodex then also listens on the
listener port plus one (`10301` by default) and rewrites `chatgpt-unblock.pac` in its home
directory at every start. The PAC sends `chatgpt.com` to opencodex first, and every other host
the way the system routes it:

| Setup | Other hosts, and `chatgpt.com` while opencodex is stopped |
|---|---|
| No proxy, or VPN in TUN mode | Direct. |
| VPN in system-proxy mode | The system proxy, then direct. |
| PAC file | The system PAC, embedded in the generated file. |

When opencodex stops, the app keeps working on that route without a restart; only the send
unblock pauses until opencodex is back. The route is captured when opencodex starts: after
changing the VPN mode, restart opencodex and run `ocx chatgpt launch`. If a system PAC is set but
cannot be read at that moment, other hosts go direct and opencodex prints a warning.

After turning `pacFallback` on or off, restart opencodex, run `ocx chatgpt launch`, and run
`ocx chatgpt install-watcher` again if you use the watcher.

## Check the state

```bash
ocx chatgpt status
```

It reports whether the feature is on, whether the listener on the port is opencodex's, whether
the certificate is trusted, the watcher state, whether the running app carries the route, and
any send locks that were kept on purpose.

## Turn it off

```bash
ocx chatgpt uninstall-watcher
ocx chatgpt restore
```

`restore` reopens a routed app with native networking. Then set
`chatgptDesktop.unblockSend` to `false` and restart opencodex. The certificate authority is
shared with opencodex's Claude integrations; remove its trust only if you use neither.

## Troubleshooting

- **Account, usage or settings pages do not load:** the certificate is not trusted. Run
step 2 again; `ocx chatgpt status` shows the trust state.
- **The send button is still grey:** check `ocx chatgpt status`. The app may be running
without the route (run `ocx chatgpt launch`), or the lock may have a reason other than usage
quota, which is listed under "send blocks kept".
- **The send button stays grey with the route working:** the lock may come from the built-in Codex
server rather than from the pages the route covers. Turn on `chatgptDesktop.appServerShim`, run
`ocx chatgpt launch`, and check `ocx chatgpt status` (the "app-server shim" line says whether the
running app was started through it).
- **The app cannot load anything after opencodex stops:** in the default mode a routed app
depends on the listener. Start opencodex again or run `ocx chatgpt restore`, or turn on
PAC fallback so the app falls back on its own.
Loading
Loading