ineersa / symfony-ai-openai-codex-platform
OpenAI Codex bridge with OAuth login for Symfony AI.
Package info
github.com/ineersa/symfony-ai-openai-codex-platform
pkg:composer/ineersa/symfony-ai-openai-codex-platform
Requires
- php: >=8.5
- ext-mbstring: *
- amphp/amp: ^3.1
- amphp/http: ^2.1
- amphp/websocket-client: ^2.0
- league/oauth2-client: ^2.8
- psr/log: ^3.0
- symfony/ai-open-responses-platform: ^0.12 || ^0.13 || ^0.14
- symfony/ai-platform: ^0.12 || ^0.13 || ^0.14
- symfony/clock: ^8.0
- symfony/console: ^8.1
- symfony/event-dispatcher-contracts: ^3.0
- symfony/filesystem: ^8.0
- symfony/http-client: ^8.0
- symfony/http-client-contracts: ^3.0
- symfony/lock: ^8.0
- symfony/process: ^8.0
- symfony/serializer: ^8.0
- symfony/uid: ^8.0
Requires (Dev)
- friendsofphp/php-cs-fixer: ^3.94
- phpstan/phpstan: ^2.1
- phpstan/phpstan-phpunit: ^2.0
- phpunit/phpunit: ^13.2
Suggests
None
Provides
None
Conflicts
None
Replaces
None
README
Connect Symfony AI to Codex with your ChatGPT account. Browser login, token refresh, and WebSocket streaming are included.
The websocket-cached transport reuses WebSocket connections between turns. GPT-5.6 and newer Codex models require WebSocket and no longer support SSE.
Requires PHP 8.5+ and a ChatGPT account with Codex access.
Install the package
composer require ineersa/symfony-ai-openai-codex-platform
Log in and send a message
Here's a small CLI example, codex.php. It saves your login in var/codex/auth.json so you don't need to log in for every request. Add that file to .gitignore.
<?php require __DIR__.'/vendor/autoload.php'; use Symfony\AI\Platform\Bridge\OpenAICodex\Auth\CodexAuthCommand; use Symfony\AI\Platform\Bridge\OpenAICodex\Auth\CodexAuthFileStore; use Symfony\AI\Platform\Bridge\OpenAICodex\Auth\CodexOAuthConfig; use Symfony\AI\Platform\Bridge\OpenAICodex\Auth\CodexOAuthService; use Symfony\AI\Platform\Bridge\OpenAICodex\Auth\CodexTokenRefresher; use Symfony\AI\Platform\Bridge\OpenAICodex\CodexModel; use Symfony\AI\Platform\Bridge\OpenAICodex\CodexTransportEnum; use Symfony\AI\Platform\Bridge\OpenAICodex\Factory; use Symfony\AI\Platform\Message\Message; use Symfony\AI\Platform\Message\MessageBag; use Symfony\Component\Console\Application; // Use your application's name instead of 'my-cli'. Share this identity // between OAuth login, token refresh, and API requests. $config = new CodexOAuthConfig( originator: 'my-cli', displayName: 'OpenAI Codex', commandName: 'auth:codex', ); $refresher = new CodexTokenRefresher(config: $config); // Choose where your application stores credentials. The store refreshes // expired tokens under a file lock when loadCredentials() is called. $store = new CodexAuthFileStore(__DIR__.'/var/codex/auth.json', tokenRefresher: $refresher); $service = new CodexOAuthService($store, $refresher, $config); // Register the login command. An existing Console app can register it too. if ('ask' !== ($argv[1] ?? null)) { $application = new Application(); $application->addCommand(new CodexAuthCommand($service, $config)); exit($application->run()); } $credentials = $store->loadCredentials(); if (null === $credentials) { throw new RuntimeException('Run php codex.php auth:codex first.'); } $provider = Factory::createProvider( accessToken: $credentials->access, accountId: $credentials->accountId, originator: $config->originator, userAgent: 'my-cli/1.0', // Replace with your application's name and version. transport: CodexTransportEnum::Websocket, // Retry once with a fresh token if the provider rejects the current one. accessTokenRefresher: static fn (): string => $service->refreshCredentials()->access, ); $result = $provider->invoke( new CodexModel('gpt-5.6'), new MessageBag(Message::ofUser('Hello')), ); foreach ($result->asTextStream() as $text) { echo $text; } echo PHP_EOL;
Run the two commands:
php codex.php auth:codex php codex.php ask
Log in once in your browser, then run ask to stream a reply over WebSocket. The example refreshes expired credentials before sending a request.
Login options
| Option | Default | What it does |
|---|---|---|
--port |
1455 |
Port for the local OAuth callback listener |
--timeout |
300 |
Seconds to wait for the browser callback |
--no-browser |
Off | Print the login URL without opening a browser |
--refresh |
Off | Refresh saved credentials instead of starting a new login |
For example, choose another callback port and open the login URL yourself:
php codex.php auth:codex --port=1456 --timeout=120 --no-browser
To refresh an existing login:
php codex.php auth:codex --refresh
Use php codex.php auth:codex --help for help, including standard Symfony Console options.
For an existing application, see registering the login command and custom storage. For a conversation with multiple turns, see reusing WebSocket connections. Factory options are listed in the API reference.
Develop
Run composer install, then castor test, castor phpstan, and castor cs-check.