codex-backend-sdk 0.3.3__tar.gz → 0.3.4__tar.gz

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.
Files changed (39) hide show
  1. codex_backend_sdk-0.3.4/CHANGELOG.md +13 -0
  2. {codex_backend_sdk-0.3.3 → codex_backend_sdk-0.3.4}/PKG-INFO +9 -1
  3. {codex_backend_sdk-0.3.3 → codex_backend_sdk-0.3.4}/README.md +8 -0
  4. {codex_backend_sdk-0.3.3 → codex_backend_sdk-0.3.4}/codex_backend_sdk/__init__.py +1 -1
  5. {codex_backend_sdk-0.3.3 → codex_backend_sdk-0.3.4}/codex_backend_sdk/_client.py +25 -1
  6. {codex_backend_sdk-0.3.3 → codex_backend_sdk-0.3.4}/pyproject.toml +1 -1
  7. codex_backend_sdk-0.3.4/tests/test_account_info.py +68 -0
  8. {codex_backend_sdk-0.3.3 → codex_backend_sdk-0.3.4}/.gitignore +0 -0
  9. {codex_backend_sdk-0.3.3 → codex_backend_sdk-0.3.4}/LICENSE +0 -0
  10. {codex_backend_sdk-0.3.3 → codex_backend_sdk-0.3.4}/codex_backend_sdk/_models.py +0 -0
  11. {codex_backend_sdk-0.3.3 → codex_backend_sdk-0.3.4}/codex_backend_sdk/_streaming.py +0 -0
  12. {codex_backend_sdk-0.3.3 → codex_backend_sdk-0.3.4}/codex_backend_sdk/_transport.py +0 -0
  13. {codex_backend_sdk-0.3.3 → codex_backend_sdk-0.3.4}/codex_backend_sdk/_utils.py +0 -0
  14. {codex_backend_sdk-0.3.3 → codex_backend_sdk-0.3.4}/codex_backend_sdk/codex_client.py +0 -0
  15. {codex_backend_sdk-0.3.3 → codex_backend_sdk-0.3.4}/codex_backend_sdk/oauth.py +0 -0
  16. {codex_backend_sdk-0.3.3 → codex_backend_sdk-0.3.4}/codex_backend_sdk/pkce.py +0 -0
  17. {codex_backend_sdk-0.3.3 → codex_backend_sdk-0.3.4}/codex_backend_sdk/resources/__init__.py +0 -0
  18. {codex_backend_sdk-0.3.3 → codex_backend_sdk-0.3.4}/codex_backend_sdk/resources/_responses_payloads.py +0 -0
  19. {codex_backend_sdk-0.3.3 → codex_backend_sdk-0.3.4}/codex_backend_sdk/resources/codex.py +0 -0
  20. {codex_backend_sdk-0.3.3 → codex_backend_sdk-0.3.4}/codex_backend_sdk/resources/files.py +0 -0
  21. {codex_backend_sdk-0.3.3 → codex_backend_sdk-0.3.4}/codex_backend_sdk/resources/models.py +0 -0
  22. {codex_backend_sdk-0.3.3 → codex_backend_sdk-0.3.4}/codex_backend_sdk/resources/openai_oauth.py +0 -0
  23. {codex_backend_sdk-0.3.3 → codex_backend_sdk-0.3.4}/codex_backend_sdk/resources/realtime.py +0 -0
  24. {codex_backend_sdk-0.3.3 → codex_backend_sdk-0.3.4}/codex_backend_sdk/resources/responses.py +0 -0
  25. {codex_backend_sdk-0.3.3 → codex_backend_sdk-0.3.4}/codex_backend_sdk/storage.py +0 -0
  26. {codex_backend_sdk-0.3.3 → codex_backend_sdk-0.3.4}/docs/backend-api.md +0 -0
  27. {codex_backend_sdk-0.3.3 → codex_backend_sdk-0.3.4}/examples/agent.py +0 -0
  28. {codex_backend_sdk-0.3.3 → codex_backend_sdk-0.3.4}/tests/conftest.py +0 -0
  29. {codex_backend_sdk-0.3.3 → codex_backend_sdk-0.3.4}/tests/test_basic.py +0 -0
  30. {codex_backend_sdk-0.3.3 → codex_backend_sdk-0.3.4}/tests/test_client_retry.py +0 -0
  31. {codex_backend_sdk-0.3.3 → codex_backend_sdk-0.3.4}/tests/test_codex_resources.py +0 -0
  32. {codex_backend_sdk-0.3.3 → codex_backend_sdk-0.3.4}/tests/test_conversation.py +0 -0
  33. {codex_backend_sdk-0.3.3 → codex_backend_sdk-0.3.4}/tests/test_files_resource.py +0 -0
  34. {codex_backend_sdk-0.3.3 → codex_backend_sdk-0.3.4}/tests/test_openai_oauth_resources.py +0 -0
  35. {codex_backend_sdk-0.3.3 → codex_backend_sdk-0.3.4}/tests/test_realtime_resource.py +0 -0
  36. {codex_backend_sdk-0.3.3 → codex_backend_sdk-0.3.4}/tests/test_reasoning.py +0 -0
  37. {codex_backend_sdk-0.3.3 → codex_backend_sdk-0.3.4}/tests/test_responses_resource.py +0 -0
  38. {codex_backend_sdk-0.3.3 → codex_backend_sdk-0.3.4}/tests/test_structured_output.py +0 -0
  39. {codex_backend_sdk-0.3.3 → codex_backend_sdk-0.3.4}/tests/test_tools.py +0 -0
