CoolFace
Datasetpublic

basant307/AI_Governance_Project

sourceHugging Faceapache-2.0updated 2mo agoView on Hugging Face
0likes48downloads
doctorChecks.ts398 linesDownload Raw Back to utils
1/**2 * @license3 * Copyright 2025 Qwen4 * SPDX-License-Identifier: Apache-2.05 */6 7import process from 'node:process';8import os from 'node:os';9import { getNpmVersion, getGitVersion } from './systemInfo.js';10import { validateAuthMethod } from '../config/auth.js';11import {12  findProviderByCredentials,13  canUseRipgrep,14  getMCPServerStatus,15  MCPServerStatus,16} from '@qwen-code/qwen-code-core';17import type { CommandContext } from '../ui/commands/types.js';18import type { DoctorCheckResult } from '../ui/types.js';19import { t } from '../i18n/index.js';20 21const MIN_NODE_MAJOR = 22;22 23function checkNodeVersion(): DoctorCheckResult {24  const version = process.version;25  const major = parseInt(version.replace(/^v/, '').split('.')[0]!, 10);26  if (isNaN(major) || major < MIN_NODE_MAJOR) {27    return {28      category: t('System'),29      name: t('Node.js version'),30      status: 'fail',31      message: version,32      detail: t('Node.js v{{min}}+ is required. Current: {{version}}', {33        min: String(MIN_NODE_MAJOR),34        version,35      }),36    };37  }38  return {39    category: t('System'),40    name: t('Node.js version'),41    status: 'pass',42    message: version,43  };44}45 46async function checkNpmVersion(): Promise<DoctorCheckResult> {47  const version = await getNpmVersion();48  if (version === 'unknown') {49    return {50      category: t('System'),51      name: t('npm version'),52      status: 'warn',53      message: t('not found'),54      detail: t('npm is not available. Some features may not work.'),55    };56  }57  return {58    category: t('System'),59    name: t('npm version'),60    status: 'pass',61    message: version,62  };63}64 65function checkPlatform(): DoctorCheckResult {66  return {67    category: t('System'),68    name: t('Platform'),69    status: 'pass',70    message: `${process.platform}/${process.arch} (${os.release()})`,71  };72}73 74function checkAuth(context: CommandContext): DoctorCheckResult {75  const config = context.services.config;76  const authType = config?.getAuthType();77  if (!authType) {78    return {79      category: t('Authentication'),80      name: t('API key'),81      status: 'fail',82      message: t('not configured'),83      detail: t('Run /auth to configure authentication.'),84    };85  }86 87  const error = validateAuthMethod(authType, config ?? undefined);88  if (error) {89    return {90      category: t('Authentication'),91      name: t('API key'),92      status: 'fail',93      message: t('invalid ({{authType}})', { authType }),94      detail: error,95    };96  }97 98  // Build enriched diagnostic information99  const cgConfig =100    typeof config?.getContentGeneratorConfig === 'function'101      ? config.getContentGeneratorConfig()102      : undefined;103  const model = cgConfig?.model ?? config?.getModel();104  const baseUrl = cgConfig?.baseUrl;105  const envKey = cgConfig?.apiKeyEnvKey;106 107  const provider = findProviderByCredentials(baseUrl, envKey);108 109  const detailParts: string[] = [];110  if (provider) {111    detailParts.push(112      t('Provider: {{provider}}', { provider: t(provider.label) }),113    );114  }115  if (baseUrl) {116    detailParts.push(t('Base URL: {{baseUrl}}', { baseUrl }));117  }118  if (model) {119    detailParts.push(t('Model: {{model}}', { model }));120  }121  if (envKey) {122    const hasKey = !!process.env[envKey];123    detailParts.push(124      hasKey125        ? t('API key: configured ({{envKey}})', { envKey })126        : t('API key: {{envKey}} not set', { envKey }),127    );128  }129 130  return {131    category: t('Authentication'),132    name: t('API key'),133    status: 'pass',134    message: t('configured ({{authType}})', { authType }),135    detail: detailParts.length > 0 ? detailParts.join('\n') : undefined,136  };137}138 139async function checkApiClient(140  context: CommandContext,141): Promise<DoctorCheckResult> {142  const config = context.services.config;143  if (!config) {144    return {145      category: t('Authentication'),146      name: t('API client'),147      status: 'fail',148      message: t('config not loaded'),149    };150  }151 152  try {153    const client = config.getGeminiClient();154    if (client.isInitialized()) {155      return {156        category: t('Authentication'),157        name: t('API client'),158        status: 'pass',159        message: t('client initialized'),160      };161    }162    return {163      category: t('Authentication'),164      name: t('API client'),165      status: 'warn',166      message: t('client not initialized'),167      detail: t('The API client has not been initialized yet.'),168    };169  } catch (error) {170    const errorMsg = error instanceof Error ? error.message : String(error);171    return {172      category: t('Authentication'),173      name: t('API client'),174      status: 'warn',175      message: t('error'),176      detail: errorMsg,177    };178  }179}180 181function checkSettings(context: CommandContext): DoctorCheckResult {182  const settings = context.services.settings;183  if (!settings) {184    return {185      category: t('Configuration'),186      name: t('Settings'),187      status: 'fail',188      message: t('not loaded'),189      detail: t(190        'Settings could not be loaded. Check your settings files for syntax errors.',191      ),192    };193  }194  return {195    category: t('Configuration'),196    name: t('Settings'),197    status: 'pass',198    message: t('loaded'),199  };200}201 202function checkModel(context: CommandContext): DoctorCheckResult {203  const model = context.services.config?.getModel();204  if (!model) {205    return {206      category: t('Configuration'),207      name: t('Model'),208      status: 'fail',209      message: t('not configured'),210      detail: t('Run /model to select a model.'),211    };212  }213  return {214    category: t('Configuration'),215    name: t('Model'),216    status: 'pass',217    message: model,218  };219}220 221function checkMcpServers(context: CommandContext): DoctorCheckResult[] {222  const config = context.services.config;223  const servers = config?.getMcpServers();224  if (!servers || Object.keys(servers).length === 0) {225    return [226      {227        category: t('MCP Servers'),228        name: t('MCP servers'),229        status: 'pass',230        message: t('none configured'),231      },232    ];233  }234 235  // In non-interactive mode MCP connections are never established, so querying236  // getMCPServerStatus would always return DISCONNECTED and produce false failures.237  // Report configured servers as unchecked instead.238  if (context.executionMode !== 'interactive') {239    return Object.keys(servers).map((name) => ({240      category: t('MCP Servers'),241      name,242      status: 'pass' as const,243      message: config?.isMcpServerDisabled(name)244        ? t('disabled')245        : t('configured (not checked in non-interactive mode)'),246    }));247  }248 249  return Object.keys(servers).map((name) => {250    // Skip disabled servers — report as informational pass251    if (config?.isMcpServerDisabled(name)) {252      return {253        category: t('MCP Servers'),254        name,255        status: 'pass' as const,256        message: t('disabled'),257      };258    }259 260    const status = getMCPServerStatus(name);261    switch (status) {262      case MCPServerStatus.CONNECTED:263        return {264          category: t('MCP Servers'),265          name,266          status: 'pass' as const,267          message: t('connected'),268        };269      case MCPServerStatus.CONNECTING:270        return {271          category: t('MCP Servers'),272          name,273          status: 'warn' as const,274          message: t('connecting'),275          detail: t('Server is still starting up.'),276        };277      case MCPServerStatus.DISCONNECTED:278      default:279        return {280          category: t('MCP Servers'),281          name,282          status: 'fail' as const,283          message: t('disconnected'),284          detail: t(285            'Check that the server process is running and configuration is correct.',286          ),287        };288    }289  });290}291 292function checkToolRegistry(context: CommandContext): DoctorCheckResult {293  const registry = context.services.config?.getToolRegistry();294  if (!registry) {295    return {296      category: t('Tools'),297      name: t('Tool registry'),298      status: 'fail',299      message: t('not loaded'),300    };301  }302  const count = registry.getAllTools().length;303  return {304    category: t('Tools'),305    name: t('Tool registry'),306    status: 'pass',307    message: t('{{count}} tools registered', { count: String(count) }),308  };309}310 311async function checkRipgrep(312  context: CommandContext,313): Promise<DoctorCheckResult> {314  try {315    const useBuiltin = context.services.config?.getUseBuiltinRipgrep() ?? false;316    const result = await canUseRipgrep(useBuiltin);317    if (result) {318      return {319        category: t('Tools'),320        name: t('Ripgrep'),321        status: 'pass',322        message: t('available'),323      };324    }325    return {326      category: t('Tools'),327      name: t('Ripgrep'),328      status: 'warn',329      message: t('not available'),330      detail: t(331        'Install ripgrep for faster file search: https://github.com/BurntSushi/ripgrep',332      ),333    };334  } catch {335    return {336      category: t('Tools'),337      name: t('Ripgrep'),338      status: 'warn',339      message: t('check failed'),340    };341  }342}343 344async function checkGit(_context: CommandContext): Promise<DoctorCheckResult> {345  const version = await getGitVersion();346  if (version === 'unknown') {347    return {348      category: t('Git'),349      name: t('Git'),350      status: 'warn',351      message: t('not available'),352      detail: t('Git features will be limited.'),353    };354  }355  return {356    category: t('Git'),357    name: t('Git'),358    status: 'pass',359    message: version,360  };361}362 363/**364 * Run all doctor diagnostic checks.365 */366export async function runDoctorChecks(367  context: CommandContext,368): Promise<DoctorCheckResult[]> {369  // Run async checks in parallel370  const [npmResult, ripgrepResult, apiClientResult, gitResult] =371    await Promise.all([372      checkNpmVersion(),373      checkRipgrep(context),374      checkApiClient(context),375      checkGit(context),376    ]);377 378  return [379    // System380    checkNodeVersion(),381    npmResult,382    checkPlatform(),383    // Authentication384    checkAuth(context),385    apiClientResult,386    // Configuration387    checkSettings(context),388    checkModel(context),389    // MCP Servers390    ...checkMcpServers(context),391    // Tools392    checkToolRegistry(context),393    ripgrepResult,394    // Git395    gitResult,396  ];397}398 
basant307/AI_Governance_Project · CoolFace