claudemon 0.2.2 → 0.3.1
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 +31 -0
- package/dist/api.d.ts +11 -0
- package/dist/api.d.ts.map +1 -1
- package/dist/api.js +53 -0
- package/dist/api.js.map +1 -1
- package/dist/components/PieChart.d.ts.map +1 -1
- package/dist/components/PieChart.js +19 -25
- package/dist/components/PieChart.js.map +1 -1
- package/dist/index.js +11 -2
- package/dist/index.js.map +1 -1
- package/dist/models.d.ts.map +1 -1
- package/dist/models.js +13 -5
- package/dist/models.js.map +1 -1
- package/extensions/claudemon.ts +607 -0
- package/package.json +22 -3
package/README.md
CHANGED
|
@@ -75,6 +75,37 @@ Config file: `~/.config/claudemon/config.toml`
|
|
|
75
75
|
|
|
76
76
|
</details>
|
|
77
77
|
|
|
78
|
+
## Pi Integration
|
|
79
|
+
|
|
80
|
+
Claudemon is also available as a [pi](https://github.com/badlogic/pi-mono) extension, so you can check your Claude quota without leaving your coding session.
|
|
81
|
+
|
|
82
|
+
### Install
|
|
83
|
+
|
|
84
|
+
```sh
|
|
85
|
+
# From npm
|
|
86
|
+
pi install npm:claudemon
|
|
87
|
+
|
|
88
|
+
# From git
|
|
89
|
+
pi install https://github.com/anistark/claudemon
|
|
90
|
+
|
|
91
|
+
# Try without installing
|
|
92
|
+
pi -e npm:claudemon
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Usage
|
|
96
|
+
|
|
97
|
+
| Command | Description |
|
|
98
|
+
|---------|-------------|
|
|
99
|
+
| `/claudemon` | Show quota usage inline (5-hour, 7-day windows, per-model breakdown) |
|
|
100
|
+
| `/claudemon --tui` | Launch the full TUI dashboard |
|
|
101
|
+
| *"Check my Claude usage"* | The LLM calls the `claudemon` tool automatically |
|
|
102
|
+
|
|
103
|
+
### Uninstall
|
|
104
|
+
|
|
105
|
+
```sh
|
|
106
|
+
pi remove npm:claudemon
|
|
107
|
+
```
|
|
108
|
+
|
|
78
109
|
## Development
|
|
79
110
|
|
|
80
111
|
```sh
|
package/dist/api.d.ts
CHANGED
|
@@ -9,4 +9,15 @@ export declare class AuthenticationError extends QuotaFetchError {
|
|
|
9
9
|
constructor(message: string);
|
|
10
10
|
}
|
|
11
11
|
export declare function fetchQuota(oauthToken: string): Promise<QuotaData>;
|
|
12
|
+
/**
|
|
13
|
+
* Quick health-check: verifies that an OAuth token exists and is accepted
|
|
14
|
+
* by the API. Returns `{ ok: true }` on success, or `{ ok: false, reason }`
|
|
15
|
+
* on failure.
|
|
16
|
+
*/
|
|
17
|
+
export declare function validateToken(): Promise<{
|
|
18
|
+
ok: true;
|
|
19
|
+
} | {
|
|
20
|
+
ok: false;
|
|
21
|
+
reason: string;
|
|
22
|
+
}>;
|
|
12
23
|
//# sourceMappingURL=api.d.ts.map
|
package/dist/api.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAmB,KAAK,SAAS,EAAmB,MAAM,aAAa,CAAC;AAG/E,qBAAa,eAAgB,SAAQ,KAAK;gBAC5B,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,mBAAoB,SAAQ,eAAe;gBAC1C,OAAO,EAAE,MAAM;CAI5B;AAED,wBAAsB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAuCvE"}
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAmB,KAAK,SAAS,EAAmB,MAAM,aAAa,CAAC;AAG/E,qBAAa,eAAgB,SAAQ,KAAK;gBAC5B,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,mBAAoB,SAAQ,eAAe;gBAC1C,OAAO,EAAE,MAAM;CAI5B;AAED,wBAAsB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,CAuCvE;AAED;;;;GAIG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAC5C;IAAE,EAAE,EAAE,IAAI,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAC7C,CAmDA"}
|
package/dist/api.js
CHANGED
|
@@ -46,6 +46,59 @@ export async function fetchQuota(oauthToken) {
|
|
|
46
46
|
const data = (await resp.json());
|
|
47
47
|
return parseQuotaResponse(data);
|
|
48
48
|
}
|
|
49
|
+
/**
|
|
50
|
+
* Quick health-check: verifies that an OAuth token exists and is accepted
|
|
51
|
+
* by the API. Returns `{ ok: true }` on success, or `{ ok: false, reason }`
|
|
52
|
+
* on failure.
|
|
53
|
+
*/
|
|
54
|
+
export async function validateToken() {
|
|
55
|
+
const { getOAuthToken } = await import("./auth.js");
|
|
56
|
+
const token = getOAuthToken();
|
|
57
|
+
if (!token) {
|
|
58
|
+
return {
|
|
59
|
+
ok: false,
|
|
60
|
+
reason: "No OAuth token found. Please run 'claudemon setup' first.",
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
const config = loadConfig();
|
|
64
|
+
const usageUrl = config["oauth_usage_url"];
|
|
65
|
+
const betaHeader = config["oauth_beta_header"];
|
|
66
|
+
try {
|
|
67
|
+
const resp = await fetch(usageUrl, {
|
|
68
|
+
headers: {
|
|
69
|
+
Authorization: `Bearer ${token}`,
|
|
70
|
+
"anthropic-beta": betaHeader,
|
|
71
|
+
},
|
|
72
|
+
signal: AbortSignal.timeout(10000),
|
|
73
|
+
});
|
|
74
|
+
if (resp.status === 401) {
|
|
75
|
+
return {
|
|
76
|
+
ok: false,
|
|
77
|
+
reason: "OAuth token is invalid or expired. Please run 'claudemon setup --re' to re-authenticate.",
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
if (resp.status === 403) {
|
|
81
|
+
return {
|
|
82
|
+
ok: false,
|
|
83
|
+
reason: "Access denied. Your token may lack the required permissions. Run 'claudemon setup --re'.",
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
if (resp.status !== 200) {
|
|
87
|
+
const text = await resp.text();
|
|
88
|
+
return {
|
|
89
|
+
ok: false,
|
|
90
|
+
reason: `API returned status ${resp.status}: ${text}`,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
return { ok: true };
|
|
94
|
+
}
|
|
95
|
+
catch (e) {
|
|
96
|
+
return {
|
|
97
|
+
ok: false,
|
|
98
|
+
reason: `Network error while validating token: ${e}`,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
}
|
|
49
102
|
function parseQuotaResponse(data) {
|
|
50
103
|
const quota = createQuotaData();
|
|
51
104
|
// Parse 5-hour window
|
package/dist/api.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../src/api.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PieChart.d.ts","sourceRoot":"","sources":["../../src/components/PieChart.tsx"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,UAAU,aAAa;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,IAAI,GAAG,IAAI,CAAC;CACxB;
|
|
1
|
+
{"version":3,"file":"PieChart.d.ts","sourceRoot":"","sources":["../../src/components/PieChart.tsx"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,UAAU,aAAa;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,IAAI,GAAG,IAAI,CAAC;CACxB;AAqCD,wBAAgB,QAAQ,CAAC,EACvB,QAAQ,EACR,KAAK,EACL,SAAS,GACV,EAAE,aAAa,GAAG,KAAK,CAAC,YAAY,CAuFpC"}
|
|
@@ -16,33 +16,27 @@ function getBoldColor(pct) {
|
|
|
16
16
|
return chalk.bold.red;
|
|
17
17
|
}
|
|
18
18
|
function formatResetTime(reset) {
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
return
|
|
29
|
-
};
|
|
30
|
-
// Same day
|
|
31
|
-
if (nowDate.getTime() === resetDate.getTime()) {
|
|
32
|
-
return fmt(resetLocal);
|
|
19
|
+
const diffMs = reset.getTime() - Date.now();
|
|
20
|
+
if (diffMs <= 0)
|
|
21
|
+
return "now";
|
|
22
|
+
const totalMinutes = Math.floor(diffMs / 60000);
|
|
23
|
+
const totalHours = Math.floor(diffMs / 3600000);
|
|
24
|
+
const totalDays = Math.floor(diffMs / 86400000);
|
|
25
|
+
if (totalMinutes < 1)
|
|
26
|
+
return "in less than a minute";
|
|
27
|
+
if (totalMinutes < 60) {
|
|
28
|
+
return `in ${totalMinutes} minute${totalMinutes !== 1 ? "s" : ""}`;
|
|
33
29
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
return `
|
|
30
|
+
if (totalHours < 24) {
|
|
31
|
+
const remainingMin = totalMinutes % 60;
|
|
32
|
+
if (remainingMin === 0)
|
|
33
|
+
return `in ${totalHours} hour${totalHours !== 1 ? "s" : ""}`;
|
|
34
|
+
return `in ${totalHours} hour${totalHours !== 1 ? "s" : ""}, ${remainingMin} min`;
|
|
39
35
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
];
|
|
45
|
-
return `${months[resetLocal.getMonth()]} ${resetLocal.getDate()} at ${fmt(resetLocal)}`;
|
|
36
|
+
const remainingHrs = totalHours % 24;
|
|
37
|
+
if (remainingHrs === 0)
|
|
38
|
+
return `in ${totalDays} day${totalDays !== 1 ? "s" : ""}`;
|
|
39
|
+
return `in ${totalDays} day${totalDays !== 1 ? "s" : ""}, ${remainingHrs} hour${remainingHrs !== 1 ? "s" : ""}`;
|
|
46
40
|
}
|
|
47
41
|
export function PieChart({ usagePct, label, resetTime, }) {
|
|
48
42
|
const pct = Math.max(0, Math.min(100, usagePct));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PieChart.js","sourceRoot":"","sources":["../../src/components/PieChart.tsx"],"names":[],"mappings":";AAKA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAChC,OAAO,KAAK,MAAM,OAAO,CAAC;AAQ1B,SAAS,QAAQ,CAAC,GAAW;IAC3B,IAAI,GAAG,GAAG,EAAE;QAAE,OAAO,KAAK,CAAC,KAAK,CAAC;IACjC,IAAI,GAAG,GAAG,EAAE;QAAE,OAAO,KAAK,CAAC,MAAM,CAAC;IAClC,OAAO,KAAK,CAAC,GAAG,CAAC;AACnB,CAAC;AAED,SAAS,YAAY,CAAC,GAAW;IAC/B,IAAI,GAAG,GAAG,EAAE;QAAE,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC;IACtC,IAAI,GAAG,GAAG,EAAE;QAAE,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,CAAC;IACvC,OAAO,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC;AACxB,CAAC;AAED,SAAS,eAAe,CAAC,KAAW;IAClC,MAAM,GAAG,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"PieChart.js","sourceRoot":"","sources":["../../src/components/PieChart.tsx"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -6,6 +6,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
6
6
|
import { createRequire } from "node:module";
|
|
7
7
|
import { render } from "ink";
|
|
8
8
|
import { App } from "./app.js";
|
|
9
|
+
import { validateToken } from "./api.js";
|
|
9
10
|
const require = createRequire(import.meta.url);
|
|
10
11
|
const { version: VERSION } = require("../package.json");
|
|
11
12
|
const LOGO = `
|
|
@@ -42,7 +43,7 @@ const LOGO = `
|
|
|
42
43
|
----===----
|
|
43
44
|
---===---
|
|
44
45
|
`;
|
|
45
|
-
function main() {
|
|
46
|
+
async function main() {
|
|
46
47
|
const args = process.argv.slice(2);
|
|
47
48
|
if (args.includes("--help") || args.includes("-h")) {
|
|
48
49
|
printHelp();
|
|
@@ -55,9 +56,17 @@ function main() {
|
|
|
55
56
|
}
|
|
56
57
|
if (args[0] === "setup") {
|
|
57
58
|
const forceReauth = args.includes("--re");
|
|
58
|
-
runSetup(forceReauth);
|
|
59
|
+
await runSetup(forceReauth);
|
|
59
60
|
return;
|
|
60
61
|
}
|
|
62
|
+
// Validate token before launching the TUI
|
|
63
|
+
console.log("Validating OAuth token...");
|
|
64
|
+
const result = await validateToken();
|
|
65
|
+
if (!result.ok) {
|
|
66
|
+
console.error(`\n✗ ${result.reason}\n`);
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
console.log("✓ Token is valid.\n");
|
|
61
70
|
// Launch TUI (full-screen alternate screen)
|
|
62
71
|
process.stdout.write("\x1b[?1049h"); // enter alternate screen
|
|
63
72
|
process.stdout.write("\x1b[2J\x1b[H"); // clear + home
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":";;AAEA;;GAEG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,OAAO,EAAE,MAAM,EAAE,MAAM,KAAK,CAAC;AAE7B,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.tsx"],"names":[],"mappings":";;AAEA;;GAEG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,OAAO,EAAE,MAAM,EAAE,MAAM,KAAK,CAAC;AAE7B,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAC/B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC/C,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAExD,MAAM,IAAI,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAiCZ,CAAC;AAEF,KAAK,UAAU,IAAI;IACjB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAEnC,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;QACnD,SAAS,EAAE,CAAC;QACZ,OAAO;IACT,CAAC;IAED,IAAI,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC;QAC/B,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAClB,OAAO,CAAC,GAAG,CAAC,aAAa,OAAO,EAAE,CAAC,CAAC;QACpC,OAAO;IACT,CAAC;IAED,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,OAAO,EAAE,CAAC;QACxB,MAAM,WAAW,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;QAC1C,MAAM,QAAQ,CAAC,WAAW,CAAC,CAAC;QAC5B,OAAO;IACT,CAAC;IAED,0CAA0C;IAC1C,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAC;IACzC,MAAM,MAAM,GAAG,MAAM,aAAa,EAAE,CAAC;IACrC,IAAI,CAAC,MAAM,CAAC,EAAE,EAAE,CAAC;QACf,OAAO,CAAC,KAAK,CAAC,OAAO,MAAM,CAAC,MAAM,IAAI,CAAC,CAAC;QACxC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IACD,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;IAEnC,4CAA4C;IAC5C,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,yBAAyB;IAC9D,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC,eAAe;IACtD,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAC,GAAG,IAAC,OAAO,EAAE,OAAO,GAAI,CAAC,CAAC;IACnD,QAAQ,CAAC,aAAa,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE;QACjC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,sBAAsB;IAC7D,CAAC,CAAC,CAAC;AACL,CAAC;AAED,KAAK,UAAU,QAAQ,CAAC,WAAW,GAAG,KAAK;IACzC,MAAM,EAAE,gBAAgB,EAAE,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC;IACvD,MAAM,gBAAgB,CAAC,WAAW,CAAC,CAAC;AACtC,CAAC;AAED,SAAS,SAAS;IAChB,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAClB,OAAO,CAAC,GAAG,CAAC;;;;;;;;;;;;;;iBAcG,CAAC,CAAC;AACnB,CAAC;AAED,IAAI,EAAE,CAAC"}
|
package/dist/models.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,SAAS;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,iBAAiB,EAAE,IAAI,GAAG,IAAI,CAAC;IAC/B,gBAAgB,EAAE,MAAM,CAAC;IACzB,iBAAiB,EAAE,IAAI,GAAG,IAAI,CAAC;IAC/B,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,SAAS;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,WAAW,EAAE,SAAS,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,eAAe,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,SAAS,CAAC,GAAG,SAAS,CAUvE;AAED,wBAAgB,eAAe,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,SAAS,CAAC,GAAG,SAAS,CAQvE;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,SAAS,GAAG,MAAM,CAE/C;AAED,wBAAgB,wBAAwB,CAAC,CAAC,EAAE,SAAS,GAAG,MAAM,CAI7D;AAED,wBAAgB,wBAAwB,CAAC,CAAC,EAAE,SAAS,GAAG,MAAM,CAI7D;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAOlD;AAED,wBAAgB,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"models.d.ts","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,SAAS;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,iBAAiB,EAAE,IAAI,GAAG,IAAI,CAAC;IAC/B,gBAAgB,EAAE,MAAM,CAAC;IACzB,iBAAiB,EAAE,IAAI,GAAG,IAAI,CAAC;IAC/B,WAAW,EAAE,UAAU,EAAE,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,SAAS;IACxB,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,WAAW,EAAE,SAAS,CAAC;IACvB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,eAAe,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,SAAS,CAAC,GAAG,SAAS,CAUvE;AAED,wBAAgB,eAAe,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,SAAS,CAAC,GAAG,SAAS,CAQvE;AAED,wBAAgB,UAAU,CAAC,CAAC,EAAE,SAAS,GAAG,MAAM,CAE/C;AAED,wBAAgB,wBAAwB,CAAC,CAAC,EAAE,SAAS,GAAG,MAAM,CAI7D;AAED,wBAAgB,wBAAwB,CAAC,CAAC,EAAE,SAAS,GAAG,MAAM,CAI7D;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAOlD;AAED,wBAAgB,eAAe,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAe5D"}
|
package/dist/models.js
CHANGED
|
@@ -51,10 +51,18 @@ export function formatCountdown(totalSeconds) {
|
|
|
51
51
|
const days = Math.floor(totalSeconds / 86400);
|
|
52
52
|
const hours = Math.floor((totalSeconds % 86400) / 3600);
|
|
53
53
|
const minutes = Math.floor((totalSeconds % 3600) / 60);
|
|
54
|
-
if (days > 0)
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
return
|
|
58
|
-
|
|
54
|
+
if (days > 0) {
|
|
55
|
+
if (hours === 0)
|
|
56
|
+
return `in ${days} day${days !== 1 ? "s" : ""}`;
|
|
57
|
+
return `in ${days} day${days !== 1 ? "s" : ""}, ${hours} hour${hours !== 1 ? "s" : ""}`;
|
|
58
|
+
}
|
|
59
|
+
if (hours > 0) {
|
|
60
|
+
if (minutes === 0)
|
|
61
|
+
return `in ${hours} hour${hours !== 1 ? "s" : ""}`;
|
|
62
|
+
return `in ${hours} hour${hours !== 1 ? "s" : ""}, ${minutes} min`;
|
|
63
|
+
}
|
|
64
|
+
if (minutes < 1)
|
|
65
|
+
return "in less than a minute";
|
|
66
|
+
return `in ${minutes} minute${minutes !== 1 ? "s" : ""}`;
|
|
59
67
|
}
|
|
60
68
|
//# sourceMappingURL=models.js.map
|
package/dist/models.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"models.js","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"AAAA;;GAEG;AA4BH,MAAM,UAAU,eAAe,CAAC,OAA4B;IAC1D,OAAO;QACL,gBAAgB,EAAE,CAAC;QACnB,iBAAiB,EAAE,IAAI;QACvB,gBAAgB,EAAE,CAAC;QACnB,iBAAiB,EAAE,IAAI;QACvB,WAAW,EAAE,EAAE;QACf,QAAQ,EAAE,KAAK;QACf,GAAG,OAAO;KACX,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,OAA4B;IAC1D,OAAO;QACL,WAAW,EAAE,CAAC;QACd,YAAY,EAAE,CAAC;QACf,SAAS,EAAE,CAAC;QACZ,UAAU,EAAE,CAAC;QACb,GAAG,OAAO;KACX,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,CAAY;IACrC,OAAO,CAAC,CAAC,WAAW,GAAG,CAAC,CAAC,YAAY,GAAG,CAAC,CAAC,SAAS,GAAG,CAAC,CAAC,UAAU,CAAC;AACrE,CAAC;AAED,MAAM,UAAU,wBAAwB,CAAC,CAAY;IACnD,IAAI,CAAC,CAAC,CAAC,iBAAiB;QAAE,OAAO,CAAC,CAAC;IACnC,MAAM,KAAK,GAAG,CAAC,CAAC,iBAAiB,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACzD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,wBAAwB,CAAC,CAAY;IACnD,IAAI,CAAC,CAAC,CAAC,iBAAiB;QAAE,OAAO,CAAC,CAAC;IACnC,MAAM,KAAK,GAAG,CAAC,CAAC,iBAAiB,CAAC,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACzD,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAAa;IACxC,IAAI,KAAK,IAAI,SAAS,EAAE,CAAC;QACvB,OAAO,GAAG,CAAC,KAAK,GAAG,SAAS,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;IAC9C,CAAC;SAAM,IAAI,KAAK,IAAI,KAAK,EAAE,CAAC;QAC1B,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC;IACzC,CAAC;IACD,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;AACvB,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,YAAoB;IAClD,IAAI,YAAY,IAAI,CAAC;QAAE,OAAO,KAAK,CAAC;IACpC,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,KAAK,CAAC,CAAC;IAC9C,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,YAAY,GAAG,KAAK,CAAC,GAAG,IAAI,CAAC,CAAC;IACxD,MAAM,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,YAAY,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC;IACvD,IAAI,IAAI,GAAG,CAAC;
|
|
1
|
+
{"version":3,"file":"models.js","sourceRoot":"","sources":["../src/models.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,607 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pi extension for claudemon - Claude Usage Monitor.
|
|
3
|
+
*
|
|
4
|
+
* Registers:
|
|
5
|
+
* /claudemon - Show quota usage inline (or launch TUI with --tui)
|
|
6
|
+
* claudemon tool - LLM-callable tool to check Claude quota
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
|
10
|
+
import { Type } from "@sinclair/typebox";
|
|
11
|
+
import { matchesKey, truncateToWidth, visibleWidth } from "@mariozechner/pi-tui";
|
|
12
|
+
import { execFileSync } from "node:child_process";
|
|
13
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
14
|
+
import { homedir, platform } from "node:os";
|
|
15
|
+
import { join } from "node:path";
|
|
16
|
+
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
// Token resolution (same logic as claudemon's auth.ts)
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
|
|
21
|
+
function readKeychainCredentials(): Record<string, unknown> | null {
|
|
22
|
+
if (platform() !== "darwin") return null;
|
|
23
|
+
try {
|
|
24
|
+
const raw = execFileSync(
|
|
25
|
+
"/usr/bin/security",
|
|
26
|
+
["find-generic-password", "-s", "Claude Code-credentials", "-w"],
|
|
27
|
+
{ timeout: 5000, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] },
|
|
28
|
+
);
|
|
29
|
+
if (!raw.trim()) return null;
|
|
30
|
+
return JSON.parse(raw.trim()) as Record<string, unknown>;
|
|
31
|
+
} catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function readFileCredentials(): Record<string, unknown> | null {
|
|
37
|
+
const credFile = join(homedir(), ".claude", ".credentials.json");
|
|
38
|
+
if (!existsSync(credFile)) return null;
|
|
39
|
+
try {
|
|
40
|
+
return JSON.parse(readFileSync(credFile, "utf-8")) as Record<string, unknown>;
|
|
41
|
+
} catch {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function readClaudemonToken(): string | null {
|
|
47
|
+
const tokenFile = join(homedir(), ".config", "claudemon", "token.json");
|
|
48
|
+
if (!existsSync(tokenFile)) return null;
|
|
49
|
+
try {
|
|
50
|
+
const data = JSON.parse(readFileSync(tokenFile, "utf-8"));
|
|
51
|
+
if (data.expires_at && data.expires_at < Date.now()) return null;
|
|
52
|
+
return data.oauth_token ?? null;
|
|
53
|
+
} catch {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function getOAuthToken(): string | null {
|
|
59
|
+
// Prefer Claude Code's live credentials
|
|
60
|
+
for (const reader of [readKeychainCredentials, readFileCredentials]) {
|
|
61
|
+
const data = reader();
|
|
62
|
+
if (data) {
|
|
63
|
+
const oauth = data["claudeAiOauth"] as { accessToken?: string; expiresAt?: number } | undefined;
|
|
64
|
+
if (oauth?.accessToken) {
|
|
65
|
+
if (oauth.expiresAt && oauth.expiresAt < Date.now()) continue;
|
|
66
|
+
return oauth.accessToken;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// Fallback to claudemon's own stored token
|
|
71
|
+
return readClaudemonToken();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// ---------------------------------------------------------------------------
|
|
75
|
+
// Quota fetch (minimal inline version)
|
|
76
|
+
// ---------------------------------------------------------------------------
|
|
77
|
+
|
|
78
|
+
interface ModelQuota {
|
|
79
|
+
modelName: string;
|
|
80
|
+
usagePct: number;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
interface QuotaData {
|
|
84
|
+
fiveHourUsagePct: number;
|
|
85
|
+
fiveHourResetTime: Date | null;
|
|
86
|
+
sevenDayUsagePct: number;
|
|
87
|
+
sevenDayResetTime: Date | null;
|
|
88
|
+
modelQuotas: ModelQuota[];
|
|
89
|
+
planType: string;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function fetchQuota(token: string): Promise<QuotaData> {
|
|
93
|
+
const resp = await fetch("https://api.anthropic.com/api/oauth/usage", {
|
|
94
|
+
headers: {
|
|
95
|
+
Authorization: `Bearer ${token}`,
|
|
96
|
+
"anthropic-beta": "oauth-2025-04-20",
|
|
97
|
+
},
|
|
98
|
+
signal: AbortSignal.timeout(15000),
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
if (resp.status === 401 || resp.status === 403) {
|
|
102
|
+
throw new Error("OAuth token expired. Run `claudemon setup` to re-authenticate.");
|
|
103
|
+
}
|
|
104
|
+
if (resp.status !== 200) {
|
|
105
|
+
throw new Error(`API returned status ${resp.status}`);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const data = (await resp.json()) as Record<string, unknown>;
|
|
109
|
+
return parseQuotaResponse(data);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function parseQuotaResponse(data: Record<string, unknown>): QuotaData {
|
|
113
|
+
const fiveHour = (data["five_hour"] ?? data["fiveHour"] ?? {}) as Record<string, unknown>;
|
|
114
|
+
const sevenDay = (data["seven_day"] ?? data["sevenDay"] ?? {}) as Record<string, unknown>;
|
|
115
|
+
const models = (data["models"] ?? data["model_quotas"] ?? []) as Array<Record<string, unknown>>;
|
|
116
|
+
|
|
117
|
+
const fiveHourReset = (fiveHour["resets_at"] ?? fiveHour["reset_at"] ?? fiveHour["resetAt"]) as string | undefined;
|
|
118
|
+
const sevenDayReset = (sevenDay["resets_at"] ?? sevenDay["reset_at"] ?? sevenDay["resetAt"]) as string | undefined;
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
fiveHourUsagePct: (fiveHour["utilization"] as number) ?? (fiveHour["usage_pct"] as number) ?? 0,
|
|
122
|
+
fiveHourResetTime: fiveHourReset ? new Date(fiveHourReset) : null,
|
|
123
|
+
sevenDayUsagePct: (sevenDay["utilization"] as number) ?? (sevenDay["usage_pct"] as number) ?? 0,
|
|
124
|
+
sevenDayResetTime: sevenDayReset ? new Date(sevenDayReset) : null,
|
|
125
|
+
modelQuotas: models.map((m) => ({
|
|
126
|
+
modelName: (m["model"] as string) ?? (m["name"] as string) ?? "unknown",
|
|
127
|
+
usagePct: (m["utilization"] as number) ?? (m["usage_pct"] as number) ?? 0,
|
|
128
|
+
})),
|
|
129
|
+
planType: (data["plan_type"] as string) ?? (data["planType"] as string) ?? "pro",
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// ---------------------------------------------------------------------------
|
|
134
|
+
// Formatting helpers
|
|
135
|
+
// ---------------------------------------------------------------------------
|
|
136
|
+
|
|
137
|
+
function formatCountdown(totalSeconds: number): string {
|
|
138
|
+
if (totalSeconds <= 0) return "now";
|
|
139
|
+
const days = Math.floor(totalSeconds / 86400);
|
|
140
|
+
const hours = Math.floor((totalSeconds % 86400) / 3600);
|
|
141
|
+
const minutes = Math.floor((totalSeconds % 3600) / 60);
|
|
142
|
+
if (days > 0) return `${days}d ${hours}h`;
|
|
143
|
+
if (hours > 0) return `${hours}h ${minutes}m`;
|
|
144
|
+
return `${minutes}m`;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function usageBar(pct: number, width = 20): string {
|
|
148
|
+
const filled = Math.round((pct / 100) * width);
|
|
149
|
+
const empty = width - filled;
|
|
150
|
+
return `[${"█".repeat(filled)}${"░".repeat(empty)}] ${pct.toFixed(1)}%`;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function formatQuota(q: QuotaData): string {
|
|
154
|
+
const lines: string[] = [];
|
|
155
|
+
lines.push(`📊 Claude Usage (${q.planType.toUpperCase()} plan)`);
|
|
156
|
+
lines.push("");
|
|
157
|
+
|
|
158
|
+
// 5-hour window
|
|
159
|
+
const fiveHourReset = q.fiveHourResetTime
|
|
160
|
+
? formatCountdown(Math.max(0, (q.fiveHourResetTime.getTime() - Date.now()) / 1000))
|
|
161
|
+
: "—";
|
|
162
|
+
lines.push(`5-hour: ${usageBar(q.fiveHourUsagePct)} resets in ${fiveHourReset}`);
|
|
163
|
+
|
|
164
|
+
// 7-day window
|
|
165
|
+
const sevenDayReset = q.sevenDayResetTime
|
|
166
|
+
? formatCountdown(Math.max(0, (q.sevenDayResetTime.getTime() - Date.now()) / 1000))
|
|
167
|
+
: "—";
|
|
168
|
+
lines.push(`7-day: ${usageBar(q.sevenDayUsagePct)} resets in ${sevenDayReset}`);
|
|
169
|
+
|
|
170
|
+
// Per-model breakdown
|
|
171
|
+
if (q.modelQuotas.length > 0) {
|
|
172
|
+
lines.push("");
|
|
173
|
+
lines.push("Per model:");
|
|
174
|
+
for (const m of q.modelQuotas) {
|
|
175
|
+
lines.push(` ${m.modelName.padEnd(30)} ${usageBar(m.usagePct)}`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return lines.join("\n");
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// ---------------------------------------------------------------------------
|
|
183
|
+
// TUI Dashboard Component (replaces broken pi.exec approach)
|
|
184
|
+
// ---------------------------------------------------------------------------
|
|
185
|
+
|
|
186
|
+
function formatResetTime(reset: Date): string {
|
|
187
|
+
const diffMs = reset.getTime() - Date.now();
|
|
188
|
+
if (diffMs <= 0) return "now";
|
|
189
|
+
const totalMinutes = Math.floor(diffMs / 60000);
|
|
190
|
+
const totalHours = Math.floor(diffMs / 3600000);
|
|
191
|
+
const totalDays = Math.floor(diffMs / 86400000);
|
|
192
|
+
if (totalMinutes < 1) return "in <1 min";
|
|
193
|
+
if (totalMinutes < 60) return `in ${totalMinutes}m`;
|
|
194
|
+
if (totalHours < 24) {
|
|
195
|
+
const remainingMin = totalMinutes % 60;
|
|
196
|
+
return remainingMin === 0 ? `in ${totalHours}h` : `in ${totalHours}h ${remainingMin}m`;
|
|
197
|
+
}
|
|
198
|
+
const remainingHrs = totalHours % 24;
|
|
199
|
+
return remainingHrs === 0 ? `in ${totalDays}d` : `in ${totalDays}d ${remainingHrs}h`;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function getColorFn(pct: number, theme: any): (s: string) => string {
|
|
203
|
+
if (pct < 50) return (s: string) => theme.fg("success", s);
|
|
204
|
+
if (pct < 80) return (s: string) => theme.fg("warning", s);
|
|
205
|
+
return (s: string) => theme.fg("error", s);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function renderDonut(pct: number, label: string, resetTime: Date | null, theme: any): string[] {
|
|
209
|
+
const clamped = Math.max(0, Math.min(100, pct));
|
|
210
|
+
const colorFn = getColorFn(clamped, theme);
|
|
211
|
+
|
|
212
|
+
const outerR = 6.5;
|
|
213
|
+
const innerR = 5.0;
|
|
214
|
+
const rows = Math.floor(outerR * 2) + 1;
|
|
215
|
+
const cols = Math.floor(outerR * 4) + 1;
|
|
216
|
+
const usedAngle = 2 * Math.PI * (clamped / 100);
|
|
217
|
+
const centerY = outerR;
|
|
218
|
+
const centerX = outerR * 2;
|
|
219
|
+
|
|
220
|
+
const grid: { char: string; style: ((s: string) => string) | null }[][] = [];
|
|
221
|
+
|
|
222
|
+
for (let row = 0; row < rows; row++) {
|
|
223
|
+
const line: { char: string; style: ((s: string) => string) | null }[] = [];
|
|
224
|
+
for (let col = 0; col < cols; col++) {
|
|
225
|
+
const dy = row - centerY;
|
|
226
|
+
const dx = (col - centerX) / 2.0;
|
|
227
|
+
const dist = Math.sqrt(dx * dx + dy * dy);
|
|
228
|
+
|
|
229
|
+
if (innerR <= dist && dist <= outerR) {
|
|
230
|
+
let angle = Math.atan2(dx, -dy);
|
|
231
|
+
if (angle < 0) angle += 2 * Math.PI;
|
|
232
|
+
if (angle <= usedAngle) {
|
|
233
|
+
line.push({ char: "█", style: colorFn });
|
|
234
|
+
} else {
|
|
235
|
+
line.push({ char: "░", style: (s: string) => theme.fg("dim", s) });
|
|
236
|
+
}
|
|
237
|
+
} else {
|
|
238
|
+
line.push({ char: " ", style: null });
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
grid.push(line);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// Place percentage in center
|
|
245
|
+
const pctStr = `${Math.round(clamped)}%`;
|
|
246
|
+
const centerRow = Math.floor(rows / 2);
|
|
247
|
+
const startCol = Math.floor(centerX - pctStr.length / 2);
|
|
248
|
+
for (let i = 0; i < pctStr.length; i++) {
|
|
249
|
+
const colIdx = startCol + i;
|
|
250
|
+
if (colIdx >= 0 && colIdx < cols) {
|
|
251
|
+
grid[centerRow]![colIdx] = { char: pctStr[i]!, style: (s: string) => theme.bold(colorFn(s)) };
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Place label below percentage
|
|
256
|
+
const labelRow = centerRow + 1;
|
|
257
|
+
const labelStart = Math.floor(centerX - label.length / 2);
|
|
258
|
+
if (labelRow < rows) {
|
|
259
|
+
for (let i = 0; i < label.length; i++) {
|
|
260
|
+
const colIdx = labelStart + i;
|
|
261
|
+
if (colIdx >= 0 && colIdx < cols) {
|
|
262
|
+
grid[labelRow]![colIdx] = { char: label[i]!, style: (s: string) => theme.fg("muted", s) };
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Render grid to lines
|
|
268
|
+
const lines: string[] = [];
|
|
269
|
+
for (const row of grid) {
|
|
270
|
+
let line = "";
|
|
271
|
+
for (const cell of row) {
|
|
272
|
+
line += cell.style ? cell.style(cell.char) : cell.char;
|
|
273
|
+
}
|
|
274
|
+
lines.push(line);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Reset time line
|
|
278
|
+
if (resetTime) {
|
|
279
|
+
const resetStr = `Resets ${formatResetTime(resetTime)}`;
|
|
280
|
+
const pad = Math.max(0, Math.floor((cols - resetStr.length) / 2));
|
|
281
|
+
lines.push(" ".repeat(pad) + theme.fg("dim", resetStr));
|
|
282
|
+
} else {
|
|
283
|
+
lines.push("");
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
return lines;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function renderModelQuotas(quotaData: QuotaData, theme: any, width: number): string[] {
|
|
290
|
+
if (quotaData.modelQuotas.length === 0) return [];
|
|
291
|
+
|
|
292
|
+
const lines: string[] = [];
|
|
293
|
+
lines.push(theme.bold(" Per-Model Breakdown"));
|
|
294
|
+
lines.push("");
|
|
295
|
+
|
|
296
|
+
for (let i = 0; i < quotaData.modelQuotas.length; i++) {
|
|
297
|
+
const mq = quotaData.modelQuotas[i]!;
|
|
298
|
+
const prefix = i === quotaData.modelQuotas.length - 1 ? " └ " : " ├ ";
|
|
299
|
+
const colorFn = getColorFn(mq.usagePct, theme);
|
|
300
|
+
const barWidth = 15;
|
|
301
|
+
const filled = Math.round((mq.usagePct / 100) * barWidth);
|
|
302
|
+
const empty = barWidth - filled;
|
|
303
|
+
const bar = colorFn("█".repeat(filled)) + theme.fg("dim", "░".repeat(empty));
|
|
304
|
+
const pctStr = colorFn(`${Math.round(mq.usagePct)}%`.padStart(4));
|
|
305
|
+
const name = mq.modelName.length > 28 ? mq.modelName.slice(0, 25) + "..." : mq.modelName;
|
|
306
|
+
lines.push(`${prefix}${name.padEnd(28)} ${bar} ${pctStr}`);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
return lines;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
class ClaudemonDashboard {
|
|
313
|
+
private quotaData: QuotaData | null = null;
|
|
314
|
+
private isLoading = true;
|
|
315
|
+
private errorMessage = "";
|
|
316
|
+
private lastRefreshAgo = 0;
|
|
317
|
+
private lastRefreshTime = 0;
|
|
318
|
+
private showHelp = false;
|
|
319
|
+
private refreshInterval: ReturnType<typeof setInterval> | null = null;
|
|
320
|
+
private tickInterval: ReturnType<typeof setInterval> | null = null;
|
|
321
|
+
private autoRefreshInterval: ReturnType<typeof setInterval> | null = null;
|
|
322
|
+
private tui: { requestRender: () => void };
|
|
323
|
+
private theme: any;
|
|
324
|
+
private onClose: () => void;
|
|
325
|
+
private token: string;
|
|
326
|
+
private cachedWidth = 0;
|
|
327
|
+
private cachedLines: string[] = [];
|
|
328
|
+
private version = 0;
|
|
329
|
+
private cachedVersion = -1;
|
|
330
|
+
|
|
331
|
+
constructor(
|
|
332
|
+
tui: { requestRender: () => void },
|
|
333
|
+
theme: any,
|
|
334
|
+
token: string,
|
|
335
|
+
onClose: () => void,
|
|
336
|
+
) {
|
|
337
|
+
this.tui = tui;
|
|
338
|
+
this.theme = theme;
|
|
339
|
+
this.token = token;
|
|
340
|
+
this.onClose = onClose;
|
|
341
|
+
|
|
342
|
+
// Initial fetch
|
|
343
|
+
this.doRefresh();
|
|
344
|
+
|
|
345
|
+
// Auto-refresh every 30 seconds
|
|
346
|
+
this.autoRefreshInterval = setInterval(() => this.doRefresh(), 30000);
|
|
347
|
+
|
|
348
|
+
// Tick the "last refreshed" counter every second
|
|
349
|
+
this.tickInterval = setInterval(() => {
|
|
350
|
+
if (this.lastRefreshTime > 0) {
|
|
351
|
+
this.lastRefreshAgo = Math.floor((Date.now() - this.lastRefreshTime) / 1000);
|
|
352
|
+
this.version++;
|
|
353
|
+
this.tui.requestRender();
|
|
354
|
+
}
|
|
355
|
+
}, 1000);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
private async doRefresh(): Promise<void> {
|
|
359
|
+
this.isLoading = true;
|
|
360
|
+
this.errorMessage = "";
|
|
361
|
+
this.version++;
|
|
362
|
+
this.tui.requestRender();
|
|
363
|
+
|
|
364
|
+
try {
|
|
365
|
+
const quota = await fetchQuota(this.token);
|
|
366
|
+
this.quotaData = quota;
|
|
367
|
+
this.lastRefreshTime = Date.now();
|
|
368
|
+
this.lastRefreshAgo = 0;
|
|
369
|
+
this.isLoading = false;
|
|
370
|
+
} catch (e) {
|
|
371
|
+
this.isLoading = false;
|
|
372
|
+
this.errorMessage = e instanceof Error ? e.message : String(e);
|
|
373
|
+
}
|
|
374
|
+
this.version++;
|
|
375
|
+
this.tui.requestRender();
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
private dispose(): void {
|
|
379
|
+
if (this.autoRefreshInterval) clearInterval(this.autoRefreshInterval);
|
|
380
|
+
if (this.tickInterval) clearInterval(this.tickInterval);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
handleInput(data: string): void {
|
|
384
|
+
if (matchesKey(data, "q") || matchesKey(data, "escape")) {
|
|
385
|
+
this.dispose();
|
|
386
|
+
this.onClose();
|
|
387
|
+
} else if (matchesKey(data, "r")) {
|
|
388
|
+
this.doRefresh();
|
|
389
|
+
} else if (data === "?") {
|
|
390
|
+
this.showHelp = !this.showHelp;
|
|
391
|
+
this.version++;
|
|
392
|
+
this.tui.requestRender();
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
render(width: number): string[] {
|
|
397
|
+
if (this.cachedVersion === this.version && this.cachedWidth === width) {
|
|
398
|
+
return this.cachedLines;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
const theme = this.theme;
|
|
402
|
+
const lines: string[] = [];
|
|
403
|
+
|
|
404
|
+
// Top border
|
|
405
|
+
lines.push(theme.fg("border", "─".repeat(width)));
|
|
406
|
+
|
|
407
|
+
// Header
|
|
408
|
+
const planType = this.quotaData?.planType ?? "pro";
|
|
409
|
+
const title = theme.bold("✨ Claude Usage Monitor") + " " + theme.fg("accent", `[${planType.toUpperCase()}]`);
|
|
410
|
+
let status: string;
|
|
411
|
+
if (this.errorMessage) {
|
|
412
|
+
status = theme.fg("error", `! ${this.errorMessage}`);
|
|
413
|
+
} else if (this.isLoading) {
|
|
414
|
+
status = theme.fg("dim", "⟳ loading...");
|
|
415
|
+
} else if (this.lastRefreshAgo === 0) {
|
|
416
|
+
status = theme.fg("success", "⟳ just now");
|
|
417
|
+
} else {
|
|
418
|
+
status = theme.fg("dim", `⟳ ${this.lastRefreshAgo}s ago`);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
const titleWidth = visibleWidth(title);
|
|
422
|
+
const statusWidth = visibleWidth(status);
|
|
423
|
+
const gap = Math.max(1, width - titleWidth - statusWidth - 4);
|
|
424
|
+
lines.push(" " + title + " ".repeat(gap) + status + " ");
|
|
425
|
+
lines.push(theme.fg("border", "─".repeat(width)));
|
|
426
|
+
|
|
427
|
+
if (this.showHelp) {
|
|
428
|
+
// Help screen
|
|
429
|
+
lines.push("");
|
|
430
|
+
lines.push(theme.bold(" Keybindings"));
|
|
431
|
+
lines.push("");
|
|
432
|
+
lines.push(" " + theme.fg("accent", "q / Esc") + " — Close dashboard");
|
|
433
|
+
lines.push(" " + theme.fg("accent", "r") + " — Force refresh");
|
|
434
|
+
lines.push(" " + theme.fg("accent", "?") + " — Toggle help");
|
|
435
|
+
lines.push("");
|
|
436
|
+
} else if (!this.quotaData && this.isLoading) {
|
|
437
|
+
// Loading state
|
|
438
|
+
lines.push("");
|
|
439
|
+
lines.push(" " + theme.fg("dim", "Loading quota data..."));
|
|
440
|
+
lines.push("");
|
|
441
|
+
} else if (!this.quotaData && this.errorMessage) {
|
|
442
|
+
// Error state
|
|
443
|
+
lines.push("");
|
|
444
|
+
lines.push(" " + theme.fg("error", this.errorMessage));
|
|
445
|
+
lines.push("");
|
|
446
|
+
} else if (this.quotaData) {
|
|
447
|
+
// Donut charts side by side
|
|
448
|
+
const fiveHourLines = renderDonut(
|
|
449
|
+
this.quotaData.fiveHourUsagePct,
|
|
450
|
+
"5-Hour Quota",
|
|
451
|
+
this.quotaData.fiveHourResetTime,
|
|
452
|
+
theme,
|
|
453
|
+
);
|
|
454
|
+
const sevenDayLines = renderDonut(
|
|
455
|
+
this.quotaData.sevenDayUsagePct,
|
|
456
|
+
"Weekly Quota",
|
|
457
|
+
this.quotaData.sevenDayResetTime,
|
|
458
|
+
theme,
|
|
459
|
+
);
|
|
460
|
+
|
|
461
|
+
// Determine chart widths
|
|
462
|
+
const chartWidth = Math.max(...fiveHourLines.map(l => visibleWidth(l)), ...sevenDayLines.map(l => visibleWidth(l)));
|
|
463
|
+
const gapBetween = 6;
|
|
464
|
+
const totalChartsWidth = chartWidth * 2 + gapBetween;
|
|
465
|
+
const leftPad = Math.max(2, Math.floor((width - totalChartsWidth) / 2));
|
|
466
|
+
|
|
467
|
+
const maxLines = Math.max(fiveHourLines.length, sevenDayLines.length);
|
|
468
|
+
lines.push("");
|
|
469
|
+
for (let i = 0; i < maxLines; i++) {
|
|
470
|
+
const left = fiveHourLines[i] ?? "";
|
|
471
|
+
const right = sevenDayLines[i] ?? "";
|
|
472
|
+
const leftPadded = left + " ".repeat(Math.max(0, chartWidth - visibleWidth(left)));
|
|
473
|
+
const row = " ".repeat(leftPad) + leftPadded + " ".repeat(gapBetween) + right;
|
|
474
|
+
lines.push(truncateToWidth(row, width));
|
|
475
|
+
}
|
|
476
|
+
lines.push("");
|
|
477
|
+
|
|
478
|
+
// Model quotas
|
|
479
|
+
const modelLines = renderModelQuotas(this.quotaData, theme, width);
|
|
480
|
+
if (modelLines.length > 0) {
|
|
481
|
+
lines.push(theme.fg("border", "─".repeat(width)));
|
|
482
|
+
for (const ml of modelLines) {
|
|
483
|
+
lines.push(truncateToWidth(ml, width));
|
|
484
|
+
}
|
|
485
|
+
lines.push("");
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
// Footer
|
|
490
|
+
lines.push(theme.fg("border", "─".repeat(width)));
|
|
491
|
+
const helpText = theme.fg("dim", "q: Close r: Refresh ?: Help");
|
|
492
|
+
lines.push(" " + helpText);
|
|
493
|
+
|
|
494
|
+
this.cachedLines = lines;
|
|
495
|
+
this.cachedWidth = width;
|
|
496
|
+
this.cachedVersion = this.version;
|
|
497
|
+
return lines;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
invalidate(): void {
|
|
501
|
+
this.cachedWidth = 0;
|
|
502
|
+
this.cachedVersion = -1;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// ---------------------------------------------------------------------------
|
|
507
|
+
// Extension entry point
|
|
508
|
+
// ---------------------------------------------------------------------------
|
|
509
|
+
|
|
510
|
+
export default function (pi: ExtensionAPI) {
|
|
511
|
+
// /claudemon command — show quota inline or launch TUI dashboard
|
|
512
|
+
pi.registerCommand("claudemon", {
|
|
513
|
+
description: "Show Claude usage quota (--tui to launch dashboard)",
|
|
514
|
+
handler: async (args, ctx) => {
|
|
515
|
+
const trimmed = (args ?? "").trim();
|
|
516
|
+
|
|
517
|
+
// --tui flag: launch the native pi TUI dashboard
|
|
518
|
+
if (trimmed === "--tui" || trimmed === "-t") {
|
|
519
|
+
const token = getOAuthToken();
|
|
520
|
+
if (!token) {
|
|
521
|
+
ctx.ui.notify(
|
|
522
|
+
"Not authenticated. Run `claudemon setup` or `npx claudemon setup` first.",
|
|
523
|
+
"error",
|
|
524
|
+
);
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
await ctx.ui.custom<void>((tui, theme, _keybindings, done) => {
|
|
529
|
+
const dashboard = new ClaudemonDashboard(tui, theme, token, () => done());
|
|
530
|
+
return {
|
|
531
|
+
render: (w: number) => dashboard.render(w),
|
|
532
|
+
invalidate: () => dashboard.invalidate(),
|
|
533
|
+
handleInput: (data: string) => dashboard.handleInput(data),
|
|
534
|
+
};
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
// Default: fetch and display inline
|
|
541
|
+
const token = getOAuthToken();
|
|
542
|
+
if (!token) {
|
|
543
|
+
ctx.ui.notify(
|
|
544
|
+
"Not authenticated. Run `claudemon setup` or `npx claudemon setup` first.",
|
|
545
|
+
"error",
|
|
546
|
+
);
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
try {
|
|
551
|
+
ctx.ui.setStatus("claudemon", "Fetching quota…");
|
|
552
|
+
const quota = await fetchQuota(token);
|
|
553
|
+
ctx.ui.setStatus("claudemon", undefined);
|
|
554
|
+
ctx.ui.notify(formatQuota(quota), "info");
|
|
555
|
+
} catch (err) {
|
|
556
|
+
ctx.ui.setStatus("claudemon", undefined);
|
|
557
|
+
ctx.ui.notify(`Failed to fetch quota: ${err instanceof Error ? err.message : err}`, "error");
|
|
558
|
+
}
|
|
559
|
+
},
|
|
560
|
+
});
|
|
561
|
+
|
|
562
|
+
// LLM-callable tool
|
|
563
|
+
pi.registerTool({
|
|
564
|
+
name: "claudemon",
|
|
565
|
+
label: "Claude Usage Monitor",
|
|
566
|
+
description:
|
|
567
|
+
"Check the user's Claude Pro/Max plan quota usage including 5-hour and 7-day windows and per-model breakdown. Use when the user asks about their Claude usage, quota, or remaining capacity.",
|
|
568
|
+
parameters: Type.Object({}),
|
|
569
|
+
async execute(_toolCallId, _params, signal) {
|
|
570
|
+
const token = getOAuthToken();
|
|
571
|
+
if (!token) {
|
|
572
|
+
return {
|
|
573
|
+
content: [
|
|
574
|
+
{
|
|
575
|
+
type: "text" as const,
|
|
576
|
+
text: "Not authenticated. The user needs to run `claudemon setup` or `npx claudemon setup` to authenticate first.",
|
|
577
|
+
},
|
|
578
|
+
],
|
|
579
|
+
isError: true,
|
|
580
|
+
};
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
try {
|
|
584
|
+
const quota = await fetchQuota(token);
|
|
585
|
+
return {
|
|
586
|
+
content: [{ type: "text" as const, text: formatQuota(quota) }],
|
|
587
|
+
details: {
|
|
588
|
+
fiveHourUsagePct: quota.fiveHourUsagePct,
|
|
589
|
+
sevenDayUsagePct: quota.sevenDayUsagePct,
|
|
590
|
+
planType: quota.planType,
|
|
591
|
+
modelQuotas: quota.modelQuotas,
|
|
592
|
+
},
|
|
593
|
+
};
|
|
594
|
+
} catch (err) {
|
|
595
|
+
return {
|
|
596
|
+
content: [
|
|
597
|
+
{
|
|
598
|
+
type: "text" as const,
|
|
599
|
+
text: `Failed to fetch quota: ${err instanceof Error ? err.message : err}`,
|
|
600
|
+
},
|
|
601
|
+
],
|
|
602
|
+
isError: true,
|
|
603
|
+
};
|
|
604
|
+
}
|
|
605
|
+
},
|
|
606
|
+
});
|
|
607
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claudemon",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Claude Usage Monitor TUI - monitor your Claude usage in real-time",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -8,15 +8,22 @@
|
|
|
8
8
|
"claudemon": "dist/index.js"
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
|
-
"dist"
|
|
11
|
+
"dist",
|
|
12
|
+
"extensions"
|
|
12
13
|
],
|
|
13
14
|
"keywords": [
|
|
14
15
|
"claude",
|
|
15
16
|
"anthropic",
|
|
16
17
|
"quota",
|
|
17
18
|
"tui",
|
|
18
|
-
"cli"
|
|
19
|
+
"cli",
|
|
20
|
+
"pi-package"
|
|
19
21
|
],
|
|
22
|
+
"pi": {
|
|
23
|
+
"extensions": [
|
|
24
|
+
"./extensions"
|
|
25
|
+
]
|
|
26
|
+
},
|
|
20
27
|
"license": "MIT",
|
|
21
28
|
"dependencies": {
|
|
22
29
|
"chalk": "^5.3.0",
|
|
@@ -24,6 +31,18 @@
|
|
|
24
31
|
"react": "^18.3.1",
|
|
25
32
|
"smol-toml": "^1.3.1"
|
|
26
33
|
},
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"@mariozechner/pi-coding-agent": "*",
|
|
36
|
+
"@sinclair/typebox": "*"
|
|
37
|
+
},
|
|
38
|
+
"peerDependenciesMeta": {
|
|
39
|
+
"@mariozechner/pi-coding-agent": {
|
|
40
|
+
"optional": true
|
|
41
|
+
},
|
|
42
|
+
"@sinclair/typebox": {
|
|
43
|
+
"optional": true
|
|
44
|
+
}
|
|
45
|
+
},
|
|
27
46
|
"devDependencies": {
|
|
28
47
|
"@types/node": "^25.2.3",
|
|
29
48
|
"@types/react": "^18.3.12",
|