@@ -0,0 +1,13 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ ## [0.3.4] - 2026-05-15
6
+
7
+ ### Added
8
+ - Added `authenticate(interactive=False)` to allow non-interactive credential checks without triggering the browser OAuth flow.
9
+ - Added `client.authenticated` for simple auth-state introspection.
10
+ - Added `client.account_info()` to expose safe non-secret account metadata (`authenticated`, `account_id`, `email`, `plan_type`).
11
+
12
+ ### Documentation
13
+ - Documented the non-interactive authentication helpers in `README.md`.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: codex-backend-sdk
3
- Version: 0.3.3
3
+ Version: 0.3.4
4
4
  Summary: Unofficial Python SDK for the ChatGPT Codex backend API
5
5
  License: MIT
6
6
  License-File: LICENSE
@@ -355,6 +355,14 @@ headers = client.realtime_websocket_headers(session_id="voice-session")
355
355
  `openai_api_key`. The default `authenticate(request_api_key=True)` flow stores
356
356
  that key when available.
357
357
 
358
+ For non-interactive checks, you can avoid triggering a browser login flow:
359
+
360
+ ```python
361
+ client = OpenAI().authenticate(interactive=False)
362
+ print(client.authenticated)
363
+ print(client.account_info())
364
+ ```
365
+
358
366
  ### Embeddings
359
367
 
360
368
  `client.embeddings.create(...)` mirrors the official OpenAI embeddings resource
@@ -329,6 +329,14 @@ headers = client.realtime_websocket_headers(session_id="voice-session")
329
329
  `openai_api_key`. The default `authenticate(request_api_key=True)` flow stores
330
330
  that key when available.
331
331
 
332
+ For non-interactive checks, you can avoid triggering a browser login flow:
333
+
334
+ ```python
335
+ client = OpenAI().authenticate(interactive=False)
336
+ print(client.authenticated)
337
+ print(client.account_info())
338
+ ```
339
+
332
340
  ### Embeddings
333
341
 
334
342
  `client.embeddings.create(...)` mirrors the official OpenAI embeddings resource
@@ -17,7 +17,7 @@ Quickstart:
17
17
  print(response.output_text)
