feat(auth,commands): instrument oauth handler, profile registry, and doctor pipeline

Emit auth-stage events around OAuth round-trips and profile lookups so auth
flows are traceable end-to-end. Doctor command emits dispatch stages around
each health-check phase for clearer diagnostic logs.

Refs #1141, #1138
This commit is contained in:
Tam Nhu Tran
2026-04-30 13:00:16 -04:00
parent 4700727915
commit bf8759460d
3 changed files with 66 additions and 0 deletions
+10
View File
@@ -9,6 +9,9 @@ import {
import type { AccountConfig } from '../config/unified-config-types';
import { getCcsDir } from '../utils/config-manager';
import { isValidContextGroupName, normalizeContextGroupName } from './account-context';
import { createLogger } from '../services/logging';
const logger = createLogger('auth:profile-registry');
/**
* Profile Registry (Simplified)
@@ -182,6 +185,10 @@ export class ProfileRegistry {
// Default always stays on implicit 'default' profile (uses ~/.claude/)
this._write(data);
logger.stage('route', 'auth.profile.created', 'Profile created in registry', {
profile: name,
profileType: metadata.type || 'account',
});
}
/**
@@ -235,6 +242,9 @@ export class ProfileRegistry {
}
this._write(data);
logger.stage('cleanup', 'auth.profile.deleted', 'Profile deleted from registry', {
profile: name,
});
}
/**
+34
View File
@@ -12,6 +12,7 @@
import * as fs from 'fs';
import { fail, info, warn, color, ok } from '../../utils/ui';
import { createLogger } from '../../services/logging';
import { ensureCLIProxyBinary } from '../binary-manager';
import { generateConfig } from '../config/config-generator';
import { CLIProxyProvider } from '../types';
@@ -82,6 +83,8 @@ interface PasteCallbackStartData {
const PASTE_CALLBACK_AUTH_URL_POLL_INTERVAL_MS = 3000;
const POLLED_AUTH_LOCAL_TOKEN_GRACE_MS = 15 * 1000;
const logger = createLogger('cliproxy:auth:oauth');
export async function requestPasteCallbackStart(
provider: CLIProxyProvider,
target: ProxyTarget,
@@ -800,6 +803,12 @@ export async function triggerOAuth(
): Promise<AccountInfo | null> {
const oauthConfig = getOAuthConfig(provider);
warnOAuthBanRisk(provider);
const oauthStartedAt = Date.now();
logger.stage('auth', 'cliproxy.oauth.start', 'Triggering OAuth flow', {
provider,
add: options.add === true,
fromUI: options.fromUI === true,
});
const { verbose = false, add = false, fromUI = false, noIncognito = true } = options;
const acceptAgyRisk = options.acceptAgyRisk === true;
const { nickname } = options;
@@ -1055,6 +1064,24 @@ export async function triggerOAuth(
}
}
if (account) {
logger.stage(
'auth',
'cliproxy.oauth.success',
'OAuth flow completed successfully',
{ provider, accountId: account.id },
{ latencyMs: Date.now() - oauthStartedAt }
);
} else {
logger.stage(
'cleanup',
'cliproxy.oauth.failed',
'OAuth flow failed or was cancelled',
{ provider },
{ level: 'warn', latencyMs: Date.now() - oauthStartedAt }
);
}
return account;
}
@@ -1067,6 +1094,9 @@ export async function ensureAuth(
options: { verbose?: boolean; headless?: boolean; account?: string } = {}
): Promise<boolean> {
if (isAuthenticated(provider)) {
logger.stage('auth', 'cliproxy.auth.cached', 'Provider already authenticated', {
provider,
});
if (options.verbose) {
console.error(`[auth] ${provider} already authenticated`);
}
@@ -1077,6 +1107,10 @@ export async function ensureAuth(
return true;
}
logger.stage('auth', 'cliproxy.auth.required', 'Provider needs authentication', {
provider,
});
const oauthConfig = getOAuthConfig(provider);
console.log(info(`${oauthConfig.displayName} authentication required`));
+22
View File
@@ -5,6 +5,9 @@
*/
import { initUI, header, dim, color, subheader } from '../utils/ui';
import { createLogger } from '../services/logging';
const logger = createLogger('commands:doctor');
/**
* Show help for doctor command
@@ -68,15 +71,34 @@ export async function handleDoctorCommand(args: string[]): Promise<void> {
}
const shouldFix = args.includes('--fix') || args.includes('-f');
const startedAt = Date.now();
logger.stage('intake', 'doctor.start', 'Doctor health check starting', {
fix: shouldFix,
});
const DoctorModule = await import('../management/doctor');
const Doctor = DoctorModule.default;
const doctor = new Doctor();
await doctor.runAllChecks();
logger.stage(
'route',
'doctor.checks_complete',
'Doctor checks complete',
{ healthy: doctor.isHealthy() },
{ latencyMs: Date.now() - startedAt }
);
if (shouldFix) {
logger.stage('dispatch', 'doctor.fix_start', 'Attempting auto-fix', {});
await doctor.fixIssues();
logger.stage(
'respond',
'doctor.fix_complete',
'Doctor auto-fix complete',
{ healthy: doctor.isHealthy() },
{ latencyMs: Date.now() - startedAt }
);
}
process.exit(doctor.isHealthy() ? 0 : 1);