cdx-manager 0.20.3 → 0.20.5
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.
- package/README.md +25 -2
- package/VERSION +1 -1
- package/changelogs/CHANGELOGS_0_20_4.md +12 -0
- package/changelogs/CHANGELOGS_0_20_5.md +22 -0
- package/checksums/release-archives.json +22 -0
- package/package.json +1 -1
- package/pyproject.toml +1 -1
- package/src/cli.py +3 -0
- package/src/cli_args.py +13 -0
- package/src/cli_commands.py +3 -1
- package/src/codex_usage.py +10 -1
- package/src/commands/auth.py +61 -2
- package/src/health.py +7 -2
- package/src/provider_runtime.py +15 -13
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
<br clear="left"/>
|
|
6
6
|
|
|
7
|
-
[](LICENSE) ](LICENSE)  
|
|
8
8
|
|
|
9
9
|
**Stop guessing which AI account still has quota.** `cdx` tracks the rate-limit window of every Codex and Claude account you own, tells you which one is usable right now, and launches it with isolated auth — in one command.
|
|
10
10
|
|
|
@@ -500,6 +500,7 @@ cdx history --summary --from 2026-05-01 --to 2026-05-28
|
|
|
500
500
|
| `cdx label <name> --clear [--json]` | Clear a session label |
|
|
501
501
|
| `cdx login <name> [--json]` | Re-authenticate a session (logout + login) |
|
|
502
502
|
| `cdx logout <name> [--json]` | Log out of a session |
|
|
503
|
+
| `cdx auth refresh <name\|all> [--json]` | Make a locked, non-generation Codex auth probe; reports `login_required` when interactive login is needed |
|
|
503
504
|
| `cdx disable <name> [--json]` | Disable a session without deleting it; disabled sessions stay visible and cannot launch |
|
|
504
505
|
| `cdx enable <name> [--json]` | Re-enable a disabled session |
|
|
505
506
|
| `cdx config <name> [--json]` | Show persistent launch settings for a session |
|
|
@@ -1077,7 +1078,29 @@ Without `--yes`, an interactive confirmation is required. `cdx` sends an idempot
|
|
|
1077
1078
|
|
|
1078
1079
|
## Troubleshooting
|
|
1079
1080
|
|
|
1080
|
-
|
|
1081
|
+
### Codex credential refresh
|
|
1082
|
+
|
|
1083
|
+
`cdx auth refresh work` makes a provider-native, non-generation request for one
|
|
1084
|
+
managed Codex profile. It can refresh an expiring access token, but never starts
|
|
1085
|
+
an agent or submits a prompt. Use `cdx auth refresh all --json` when a scheduler
|
|
1086
|
+
needs a stable result; CDX never installs or enables that scheduler for you.
|
|
1087
|
+
|
|
1088
|
+
For example, a user-owned daily cron entry can run:
|
|
1089
|
+
|
|
1090
|
+
```sh
|
|
1091
|
+
0 6 * * * cdx auth refresh all --json
|
|
1092
|
+
```
|
|
1093
|
+
|
|
1094
|
+
`locked` means an interactive session owns the profile refresh lock and was left
|
|
1095
|
+
untouched. If every selected profile is locked, the command is non-zero because
|
|
1096
|
+
it verified no credential; a mix of valid and locked profiles remains successful.
|
|
1097
|
+
Bulk refresh skips disabled profiles, while `cdx auth refresh <name>` still lets
|
|
1098
|
+
an operator inspect one explicitly. `login_required` means the provider rejected
|
|
1099
|
+
the local refresh token; run the supplied `cdx login <name>` command interactively.
|
|
1100
|
+
Hosts keep independent rotating credentials, so copying an auth file is not ongoing
|
|
1101
|
+
credential sync.
|
|
1102
|
+
|
|
1103
|
+
- **`cdx <name>` fails with "not authenticated"** — run `cdx login <name>` first. `cdx doctor` uses the same Codex app-server probe as `cdx auth refresh`, so a local auth file alone is never reported as live authentication; a rejected refresh token is reported as `login_required` with the matching login command.
|
|
1081
1104
|
- **One of two Codex accounts keeps asking for login** — run `cdx doctor --json` and inspect each Codex session's `codex_auth_file`, `codex_live_auth`, and `codex_stale_auth_logs` checks. If recent logs mention expired auth, repair only that isolated profile with `cdx login <name>`. Codex Business profiles can share the same `tokens.account_id`; `cdx doctor` reports `codex_shared_account_id` as a workspace-level clue, not proof that two profiles use the same user. Newly created sessions seed from the current global `~/.codex/auth.json` when one exists. For two separate Codex accounts, create or repair each session by running `cdx login <name>` for that session; `cdx login` does not log out first, so use `cdx logout <name>` explicitly only when you want to clear that isolated profile.
|
|
1082
1105
|
- **`cdx` says no compatible Python 3 interpreter was found** — install Python 3 and make `py -3`, `python`, or `python3` available on PATH.
|
|
1083
1106
|
- **`cdx add` succeeds but the session does not appear** — check that `CDX_HOME` is consistent between calls; a mismatch creates two separate registries.
|
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.20.
|
|
1
|
+
0.20.5
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# CDX Manager 0.20.4
|
|
2
|
+
|
|
3
|
+
## Authentication
|
|
4
|
+
|
|
5
|
+
- Added `cdx auth refresh <name|all> [--json]`, a locked, no-generation Codex
|
|
6
|
+
authentication probe. It can refresh an active provider session when the
|
|
7
|
+
upstream CLI supports it, but never opens a browser, prompts for credentials,
|
|
8
|
+
or fabricates a token.
|
|
9
|
+
- The result distinguishes `valid`, `locked`, `login_required`, and `failed`
|
|
10
|
+
without exposing credential material.
|
|
11
|
+
- Documented manual use and an opt-in cron example. CDX never installs or
|
|
12
|
+
enables a scheduler on its own.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# CDX Manager 0.20.5
|
|
2
|
+
|
|
3
|
+
## Fixes
|
|
4
|
+
|
|
5
|
+
- `cdx doctor` now uses the live Codex app-server authentication probe instead
|
|
6
|
+
of trusting a local auth file, so an expired or rejected refresh token is
|
|
7
|
+
reported as `login_required` with an actionable `cdx login <name>` remedy.
|
|
8
|
+
- `cdx auth refresh all` skips disabled Codex profiles, while an explicit
|
|
9
|
+
`cdx auth refresh <name>` still lets an operator inspect a disabled profile
|
|
10
|
+
deliberately.
|
|
11
|
+
- A bulk Codex auth refresh where every selected profile is locked now exits
|
|
12
|
+
non-zero as `auth_refresh_indeterminate` instead of looking like a silent
|
|
13
|
+
success without verifying any credential.
|
|
14
|
+
- Codex app-server diagnostics no longer keep an unread stderr pipe while
|
|
15
|
+
waiting for stdout, avoiding a possible deadlock or timeout from noisy
|
|
16
|
+
provider diagnostics.
|
|
17
|
+
|
|
18
|
+
## Validation
|
|
19
|
+
|
|
20
|
+
- Package smoke coverage now runs `cdx doctor --json` from both packed npm and
|
|
21
|
+
wheel installs with isolated local provider shims, so packaged doctor
|
|
22
|
+
regressions are caught outside the source checkout.
|
|
@@ -342,6 +342,28 @@
|
|
|
342
342
|
"x86_64-pc-windows-msvc": "5ceff82ecd907fda949dfc27835112ad9e3597da78191a0ede4f7d9db82607a8",
|
|
343
343
|
"x86_64-unknown-linux-musl": "db70bb77f08fd954a7378561954d00ad3084b4ada99a4930aa8f021978439e65"
|
|
344
344
|
}
|
|
345
|
+
},
|
|
346
|
+
"v0.20.3": {
|
|
347
|
+
"github_tarball_sha256": "40990d89ea7130452a95b6cf6a3a63c804f36f08250cf3c325ce7fb63601f875",
|
|
348
|
+
"github_zip_sha256": "be334f6b23145a61d936d50d9609d6a9e6ba2e0749233aede0ed9ce8ff20df8e",
|
|
349
|
+
"tray_assets": {
|
|
350
|
+
"aarch64-apple-darwin": "5ec913f779c11df19aab1f09e8c68dcb4ec54d4a66b3dfbda43d73d67c597797",
|
|
351
|
+
"aarch64-unknown-linux-musl": "d59f41e9521175e22200b38638896d76c2d27f538e52a1f123e1dfdd6521cf4a",
|
|
352
|
+
"x86_64-apple-darwin": "1ec60ca652a18f3991b5d42c64cdf090eb19a658a88005d82642cb6ea9b6455e",
|
|
353
|
+
"x86_64-pc-windows-msvc": "1f9971c1aee7e92685370db1e6a4bcf37b6012fa7452fc14ca3dd545998af82e",
|
|
354
|
+
"x86_64-unknown-linux-musl": "2aabbc00b0fa3a2c8862cc1a5425a074fec99f072bf7b695d31e6e96b9b08c5d"
|
|
355
|
+
}
|
|
356
|
+
},
|
|
357
|
+
"v0.20.4": {
|
|
358
|
+
"github_tarball_sha256": "55323fe428d6d297f14a210a590757443080dc55a87bb6fae4d967fddb505af3",
|
|
359
|
+
"github_zip_sha256": "37f4abb392b372d0f5fe5d5eb7c46e1e81a30b319d1961b5518f20e776f59b75",
|
|
360
|
+
"tray_assets": {
|
|
361
|
+
"aarch64-apple-darwin": "6d61a45ab6f310a83aefbb80d77dc31b1f3a738640197bcca1264c6ca7b70d26",
|
|
362
|
+
"aarch64-unknown-linux-musl": "f2f3a2c45c7deb2ea69cdc7c7b68524e6b5db5d1eda0ff902bda49d875636402",
|
|
363
|
+
"x86_64-apple-darwin": "93d3d9180588edcf573ffa8f9f2e870e8dff9659e198cde531bec0acfc37184f",
|
|
364
|
+
"x86_64-pc-windows-msvc": "7508e04ee07adb7a500b346be1d3891d93aec54dc0ff53643bd8c57e4e411118",
|
|
365
|
+
"x86_64-unknown-linux-musl": "47a1c8cff60c896db659d30d74496045d8c343e74a00f826e608f3973e735532"
|
|
366
|
+
}
|
|
345
367
|
}
|
|
346
368
|
}
|
|
347
369
|
}
|
package/package.json
CHANGED
package/pyproject.toml
CHANGED
package/src/cli.py
CHANGED
|
@@ -13,6 +13,7 @@ from .cli_commands import (
|
|
|
13
13
|
_directory_child_sizes,
|
|
14
14
|
_directory_size_bytes,
|
|
15
15
|
handle_add,
|
|
16
|
+
handle_auth,
|
|
16
17
|
handle_can_resume,
|
|
17
18
|
handle_clean,
|
|
18
19
|
handle_config,
|
|
@@ -151,6 +152,7 @@ __all__ = [
|
|
|
151
152
|
|
|
152
153
|
|
|
153
154
|
_COMMAND_HANDLERS = {
|
|
155
|
+
"auth": handle_auth,
|
|
154
156
|
"add": handle_add,
|
|
155
157
|
"cp": handle_copy,
|
|
156
158
|
"clean": handle_clean,
|
|
@@ -236,6 +238,7 @@ def _print_help(use_color=False):
|
|
|
236
238
|
f" {_style('cdx status <name> [--json] [--refresh|--cached] [--timeout SECONDS]', '36', use_color)}",
|
|
237
239
|
f" {_style('cdx next [--json] [--refresh]', '36', use_color)}",
|
|
238
240
|
f" {_style('cdx reset <name> [--yes] [--json]', '36', use_color)}",
|
|
241
|
+
f" {_style('cdx auth refresh <name|all> [--json]', '36', use_color)}",
|
|
239
242
|
f" {_style('cdx select --provider PROVIDER [--min-reasoning-effort minimal|low|medium|high|xhigh] [--min-power minimal|low|medium|high|xhigh] [--require-ready] [--refresh] --json', '36', use_color)}",
|
|
240
243
|
f" {_style('cdx run [session] --cwd PATH (--prompt-file PATH|--prompt TEXT|--prompt-file -) [--provider PROVIDER] [--model MODEL] [--kind assistant|code-review] [--reasoning-effort minimal|low|medium|high|xhigh] [--power minimal|low|medium|high|xhigh] [--permission review|default|auto|full|workspace-write|read-only|danger-full-access] [--timeout-seconds N] [--detach] [--refresh] --json', '36', use_color)}",
|
|
241
244
|
f" {_style('cdx runs [--limit N] [--since 7d|today|DATE] --json', '36', use_color)}",
|
package/src/cli_args.py
CHANGED
|
@@ -25,6 +25,7 @@ DOCTOR_USAGE = "Usage: cdx doctor [--severity OK|WARN|FAIL[,OK|WARN|FAIL...]] [-
|
|
|
25
25
|
DISK_USAGE = "Usage: cdx disk [profiles] [--candidates] [--json]"
|
|
26
26
|
REPAIR_USAGE = "Usage: cdx repair [--dry-run] [--force] [--json]"
|
|
27
27
|
UPDATE_USAGE = "Usage: cdx update [all] [--check] [--yes] [--json] [--version TAG]"
|
|
28
|
+
AUTH_USAGE = "Usage: cdx auth refresh <name|all> [--json]"
|
|
28
29
|
EXPORT_USAGE = "Usage: cdx export <file> [--include-auth] [--force] [--json] [--sessions name1,name2] [--passphrase-env VAR|--passphrase-stdin]"
|
|
29
30
|
IMPORT_USAGE = "Usage: cdx import <file> [--force|--merge] [--allow-authless-force] [--json] [--sessions name1,name2] [--passphrase-env VAR|--passphrase-stdin]"
|
|
30
31
|
CONTEXT_USAGE = "Usage: cdx context show|path|init|edit|clear|set|append [text...] [--json]"
|
|
@@ -909,6 +910,18 @@ def _parse_update_args(args):
|
|
|
909
910
|
return parsed
|
|
910
911
|
|
|
911
912
|
|
|
913
|
+
def _parse_auth_args(args):
|
|
914
|
+
parsed = _parse_flag_args(args, {
|
|
915
|
+
"--json": {"key": "json", "type": "bool", "default": False},
|
|
916
|
+
}, AUTH_USAGE, positionals_key="values", max_positionals=2)
|
|
917
|
+
if len(parsed["values"]) != 2 or parsed["values"][0] != "refresh":
|
|
918
|
+
raise CdxError(AUTH_USAGE)
|
|
919
|
+
target = parsed["values"][1]
|
|
920
|
+
if not target.strip():
|
|
921
|
+
raise CdxError(AUTH_USAGE)
|
|
922
|
+
return {"target": target, "json": parsed["json"]}
|
|
923
|
+
|
|
924
|
+
|
|
912
925
|
def _parse_export_args(args):
|
|
913
926
|
parsed = _parse_flag_args(args, {
|
|
914
927
|
"--include-auth": {"key": "include_auth", "type": "bool", "default": False},
|
package/src/cli_commands.py
CHANGED
|
@@ -41,6 +41,9 @@ from .cli_view import handle_view as handle_view # re-export for cli.py / tests
|
|
|
41
41
|
from .commands.auth import ( # re-export for cli.py / tests
|
|
42
42
|
_confirm_reset as _confirm_reset,
|
|
43
43
|
)
|
|
44
|
+
from .commands.auth import (
|
|
45
|
+
handle_auth as handle_auth,
|
|
46
|
+
)
|
|
44
47
|
from .commands.auth import (
|
|
45
48
|
handle_login as handle_login,
|
|
46
49
|
)
|
|
@@ -276,4 +279,3 @@ from .commands.tray import (
|
|
|
276
279
|
handle_tray as handle_tray,
|
|
277
280
|
)
|
|
278
281
|
|
|
279
|
-
|
package/src/codex_usage.py
CHANGED
|
@@ -199,6 +199,15 @@ def fetch_codex_rate_limit_diagnostic(session, timeout=5, popen_factory=None):
|
|
|
199
199
|
return _probe_codex_rate_limit_diagnostic(session, auth_home, timeout, popen_factory)
|
|
200
200
|
|
|
201
201
|
|
|
202
|
+
def diagnostic_needs_codex_login(diagnostic):
|
|
203
|
+
"""Classify known auth failures without returning provider text."""
|
|
204
|
+
text = json.dumps(diagnostic.get("response") or {}).lower()
|
|
205
|
+
return any(marker in text for marker in (
|
|
206
|
+
"not logged in", "unauthorized", "authentication token is expired",
|
|
207
|
+
"token_expired", "invalid_grant", "http 401",
|
|
208
|
+
))
|
|
209
|
+
|
|
210
|
+
|
|
202
211
|
def _probe_codex_rate_limit_diagnostic(session, auth_home, timeout=5, popen_factory=None):
|
|
203
212
|
diagnostic = _request_codex_app_server(
|
|
204
213
|
auth_home,
|
|
@@ -236,7 +245,7 @@ def _request_codex_app_server(auth_home, method, params, failure_reason, timeout
|
|
|
236
245
|
["codex", "app-server", "--listen", "stdio://"],
|
|
237
246
|
stdin=subprocess.PIPE,
|
|
238
247
|
stdout=subprocess.PIPE,
|
|
239
|
-
stderr=subprocess.
|
|
248
|
+
stderr=subprocess.DEVNULL,
|
|
240
249
|
env=env,
|
|
241
250
|
text=True,
|
|
242
251
|
bufsize=1,
|
package/src/commands/auth.py
CHANGED
|
@@ -6,9 +6,10 @@ Split out of cli_commands.py. Moved verbatim; re-exported by cli_commands.
|
|
|
6
6
|
import time
|
|
7
7
|
import uuid
|
|
8
8
|
|
|
9
|
-
from ..cli_args import _parse_flag_args, _parse_json_flag
|
|
9
|
+
from ..cli_args import _parse_auth_args, _parse_flag_args, _parse_json_flag
|
|
10
10
|
from ..cli_helpers import (
|
|
11
11
|
_bootstrap_claude_setup_token,
|
|
12
|
+
_json_failure,
|
|
12
13
|
_json_success,
|
|
13
14
|
_local_now_iso,
|
|
14
15
|
_make_notify_progress,
|
|
@@ -16,7 +17,11 @@ from ..cli_helpers import (
|
|
|
16
17
|
_write_json,
|
|
17
18
|
)
|
|
18
19
|
from ..cli_render import _success, _warn
|
|
19
|
-
from ..codex_usage import
|
|
20
|
+
from ..codex_usage import (
|
|
21
|
+
consume_codex_rate_limit_reset_credit,
|
|
22
|
+
diagnostic_needs_codex_login,
|
|
23
|
+
fetch_codex_rate_limit_diagnostic,
|
|
24
|
+
)
|
|
20
25
|
from ..config import PROVIDER_ANTIGRAVITY, PROVIDER_CLAUDE, PROVIDER_CODEX, PROVIDER_OLLAMA
|
|
21
26
|
from ..errors import CdxError
|
|
22
27
|
from ..notify import (
|
|
@@ -33,6 +38,60 @@ def _confirm_reset(name):
|
|
|
33
38
|
answer = input(f"Consume one banked Codex reset for {name}? [y/N] ")
|
|
34
39
|
return answer.strip().lower() in ("y", "yes")
|
|
35
40
|
|
|
41
|
+
|
|
42
|
+
def _refresh_result(session, diagnostic):
|
|
43
|
+
if diagnostic.get("ok"):
|
|
44
|
+
return {"session": session["name"], "provider": PROVIDER_CODEX, "outcome": "valid"}
|
|
45
|
+
if diagnostic.get("reason") == "auth_locked":
|
|
46
|
+
return {"session": session["name"], "provider": PROVIDER_CODEX, "outcome": "locked"}
|
|
47
|
+
if diagnostic_needs_codex_login(diagnostic):
|
|
48
|
+
return {
|
|
49
|
+
"session": session["name"], "provider": PROVIDER_CODEX,
|
|
50
|
+
"outcome": "login_required", "login_command": f"cdx login {session['name']}",
|
|
51
|
+
}
|
|
52
|
+
return {"session": session["name"], "provider": PROVIDER_CODEX, "outcome": "failed"}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def handle_auth(rest, ctx):
|
|
56
|
+
parsed = _parse_auth_args(rest)
|
|
57
|
+
target = parsed["target"]
|
|
58
|
+
if target == "all":
|
|
59
|
+
sessions = [session for session in ctx["service"]["list_sessions"]()
|
|
60
|
+
if session["provider"] == PROVIDER_CODEX and session.get("enabled", True)]
|
|
61
|
+
else:
|
|
62
|
+
session = ctx["service"]["get_session"](target)
|
|
63
|
+
if not session:
|
|
64
|
+
raise CdxError(f"Unknown session: {target}")
|
|
65
|
+
if session["provider"] != PROVIDER_CODEX:
|
|
66
|
+
raise CdxError("Authentication refresh is only available for Codex sessions.")
|
|
67
|
+
sessions = [session]
|
|
68
|
+
if not sessions and target != "all":
|
|
69
|
+
raise CdxError("No managed Codex sessions to refresh.")
|
|
70
|
+
|
|
71
|
+
probe = ctx["options"].get("fetchCodexRateLimitDiagnostic") or fetch_codex_rate_limit_diagnostic
|
|
72
|
+
results = [_refresh_result(session, probe(session)) for session in sessions]
|
|
73
|
+
failures = [result for result in results if result["outcome"] in ("login_required", "failed")]
|
|
74
|
+
all_locked = bool(results) and all(result["outcome"] == "locked" for result in results)
|
|
75
|
+
message = (
|
|
76
|
+
"Codex authentication probe needs attention" if failures
|
|
77
|
+
else "Codex authentication probe could not verify any session" if all_locked
|
|
78
|
+
else "No enabled Codex sessions to refresh" if not results
|
|
79
|
+
else "Codex authentication probe completed"
|
|
80
|
+
)
|
|
81
|
+
if parsed["json"]:
|
|
82
|
+
payload = (
|
|
83
|
+
_json_failure("auth.refresh", "auth_refresh_failed", message, results=results)
|
|
84
|
+
if failures else _json_success("auth.refresh", message, results=results)
|
|
85
|
+
)
|
|
86
|
+
if all_locked:
|
|
87
|
+
payload = _json_failure("auth.refresh", "auth_refresh_indeterminate", message, results=results)
|
|
88
|
+
_write_json(ctx, payload)
|
|
89
|
+
else:
|
|
90
|
+
for result in results:
|
|
91
|
+
suffix = f"; run {result['login_command']}" if result.get("login_command") else ""
|
|
92
|
+
ctx["out"](f"{result['session']}: {result['outcome']}{suffix}\n")
|
|
93
|
+
return 1 if failures or all_locked else 0
|
|
94
|
+
|
|
36
95
|
def handle_ready(rest, ctx):
|
|
37
96
|
"""`cdx ready`: schedule an OS notification for the next session to come back.
|
|
38
97
|
|
package/src/health.py
CHANGED
|
@@ -337,11 +337,16 @@ def _codex_auth_issues(session, diag):
|
|
|
337
337
|
]
|
|
338
338
|
live_status = diag.get("live_status")
|
|
339
339
|
live_ok = live_status == "authenticated"
|
|
340
|
-
live_warn = live_status in ("logged_out", "error")
|
|
340
|
+
live_warn = live_status in ("logged_out", "login_required", "error")
|
|
341
|
+
live_message = (
|
|
342
|
+
f"session {name} Codex authentication requires login; run cdx login {name}"
|
|
343
|
+
if live_status == "login_required"
|
|
344
|
+
else f"session {name} live Codex auth status: {live_status}"
|
|
345
|
+
)
|
|
341
346
|
issues.append(_issue(
|
|
342
347
|
"OK" if live_ok else "WARN" if live_warn else "WARN",
|
|
343
348
|
"codex_live_auth",
|
|
344
|
-
|
|
349
|
+
live_message,
|
|
345
350
|
{
|
|
346
351
|
"session": name,
|
|
347
352
|
"auth_home": diag.get("auth_home"),
|
package/src/provider_runtime.py
CHANGED
|
@@ -13,7 +13,7 @@ from datetime import datetime, timezone
|
|
|
13
13
|
|
|
14
14
|
from .agent_notify import launch_notify_env, notifications_enabled
|
|
15
15
|
from .claude_usage import _clean_oauth_token, _decode_jwt_claims
|
|
16
|
-
from .codex_usage import codex_auth_lock
|
|
16
|
+
from .codex_usage import codex_auth_lock, diagnostic_needs_codex_login, fetch_codex_rate_limit_diagnostic
|
|
17
17
|
from .config import (
|
|
18
18
|
PROVIDER_ANTIGRAVITY,
|
|
19
19
|
PROVIDER_CLAUDE,
|
|
@@ -294,19 +294,21 @@ def codex_auth_diagnostic(session, spawn_sync=None, env_override=None):
|
|
|
294
294
|
"live_status": "unknown",
|
|
295
295
|
"live_error": None,
|
|
296
296
|
}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
result["live_status"] =
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
297
|
+
if not result["local_tokens_present"]:
|
|
298
|
+
result["live_status"] = AUTH_PROBE_LOGGED_OUT
|
|
299
|
+
return result
|
|
300
|
+
diagnostic = fetch_codex_rate_limit_diagnostic(session)
|
|
301
|
+
if diagnostic["ok"]:
|
|
302
|
+
result["live_status"] = AUTH_PROBE_AUTHENTICATED
|
|
303
|
+
elif diagnostic["reason"] == "auth_locked":
|
|
304
|
+
result["live_status"] = AUTH_PROBE_DEGRADED
|
|
305
|
+
result["live_error"] = "Codex authentication probe is locked by an active session."
|
|
306
|
+
elif diagnostic_needs_codex_login(diagnostic):
|
|
307
|
+
result["live_status"] = "login_required"
|
|
308
|
+
result["live_error"] = f"Codex authentication requires login; run cdx login {session['name']}."
|
|
309
|
+
else:
|
|
308
310
|
result["live_status"] = "error"
|
|
309
|
-
result["live_error"] =
|
|
311
|
+
result["live_error"] = f"Codex app-server authentication probe failed ({diagnostic['reason']})."
|
|
310
312
|
return result
|
|
311
313
|
|
|
312
314
|
|