18
18
  """
19
19
 
20
- __version__ = "0.3.3"
20
+ __version__ = "0.3.4"
21
21
 
22
22
  from .oauth import run_oauth_flow, refresh_access_token, obtain_api_key
23
23
  from .storage import load_tokens, save_tokens, TokenStore
@@ -62,7 +62,12 @@ class CodexClient:
62
62
  self.files = Files(self)
63
63
  self.codex = CodexResources(self)
64
64
 
65
- def authenticate(self, *, request_api_key: bool = True) -> "CodexClient":
65
+ def authenticate(
66
+ self,
67
+ *,
68
+ request_api_key: bool = True,
69
+ interactive: bool = True,
70
+ ) -> "CodexClient":
66
71
  from .oauth import refresh_access_token, run_oauth_flow
67
72
 
68
73
  def refresh(store: TokenStore) -> Optional[TokenStore]:
@@ -87,9 +92,28 @@ class CodexClient:
87
92
  self._set_store(store)
88
93
  return self
89
94
 
95
+ if not interactive:
96
+ raise RuntimeError("No usable stored Codex credentials; interactive login required.")
97
+
90
98
  self._set_store(run_oauth_flow(request_api_key=request_api_key))
91
99
  return self
92
100
 
101
+ @property
102
+ def authenticated(self) -> bool:
103
+ """Whether this client currently has OAuth credentials loaded."""
104
+ return bool(self._store and self._store.account_id)
105
+
106
+ def account_info(self) -> dict[str, Any]:
107
+ """Return safe non-secret account metadata decoded from stored tokens."""
108
+ store = self._store
109
+ authenticated = bool(store and store.account_id)
110
+ return {
111
+ "authenticated": authenticated,
112
+ "account_id": store.account_id if store else None,
113
+ "email": store.email if store else None,
114
+ "plan_type": store.plan_type if store else None,
115
+ }
116
+
93
117
  def _set_store(self, store: TokenStore) -> None:
94
118
  self._store = store
95
119
  self._session.headers.update(self._auth_headers())
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "codex-backend-sdk"
7
- version = "0.3.3"
7
+ version = "0.3.4"
8
8
  description = "Unofficial Python SDK for the ChatGPT Codex backend API"
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -0,0 +1,68 @@
1
+ from codex_backend_sdk import CodexClient, TokenStore
2
+
3
+
4
+
5
+ def _store() -> TokenStore:
6
+ return TokenStore(
7
+ access_token="access-secret",
8
+ refresh_token="refresh-secret",
9
+ id_token_raw="id-secret",
10
+ account_id="acct_123",
11
+ openai_api_key="api-secret",
12
+ email="user@example.com",
13
+ plan_type="pro",
14
+ )
15
+
16
+
17
+
18
+ def test_account_info_exposes_safe_metadata_only():
19
+ client = CodexClient(store=_store())
20
+
21
+ info = client.account_info()
22
+
23
+ assert client.authenticated is True
24
+ assert info == {
25
+ "authenticated": True,
26
+ "account_id": "acct_123",
27
+ "email": "user@example.com",
28
+ "plan_type": "pro",
29
+ }
30
+ assert "access-secret" not in repr(info)
31
+ assert "refresh-secret" not in repr(info)
32
+ assert "api-secret" not in repr(info)
33
+
34
+
35
+
36
+ def test_account_info_when_not_authenticated():
37
+ client = CodexClient()
38
+
39
+ assert client.authenticated is False
40
+ assert client.account_info() == {
41
+ "authenticated": False,
42
+ "account_id": None,
43
+ "email": None,
44
+ "plan_type": None,
45
+ }
46
+
47
+
48
+
49
+ def test_authenticate_non_interactive_uses_loaded_tokens(monkeypatch):
50
+ monkeypatch.setattr("codex_backend_sdk._client.load_tokens", lambda: _store())
51
+ monkeypatch.setattr("codex_backend_sdk._client.token_needs_refresh", lambda store: False)
52
+
53
+ client = CodexClient().authenticate(interactive=False)
54
+
55
+ assert client.authenticated is True
56
+ assert client.account_info()["email"] == "user@example.com"
57
+
58
+
59
+
60
+ def test_authenticate_non_interactive_without_tokens_raises(monkeypatch):
61
+ monkeypatch.setattr("codex_backend_sdk._client.load_tokens", lambda: None)
62
+
63
+ try:
64
+ CodexClient().authenticate(interactive=False)
65
+ except RuntimeError as exc:
66
+ assert "interactive login required" in str(exc)
67
+ else:
68
+ raise AssertionError("Expected RuntimeError")