agent-limit 0.7.0 → 0.7.2
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 +52 -53
- package/bin/cli.tsx +3 -2
- package/package.json +1 -1
- package/src/providers/claude.ts +2 -32
- package/src/utils/keychain.ts +0 -68
package/README.md
CHANGED
|
@@ -2,35 +2,31 @@
|
|
|
2
2
|
|
|
3
3
|
Terminal dashboard to monitor Claude Code and Codex usage limits.
|
|
4
4
|
|
|
5
|
-
##
|
|
6
|
-
|
|
7
|
-
### Via npm (requires Bun)
|
|
5
|
+
## Quickstart
|
|
8
6
|
|
|
9
7
|
```bash
|
|
10
8
|
npm install -g agent-limit
|
|
9
|
+
agent-limit usage
|
|
11
10
|
```
|
|
12
11
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
Download from [GitHub Releases](https://github.com/AgentWorkforce/limit/releases):
|
|
16
|
-
|
|
17
|
-
```bash
|
|
18
|
-
# Apple Silicon
|
|
19
|
-
curl -L https://github.com/AgentWorkforce/limit/releases/latest/download/agent-limit-darwin-arm64 -o /usr/local/bin/agent-limit
|
|
20
|
-
chmod +x /usr/local/bin/agent-limit
|
|
12
|
+
## Example
|
|
21
13
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
14
|
+
```
|
|
15
|
+
[███████░░░░|░░░░░░░░░] 30% ↓12%
|
|
16
|
+
^ you should be at 42%, but you're at 30% (12% under pace)
|
|
25
17
|
```
|
|
26
18
|
|
|
27
|
-
|
|
19
|
+
- `↓X%` (green) = under pace, you have headroom
|
|
20
|
+
- `↑X%` (red) = over pace, might hit limits early
|
|
28
21
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
22
|
+
## Features
|
|
23
|
+
|
|
24
|
+
- Real-time usage tracking for Claude Code and Codex
|
|
25
|
+
- Trajectory markers showing if you're ahead or behind your usage pace
|
|
26
|
+
- Auto-refresh every 60 seconds
|
|
27
|
+
- Color-coded usage indicators
|
|
32
28
|
|
|
33
|
-
## CLI
|
|
29
|
+
## CLI Reference
|
|
34
30
|
|
|
35
31
|
| Command | Description |
|
|
36
32
|
|---------|-------------|
|
|
@@ -38,20 +34,13 @@ agent-limit usage
|
|
|
38
34
|
| `agent-limit version` | Show version |
|
|
39
35
|
| `agent-limit help` | Show help message |
|
|
40
36
|
|
|
41
|
-
|
|
37
|
+
### Dashboard Controls
|
|
42
38
|
|
|
43
39
|
| Key | Action |
|
|
44
40
|
|-----|--------|
|
|
45
41
|
| `q` | Quit |
|
|
46
42
|
| `r` | Refresh |
|
|
47
43
|
|
|
48
|
-
## Features
|
|
49
|
-
|
|
50
|
-
- Real-time usage tracking for Claude Code and Codex
|
|
51
|
-
- Trajectory markers showing if you're ahead or behind your usage pace
|
|
52
|
-
- Auto-refresh every 60 seconds
|
|
53
|
-
- Color-coded usage indicators
|
|
54
|
-
|
|
55
44
|
## Supported Providers
|
|
56
45
|
|
|
57
46
|
| Provider | Status | Data Source |
|
|
@@ -59,6 +48,42 @@ agent-limit usage
|
|
|
59
48
|
| Claude Code | Full support | macOS Keychain + Anthropic API |
|
|
60
49
|
| Codex | Full support | `~/.codex/auth.json` + OpenAI API |
|
|
61
50
|
|
|
51
|
+
## How It Works
|
|
52
|
+
|
|
53
|
+
agent-limit reads credentials from standard locations:
|
|
54
|
+
|
|
55
|
+
- **Claude Code**: macOS Keychain (`Claude Code-credentials`)
|
|
56
|
+
- **Codex**: `~/.codex/auth.json`
|
|
57
|
+
|
|
58
|
+
It then fetches usage data from each provider's API and displays it in a unified dashboard.
|
|
59
|
+
|
|
60
|
+
## Installation Options
|
|
61
|
+
|
|
62
|
+
### Via npm
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
npm install -g agent-limit
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Standalone Binary (no dependencies)
|
|
69
|
+
|
|
70
|
+
Download from [GitHub Releases](https://github.com/AgentWorkforce/limit/releases):
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
# Apple Silicon
|
|
74
|
+
curl -L https://github.com/AgentWorkforce/limit/releases/latest/download/agent-limit-darwin-arm64 -o /usr/local/bin/agent-limit
|
|
75
|
+
chmod +x /usr/local/bin/agent-limit
|
|
76
|
+
|
|
77
|
+
# Intel Mac
|
|
78
|
+
curl -L https://github.com/AgentWorkforce/limit/releases/latest/download/agent-limit-darwin-x64 -o /usr/local/bin/agent-limit
|
|
79
|
+
chmod +x /usr/local/bin/agent-limit
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Requirements
|
|
83
|
+
|
|
84
|
+
- macOS (uses Keychain for credential storage)
|
|
85
|
+
- Active CLI authentication for providers you want to monitor
|
|
86
|
+
|
|
62
87
|
## Development
|
|
63
88
|
|
|
64
89
|
```bash
|
|
@@ -96,32 +121,6 @@ bun run build:x64 # Intel
|
|
|
96
121
|
|
|
97
122
|
Binaries are output to `dist/`.
|
|
98
123
|
|
|
99
|
-
## How It Works
|
|
100
|
-
|
|
101
|
-
agent-limit reads credentials from standard locations:
|
|
102
|
-
|
|
103
|
-
- **Claude Code**: macOS Keychain (`Claude Code-credentials`)
|
|
104
|
-
- **Codex**: `~/.codex/auth.json`
|
|
105
|
-
|
|
106
|
-
It then fetches usage data from each provider's API and displays it in a unified dashboard.
|
|
107
|
-
|
|
108
|
-
### Trajectory Indicator
|
|
109
|
-
|
|
110
|
-
Each progress bar shows a `|` marker indicating where your usage "should be" based on time elapsed in the reset period:
|
|
111
|
-
|
|
112
|
-
```
|
|
113
|
-
[███████░░░░|░░░░░░░░░] 30% ↓12%
|
|
114
|
-
^ you should be at 42%, but you're at 30% (12% under pace)
|
|
115
|
-
```
|
|
116
|
-
|
|
117
|
-
- `↓X%` (green) = under pace, you have headroom
|
|
118
|
-
- `↑X%` (red) = over pace, might hit limits early
|
|
119
|
-
|
|
120
|
-
## Requirements
|
|
121
|
-
|
|
122
|
-
- macOS (uses Keychain for credential storage)
|
|
123
|
-
- Active CLI authentication for providers you want to monitor
|
|
124
|
-
|
|
125
124
|
## License
|
|
126
125
|
|
|
127
126
|
MIT
|
package/bin/cli.tsx
CHANGED
|
@@ -14,9 +14,10 @@ if (command === "usage") {
|
|
|
14
14
|
|
|
15
15
|
const root = createRoot(renderer);
|
|
16
16
|
|
|
17
|
-
const cleanup = () => {
|
|
17
|
+
const cleanup = async () => {
|
|
18
18
|
root.unmount();
|
|
19
|
-
renderer.destroy();
|
|
19
|
+
await renderer.destroy();
|
|
20
|
+
process.stdout.write("\x1b[?25h"); // Ensure cursor is visible
|
|
20
21
|
process.exit(0);
|
|
21
22
|
};
|
|
22
23
|
|
package/package.json
CHANGED
package/src/providers/claude.ts
CHANGED
|
@@ -1,30 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getClaudeCredentials,
|
|
3
|
-
refreshClaudeToken,
|
|
4
|
-
saveClaudeCredentials,
|
|
5
3
|
type ClaudeCredentials,
|
|
6
4
|
} from "../utils/keychain";
|
|
7
5
|
import { timeUntil } from "../utils/time";
|
|
8
6
|
import type { ProviderStatus } from "./types";
|
|
9
7
|
|
|
10
|
-
const TOKEN_REFRESH_BUFFER_MS = 5 * 60 * 1000;
|
|
11
|
-
|
|
12
8
|
interface ClaudeUsageResponse {
|
|
13
9
|
five_hour: { utilization: number; resets_at: string | null } | null;
|
|
14
10
|
seven_day: { utilization: number; resets_at: string | null } | null;
|
|
15
11
|
seven_day_opus: { utilization: number; resets_at: string | null } | null;
|
|
16
12
|
}
|
|
17
13
|
|
|
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
|
-
|
|
28
14
|
async function fetchUsageWithCredentials(
|
|
29
15
|
credentials: ClaudeCredentials
|
|
30
16
|
): Promise<Response> {
|
|
@@ -41,7 +27,7 @@ async function fetchUsageWithCredentials(
|
|
|
41
27
|
}
|
|
42
28
|
|
|
43
29
|
export async function fetchClaudeUsage(): Promise<ProviderStatus> {
|
|
44
|
-
|
|
30
|
+
const credentials = await getClaudeCredentials();
|
|
45
31
|
|
|
46
32
|
if (!credentials) {
|
|
47
33
|
return {
|
|
@@ -52,24 +38,8 @@ export async function fetchClaudeUsage(): Promise<ProviderStatus> {
|
|
|
52
38
|
};
|
|
53
39
|
}
|
|
54
40
|
|
|
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
|
-
|
|
63
41
|
try {
|
|
64
|
-
|
|
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);
|
|
73
43
|
|
|
74
44
|
if (!response.ok) {
|
|
75
45
|
if (response.status === 401) {
|
package/src/utils/keychain.ts
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { $ } from "bun";
|
|
2
2
|
|
|
3
|
-
const CLAUDE_CLIENT_ID = "9d1c250a-e61b-44d9-88ed-5944d1962f5e";
|
|
4
|
-
const CLAUDE_TOKEN_ENDPOINT = "https://console.anthropic.com/v1/oauth/token";
|
|
5
3
|
const KEYCHAIN_SERVICE = "Claude Code-credentials";
|
|
6
4
|
|
|
7
5
|
export async function getKeychainCredentials(service: string): Promise<string | null> {
|
|
@@ -15,16 +13,6 @@ export async function getKeychainCredentials(service: string): Promise<string |
|
|
|
15
13
|
}
|
|
16
14
|
}
|
|
17
15
|
|
|
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
|
-
|
|
28
16
|
export interface ClaudeCredentials {
|
|
29
17
|
accessToken: string;
|
|
30
18
|
refreshToken: string;
|
|
@@ -45,62 +33,6 @@ export async function getClaudeCredentials(): Promise<ClaudeCredentials | null>
|
|
|
45
33
|
}
|
|
46
34
|
}
|
|
47
35
|
|
|
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
|
-
|
|
104
36
|
export interface CodexCredentials {
|
|
105
37
|
accessToken: string;
|
|
106
38
|
accountId: string;
|