Skip to content

Commit c02d976

Browse files
committed
Remove automatic claude token refresh
1 parent da3affe commit c02d976

File tree

4 files changed

+11
-102
lines changed

4 files changed

+11
-102
lines changed

CHANGELOG.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [0.7.2] - 2026-01-03
11+
12+
### Removed
13+
14+
- Automatic OAuth token refresh for Claude - was corrupting tokens stored in Keychain
15+
1016
## [0.7.1] - 2026-01-02
1117

1218
### Fixed
@@ -99,7 +105,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
99105
- Standalone binary builds for macOS (arm64 and x64)
100106
- npm package distribution
101107

102-
[Unreleased]: https://github.com/AgentWorkforce/limit/compare/v0.7.1...HEAD
108+
[Unreleased]: https://github.com/AgentWorkforce/limit/compare/v0.7.2...HEAD
109+
[0.7.2]: https://github.com/AgentWorkforce/limit/compare/v0.7.1...v0.7.2
103110
[0.7.1]: https://github.com/AgentWorkforce/limit/compare/v0.7.0...v0.7.1
104111
[0.7.0]: https://github.com/AgentWorkforce/limit/compare/v0.6.2...v0.7.0
105112
[0.6.2]: https://github.com/AgentWorkforce/limit/compare/v0.6.0...v0.6.2

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "agent-limit",
3-
"version": "0.7.1",
3+
"version": "0.7.2",
44
"description": "Terminal dashboard to monitor Claude Code, Codex, and other agent usage limits",
55
"type": "module",
66
"main": "src/index.tsx",

src/providers/claude.ts

Lines changed: 2 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,16 @@
11
import {
22
getClaudeCredentials,
3-
refreshClaudeToken,
4-
saveClaudeCredentials,
53
type ClaudeCredentials,
64
} from "../utils/keychain";
75
import { timeUntil } from "../utils/time";
86
import type { ProviderStatus } from "./types";
97

10-
const TOKEN_REFRESH_BUFFER_MS = 5 * 60 * 1000;
11-
128
interface ClaudeUsageResponse {
139
five_hour: { utilization: number; resets_at: string | null } | null;
1410
seven_day: { utilization: number; resets_at: string | null } | null;
1511
seven_day_opus: { utilization: number; resets_at: string | null } | null;
1612
}
1713

18-
async function tryRefreshCredentials(
19-
credentials: ClaudeCredentials
20-
): Promise<ClaudeCredentials | null> {
21-
const refreshed = await refreshClaudeToken(credentials.refreshToken);
22-
if (refreshed) {
23-
await saveClaudeCredentials(refreshed);
24-
}
25-
return refreshed;
26-
}
27-
2814
async function fetchUsageWithCredentials(
2915
credentials: ClaudeCredentials
3016
): Promise<Response> {
@@ -41,7 +27,7 @@ async function fetchUsageWithCredentials(
4127
}
4228

4329
export async function fetchClaudeUsage(): Promise<ProviderStatus> {
44-
let credentials = await getClaudeCredentials();
30+
const credentials = await getClaudeCredentials();
4531

4632
if (!credentials) {
4733
return {
@@ -52,24 +38,8 @@ export async function fetchClaudeUsage(): Promise<ProviderStatus> {
5238
};
5339
}
5440

55-
const tokenExpiresSoon = credentials.expiresAt < Date.now() + TOKEN_REFRESH_BUFFER_MS;
56-
if (tokenExpiresSoon) {
57-
const refreshed = await tryRefreshCredentials(credentials);
58-
if (refreshed) {
59-
credentials = refreshed;
60-
}
61-
}
62-
6341
try {
64-
let response = await fetchUsageWithCredentials(credentials);
65-
66-
if (response.status === 401) {
67-
const refreshed = await tryRefreshCredentials(credentials);
68-
if (refreshed) {
69-
credentials = refreshed;
70-
response = await fetchUsageWithCredentials(credentials);
71-
}
72-
}
42+
const response = await fetchUsageWithCredentials(credentials);
7343

7444
if (!response.ok) {
7545
if (response.status === 401) {

src/utils/keychain.ts

Lines changed: 0 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
import { $ } from "bun";
22

3-
const CLAUDE_CLIENT_ID = "9d1c250a-e61b-44d9-88ed-5944d1962f5e";
4-
const CLAUDE_TOKEN_ENDPOINT = "https://console.anthropic.com/v1/oauth/token";
53
const KEYCHAIN_SERVICE = "Claude Code-credentials";
64

75
export async function getKeychainCredentials(service: string): Promise<string | null> {
@@ -15,16 +13,6 @@ export async function getKeychainCredentials(service: string): Promise<string |
1513
}
1614
}
1715

18-
async function setKeychainCredentials(service: string, data: string): Promise<boolean> {
19-
try {
20-
await $`security delete-generic-password -s ${service}`.quiet().nothrow();
21-
await $`security add-generic-password -s ${service} -w ${data}`.quiet();
22-
return true;
23-
} catch {
24-
return false;
25-
}
26-
}
27-
2816
export interface ClaudeCredentials {
2917
accessToken: string;
3018
refreshToken: string;
@@ -45,62 +33,6 @@ export async function getClaudeCredentials(): Promise<ClaudeCredentials | null>
4533
}
4634
}
4735

48-
interface TokenRefreshResponse {
49-
token_type: string;
50-
access_token: string;
51-
expires_in: number;
52-
refresh_token: string;
53-
scope: string;
54-
}
55-
56-
export async function refreshClaudeToken(
57-
refreshToken: string
58-
): Promise<ClaudeCredentials | null> {
59-
try {
60-
const response = await fetch(CLAUDE_TOKEN_ENDPOINT, {
61-
method: "POST",
62-
headers: { "Content-Type": "application/json" },
63-
body: JSON.stringify({
64-
grant_type: "refresh_token",
65-
refresh_token: refreshToken,
66-
client_id: CLAUDE_CLIENT_ID,
67-
}),
68-
});
69-
70-
if (!response.ok) {
71-
return null;
72-
}
73-
74-
const data: TokenRefreshResponse = await response.json();
75-
76-
return {
77-
accessToken: data.access_token,
78-
refreshToken: data.refresh_token,
79-
expiresAt: Date.now() + data.expires_in * 1000,
80-
scopes: data.scope.split(" "),
81-
subscriptionType: "Pro",
82-
};
83-
} catch {
84-
return null;
85-
}
86-
}
87-
88-
export async function saveClaudeCredentials(
89-
credentials: ClaudeCredentials
90-
): Promise<boolean> {
91-
try {
92-
const raw = await getKeychainCredentials(KEYCHAIN_SERVICE);
93-
if (!raw) return false;
94-
95-
const parsed = JSON.parse(raw);
96-
parsed.claudeAiOauth = credentials;
97-
98-
return await setKeychainCredentials(KEYCHAIN_SERVICE, JSON.stringify(parsed));
99-
} catch {
100-
return false;
101-
}
102-
}
103-
10436
export interface CodexCredentials {
10537
accessToken: string;
10638
accountId: string;

0 commit comments

Comments
 (0)