mirror of
https://github.com/openai/codex.git
synced 2026-05-30 07:50:17 +00:00
## Why
`AppServerConfig` is exported as part of the ergonomic Python SDK
surface and passed to `Codex(...)` and `AsyncCodex(...)`. That name
exposes the underlying app-server transport at the same layer where
users are configuring the Codex client. `CodexConfig` makes the common
callsite read naturally and names the object it configures.
## What changed
- Renamed the public configuration dataclass from `AppServerConfig` to
`CodexConfig`.
- Updated `Codex`, `AsyncCodex`, and the transport clients to accept
`CodexConfig`.
- Updated binary-resolution messages, package exports, docs, examples,
and related coverage to use the new public name.
## API impact
```python
from openai_codex import Codex, CodexConfig
with Codex(config=CodexConfig(codex_bin="/path/to/codex")) as codex:
...
```
Callers should now import and construct `CodexConfig`; `AppServerConfig`
is no longer part of the Python SDK surface.
## Validation
- `uv run --frozen --extra dev ruff check src/openai_codex scripts
examples tests`
- Tests are deferred to online CI for this PR.
173 lines
5.8 KiB
Python
173 lines
5.8 KiB
Python
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass
|
|
from typing import Protocol
|
|
|
|
from .async_client import AsyncCodexClient
|
|
from .client import CodexClient
|
|
from .generated.v2_all import (
|
|
AccountLoginCompletedNotification,
|
|
CancelLoginAccountResponse,
|
|
ChatgptDeviceCodeLoginAccountParams,
|
|
ChatgptDeviceCodeLoginAccountResponse,
|
|
ChatgptLoginAccountParams,
|
|
ChatgptLoginAccountResponse,
|
|
LoginAccountParams,
|
|
)
|
|
|
|
|
|
class _AsyncLoginOwner(Protocol):
|
|
"""Subset of AsyncCodex needed by async login handles."""
|
|
|
|
_client: AsyncCodexClient
|
|
|
|
async def _ensure_initialized(self) -> None:
|
|
"""Ensure the owning SDK client has a live Codex connection."""
|
|
...
|
|
|
|
|
|
def start_chatgpt_login(client: CodexClient) -> ChatgptLoginHandle:
|
|
"""Start browser ChatGPT login and return the handle for that attempt."""
|
|
response = client.account_login_start(
|
|
LoginAccountParams(
|
|
root=ChatgptLoginAccountParams(type="chatgpt"),
|
|
)
|
|
)
|
|
response_root = response.root
|
|
if not isinstance(response_root, ChatgptLoginAccountResponse):
|
|
raise RuntimeError(f"unexpected ChatGPT login response: {response_root!r}")
|
|
return ChatgptLoginHandle(
|
|
client,
|
|
response_root.login_id,
|
|
response_root.auth_url,
|
|
)
|
|
|
|
|
|
async def async_start_chatgpt_login(owner: _AsyncLoginOwner) -> AsyncChatgptLoginHandle:
|
|
"""Start async browser ChatGPT login and return that attempt's handle."""
|
|
response = await owner._client.account_login_start(
|
|
LoginAccountParams(
|
|
root=ChatgptLoginAccountParams(type="chatgpt"),
|
|
)
|
|
)
|
|
response_root = response.root
|
|
if not isinstance(response_root, ChatgptLoginAccountResponse):
|
|
raise RuntimeError(f"unexpected ChatGPT login response: {response_root!r}")
|
|
return AsyncChatgptLoginHandle(
|
|
owner,
|
|
response_root.login_id,
|
|
response_root.auth_url,
|
|
)
|
|
|
|
|
|
def start_device_code_login(client: CodexClient) -> DeviceCodeLoginHandle:
|
|
"""Start device-code ChatGPT login and return the handle for that attempt."""
|
|
response = client.account_login_start(
|
|
LoginAccountParams(
|
|
root=ChatgptDeviceCodeLoginAccountParams(type="chatgptDeviceCode"),
|
|
)
|
|
)
|
|
response_root = response.root
|
|
if not isinstance(response_root, ChatgptDeviceCodeLoginAccountResponse):
|
|
raise RuntimeError(f"unexpected device-code login response: {response_root!r}")
|
|
return DeviceCodeLoginHandle(
|
|
client,
|
|
response_root.login_id,
|
|
response_root.verification_url,
|
|
response_root.user_code,
|
|
)
|
|
|
|
|
|
async def async_start_device_code_login(
|
|
owner: _AsyncLoginOwner,
|
|
) -> AsyncDeviceCodeLoginHandle:
|
|
"""Start async device-code ChatGPT login and return that attempt's handle."""
|
|
response = await owner._client.account_login_start(
|
|
LoginAccountParams(
|
|
root=ChatgptDeviceCodeLoginAccountParams(type="chatgptDeviceCode"),
|
|
)
|
|
)
|
|
response_root = response.root
|
|
if not isinstance(response_root, ChatgptDeviceCodeLoginAccountResponse):
|
|
raise RuntimeError(f"unexpected device-code login response: {response_root!r}")
|
|
return AsyncDeviceCodeLoginHandle(
|
|
owner,
|
|
response_root.login_id,
|
|
response_root.verification_url,
|
|
response_root.user_code,
|
|
)
|
|
|
|
|
|
@dataclass(slots=True)
|
|
class ChatgptLoginHandle:
|
|
"""Live browser-login attempt returned by `Codex.login_chatgpt()`."""
|
|
|
|
_client: CodexClient
|
|
login_id: str
|
|
auth_url: str
|
|
|
|
def wait(self) -> AccountLoginCompletedNotification:
|
|
"""Wait for this browser login attempt's completion notification."""
|
|
return self._client.wait_for_login_completed(self.login_id)
|
|
|
|
def cancel(self) -> CancelLoginAccountResponse:
|
|
"""Cancel this browser login attempt."""
|
|
return self._client.account_login_cancel(self.login_id)
|
|
|
|
|
|
@dataclass(slots=True)
|
|
class DeviceCodeLoginHandle:
|
|
"""Live device-code login attempt returned by `Codex.login_chatgpt_device_code()`."""
|
|
|
|
_client: CodexClient
|
|
login_id: str
|
|
verification_url: str
|
|
user_code: str
|
|
|
|
def wait(self) -> AccountLoginCompletedNotification:
|
|
"""Wait for this device-code login attempt's completion notification."""
|
|
return self._client.wait_for_login_completed(self.login_id)
|
|
|
|
def cancel(self) -> CancelLoginAccountResponse:
|
|
"""Cancel this device-code login attempt."""
|
|
return self._client.account_login_cancel(self.login_id)
|
|
|
|
|
|
@dataclass(slots=True)
|
|
class AsyncChatgptLoginHandle:
|
|
"""Live browser-login attempt returned by `AsyncCodex.login_chatgpt()`."""
|
|
|
|
_codex: _AsyncLoginOwner
|
|
login_id: str
|
|
auth_url: str
|
|
|
|
async def wait(self) -> AccountLoginCompletedNotification:
|
|
"""Wait for this browser login attempt's completion notification."""
|
|
await self._codex._ensure_initialized()
|
|
return await self._codex._client.wait_for_login_completed(self.login_id)
|
|
|
|
async def cancel(self) -> CancelLoginAccountResponse:
|
|
"""Cancel this browser login attempt."""
|
|
await self._codex._ensure_initialized()
|
|
return await self._codex._client.account_login_cancel(self.login_id)
|
|
|
|
|
|
@dataclass(slots=True)
|
|
class AsyncDeviceCodeLoginHandle:
|
|
"""Live device-code attempt returned by `AsyncCodex.login_chatgpt_device_code()`."""
|
|
|
|
_codex: _AsyncLoginOwner
|
|
login_id: str
|
|
verification_url: str
|
|
user_code: str
|
|
|
|
async def wait(self) -> AccountLoginCompletedNotification:
|
|
"""Wait for this device-code login attempt's completion notification."""
|
|
await self._codex._ensure_initialized()
|
|
return await self._codex._client.wait_for_login_completed(self.login_id)
|
|
|
|
async def cancel(self) -> CancelLoginAccountResponse:
|
|
"""Cancel this device-code login attempt."""
|
|
await self._codex._ensure_initialized()
|
|
return await self._codex._client.account_login_cancel(self.login_id)
|