fix: usage dashboard - remove title, use system uptime, handle null limitName, drop seconds from timestamp
- Remove "📊 Usage 보고" title line
- Use os.uptime() instead of process.uptime() for actual server uptime
- Handle null limitName in Codex rate limits (fallback to limitId or 'Codex')
- Fix Codex usage parsing: extract from rateLimitsByLimitId object
- Fix formatResetKST to handle unix timestamps (number type)
- Remove seconds from update timestamp display
This commit is contained in:
35
src/index.ts
35
src/index.ts
@@ -416,9 +416,12 @@ function usageEmoji(pct: number): string {
|
|||||||
return '🟢';
|
return '🟢';
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatResetKST(iso: string): string {
|
function formatResetKST(value: string | number): string {
|
||||||
try {
|
try {
|
||||||
return new Date(iso).toLocaleString('ko-KR', {
|
// Handle unix timestamp (seconds) or ISO string
|
||||||
|
const date =
|
||||||
|
typeof value === 'number' ? new Date(value * 1000) : new Date(value);
|
||||||
|
return date.toLocaleString('ko-KR', {
|
||||||
timeZone: 'Asia/Seoul',
|
timeZone: 'Asia/Seoul',
|
||||||
month: 'short',
|
month: 'short',
|
||||||
day: 'numeric',
|
day: 'numeric',
|
||||||
@@ -426,7 +429,7 @@ function formatResetKST(iso: string): string {
|
|||||||
minute: '2-digit',
|
minute: '2-digit',
|
||||||
});
|
});
|
||||||
} catch {
|
} catch {
|
||||||
return iso;
|
return String(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -544,9 +547,10 @@ interface ClaudeUsageData {
|
|||||||
}
|
}
|
||||||
|
|
||||||
interface CodexRateLimit {
|
interface CodexRateLimit {
|
||||||
limitName: string;
|
limitId?: string;
|
||||||
primary: { usedPercent: number; resetsAt: string };
|
limitName: string | null;
|
||||||
secondary: { usedPercent: number; resetsAt: string };
|
primary: { usedPercent: number; resetsAt: string | number };
|
||||||
|
secondary: { usedPercent: number; resetsAt: string | number };
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fetchClaudeUsage(): Promise<ClaudeUsageData | null> {
|
async function fetchClaudeUsage(): Promise<ClaudeUsageData | null> {
|
||||||
@@ -635,7 +639,13 @@ async function fetchCodexUsage(): Promise<CodexRateLimit[] | null> {
|
|||||||
}) + '\n',
|
}) + '\n',
|
||||||
);
|
);
|
||||||
} else if (msg.id === 2 && msg.result) {
|
} else if (msg.id === 2 && msg.result) {
|
||||||
finish(msg.result);
|
// Extract rate limits from rateLimitsByLimitId object
|
||||||
|
const byId = msg.result.rateLimitsByLimitId;
|
||||||
|
if (byId && typeof byId === 'object') {
|
||||||
|
finish(Object.values(byId) as CodexRateLimit[]);
|
||||||
|
} else {
|
||||||
|
finish(null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
/* non-JSON line, skip */
|
/* non-JSON line, skip */
|
||||||
@@ -659,8 +669,6 @@ async function fetchCodexUsage(): Promise<CodexRateLimit[] | null> {
|
|||||||
|
|
||||||
async function buildUsageContent(): Promise<string> {
|
async function buildUsageContent(): Promise<string> {
|
||||||
const lines: string[] = [];
|
const lines: string[] = [];
|
||||||
lines.push(`📊 **Usage 보고** (${ASSISTANT_NAME})`);
|
|
||||||
lines.push('');
|
|
||||||
|
|
||||||
// Fetch API usage in parallel
|
// Fetch API usage in parallel
|
||||||
const [claudeUsage, codexUsage] = await Promise.all([
|
const [claudeUsage, codexUsage] = await Promise.all([
|
||||||
@@ -695,11 +703,12 @@ async function buildUsageContent(): Promise<string> {
|
|||||||
for (const limit of codexUsage) {
|
for (const limit of codexUsage) {
|
||||||
const p = Math.round(limit.primary.usedPercent);
|
const p = Math.round(limit.primary.usedPercent);
|
||||||
const s = Math.round(limit.secondary.usedPercent);
|
const s = Math.round(limit.secondary.usedPercent);
|
||||||
|
const name = limit.limitName || limit.limitId || 'Codex';
|
||||||
lines.push(
|
lines.push(
|
||||||
`• ${limit.limitName} 5시간: ${usageEmoji(p)} ${p}% (리셋: ${formatResetKST(limit.primary.resetsAt)})`,
|
`• ${name} 5시간: ${usageEmoji(p)} ${p}% (리셋: ${formatResetKST(limit.primary.resetsAt)})`,
|
||||||
);
|
);
|
||||||
lines.push(
|
lines.push(
|
||||||
`• ${limit.limitName} 7일: ${usageEmoji(s)} ${s}% (리셋: ${formatResetKST(limit.secondary.resetsAt)})`,
|
`• ${name} 7일: ${usageEmoji(s)} ${s}% (리셋: ${formatResetKST(limit.secondary.resetsAt)})`,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
lines.push('');
|
lines.push('');
|
||||||
@@ -745,9 +754,9 @@ async function buildUsageContent(): Promise<string> {
|
|||||||
}
|
}
|
||||||
lines.push(diskLine);
|
lines.push(diskLine);
|
||||||
|
|
||||||
lines.push(`• 업타임: ${formatElapsed(process.uptime() * 1000)}`);
|
lines.push(`• 업타임: ${formatElapsed(os.uptime() * 1000)}`);
|
||||||
|
|
||||||
return lines.join('\n') + `\n\n_${new Date().toLocaleTimeString('ko-KR')}_`;
|
return lines.join('\n') + `\n\n_${new Date().toLocaleTimeString('ko-KR', { hour: '2-digit', minute: '2-digit' })}_`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Dashboard Lifecycle ─────────────────────────────────────────
|
// ── Dashboard Lifecycle ─────────────────────────────────────────
|
||||||
|
|||||||
Reference in New Issue
Block a user