fix: read tokens from .env file, clean up debug logs

- token-rotation reads from readEnvFile() (systemd doesn't load .env)
- fetchAllClaudeUsage polling log demoted to debug level
- Remove verbose dashboard send/create logs
This commit is contained in:
Eyejoker
2026-03-23 22:41:07 +09:00
parent 83b25fb7bd
commit 0b86f937f4
4 changed files with 62 additions and 26 deletions

View File

@@ -7,7 +7,10 @@ describe('ClaudeUsageData', () => {
const data: ClaudeUsageData = {
five_hour: { utilization: 45.2, resets_at: '2026-03-23T17:00:00Z' },
seven_day: { utilization: 72.1, resets_at: '2026-03-29T00:00:00Z' },
seven_day_sonnet: { utilization: 60.0, resets_at: '2026-03-29T00:00:00Z' },
seven_day_sonnet: {
utilization: 60.0,
resets_at: '2026-03-29T00:00:00Z',
},
seven_day_opus: { utilization: 80.0, resets_at: '2026-03-29T00:00:00Z' },
};

View File

@@ -25,9 +25,10 @@ interface UsageApiResponse {
seven_day_opus?: { utilization: number; resets_at?: string };
}
function mapWindow(
w?: { utilization: number; resets_at?: string },
): { utilization: number; resets_at: string } | undefined {
function mapWindow(w?: {
utilization: number;
resets_at?: string;
}): { utilization: number; resets_at: string } | undefined {
if (!w) return undefined;
return { utilization: w.utilization, resets_at: w.resets_at || '' };
}
@@ -77,10 +78,7 @@ async function fetchUsageForToken(
if ((err as Error).name === 'AbortError') {
logger.warn('Claude usage API: request timed out');
} else {
logger.warn(
{ err },
'Claude usage API: fetch failed',
);
logger.warn({ err }, 'Claude usage API: fetch failed');
}
return null;
} finally {
@@ -93,8 +91,7 @@ async function fetchUsageForToken(
* Uses the current active token from rotation.
*/
export async function fetchClaudeUsage(): Promise<ClaudeUsageData | null> {
const token =
getCurrentToken() || process.env.CLAUDE_CODE_OAUTH_TOKEN;
const token = getCurrentToken() || process.env.CLAUDE_CODE_OAUTH_TOKEN;
if (!token) {
logger.debug('No Claude OAuth token available for usage check');
return null;
@@ -116,18 +113,21 @@ export interface ClaudeAccountUsage {
*/
export async function fetchAllClaudeUsage(): Promise<ClaudeAccountUsage[]> {
const allTokens = getAllTokens();
logger.debug({ tokenCount: allTokens.length }, 'fetchAllClaudeUsage called');
if (allTokens.length === 0) {
// Single token fallback
const token = process.env.CLAUDE_CODE_OAUTH_TOKEN;
if (!token) return [];
const usage = await fetchUsageForToken(token);
return [{
index: 0,
masked: `${token.slice(0, 20)}...${token.slice(-4)}`,
isActive: true,
isRateLimited: false,
usage,
}];
return [
{
index: 0,
masked: `${token.slice(0, 20)}...${token.slice(-4)}`,
isActive: true,
isRateLimited: false,
usage,
},
];
}
const results: ClaudeAccountUsage[] = [];

View File

@@ -10,6 +10,7 @@
* All exhausted: fall through to provider fallback (Kimi etc.)
*/
import { readEnvFile } from './env.js';
import { logger } from './logger.js';
interface TokenState {
@@ -25,8 +26,16 @@ export function initTokenRotation(): void {
if (initialized) return;
initialized = true;
const multi = process.env.CLAUDE_CODE_OAUTH_TOKENS;
const single = process.env.CLAUDE_CODE_OAUTH_TOKEN;
const envFile = readEnvFile([
'CLAUDE_CODE_OAUTH_TOKENS',
'CLAUDE_CODE_OAUTH_TOKEN',
]);
const multi =
process.env.CLAUDE_CODE_OAUTH_TOKENS ||
envFile.CLAUDE_CODE_OAUTH_TOKENS;
const single =
process.env.CLAUDE_CODE_OAUTH_TOKEN ||
envFile.CLAUDE_CODE_OAUTH_TOKEN;
const raw = multi
? multi
@@ -103,7 +112,13 @@ export function getTokenCount(): number {
}
/** Get all configured tokens (masked for display, raw for API calls). */
export function getAllTokens(): { index: number; token: string; masked: string; isActive: boolean; isRateLimited: boolean }[] {
export function getAllTokens(): {
index: number;
token: string;
masked: string;
isActive: boolean;
isRateLimited: boolean;
}[] {
const now = Date.now();
return tokens.map((t, i) => ({
index: i,

View File

@@ -551,17 +551,31 @@ async function buildUsageContent(): Promise<string> {
};
const rows: UsageRow[] = [];
const claudeAccounts = liveClaudeAccounts
|| (cachedClaudeUsageData ? [{ index: 0, masked: '', isActive: true, isRateLimited: false, usage: cachedClaudeUsageData }] : []);
const claudeAccounts =
liveClaudeAccounts ||
(cachedClaudeUsageData
? [
{
index: 0,
masked: '',
isActive: true,
isRateLimited: false,
usage: cachedClaudeUsageData,
},
]
: []);
for (const account of claudeAccounts) {
const u = account.usage;
if (!u) continue;
const h5 = u.five_hour;
const d7 = u.seven_day;
const label = claudeAccounts.length > 1
? `Claude${account.index + 1}${account.isActive ? '⚡' : ''}${account.isRateLimited ? '🚫' : ''}`
: claudeUsageIsCached ? 'Claude*' : 'Claude';
const label =
claudeAccounts.length > 1
? `Claude${account.index + 1}${account.isActive ? '⚡' : ''}${account.isRateLimited ? '🚫' : ''}`
: claudeUsageIsCached
? 'Claude*'
: 'Claude';
rows.push({
name: label,
h5pct: h5
@@ -717,6 +731,10 @@ export async function startUnifiedDashboard(
await refreshChannelMeta(opts);
const content = buildUnifiedDashboardContent();
if (!content) {
logger.warn(
{ cachedUsageLength: cachedUsageContent.length, statusShowRooms: STATUS_SHOW_ROOMS },
'Dashboard content empty, skipping render',
);
statusMessageId = null;
return;
}
@@ -728,7 +746,7 @@ export async function startUnifiedDashboard(
if (id) statusMessageId = id;
}
} catch (err) {
logger.debug({ err }, 'Dashboard update failed');
logger.warn({ err }, 'Dashboard update failed');
statusMessageId = null;
}
};