cc-usage-bar 0.1.0

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.
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.glmAdapter = void 0;
4
+ exports.parseGlm = parseGlm;
5
+ const http_1 = require("./http");
6
+ const ENDPOINT = 'https://api.z.ai/api/monitor/usage/quota/limit';
7
+ function parseGlm(body) {
8
+ if (!body || typeof body !== 'object')
9
+ return null;
10
+ const r = body;
11
+ if (r.success === false)
12
+ return { error: typeof r.msg === 'string' ? r.msg : 'GLM API error' };
13
+ const limits = r.data?.limits;
14
+ if (!Array.isArray(limits))
15
+ return null;
16
+ const tokens = limits.find((l) => l && l.type === 'TOKENS_LIMIT');
17
+ if (!tokens || typeof tokens.percentage !== 'number')
18
+ return null;
19
+ const out = {
20
+ kind: 'subscription',
21
+ planName: typeof r.data?.level === 'string' ? `GLM ${r.data.level}` : 'GLM',
22
+ five_hour: {
23
+ utilization: tokens.percentage,
24
+ resets_at: typeof tokens.nextResetTime === 'number'
25
+ ? new Date(tokens.nextResetTime).toISOString()
26
+ : undefined,
27
+ },
28
+ };
29
+ return out;
30
+ }
31
+ exports.glmAdapter = {
32
+ id: 'glm',
33
+ displayName: 'GLM',
34
+ matches(env) {
35
+ if (!env.baseUrl)
36
+ return false;
37
+ const u = env.baseUrl.toLowerCase();
38
+ return u.includes('z.ai') || u.includes('bigmodel.cn');
39
+ },
40
+ async fetch(env) {
41
+ if (!env.authToken)
42
+ return { ok: false, error: 'ANTHROPIC_AUTH_TOKEN not set' };
43
+ const res = await (0, http_1.httpGetJson)(ENDPOINT, {
44
+ Authorization: env.authToken,
45
+ 'Content-Type': 'application/json',
46
+ 'Accept-Language': 'en-US,en',
47
+ });
48
+ if (res.authFailed)
49
+ return { ok: false, authFailed: true, error: res.error };
50
+ if (!res.ok || !res.body)
51
+ return { ok: false, error: res.error ?? 'request failed' };
52
+ const parsed = parseGlm(res.body);
53
+ if (!parsed)
54
+ return { ok: false, error: 'invalid response shape' };
55
+ if ('error' in parsed)
56
+ return { ok: false, error: parsed.error };
57
+ return { ok: true, data: parsed };
58
+ },
59
+ };
@@ -0,0 +1,26 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HTTP_TIMEOUT_MS = void 0;
4
+ exports.httpGetJson = httpGetJson;
5
+ exports.HTTP_TIMEOUT_MS = 2_000;
6
+ async function httpGetJson(url, headers, timeoutMs = exports.HTTP_TIMEOUT_MS) {
7
+ try {
8
+ const res = await fetch(url, {
9
+ method: 'GET',
10
+ headers: { Accept: 'application/json', ...headers },
11
+ signal: AbortSignal.timeout(timeoutMs),
12
+ });
13
+ if (res.status === 401 || res.status === 403) {
14
+ return { ok: false, status: res.status, authFailed: true, error: `unauthorized (${res.status})` };
15
+ }
16
+ if (!res.ok) {
17
+ return { ok: false, status: res.status, error: `http ${res.status}` };
18
+ }
19
+ const body = (await res.json());
20
+ return { ok: true, status: res.status, body };
21
+ }
22
+ catch (e) {
23
+ const msg = e instanceof Error ? e.message : 'network error';
24
+ return { ok: false, error: msg };
25
+ }
26
+ }
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.kimiAdapter = void 0;
4
+ exports.parseKimi = parseKimi;
5
+ const http_1 = require("./http");
6
+ const ENDPOINT = 'https://api.kimi.com/coding/v1/usages';
7
+ function toIso(t) {
8
+ if (typeof t === 'number' && Number.isFinite(t)) {
9
+ const ms = t > 1e12 ? t : t * 1000;
10
+ return new Date(ms).toISOString();
11
+ }
12
+ if (typeof t === 'string' && t)
13
+ return t;
14
+ return undefined;
15
+ }
16
+ function tierFrom(t) {
17
+ if (!t || typeof t.limit !== 'number' || typeof t.remaining !== 'number' || t.limit <= 0)
18
+ return undefined;
19
+ const utilization = ((t.limit - t.remaining) / t.limit) * 100;
20
+ return { utilization, resets_at: toIso(t.resetTime) };
21
+ }
22
+ function parseKimi(body) {
23
+ if (!body || typeof body !== 'object')
24
+ return null;
25
+ const r = body;
26
+ const out = { kind: 'subscription', planName: 'Kimi' };
27
+ const fhDetail = Array.isArray(r.limits) && r.limits.length > 0 ? r.limits[0]?.detail : undefined;
28
+ const fh = tierFrom(fhDetail);
29
+ if (fh)
30
+ out.five_hour = fh;
31
+ const wk = tierFrom(r.usage);
32
+ if (wk)
33
+ out.seven_day = wk;
34
+ if (!out.five_hour && !out.seven_day)
35
+ return null;
36
+ return out;
37
+ }
38
+ exports.kimiAdapter = {
39
+ id: 'kimi',
40
+ displayName: 'Kimi',
41
+ matches(env) {
42
+ return !!env.baseUrl && env.baseUrl.toLowerCase().includes('api.kimi.com');
43
+ },
44
+ async fetch(env) {
45
+ if (!env.authToken)
46
+ return { ok: false, error: 'ANTHROPIC_AUTH_TOKEN not set' };
47
+ const res = await (0, http_1.httpGetJson)(ENDPOINT, { Authorization: `Bearer ${env.authToken}` });
48
+ if (res.authFailed)
49
+ return { ok: false, authFailed: true, error: res.error };
50
+ if (!res.ok || !res.body)
51
+ return { ok: false, error: res.error ?? 'request failed' };
52
+ const data = parseKimi(res.body);
53
+ if (!data)
54
+ return { ok: false, error: 'invalid response shape' };
55
+ return { ok: true, data };
56
+ },
57
+ };
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.minimaxAdapter = void 0;
4
+ exports.parseMinimax = parseMinimax;
5
+ const http_1 = require("./http");
6
+ function tier(total, used, endMs) {
7
+ if (typeof total !== 'number' || typeof used !== 'number' || total <= 0)
8
+ return undefined;
9
+ return {
10
+ utilization: ((total - (total - used)) / total) * 100,
11
+ resets_at: typeof endMs === 'number' ? new Date(endMs).toISOString() : undefined,
12
+ };
13
+ }
14
+ function parseMinimax(body) {
15
+ if (!body || typeof body !== 'object')
16
+ return null;
17
+ const r = body;
18
+ const code = r.base_resp?.status_code;
19
+ if (typeof code === 'number' && code !== 0) {
20
+ return { error: r.base_resp?.status_msg ?? `MiniMax error ${code}` };
21
+ }
22
+ const remains = r.model_remains?.[0];
23
+ if (!remains)
24
+ return null;
25
+ const out = { kind: 'subscription', planName: 'MiniMax' };
26
+ const fh = tier(remains.current_interval_total_count, remains.current_interval_usage_count, remains.end_time);
27
+ if (fh)
28
+ out.five_hour = fh;
29
+ const wk = tier(remains.current_weekly_total_count, remains.current_weekly_usage_count, remains.weekly_end_time);
30
+ if (wk)
31
+ out.seven_day = wk;
32
+ if (!out.five_hour && !out.seven_day)
33
+ return null;
34
+ return out;
35
+ }
36
+ exports.minimaxAdapter = {
37
+ id: 'minimax',
38
+ displayName: 'MiniMax',
39
+ matches(env) {
40
+ if (!env.baseUrl)
41
+ return false;
42
+ const u = env.baseUrl.toLowerCase();
43
+ return u.includes('minimaxi.com') || u.includes('minimax.io');
44
+ },
45
+ async fetch(env) {
46
+ if (!env.authToken)
47
+ return { ok: false, error: 'ANTHROPIC_AUTH_TOKEN not set' };
48
+ const u = env.baseUrl?.toLowerCase() ?? '';
49
+ const domain = u.includes('minimax.io') ? 'api.minimax.io' : 'api.minimaxi.com';
50
+ const url = `https://${domain}/v1/api/openplatform/coding_plan/remains`;
51
+ const res = await (0, http_1.httpGetJson)(url, {
52
+ Authorization: `Bearer ${env.authToken}`,
53
+ 'Content-Type': 'application/json',
54
+ });
55
+ if (res.authFailed)
56
+ return { ok: false, authFailed: true, error: res.error };
57
+ if (!res.ok || !res.body)
58
+ return { ok: false, error: res.error ?? 'request failed' };
59
+ const parsed = parseMinimax(res.body);
60
+ if (!parsed)
61
+ return { ok: false, error: 'invalid response shape' };
62
+ if ('error' in parsed)
63
+ return { ok: false, error: parsed.error };
64
+ return { ok: true, data: parsed };
65
+ },
66
+ };
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.novitaAdapter = void 0;
4
+ exports.parseNovita = parseNovita;
5
+ const http_1 = require("./http");
6
+ const ENDPOINT = 'https://api.novita.ai/v3/user/balance';
7
+ function parseNovita(body) {
8
+ if (!body || typeof body !== 'object')
9
+ return null;
10
+ const r = body;
11
+ const raw = r.availableBalance;
12
+ if (typeof raw !== 'number' || !Number.isFinite(raw))
13
+ return null;
14
+ const remaining = raw / 10000;
15
+ return {
16
+ kind: 'balance',
17
+ remaining,
18
+ unit: 'USD',
19
+ planName: 'Novita',
20
+ isValid: remaining > 0,
21
+ invalidMessage: remaining <= 0 ? 'No balance remaining' : undefined,
22
+ };
23
+ }
24
+ exports.novitaAdapter = {
25
+ id: 'novita',
26
+ displayName: 'Novita',
27
+ matches(env) {
28
+ return !!env.baseUrl && env.baseUrl.toLowerCase().includes('api.novita.ai');
29
+ },
30
+ async fetch(env) {
31
+ if (!env.authToken)
32
+ return { ok: false, error: 'ANTHROPIC_AUTH_TOKEN not set' };
33
+ const res = await (0, http_1.httpGetJson)(ENDPOINT, { Authorization: `Bearer ${env.authToken}` });
34
+ if (res.authFailed)
35
+ return { ok: false, authFailed: true, error: res.error };
36
+ if (!res.ok || !res.body)
37
+ return { ok: false, error: res.error ?? 'request failed' };
38
+ const data = parseNovita(res.body);
39
+ if (!data)
40
+ return { ok: false, error: 'invalid response shape' };
41
+ return { ok: true, data };
42
+ },
43
+ };
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.openrouterAdapter = void 0;
4
+ exports.parseOpenRouter = parseOpenRouter;
5
+ const http_1 = require("./http");
6
+ const ENDPOINT = 'https://openrouter.ai/api/v1/credits';
7
+ function parseOpenRouter(body) {
8
+ if (!body || typeof body !== 'object')
9
+ return null;
10
+ const r = body;
11
+ const inner = r.data ?? r;
12
+ const total = typeof inner.total_credits === 'number' ? inner.total_credits : null;
13
+ const used = typeof inner.total_usage === 'number' ? inner.total_usage : null;
14
+ if (total === null || used === null)
15
+ return null;
16
+ const remaining = total - used;
17
+ return {
18
+ kind: 'balance',
19
+ remaining,
20
+ total,
21
+ used,
22
+ unit: 'USD',
23
+ planName: 'OpenRouter',
24
+ isValid: remaining > 0,
25
+ invalidMessage: remaining <= 0 ? 'No credits remaining' : undefined,
26
+ };
27
+ }
28
+ exports.openrouterAdapter = {
29
+ id: 'openrouter',
30
+ displayName: 'OR',
31
+ matches(env) {
32
+ return !!env.baseUrl && env.baseUrl.toLowerCase().includes('openrouter.ai');
33
+ },
34
+ async fetch(env) {
35
+ if (!env.authToken)
36
+ return { ok: false, error: 'ANTHROPIC_AUTH_TOKEN not set' };
37
+ const res = await (0, http_1.httpGetJson)(ENDPOINT, { Authorization: `Bearer ${env.authToken}` });
38
+ if (res.authFailed)
39
+ return { ok: false, authFailed: true, error: res.error };
40
+ if (!res.ok || !res.body)
41
+ return { ok: false, error: res.error ?? 'request failed' };
42
+ const data = parseOpenRouter(res.body);
43
+ if (!data)
44
+ return { ok: false, error: 'invalid response shape' };
45
+ return { ok: true, data };
46
+ },
47
+ };
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ALL_ADAPTERS = void 0;
4
+ exports.selectAdapter = selectAdapter;
5
+ exports.getEnv = getEnv;
6
+ const anthropic_1 = require("./anthropic");
7
+ const kimi_1 = require("./kimi");
8
+ const glm_1 = require("./glm");
9
+ const minimax_1 = require("./minimax");
10
+ const deepseek_1 = require("./deepseek");
11
+ const stepfun_1 = require("./stepfun");
12
+ const siliconflow_1 = require("./siliconflow");
13
+ const openrouter_1 = require("./openrouter");
14
+ const novita_1 = require("./novita");
15
+ const NON_DEFAULT = [
16
+ kimi_1.kimiAdapter,
17
+ glm_1.glmAdapter,
18
+ minimax_1.minimaxAdapter,
19
+ deepseek_1.deepseekAdapter,
20
+ stepfun_1.stepfunAdapter,
21
+ siliconflow_1.siliconflowAdapter,
22
+ openrouter_1.openrouterAdapter,
23
+ novita_1.novitaAdapter,
24
+ ];
25
+ exports.ALL_ADAPTERS = [...NON_DEFAULT, anthropic_1.anthropicAdapter];
26
+ function selectAdapter(env) {
27
+ for (const a of NON_DEFAULT) {
28
+ if (a.matches(env))
29
+ return a;
30
+ }
31
+ if (anthropic_1.anthropicAdapter.matches(env))
32
+ return anthropic_1.anthropicAdapter;
33
+ return null;
34
+ }
35
+ function getEnv() {
36
+ return {
37
+ baseUrl: process.env.ANTHROPIC_BASE_URL,
38
+ authToken: process.env.ANTHROPIC_AUTH_TOKEN ?? process.env.ANTHROPIC_API_KEY,
39
+ };
40
+ }
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.siliconflowAdapter = void 0;
4
+ exports.parseSiliconFlow = parseSiliconFlow;
5
+ const http_1 = require("./http");
6
+ function toNum(v) {
7
+ if (typeof v === 'number' && Number.isFinite(v))
8
+ return v;
9
+ if (typeof v === 'string') {
10
+ const n = parseFloat(v);
11
+ return Number.isFinite(n) ? n : null;
12
+ }
13
+ return null;
14
+ }
15
+ function parseSiliconFlow(body) {
16
+ if (!body || typeof body !== 'object')
17
+ return null;
18
+ const r = body;
19
+ const balance = toNum(r.data?.totalBalance);
20
+ if (balance === null)
21
+ return null;
22
+ return {
23
+ kind: 'balance',
24
+ remaining: balance,
25
+ unit: 'CNY',
26
+ planName: 'SiliconFlow',
27
+ isValid: true,
28
+ };
29
+ }
30
+ exports.siliconflowAdapter = {
31
+ id: 'siliconflow',
32
+ displayName: 'SF',
33
+ matches(env) {
34
+ if (!env.baseUrl)
35
+ return false;
36
+ const u = env.baseUrl.toLowerCase();
37
+ return u.includes('api.siliconflow.cn') || u.includes('api.siliconflow.com');
38
+ },
39
+ async fetch(env) {
40
+ if (!env.authToken)
41
+ return { ok: false, error: 'ANTHROPIC_AUTH_TOKEN not set' };
42
+ const u = env.baseUrl?.toLowerCase() ?? '';
43
+ const domain = u.includes('siliconflow.com') ? 'api.siliconflow.com' : 'api.siliconflow.cn';
44
+ const url = `https://${domain}/v1/user/info`;
45
+ const res = await (0, http_1.httpGetJson)(url, { Authorization: `Bearer ${env.authToken}` });
46
+ if (res.authFailed)
47
+ return { ok: false, authFailed: true, error: res.error };
48
+ if (!res.ok || !res.body)
49
+ return { ok: false, error: res.error ?? 'request failed' };
50
+ const data = parseSiliconFlow(res.body);
51
+ if (!data)
52
+ return { ok: false, error: 'invalid response shape' };
53
+ return { ok: true, data };
54
+ },
55
+ };
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.stepfunAdapter = void 0;
4
+ exports.parseStepFun = parseStepFun;
5
+ const http_1 = require("./http");
6
+ const ENDPOINT = 'https://api.stepfun.com/v1/accounts';
7
+ function toNum(v) {
8
+ if (typeof v === 'number' && Number.isFinite(v))
9
+ return v;
10
+ if (typeof v === 'string') {
11
+ const n = parseFloat(v);
12
+ return Number.isFinite(n) ? n : null;
13
+ }
14
+ return null;
15
+ }
16
+ function parseStepFun(body) {
17
+ if (!body || typeof body !== 'object')
18
+ return null;
19
+ const balance = toNum(body.balance);
20
+ if (balance === null)
21
+ return null;
22
+ return {
23
+ kind: 'balance',
24
+ remaining: balance,
25
+ unit: 'CNY',
26
+ planName: 'StepFun',
27
+ isValid: true,
28
+ };
29
+ }
30
+ exports.stepfunAdapter = {
31
+ id: 'stepfun',
32
+ displayName: 'StepFun',
33
+ matches(env) {
34
+ if (!env.baseUrl)
35
+ return false;
36
+ const u = env.baseUrl.toLowerCase();
37
+ return u.includes('api.stepfun.com') || u.includes('api.stepfun.ai');
38
+ },
39
+ async fetch(env) {
40
+ if (!env.authToken)
41
+ return { ok: false, error: 'ANTHROPIC_AUTH_TOKEN not set' };
42
+ const res = await (0, http_1.httpGetJson)(ENDPOINT, { Authorization: `Bearer ${env.authToken}` });
43
+ if (res.authFailed)
44
+ return { ok: false, authFailed: true, error: res.error };
45
+ if (!res.ok || !res.body)
46
+ return { ok: false, error: res.error ?? 'request failed' };
47
+ const data = parseStepFun(res.body);
48
+ if (!data)
49
+ return { ok: false, error: 'invalid response shape' };
50
+ return { ok: true, data };
51
+ },
52
+ };
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.shouldUseColor = void 0;
4
+ exports.render = render;
5
+ exports.renderNormalized = renderNormalized;
6
+ const format_1 = require("./format");
7
+ Object.defineProperty(exports, "shouldUseColor", { enumerable: true, get: function () { return format_1.shouldUseColor; } });
8
+ function toFormatOptions(opts) {
9
+ return {
10
+ ...format_1.DEFAULT_FORMAT,
11
+ color: opts.color,
12
+ showProviderName: opts.showProviderName ?? false,
13
+ };
14
+ }
15
+ function render(data, opts = { color: true }) {
16
+ if (!data)
17
+ return '';
18
+ const sub = { kind: 'subscription' };
19
+ if (typeof data.five_hour?.utilization === 'number') {
20
+ sub.five_hour = { utilization: data.five_hour.utilization, resets_at: data.five_hour.resets_at };
21
+ }
22
+ if (typeof data.seven_day?.utilization === 'number') {
23
+ sub.seven_day = { utilization: data.seven_day.utilization, resets_at: data.seven_day.resets_at };
24
+ }
25
+ return (0, format_1.renderUsage)(sub, toFormatOptions(opts));
26
+ }
27
+ function renderNormalized(data, opts = {}) {
28
+ return (0, format_1.renderUsage)(data, { ...format_1.DEFAULT_FORMAT, ...opts });
29
+ }