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:
@@ -7,7 +7,10 @@ describe('ClaudeUsageData', () => {
|
|||||||
const data: ClaudeUsageData = {
|
const data: ClaudeUsageData = {
|
||||||
five_hour: { utilization: 45.2, resets_at: '2026-03-23T17:00:00Z' },
|
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: { 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' },
|
seven_day_opus: { utilization: 80.0, resets_at: '2026-03-29T00:00:00Z' },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -25,9 +25,10 @@ interface UsageApiResponse {
|
|||||||
seven_day_opus?: { utilization: number; resets_at?: string };
|
seven_day_opus?: { utilization: number; resets_at?: string };
|
||||||
}
|
}
|
||||||
|
|
||||||
function mapWindow(
|
function mapWindow(w?: {
|
||||||
w?: { utilization: number; resets_at?: string },
|
utilization: number;
|
||||||
): { utilization: number; resets_at: string } | undefined {
|
resets_at?: string;
|
||||||
|
}): { utilization: number; resets_at: string } | undefined {
|
||||||
if (!w) return undefined;
|
if (!w) return undefined;
|
||||||
return { utilization: w.utilization, resets_at: w.resets_at || '' };
|
return { utilization: w.utilization, resets_at: w.resets_at || '' };
|
||||||
}
|
}
|
||||||
@@ -77,10 +78,7 @@ async function fetchUsageForToken(
|
|||||||
if ((err as Error).name === 'AbortError') {
|
if ((err as Error).name === 'AbortError') {
|
||||||
logger.warn('Claude usage API: request timed out');
|
logger.warn('Claude usage API: request timed out');
|
||||||
} else {
|
} else {
|
||||||
logger.warn(
|
logger.warn({ err }, 'Claude usage API: fetch failed');
|
||||||
{ err },
|
|
||||||
'Claude usage API: fetch failed',
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
} finally {
|
} finally {
|
||||||
@@ -93,8 +91,7 @@ async function fetchUsageForToken(
|
|||||||
* Uses the current active token from rotation.
|
* Uses the current active token from rotation.
|
||||||
*/
|
*/
|
||||||
export async function fetchClaudeUsage(): Promise<ClaudeUsageData | null> {
|
export async function fetchClaudeUsage(): Promise<ClaudeUsageData | null> {
|
||||||
const token =
|
const token = getCurrentToken() || process.env.CLAUDE_CODE_OAUTH_TOKEN;
|
||||||
getCurrentToken() || process.env.CLAUDE_CODE_OAUTH_TOKEN;
|
|
||||||
if (!token) {
|
if (!token) {
|
||||||
logger.debug('No Claude OAuth token available for usage check');
|
logger.debug('No Claude OAuth token available for usage check');
|
||||||
return null;
|
return null;
|
||||||
@@ -116,18 +113,21 @@ export interface ClaudeAccountUsage {
|
|||||||
*/
|
*/
|
||||||
export async function fetchAllClaudeUsage(): Promise<ClaudeAccountUsage[]> {
|
export async function fetchAllClaudeUsage(): Promise<ClaudeAccountUsage[]> {
|
||||||
const allTokens = getAllTokens();
|
const allTokens = getAllTokens();
|
||||||
|
logger.debug({ tokenCount: allTokens.length }, 'fetchAllClaudeUsage called');
|
||||||
if (allTokens.length === 0) {
|
if (allTokens.length === 0) {
|
||||||
// Single token fallback
|
// Single token fallback
|
||||||
const token = process.env.CLAUDE_CODE_OAUTH_TOKEN;
|
const token = process.env.CLAUDE_CODE_OAUTH_TOKEN;
|
||||||
if (!token) return [];
|
if (!token) return [];
|
||||||
const usage = await fetchUsageForToken(token);
|
const usage = await fetchUsageForToken(token);
|
||||||
return [{
|
return [
|
||||||
index: 0,
|
{
|
||||||
masked: `${token.slice(0, 20)}...${token.slice(-4)}`,
|
index: 0,
|
||||||
isActive: true,
|
masked: `${token.slice(0, 20)}...${token.slice(-4)}`,
|
||||||
isRateLimited: false,
|
isActive: true,
|
||||||
usage,
|
isRateLimited: false,
|
||||||
}];
|
usage,
|
||||||
|
},
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
const results: ClaudeAccountUsage[] = [];
|
const results: ClaudeAccountUsage[] = [];
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
* All exhausted: fall through to provider fallback (Kimi etc.)
|
* All exhausted: fall through to provider fallback (Kimi etc.)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { readEnvFile } from './env.js';
|
||||||
import { logger } from './logger.js';
|
import { logger } from './logger.js';
|
||||||
|
|
||||||
interface TokenState {
|
interface TokenState {
|
||||||
@@ -25,8 +26,16 @@ export function initTokenRotation(): void {
|
|||||||
if (initialized) return;
|
if (initialized) return;
|
||||||
initialized = true;
|
initialized = true;
|
||||||
|
|
||||||
const multi = process.env.CLAUDE_CODE_OAUTH_TOKENS;
|
const envFile = readEnvFile([
|
||||||
const single = process.env.CLAUDE_CODE_OAUTH_TOKEN;
|
'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
|
const raw = multi
|
||||||
? multi
|
? multi
|
||||||
@@ -103,7 +112,13 @@ export function getTokenCount(): number {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Get all configured tokens (masked for display, raw for API calls). */
|
/** 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();
|
const now = Date.now();
|
||||||
return tokens.map((t, i) => ({
|
return tokens.map((t, i) => ({
|
||||||
index: i,
|
index: i,
|
||||||
|
|||||||
@@ -551,17 +551,31 @@ async function buildUsageContent(): Promise<string> {
|
|||||||
};
|
};
|
||||||
const rows: UsageRow[] = [];
|
const rows: UsageRow[] = [];
|
||||||
|
|
||||||
const claudeAccounts = liveClaudeAccounts
|
const claudeAccounts =
|
||||||
|| (cachedClaudeUsageData ? [{ index: 0, masked: '', isActive: true, isRateLimited: false, usage: cachedClaudeUsageData }] : []);
|
liveClaudeAccounts ||
|
||||||
|
(cachedClaudeUsageData
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
index: 0,
|
||||||
|
masked: '',
|
||||||
|
isActive: true,
|
||||||
|
isRateLimited: false,
|
||||||
|
usage: cachedClaudeUsageData,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: []);
|
||||||
|
|
||||||
for (const account of claudeAccounts) {
|
for (const account of claudeAccounts) {
|
||||||
const u = account.usage;
|
const u = account.usage;
|
||||||
if (!u) continue;
|
if (!u) continue;
|
||||||
const h5 = u.five_hour;
|
const h5 = u.five_hour;
|
||||||
const d7 = u.seven_day;
|
const d7 = u.seven_day;
|
||||||
const label = claudeAccounts.length > 1
|
const label =
|
||||||
? `Claude${account.index + 1}${account.isActive ? '⚡' : ''}${account.isRateLimited ? '🚫' : ''}`
|
claudeAccounts.length > 1
|
||||||
: claudeUsageIsCached ? 'Claude*' : 'Claude';
|
? `Claude${account.index + 1}${account.isActive ? '⚡' : ''}${account.isRateLimited ? '🚫' : ''}`
|
||||||
|
: claudeUsageIsCached
|
||||||
|
? 'Claude*'
|
||||||
|
: 'Claude';
|
||||||
rows.push({
|
rows.push({
|
||||||
name: label,
|
name: label,
|
||||||
h5pct: h5
|
h5pct: h5
|
||||||
@@ -717,6 +731,10 @@ export async function startUnifiedDashboard(
|
|||||||
await refreshChannelMeta(opts);
|
await refreshChannelMeta(opts);
|
||||||
const content = buildUnifiedDashboardContent();
|
const content = buildUnifiedDashboardContent();
|
||||||
if (!content) {
|
if (!content) {
|
||||||
|
logger.warn(
|
||||||
|
{ cachedUsageLength: cachedUsageContent.length, statusShowRooms: STATUS_SHOW_ROOMS },
|
||||||
|
'Dashboard content empty, skipping render',
|
||||||
|
);
|
||||||
statusMessageId = null;
|
statusMessageId = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -728,7 +746,7 @@ export async function startUnifiedDashboard(
|
|||||||
if (id) statusMessageId = id;
|
if (id) statusMessageId = id;
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.debug({ err }, 'Dashboard update failed');
|
logger.warn({ err }, 'Dashboard update failed');
|
||||||
statusMessageId = null;
|
statusMessageId = null;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user