Skip to content

Commit ef99a8f

Browse files
ndemiancclaude
andcommitted
fix(ai): correct the cache comment; guard the refreshed token (PR #43 review)
1. The comment claimed "credits/turns aren't cached", but cloudRoster = data.models caches the per-model fields including turns_left, so the cached-roster fallback CAN show a slightly stale "≈ turns left". Only the account-level credit BALANCE is genuinely not carried (the "$X credits left" header is omitted until the next good fetch). Comment now says exactly that. 2. After refreshCloudToken() succeeds, the refreshed secret was used unchecked — if it came back falsy the retry would send `Authorization: Bearer null`, noise that masks the real 401. Now the retry only fires when the refreshed token is truthy; otherwise it falls through to the cached-roster path. Comment-and-guard only, no behaviour change on the happy path. Full gate: 24 suites, 0 failures. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent f4278bb commit ef99a8f

1 file changed

Lines changed: 8 additions & 3 deletions

File tree

extensions/levelcode-ai/extension.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,9 @@ async function fetchCloudRoster() {
235235
const api = cloudApiUrl();
236236
if (!/^https:\/\//i.test(api) && !/^http:\/\/(localhost|127\.0\.0\.1)([:/]|$)/i.test(api)) { return null; }
237237
// Last-known-good roster: a transient failure keeps the FULL model list rather than collapsing to the
238-
// 2-model offline fallback. credits/turns aren't cached, so the menu just omits those detail lines.
238+
// 2-model offline fallback. The per-model fields — INCLUDING "≈ turns left" — are whatever the last
239+
// good fetch returned, so they may be slightly stale. The account-level credit BALANCE is NOT carried
240+
// here, so pickCloudModel just omits the "$X credits left" header until the next successful fetch.
239241
const cached = () => (cloudRoster && cloudRoster.length ? { plan: cloudPlanName(), models: cloudRoster } : null);
240242
const get = (bearer) => fetch(api + '/api/levelcode/v1/account/models', { headers: { authorization: 'Bearer ' + bearer } });
241243
try {
@@ -245,8 +247,11 @@ async function fetchCloudRoster() {
245247
// 2-model offline fallback and hides the plan's real roster (Opus, K3, …) — exactly the reported
246248
// bug. The profile fetch and the agent loop already refresh on 401; the roster fetch didn't.
247249
if (res.status === 401 && await refreshCloudToken()) {
248-
token = await ctx.secrets.get(ACCOUNT_TOKEN_KEY);
249-
res = await get(token);
250+
// Guard the refreshed token: if it comes back falsy for any reason, retrying would send
251+
// `Authorization: Bearer null` — noise that masks the real 401. Skip the retry instead and let
252+
// the !res.ok path below fall back to the cached roster.
253+
const fresh = await ctx.secrets.get(ACCOUNT_TOKEN_KEY);
254+
if (fresh) { token = fresh; res = await get(token); }
250255
}
251256
if (!res.ok) { dbg('cloud.roster', { ok: false, status: res.status }); return cached(); }
252257
const data = await res.json().catch(() => null);

0 commit comments

Comments
 (0)