code-puppy 0.0.372__py3-none-any.whl → 0.0.373__py3-none-any.whl
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.
- code_puppy/claude_cache_client.py +9 -9
- code_puppy/plugins/claude_code_oauth/README.md +1 -1
- code_puppy/plugins/claude_code_oauth/SETUP.md +1 -1
- code_puppy/plugins/claude_code_oauth/utils.py +44 -13
- {code_puppy-0.0.372.dist-info → code_puppy-0.0.373.dist-info}/METADATA +1 -1
- {code_puppy-0.0.372.dist-info → code_puppy-0.0.373.dist-info}/RECORD +11 -11
- {code_puppy-0.0.372.data → code_puppy-0.0.373.data}/data/code_puppy/models.json +0 -0
- {code_puppy-0.0.372.data → code_puppy-0.0.373.data}/data/code_puppy/models_dev_api.json +0 -0
- {code_puppy-0.0.372.dist-info → code_puppy-0.0.373.dist-info}/WHEEL +0 -0
- {code_puppy-0.0.372.dist-info → code_puppy-0.0.373.dist-info}/entry_points.txt +0 -0
- {code_puppy-0.0.372.dist-info → code_puppy-0.0.373.dist-info}/licenses/LICENSE +0 -0
|
@@ -26,7 +26,7 @@ import httpx
|
|
|
26
26
|
|
|
27
27
|
logger = logging.getLogger(__name__)
|
|
28
28
|
|
|
29
|
-
# Refresh token if it's older than
|
|
29
|
+
# Refresh token if it's older than the configured max age (seconds)
|
|
30
30
|
TOKEN_MAX_AGE_SECONDS = 3600
|
|
31
31
|
|
|
32
32
|
# Tool name prefix for Claude Code OAuth compatibility
|
|
@@ -94,13 +94,13 @@ class ClaudeCacheAsyncClient(httpx.AsyncClient):
|
|
|
94
94
|
return age
|
|
95
95
|
|
|
96
96
|
# Fall back to calculating from 'exp' claim
|
|
97
|
-
# Assume tokens are typically valid for
|
|
97
|
+
# Assume tokens are typically valid for TOKEN_MAX_AGE_SECONDS
|
|
98
98
|
if "exp" in payload:
|
|
99
99
|
exp = float(payload["exp"])
|
|
100
100
|
# If exp is in the future, calculate how long until expiry
|
|
101
|
-
# and assume the token was issued
|
|
101
|
+
# and assume the token was issued TOKEN_MAX_AGE_SECONDS before expiry
|
|
102
102
|
time_until_exp = exp - now
|
|
103
|
-
# If token has less than
|
|
103
|
+
# If token has less than TOKEN_MAX_AGE_SECONDS left, it's "old"
|
|
104
104
|
age = TOKEN_MAX_AGE_SECONDS - time_until_exp
|
|
105
105
|
return max(0, age)
|
|
106
106
|
|
|
@@ -119,13 +119,13 @@ class ClaudeCacheAsyncClient(httpx.AsyncClient):
|
|
|
119
119
|
return None
|
|
120
120
|
|
|
121
121
|
def _should_refresh_token(self, request: httpx.Request) -> bool:
|
|
122
|
-
"""Check if the token should be refreshed (within
|
|
122
|
+
"""Check if the token should be refreshed (within the max-age window).
|
|
123
123
|
|
|
124
124
|
Uses two strategies:
|
|
125
125
|
1. Decode JWT to check token age (if possible)
|
|
126
126
|
2. Fall back to stored expires_at from token file
|
|
127
127
|
|
|
128
|
-
Returns True if token expires within TOKEN_MAX_AGE_SECONDS
|
|
128
|
+
Returns True if token expires within TOKEN_MAX_AGE_SECONDS.
|
|
129
129
|
"""
|
|
130
130
|
token = self._extract_bearer_token(request)
|
|
131
131
|
if not token:
|
|
@@ -169,7 +169,7 @@ class ClaudeCacheAsyncClient(httpx.AsyncClient):
|
|
|
169
169
|
if not tokens:
|
|
170
170
|
return False
|
|
171
171
|
|
|
172
|
-
# is_token_expired already uses
|
|
172
|
+
# is_token_expired already uses the configured refresh buffer window
|
|
173
173
|
return is_token_expired(tokens)
|
|
174
174
|
except Exception as exc:
|
|
175
175
|
logger.debug("Error checking stored token expiry: %s", exc)
|
|
@@ -366,10 +366,10 @@ class ClaudeCacheAsyncClient(httpx.AsyncClient):
|
|
|
366
366
|
|
|
367
367
|
# Handle auth errors with token refresh
|
|
368
368
|
try:
|
|
369
|
-
if response.status_code in (400, 401) and not request.extensions.get(
|
|
369
|
+
if response.status_code in (400, 401, 403) and not request.extensions.get(
|
|
370
370
|
"claude_oauth_refresh_attempted"
|
|
371
371
|
):
|
|
372
|
-
is_auth_error = response.status_code
|
|
372
|
+
is_auth_error = response.status_code in (401, 403)
|
|
373
373
|
|
|
374
374
|
if response.status_code == 400:
|
|
375
375
|
is_auth_error = self._is_cloudflare_html_error(response)
|
|
@@ -52,7 +52,7 @@ The plugin ships with sensible defaults in `config.py`:
|
|
|
52
52
|
```python
|
|
53
53
|
CLAUDE_CODE_OAUTH_CONFIG = {
|
|
54
54
|
"auth_url": "https://claude.ai/oauth/authorize",
|
|
55
|
-
"token_url": "https://
|
|
55
|
+
"token_url": "https://console.anthropic.com/v1/oauth/token",
|
|
56
56
|
"api_base_url": "https://api.anthropic.com",
|
|
57
57
|
"client_id": "9d1c250a-e61b-44d9-88ed-5944d1962f5e",
|
|
58
58
|
"scope": "org:create_api_key user:profile user:inference",
|
|
@@ -37,7 +37,7 @@ Anthropic exposes a shared **public client** (`claude-cli`) for command-line too
|
|
|
37
37
|
```python
|
|
38
38
|
CLAUDE_CODE_OAUTH_CONFIG = {
|
|
39
39
|
"auth_url": "https://claude.ai/oauth/authorize",
|
|
40
|
-
"token_url": "https://
|
|
40
|
+
"token_url": "https://console.anthropic.com/v1/oauth/token",
|
|
41
41
|
"api_base_url": "https://api.anthropic.com",
|
|
42
42
|
"client_id": "9d1c250a-e61b-44d9-88ed-5944d1962f5e",
|
|
43
43
|
"scope": "org:create_api_key user:profile user:inference",
|
|
@@ -21,10 +21,10 @@ from .config import (
|
|
|
21
21
|
get_token_storage_path,
|
|
22
22
|
)
|
|
23
23
|
|
|
24
|
-
# Proactive refresh buffer
|
|
25
|
-
#
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
# Proactive refresh buffer default (seconds). Actual buffer is dynamic
|
|
25
|
+
# based on expires_in to avoid overly aggressive refreshes.
|
|
26
|
+
TOKEN_REFRESH_BUFFER_SECONDS = 300
|
|
27
|
+
MIN_REFRESH_BUFFER_SECONDS = 30
|
|
28
28
|
|
|
29
29
|
logger = logging.getLogger(__name__)
|
|
30
30
|
|
|
@@ -146,15 +146,40 @@ def _calculate_expires_at(expires_in: Optional[float]) -> Optional[float]:
|
|
|
146
146
|
return None
|
|
147
147
|
|
|
148
148
|
|
|
149
|
-
def
|
|
149
|
+
def _calculate_refresh_buffer(expires_in: Optional[float]) -> float:
|
|
150
|
+
default_buffer = float(TOKEN_REFRESH_BUFFER_SECONDS)
|
|
151
|
+
if expires_in is None:
|
|
152
|
+
return default_buffer
|
|
153
|
+
try:
|
|
154
|
+
expires_value = float(expires_in)
|
|
155
|
+
except (TypeError, ValueError):
|
|
156
|
+
return default_buffer
|
|
157
|
+
return min(default_buffer, max(MIN_REFRESH_BUFFER_SECONDS, expires_value * 0.1))
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _get_expires_at_value(tokens: Dict[str, Any]) -> Optional[float]:
|
|
150
161
|
expires_at = tokens.get("expires_at")
|
|
151
162
|
if expires_at is None:
|
|
152
|
-
return
|
|
163
|
+
return None
|
|
153
164
|
try:
|
|
154
|
-
|
|
165
|
+
return float(expires_at)
|
|
155
166
|
except (TypeError, ValueError):
|
|
167
|
+
return None
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def _is_token_actually_expired(tokens: Dict[str, Any]) -> bool:
|
|
171
|
+
expires_at_value = _get_expires_at_value(tokens)
|
|
172
|
+
if expires_at_value is None:
|
|
173
|
+
return False
|
|
174
|
+
return time.time() >= expires_at_value
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def is_token_expired(tokens: Dict[str, Any]) -> bool:
|
|
178
|
+
expires_at_value = _get_expires_at_value(tokens)
|
|
179
|
+
if expires_at_value is None:
|
|
156
180
|
return False
|
|
157
|
-
|
|
181
|
+
buffer_seconds = _calculate_refresh_buffer(tokens.get("expires_in"))
|
|
182
|
+
return time.time() >= expires_at_value - buffer_seconds
|
|
158
183
|
|
|
159
184
|
|
|
160
185
|
def update_claude_code_model_tokens(access_token: str) -> bool:
|
|
@@ -216,11 +241,12 @@ def refresh_access_token(force: bool = False) -> Optional[str]:
|
|
|
216
241
|
new_tokens = response.json()
|
|
217
242
|
tokens["access_token"] = new_tokens.get("access_token")
|
|
218
243
|
tokens["refresh_token"] = new_tokens.get("refresh_token", refresh_token)
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
244
|
+
expires_in_value = new_tokens.get("expires_in")
|
|
245
|
+
if expires_in_value is None:
|
|
246
|
+
expires_in_value = tokens.get("expires_in")
|
|
247
|
+
if expires_in_value is not None:
|
|
248
|
+
tokens["expires_in"] = expires_in_value
|
|
249
|
+
tokens["expires_at"] = _calculate_expires_at(expires_in_value)
|
|
224
250
|
if save_tokens(tokens):
|
|
225
251
|
update_claude_code_model_tokens(tokens["access_token"])
|
|
226
252
|
return tokens["access_token"]
|
|
@@ -249,6 +275,11 @@ def get_valid_access_token() -> Optional[str]:
|
|
|
249
275
|
refreshed = refresh_access_token()
|
|
250
276
|
if refreshed:
|
|
251
277
|
return refreshed
|
|
278
|
+
if not _is_token_actually_expired(tokens):
|
|
279
|
+
logger.warning(
|
|
280
|
+
"Claude Code token refresh failed; using existing access token until expiry"
|
|
281
|
+
)
|
|
282
|
+
return access_token
|
|
252
283
|
logger.warning("Claude Code token refresh failed")
|
|
253
284
|
return None
|
|
254
285
|
|
|
@@ -2,7 +2,7 @@ code_puppy/__init__.py,sha256=xMPewo9RNHb3yfFNIk5WCbv2cvSPtJOCgK2-GqLbNnU,373
|
|
|
2
2
|
code_puppy/__main__.py,sha256=pDVssJOWP8A83iFkxMLY9YteHYat0EyWDQqMkKHpWp4,203
|
|
3
3
|
code_puppy/callbacks.py,sha256=Pp0VyeXJBEtk-N_RSWr5pbveelovsdLUiJ4f11dzwGw,10775
|
|
4
4
|
code_puppy/chatgpt_codex_client.py,sha256=upMuAfOhMB7SEpVw4CU4GjgaeZ8X65ri3yNM-dnlmYA,12308
|
|
5
|
-
code_puppy/claude_cache_client.py,sha256=
|
|
5
|
+
code_puppy/claude_cache_client.py,sha256=GtwYrxcTe0pE-JGtl1ysR2qskfeE73_x4w7q_u-kR1k,24026
|
|
6
6
|
code_puppy/cli_runner.py,sha256=w5CLKgQYYaT7My3Cga2StXYol-u6DBxNzzUuhhsfhsA,34952
|
|
7
7
|
code_puppy/config.py,sha256=aKWADF6PdHnr9_0ZVZHwBh5NH9uSAx1lmIiycfaYEF8,54737
|
|
8
8
|
code_puppy/error_logging.py,sha256=a80OILCUtJhexI6a9GM-r5LqIdjvSRzggfgPp2jv1X0,3297
|
|
@@ -165,13 +165,13 @@ code_puppy/plugins/chatgpt_oauth/oauth_flow.py,sha256=i-CP2gpzEBT3ogUt-oTMexiP2o
|
|
|
165
165
|
code_puppy/plugins/chatgpt_oauth/register_callbacks.py,sha256=KNi5-R0EXtkBm3p55ttAxuA_ApaOs_tsGDnPt-5vgGA,2998
|
|
166
166
|
code_puppy/plugins/chatgpt_oauth/test_plugin.py,sha256=oHX7Eb_Hb4rgRpOWdhtFp8Jj6_FDuvXQITRPiNy4tRo,9622
|
|
167
167
|
code_puppy/plugins/chatgpt_oauth/utils.py,sha256=fzpsCQOv0kqPWmG5vNEV_GLSUrMQh8cF7tdIjSOt1Dc,16504
|
|
168
|
-
code_puppy/plugins/claude_code_oauth/README.md,sha256=
|
|
169
|
-
code_puppy/plugins/claude_code_oauth/SETUP.md,sha256=
|
|
168
|
+
code_puppy/plugins/claude_code_oauth/README.md,sha256=fOSDDzCdm2JCKjU5J82IRHIAhxYxl8_UmHo7uH4AbFo,5469
|
|
169
|
+
code_puppy/plugins/claude_code_oauth/SETUP.md,sha256=DCNLkSU9nf86S1rsrIg8HBe87NZrF8YND8P4ettWeEM,3289
|
|
170
170
|
code_puppy/plugins/claude_code_oauth/__init__.py,sha256=mCcOU-wM7LNCDjr-w-WLPzom8nTF1UNt4nqxGE6Rt0k,187
|
|
171
171
|
code_puppy/plugins/claude_code_oauth/config.py,sha256=DjGySCkvjSGZds6DYErLMAi3TItt8iSLGvyJN98nSEM,2013
|
|
172
172
|
code_puppy/plugins/claude_code_oauth/register_callbacks.py,sha256=ZnLQfwssbQXdCpnMGHjzEIzXTLc_OZ1UGS4npU5k5m8,9168
|
|
173
173
|
code_puppy/plugins/claude_code_oauth/test_plugin.py,sha256=yQy4EeZl4bjrcog1d8BjknoDTRK75mRXXvkSQJYSSEM,9286
|
|
174
|
-
code_puppy/plugins/claude_code_oauth/utils.py,sha256=
|
|
174
|
+
code_puppy/plugins/claude_code_oauth/utils.py,sha256=2ioGG-4FCh4WdHrN2MJvWKbPWA-YVg_WTEeddc1xv4U,18557
|
|
175
175
|
code_puppy/plugins/customizable_commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
176
176
|
code_puppy/plugins/customizable_commands/register_callbacks.py,sha256=zVMfIzr--hVn0IOXxIicbmgj2s-HZUgtrOc0NCDOnDw,5183
|
|
177
177
|
code_puppy/plugins/example_custom_command/README.md,sha256=5c5Zkm7CW6BDSfe3WoLU7GW6t5mjjYAbu9-_pu-b3p4,8244
|
|
@@ -208,10 +208,10 @@ code_puppy/tools/browser/chromium_terminal_manager.py,sha256=w1thQ_ACb6oV45L93TS
|
|
|
208
208
|
code_puppy/tools/browser/terminal_command_tools.py,sha256=9byOZku-dwvTtCl532xt7Lumed_jTn0sLvUe_X75XCQ,19068
|
|
209
209
|
code_puppy/tools/browser/terminal_screenshot_tools.py,sha256=J_21YO_495NvYgNFu9KQP6VYg2K_f8CtSdZuF94Yhnw,18448
|
|
210
210
|
code_puppy/tools/browser/terminal_tools.py,sha256=F5LjVH3udSCFHmqC3O1UJLoLozZFZsEdX42jOmkqkW0,17853
|
|
211
|
-
code_puppy-0.0.
|
|
212
|
-
code_puppy-0.0.
|
|
213
|
-
code_puppy-0.0.
|
|
214
|
-
code_puppy-0.0.
|
|
215
|
-
code_puppy-0.0.
|
|
216
|
-
code_puppy-0.0.
|
|
217
|
-
code_puppy-0.0.
|
|
211
|
+
code_puppy-0.0.373.data/data/code_puppy/models.json,sha256=jAHRsCl3trysP4vU_k_ltA8GcFU2APd4lxFl8-4Jnvc,3243
|
|
212
|
+
code_puppy-0.0.373.data/data/code_puppy/models_dev_api.json,sha256=wHjkj-IM_fx1oHki6-GqtOoCrRMR0ScK0f-Iz0UEcy8,548187
|
|
213
|
+
code_puppy-0.0.373.dist-info/METADATA,sha256=bdAEhxmZ0q5kT15tSnZ2yuLdwhZ-a2-UiiQY0NZXN9M,27604
|
|
214
|
+
code_puppy-0.0.373.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
215
|
+
code_puppy-0.0.373.dist-info/entry_points.txt,sha256=Tp4eQC99WY3HOKd3sdvb22vZODRq0XkZVNpXOag_KdI,91
|
|
216
|
+
code_puppy-0.0.373.dist-info/licenses/LICENSE,sha256=31u8x0SPgdOq3izJX41kgFazWsM43zPEF9eskzqbJMY,1075
|
|
217
|
+
code_puppy-0.0.373.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|