cc-api-statusline 0.1.1 → 0.1.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/dist/cc-api-statusline.js +124 -30
- package/package.json +1 -1
|
@@ -134,7 +134,7 @@ var DEFAULT_CONFIG = {
|
|
|
134
134
|
barSize: "medium",
|
|
135
135
|
barStyle: "classic",
|
|
136
136
|
separator: " | ",
|
|
137
|
-
maxWidth:
|
|
137
|
+
maxWidth: 100,
|
|
138
138
|
clockFormat: "24h"
|
|
139
139
|
},
|
|
140
140
|
components: {
|
|
@@ -557,8 +557,13 @@ class Logger {
|
|
|
557
557
|
this.enabled = false;
|
|
558
558
|
}
|
|
559
559
|
}
|
|
560
|
+
formatLocalTimestamp() {
|
|
561
|
+
const d = new Date;
|
|
562
|
+
const pad = (n, len = 2) => n.toString().padStart(len, "0");
|
|
563
|
+
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}.${pad(d.getMilliseconds(), 3)}`;
|
|
564
|
+
}
|
|
560
565
|
format(level, message, data) {
|
|
561
|
-
const timestamp =
|
|
566
|
+
const timestamp = this.formatLocalTimestamp();
|
|
562
567
|
const dataStr = data ? ` ${JSON.stringify(data)}` : "";
|
|
563
568
|
return `[${timestamp}] [${level.toUpperCase()}] ${message}${dataStr}
|
|
564
569
|
`;
|
|
@@ -634,21 +639,21 @@ function detectClaudeVersion() {
|
|
|
634
639
|
}
|
|
635
640
|
|
|
636
641
|
// src/providers/sub2api.ts
|
|
637
|
-
function
|
|
642
|
+
function computeNextMidnightLocal() {
|
|
638
643
|
const now = new Date;
|
|
639
|
-
const tomorrow = new Date(
|
|
644
|
+
const tomorrow = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1, 0, 0, 0, 0);
|
|
640
645
|
return tomorrow.toISOString();
|
|
641
646
|
}
|
|
642
|
-
function
|
|
647
|
+
function computeNextMondayLocal() {
|
|
643
648
|
const now = new Date;
|
|
644
|
-
const dayOfWeek = now.
|
|
649
|
+
const dayOfWeek = now.getDay();
|
|
645
650
|
const daysUntilMonday = dayOfWeek === 0 ? 1 : 8 - dayOfWeek;
|
|
646
|
-
const nextMonday = new Date(
|
|
651
|
+
const nextMonday = new Date(now.getFullYear(), now.getMonth(), now.getDate() + daysUntilMonday, 0, 0, 0, 0);
|
|
647
652
|
return nextMonday.toISOString();
|
|
648
653
|
}
|
|
649
|
-
function
|
|
654
|
+
function computeFirstOfNextMonthLocal() {
|
|
650
655
|
const now = new Date;
|
|
651
|
-
const nextMonth = new Date(
|
|
656
|
+
const nextMonth = new Date(now.getFullYear(), now.getMonth() + 1, 1, 0, 0, 0, 0);
|
|
652
657
|
return nextMonth.toISOString();
|
|
653
658
|
}
|
|
654
659
|
function mapPeriodTokens(data) {
|
|
@@ -730,9 +735,9 @@ async function fetchSub2api(baseUrl, token, config, timeoutMs = 5000) {
|
|
|
730
735
|
if (!sub) {
|
|
731
736
|
throw new Error("Subscription mode but no subscription object in response");
|
|
732
737
|
}
|
|
733
|
-
result.daily = createQuotaWindow(sub.daily_usage_usd, sub.daily_limit_usd,
|
|
734
|
-
result.weekly = createQuotaWindow(sub.weekly_usage_usd, sub.weekly_limit_usd,
|
|
735
|
-
result.monthly = createQuotaWindow(sub.monthly_usage_usd, sub.monthly_limit_usd,
|
|
738
|
+
result.daily = createQuotaWindow(sub.daily_usage_usd, sub.daily_limit_usd, computeNextMidnightLocal());
|
|
739
|
+
result.weekly = createQuotaWindow(sub.weekly_usage_usd, sub.weekly_limit_usd, computeNextMondayLocal());
|
|
740
|
+
result.monthly = createQuotaWindow(sub.monthly_usage_usd, sub.monthly_limit_usd, computeFirstOfNextMonthLocal());
|
|
736
741
|
result.resetsAt = computeSoonestReset(result);
|
|
737
742
|
}
|
|
738
743
|
if (data.usage) {
|
|
@@ -756,12 +761,12 @@ async function fetchSub2api(baseUrl, token, config, timeoutMs = 5000) {
|
|
|
756
761
|
used: 0,
|
|
757
762
|
limit: 0,
|
|
758
763
|
remaining: 0,
|
|
759
|
-
resetsAt:
|
|
764
|
+
resetsAt: computeNextMidnightLocal()
|
|
760
765
|
},
|
|
761
766
|
weekly: null,
|
|
762
767
|
monthly: null,
|
|
763
768
|
balance: null,
|
|
764
|
-
resetsAt:
|
|
769
|
+
resetsAt: computeNextMidnightLocal(),
|
|
765
770
|
tokenStats: null,
|
|
766
771
|
rateLimit: null
|
|
767
772
|
};
|
|
@@ -1433,10 +1438,10 @@ function renderCountdown(resetsAt, config, clockFormat) {
|
|
|
1433
1438
|
if (format === "auto") {
|
|
1434
1439
|
if (remainingMs < 60000) {
|
|
1435
1440
|
timeStr = "now";
|
|
1436
|
-
} else if (remainingMs <=
|
|
1441
|
+
} else if (remainingMs <= 604800000) {
|
|
1437
1442
|
timeStr = formatDuration(remainingMs);
|
|
1438
1443
|
} else {
|
|
1439
|
-
timeStr =
|
|
1444
|
+
timeStr = formatDateOnly(resetDate);
|
|
1440
1445
|
}
|
|
1441
1446
|
} else if (format === "duration") {
|
|
1442
1447
|
if (remainingMs < 60000) {
|
|
@@ -1459,11 +1464,23 @@ function formatDuration(ms) {
|
|
|
1459
1464
|
return `${days}d ${remainingHours}h`;
|
|
1460
1465
|
} else if (hours >= 1) {
|
|
1461
1466
|
const remainingMinutes = minutes % 60;
|
|
1462
|
-
return `${hours}h${remainingMinutes}m`;
|
|
1467
|
+
return `${hours}h ${remainingMinutes}m`;
|
|
1463
1468
|
} else {
|
|
1464
1469
|
return `${minutes}m`;
|
|
1465
1470
|
}
|
|
1466
1471
|
}
|
|
1472
|
+
function formatDateOnly(date) {
|
|
1473
|
+
const now = new Date;
|
|
1474
|
+
const dayOfWeek = date.toLocaleDateString("en-US", { weekday: "short" });
|
|
1475
|
+
const month = date.toLocaleDateString("en-US", { month: "short" });
|
|
1476
|
+
const dayOfMonth = date.getDate();
|
|
1477
|
+
const isSameMonth = date.getFullYear() === now.getFullYear() && date.getMonth() === now.getMonth();
|
|
1478
|
+
if (isSameMonth) {
|
|
1479
|
+
return `${dayOfWeek} ${dayOfMonth}`;
|
|
1480
|
+
} else {
|
|
1481
|
+
return `${month} ${dayOfMonth}`;
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1467
1484
|
function formatWallClock(date, clockFormat) {
|
|
1468
1485
|
const now = new Date;
|
|
1469
1486
|
const dayOfWeek = date.toLocaleDateString("en-US", { weekday: "short" });
|
|
@@ -1715,7 +1732,14 @@ function getTerminalWidth() {
|
|
|
1715
1732
|
if (process.stdout.columns && process.stdout.columns > 0) {
|
|
1716
1733
|
return process.stdout.columns;
|
|
1717
1734
|
}
|
|
1718
|
-
|
|
1735
|
+
const colsOverride = process.env["CC_STATUSLINE_COLS"];
|
|
1736
|
+
if (colsOverride) {
|
|
1737
|
+
const parsed = parseInt(colsOverride, 10);
|
|
1738
|
+
if (!isNaN(parsed) && parsed > 0) {
|
|
1739
|
+
return parsed;
|
|
1740
|
+
}
|
|
1741
|
+
}
|
|
1742
|
+
return 200;
|
|
1719
1743
|
}
|
|
1720
1744
|
function computeMaxWidth(termWidth, maxWidthPct) {
|
|
1721
1745
|
const pct = Math.max(20, Math.min(100, maxWidthPct));
|
|
@@ -1756,6 +1780,16 @@ function ansiAwareTruncate(text, maxWidth) {
|
|
|
1756
1780
|
}
|
|
1757
1781
|
return output + "…";
|
|
1758
1782
|
}
|
|
1783
|
+
var COMPONENT_DROP_PRIORITY = [
|
|
1784
|
+
"plan",
|
|
1785
|
+
"tokens",
|
|
1786
|
+
"rateLimit",
|
|
1787
|
+
"monthly",
|
|
1788
|
+
"countdown",
|
|
1789
|
+
"weekly",
|
|
1790
|
+
"daily",
|
|
1791
|
+
"balance"
|
|
1792
|
+
];
|
|
1759
1793
|
|
|
1760
1794
|
// src/renderer/index.ts
|
|
1761
1795
|
var DEFAULT_COMPONENT_ORDER2 = [
|
|
@@ -1769,7 +1803,7 @@ var DEFAULT_COMPONENT_ORDER2 = [
|
|
|
1769
1803
|
];
|
|
1770
1804
|
function renderStatusline(data, config, errorState, cacheAge) {
|
|
1771
1805
|
const componentOrder = getComponentOrder(config);
|
|
1772
|
-
const
|
|
1806
|
+
const componentMap = new Map;
|
|
1773
1807
|
for (const componentId of componentOrder) {
|
|
1774
1808
|
const componentConfig = config.components[componentId];
|
|
1775
1809
|
if (componentConfig === false) {
|
|
@@ -1777,10 +1811,38 @@ function renderStatusline(data, config, errorState, cacheAge) {
|
|
|
1777
1811
|
}
|
|
1778
1812
|
const rendered = renderComponent(componentId, data, componentConfig === true || componentConfig === undefined ? {} : componentConfig, config);
|
|
1779
1813
|
if (rendered !== null) {
|
|
1780
|
-
|
|
1814
|
+
componentMap.set(componentId, rendered);
|
|
1781
1815
|
}
|
|
1782
1816
|
}
|
|
1817
|
+
const termWidth = getTerminalWidth();
|
|
1818
|
+
const maxWidth = computeMaxWidth(termWidth, config.display.maxWidth ?? 100);
|
|
1783
1819
|
const separator = config.display.separator ?? " | ";
|
|
1820
|
+
const activeComponents = new Set(componentMap.keys());
|
|
1821
|
+
let currentWidth = calculateStatuslineWidth(componentMap, activeComponents, componentOrder, separator, errorState, data, cacheAge);
|
|
1822
|
+
for (const dropCandidate of COMPONENT_DROP_PRIORITY) {
|
|
1823
|
+
if (dropCandidate === "countdown") {
|
|
1824
|
+
continue;
|
|
1825
|
+
}
|
|
1826
|
+
if (currentWidth <= maxWidth) {
|
|
1827
|
+
break;
|
|
1828
|
+
}
|
|
1829
|
+
if (activeComponents.size <= 1) {
|
|
1830
|
+
break;
|
|
1831
|
+
}
|
|
1832
|
+
if (activeComponents.has(dropCandidate)) {
|
|
1833
|
+
activeComponents.delete(dropCandidate);
|
|
1834
|
+
currentWidth = calculateStatuslineWidth(componentMap, activeComponents, componentOrder, separator, errorState, data, cacheAge);
|
|
1835
|
+
}
|
|
1836
|
+
}
|
|
1837
|
+
const renderedComponents = [];
|
|
1838
|
+
for (const componentId of componentOrder) {
|
|
1839
|
+
if (activeComponents.has(componentId)) {
|
|
1840
|
+
const rendered = componentMap.get(componentId);
|
|
1841
|
+
if (rendered) {
|
|
1842
|
+
renderedComponents.push(rendered);
|
|
1843
|
+
}
|
|
1844
|
+
}
|
|
1845
|
+
}
|
|
1784
1846
|
let statusline = renderedComponents.join(separator);
|
|
1785
1847
|
if (errorState) {
|
|
1786
1848
|
const isTransition = errorState === "switching-provider" || errorState === "new-credentials" || errorState === "new-endpoint" || errorState === "auth-error-waiting";
|
|
@@ -1797,11 +1859,37 @@ function renderStatusline(data, config, errorState, cacheAge) {
|
|
|
1797
1859
|
}
|
|
1798
1860
|
}
|
|
1799
1861
|
}
|
|
1800
|
-
const termWidth = getTerminalWidth();
|
|
1801
|
-
const maxWidth = computeMaxWidth(termWidth, config.display.maxWidth ?? 80);
|
|
1802
1862
|
statusline = ansiAwareTruncate(statusline, maxWidth);
|
|
1803
1863
|
return statusline;
|
|
1804
1864
|
}
|
|
1865
|
+
function calculateStatuslineWidth(componentMap, activeComponents, componentOrder, separator, errorState, data, cacheAge) {
|
|
1866
|
+
const components = [];
|
|
1867
|
+
for (const id of componentOrder) {
|
|
1868
|
+
if (activeComponents.has(id)) {
|
|
1869
|
+
const rendered = componentMap.get(id);
|
|
1870
|
+
if (rendered) {
|
|
1871
|
+
components.push(rendered);
|
|
1872
|
+
}
|
|
1873
|
+
}
|
|
1874
|
+
}
|
|
1875
|
+
let statusline = components.join(separator);
|
|
1876
|
+
if (errorState) {
|
|
1877
|
+
const isTransition = errorState === "switching-provider" || errorState === "new-credentials" || errorState === "new-endpoint" || errorState === "auth-error-waiting";
|
|
1878
|
+
if (isTransition) {
|
|
1879
|
+
statusline = renderError(errorState, "with-cache", data.provider, undefined, cacheAge);
|
|
1880
|
+
} else {
|
|
1881
|
+
const hasCache = components.length > 0;
|
|
1882
|
+
const errorMode = hasCache ? "with-cache" : "without-cache";
|
|
1883
|
+
const errorIndicator = renderError(errorState, errorMode, data.provider, undefined, cacheAge);
|
|
1884
|
+
if (hasCache) {
|
|
1885
|
+
statusline = `${statusline} ${errorIndicator}`;
|
|
1886
|
+
} else {
|
|
1887
|
+
statusline = errorIndicator;
|
|
1888
|
+
}
|
|
1889
|
+
}
|
|
1890
|
+
}
|
|
1891
|
+
return visibleLength(statusline);
|
|
1892
|
+
}
|
|
1805
1893
|
function getComponentOrder(config) {
|
|
1806
1894
|
const explicitOrder = [];
|
|
1807
1895
|
const explicitSet = new Set;
|
|
@@ -1878,7 +1966,7 @@ async function executeCycle(ctx) {
|
|
|
1878
1966
|
if (!baseUrl || !authToken) {
|
|
1879
1967
|
return {
|
|
1880
1968
|
output: renderError("missing-env", "without-cache"),
|
|
1881
|
-
exitCode:
|
|
1969
|
+
exitCode: 0,
|
|
1882
1970
|
cacheUpdate: null
|
|
1883
1971
|
};
|
|
1884
1972
|
}
|
|
@@ -1898,7 +1986,8 @@ async function executeCycle(ctx) {
|
|
|
1898
1986
|
ttlSeconds,
|
|
1899
1987
|
data,
|
|
1900
1988
|
renderedLine: statusline,
|
|
1901
|
-
configHash
|
|
1989
|
+
configHash,
|
|
1990
|
+
errorState: null
|
|
1902
1991
|
};
|
|
1903
1992
|
return {
|
|
1904
1993
|
output: statusline,
|
|
@@ -1921,7 +2010,7 @@ async function executeCycle(ctx) {
|
|
|
1921
2010
|
const errorOutput = renderError("network-error", "without-cache", providerId);
|
|
1922
2011
|
return {
|
|
1923
2012
|
output: errorOutput,
|
|
1924
|
-
exitCode:
|
|
2013
|
+
exitCode: 0,
|
|
1925
2014
|
cacheUpdate: null
|
|
1926
2015
|
};
|
|
1927
2016
|
}
|
|
@@ -1997,7 +2086,7 @@ function uninstallStatusLine() {
|
|
|
1997
2086
|
// package.json
|
|
1998
2087
|
var package_default = {
|
|
1999
2088
|
name: "cc-api-statusline",
|
|
2000
|
-
version: "0.1.
|
|
2089
|
+
version: "0.1.2",
|
|
2001
2090
|
description: "Claude Code statusline tool that polls API usage from third-party proxy backends",
|
|
2002
2091
|
type: "module",
|
|
2003
2092
|
bin: {
|
|
@@ -2192,7 +2281,7 @@ async function main() {
|
|
|
2192
2281
|
if (envError) {
|
|
2193
2282
|
const errorOutput = renderError("missing-env", "without-cache");
|
|
2194
2283
|
process.stdout.write(errorOutput);
|
|
2195
|
-
process.exit(
|
|
2284
|
+
process.exit(0);
|
|
2196
2285
|
}
|
|
2197
2286
|
const baseUrl = env.baseUrl;
|
|
2198
2287
|
const authToken = env.authToken;
|
|
@@ -2210,7 +2299,7 @@ async function main() {
|
|
|
2210
2299
|
logger.error("Provider not found", { providerId });
|
|
2211
2300
|
const errorOutput = renderError("provider-unknown", "without-cache");
|
|
2212
2301
|
process.stdout.write(errorOutput);
|
|
2213
|
-
process.exit(
|
|
2302
|
+
process.exit(0);
|
|
2214
2303
|
}
|
|
2215
2304
|
const cachedEntry = readCache(baseUrl);
|
|
2216
2305
|
logger.debug("Cache read", {
|
|
@@ -2239,12 +2328,17 @@ async function main() {
|
|
|
2239
2328
|
outputLength: result.output.length,
|
|
2240
2329
|
cacheUpdate: !!result.cacheUpdate
|
|
2241
2330
|
});
|
|
2331
|
+
let output = result.output;
|
|
2332
|
+
if (!output || output.trim().length === 0) {
|
|
2333
|
+
output = "[loading...]";
|
|
2334
|
+
logger.debug("Empty output detected, using fallback");
|
|
2335
|
+
}
|
|
2242
2336
|
if (isPiped) {
|
|
2243
|
-
const formatted = "\x1B[0m" +
|
|
2337
|
+
const formatted = "\x1B[0m" + output.replace(/ /g, " ");
|
|
2244
2338
|
process.stdout.write(formatted);
|
|
2245
2339
|
logger.debug("Output formatted for piped mode (ANSI reset + NBSP)");
|
|
2246
2340
|
} else {
|
|
2247
|
-
process.stdout.write(
|
|
2341
|
+
process.stdout.write(output);
|
|
2248
2342
|
logger.debug("Output written (TTY mode)");
|
|
2249
2343
|
}
|
|
2250
2344
|
if (result.cacheUpdate) {
|