From 0432a5d3fff996418343422a0669644ecb6a63d0 Mon Sep 17 00:00:00 2001 From: didericis Date: Tue, 2 Jun 2026 12:40:14 -0400 Subject: [PATCH] fix(codex): keep dummy auth refresh timestamp valid --- bot_bottle/codex_auth.py | 12 ++++++++++++ tests/unit/test_codex_auth.py | 19 +++++++++++++++++++ 2 files changed, 31 insertions(+) diff --git a/bot_bottle/codex_auth.py b/bot_bottle/codex_auth.py index 73e9d1c..9a0e6f9 100644 --- a/bot_bottle/codex_auth.py +++ b/bot_bottle/codex_auth.py @@ -122,6 +122,14 @@ def _dummy_exp(now: datetime | None, exp_ts: int | None) -> int: return int(check_now.timestamp()) + 3600 +def _dummy_timestamp(now: datetime | None = None) -> str: + check_now = now or datetime.now(timezone.utc) + if check_now.tzinfo is None: + check_now = check_now.replace(tzinfo=timezone.utc) + check_now = check_now.astimezone(timezone.utc) + return check_now.isoformat(timespec="milliseconds").replace("+00:00", "Z") + + def _dummy_jwt(now: datetime | None = None, *, exp_ts: int | None = None) -> str: return _encode_dummy_jwt({ "exp": _dummy_exp(now, exp_ts), @@ -247,6 +255,10 @@ def _redact_codex_auth( out[key] = inner elif lower == "openai_api_key": out[key] = None + elif lower == "last_refresh": + # Codex parses this as a timestamp on startup. Keep the + # schema valid without copying host-side session metadata. + out[key] = _dummy_timestamp(now) elif lower == "tokens": out[key] = _redact_token_block(inner, now=now, exp_ts=exp_ts) else: diff --git a/tests/unit/test_codex_auth.py b/tests/unit/test_codex_auth.py index ef9d575..e996bac 100644 --- a/tests/unit/test_codex_auth.py +++ b/tests/unit/test_codex_auth.py @@ -157,6 +157,22 @@ class TestCodexHostAccessToken(unittest.TestCase): host_exp, _jwt_payload(dummy["tokens"]["id_token"])["exp"], ) + def test_dummy_auth_replaces_last_refresh_with_valid_timestamp(self): + self._write({ + "auth_mode": "chatgpt", + "last_refresh": "host-refresh-metadata", + "tokens": { + "access_token": _jwt(2000000000), + "refresh_token": "hidden", + }, + }) + dummy = json.loads(codex_dummy_auth_json( + {"CODEX_HOME": str(self.home)}, + now=datetime(2026, 1, 1, 2, 3, 4, 5000, tzinfo=timezone.utc), + )) + self.assertEqual("2026-01-01T02:03:04.005Z", dummy["last_refresh"]) + self.assertNotEqual("host-refresh-metadata", dummy["last_refresh"]) + def test_dummy_auth_keeps_required_account_claim_shape(self): self._write({ "auth_mode": "chatgpt", @@ -215,10 +231,12 @@ class TestCodexHostAccessToken(unittest.TestCase): "top-list-secret", "token-nested-secret", "token-list-secret", + "last-refresh-secret", ] self._write({ "auth_mode": "chatgpt", "session_context": "top-session-secret", + "last_refresh": "last-refresh-secret", "future_nested": {"value": "top-nested-secret"}, "future_list": ["top-list-secret"], "tokens": { @@ -255,6 +273,7 @@ class TestCodexHostAccessToken(unittest.TestCase): dummy = json.loads(dummy_json) self.assertEqual("bot-bottle-placeholder", dummy["session_context"]) + self.assertEqual("2026-01-01T00:00:00.000Z", dummy["last_refresh"]) self.assertEqual({}, dummy["future_nested"]) self.assertEqual([], dummy["future_list"]) self.assertEqual("bot-bottle-placeholder", dummy["tokens"]["refresh_token"])