From 3e1d5fce12617da84a4cc58a25d2f78094a0b667 Mon Sep 17 00:00:00 2001 From: can1357 Date: Thu, 25 Jun 2026 04:44:36 +0200 Subject: [PATCH] feat(coding-agent): filtered out non-reportable accounts in usage CLI - Add `usageProviderFor` helper to `AuthStorage` to distinguish between providers without usage endpoints and missing data. - Implement account filtering in the usage CLI to exclude providers that do not support usage reporting unless explicitly requested. - Improve CLI diagnostic messages when no usage data is retrieved by differentiating between missing credentials and unsupported providers. --- packages/ai/src/auth-storage.ts | 11 ++++++ packages/coding-agent/src/cli/usage-cli.ts | 39 +++++++++++++++++++--- 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/packages/ai/src/auth-storage.ts b/packages/ai/src/auth-storage.ts index a5711ee34..f3ee4926c 100644 --- a/packages/ai/src/auth-storage.ts +++ b/packages/ai/src/auth-storage.ts @@ -2660,6 +2660,17 @@ export class AuthStorage { ); } + /** + * The {@link UsageProvider} registered for `provider`, or undefined when the + * provider has no usage endpoint at all. Lets callers tell "a credential we + * could have fetched usage for but didn't" apart from "a provider with no + * usage concept" (web-search keys, local/keyless servers, inference + * providers without a usage API) — the latter never warrants a usage row. + */ + usageProviderFor(provider: Provider): UsageProvider | undefined { + return this.#usageProviderResolver?.(provider); + } + async fetchUsageReports(options?: { baseUrlResolver?: (provider: Provider) => string | undefined; /** Caller's cancel signal; only rejects this caller, never the shared upstream fetch. */ diff --git a/packages/coding-agent/src/cli/usage-cli.ts b/packages/coding-agent/src/cli/usage-cli.ts index fe8a7e65d..aaa5408d0 100644 --- a/packages/coding-agent/src/cli/usage-cli.ts +++ b/packages/coding-agent/src/cli/usage-cli.ts @@ -650,6 +650,28 @@ function collectStoredAccounts(authStorage: AuthStorage): UsageAccountIdentity[] return accounts; } +/** + * Keep only accounts worth a usage row: those whose provider has a usage + * provider, so a missing report is a real gap rather than the absence of any + * usage concept. Providers with no usage endpoint (web-search keys, local / + * keyless servers, inference providers without a usage API) would only ever + * render as noise, so they are dropped. + * + * `hasUsageProvider` is injected (in practice {@link AuthStorage.usageProviderFor}) + * so custom/broker resolvers stay authoritative — no provider list is duplicated + * here. An explicit `--provider` request bypasses the cull, so + * `omp usage --provider xai` can still confirm the stored credential has no + * usage endpoint. + */ +export function selectReportableAccounts( + accounts: UsageAccountIdentity[], + hasUsageProvider: (provider: string) => boolean, + explicitProvider?: string, +): UsageAccountIdentity[] { + if (explicitProvider) return accounts; + return accounts.filter(account => hasUsageProvider(account.provider)); +} + /** Apply a redaction mask to an optional identity field. */ function maskIdentity(redaction: Map, value: string | undefined): string | undefined { return value === undefined ? undefined : (redaction.get(value) ?? value); @@ -721,7 +743,12 @@ export async function runUsageCommand(cmd: UsageCommandArgs): Promise { (await authStorage.fetchUsageReports({ baseUrlResolver: provider => modelRegistry.getProviderBaseUrl(provider), })) ?? []; - let accounts = collectStoredAccounts(authStorage); + const storedAccounts = collectStoredAccounts(authStorage); + let accounts = selectReportableAccounts( + storedAccounts, + provider => authStorage.usageProviderFor(provider) !== undefined, + cmd.provider, + ); let filteredReports = reports; if (cmd.provider) { const wanted = cmd.provider.toLowerCase(); @@ -764,9 +791,13 @@ export async function runUsageCommand(cmd: UsageCommandArgs): Promise { if (filteredReports.length === 0 && accounts.length === 0) { const scope = cmd.provider ? ` for provider "${cmd.provider}"` : ""; - process.stderr.write( - chalk.yellow(`No credentials found${scope}. Run \`omp\` and use /login to add accounts.\n`), - ); + // Credentials exist but every one is for a provider without a usage + // endpoint — say so rather than implying nothing is logged in. + const message = + storedAccounts.length > 0 + ? `No usage data${scope}. Stored credentials are for providers without a usage endpoint.\n` + : `No credentials found${scope}. Run \`omp\` and use /login to add accounts.\n`; + process.stderr.write(chalk.yellow(message)); process.exitCode = 1; return; }