mirror of
https://github.com/tiennm99/ccs.git
synced 2026-07-16 04:18:05 +00:00
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:
@@ -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,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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`));
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user