CoolFace
Datasetpublic

basant307/AI_Governance_Project

sourceHugging Faceapache-2.0updated 2mo agoView on Hugging Face
0likes45downloads
systemInfo.ts299 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 { execFile } from 'node:child_process';10import type { CommandContext } from '../ui/commands/types.js';11import { getCliVersion } from './version.js';12import {13  IdeClient,14  AuthType,15  createDebugLogger,16  type LspStatusSnapshot,17} from '@qwen-code/qwen-code-core';18import { formatMemoryUsage } from '../ui/utils/formatters.js';19import { GIT_COMMIT_INFO } from '../generated/git-commit.js';20 21const debugLogger = createDebugLogger('STATUS');22 23/**24 * The subset of {@link CommandContext} these helpers actually read: only25 * `services.config` and `services.settings`. Narrowing the parameter to this26 * shape lets call sites that don't have a full `CommandContext` (e.g. the27 * Settings dialog, which holds `config` + `settings` as props) pass a plain28 * object without an unsafe cast. A full `CommandContext` is still assignable.29 */30type SystemInfoContext = {31  services: Partial<Pick<CommandContext['services'], 'config' | 'settings'>>;32};33 34/**35 * System information interface containing all system-related details36 * that can be collected for debugging and reporting purposes.37 */38export interface SystemInfo {39  cliVersion: string;40  osPlatform: string;41  osArch: string;42  osRelease: string;43  nodeVersion: string;44  npmVersion: string;45  sandboxEnv: string;46  modelVersion: string;47  selectedAuthType: string;48  ideClient: string;49  sessionId: string;50  proxy?: string;51}52 53/**54 * Additional system information for bug reports55 */56export interface ExtendedSystemInfo extends SystemInfo {57  memoryUsage: string;58  baseUrl?: string;59  apiKeyEnvKey?: string;60  gitCommit?: string;61  proxy?: string;62  fastModel?: string;63  lspStatus?: string;64}65 66// `execFile` (not the shell-spawning `exec`) so a hostile binary on PATH67// can't inject shell metacharacters. The timeout protects the daemon's68// event loop from a hung `git` / `npm` (NFS stall, Gatekeeper prompt,69// broken install) — `execSync` would have blocked indefinitely.70const VERSION_PROBE_TIMEOUT_MS = 5_000;71 72/**73 * Run a tiny `<binary> --version` probe with a hard timeout, return stdout74 * trimmed, or `'unknown'` on any failure (including timeout). Helper kept75 * inline (rather than `const probeVersion = promisify(execFile)`) so a76 * `vi.mock('node:child_process', { execFile: vi.fn() })` test can override77 * each call individually — the promisified value would otherwise capture78 * the original `execFile` reference at module load.79 */80function probeVersion(binary: string): Promise<string> {81  return new Promise<string>((resolve) => {82    execFile(83      binary,84      ['--version'],85      { timeout: VERSION_PROBE_TIMEOUT_MS, encoding: 'utf-8' },86      (err, stdout) => {87        if (err) {88          resolve('unknown');89          return;90        }91        resolve(typeof stdout === 'string' ? stdout.trim() : 'unknown');92      },93    );94  });95}96 97/**98 * Gets the NPM version, handling cases where npm might not be available.99 * Returns 'unknown' if npm command fails, is not found, or exceeds the100 * version-probe timeout.101 */102export async function getNpmVersion(): Promise<string> {103  return probeVersion('npm');104}105 106/**107 * Gets the Git version, handling cases where git might not be available.108 * Returns 'unknown' if git command fails, is not found, or exceeds the109 * version-probe timeout.110 */111export async function getGitVersion(): Promise<string> {112  return probeVersion('git');113}114 115/**116 * Gets the IDE client name if IDE mode is enabled.117 * Returns empty string if IDE mode is disabled or IDE client is not detected.118 */119export async function getIdeClientName(120  context: SystemInfoContext,121): Promise<string> {122  if (!context.services.config?.getIdeMode()) {123    return '';124  }125  try {126    const ideClient = await IdeClient.getInstance();127    return ideClient?.getDetectedIdeDisplayName() ?? '';128  } catch {129    return '';130  }131}132 133/**134 * Gets the sandbox environment information.135 * Handles different sandbox types including sandbox-exec and custom sandbox environments.136 * For bug reports, removes 'qwen-' or 'qwen-code-' prefixes from sandbox names.137 *138 * @param stripPrefix - Whether to strip 'qwen-' prefix (used for bug reports)139 */140export function getSandboxEnv(stripPrefix = false): string {141  const sandbox = process.env['SANDBOX'];142 143  if (!sandbox || sandbox === 'sandbox-exec') {144    if (sandbox === 'sandbox-exec') {145      const profile = process.env['SEATBELT_PROFILE'] || 'unknown';146      return `sandbox-exec (${profile})`;147    }148    return 'no sandbox';149  }150 151  // For bug reports, remove qwen- prefix152  if (stripPrefix) {153    return sandbox.replace(/^qwen-(?:code-)?/, '');154  }155 156  return sandbox;157}158 159/**160 * Collects comprehensive system information for debugging and reporting.161 * This function gathers all system-related details including OS, versions,162 * sandbox environment, authentication, and session information.163 *164 * @param context - Command context containing config and settings165 * @returns Promise resolving to SystemInfo object with all collected information166 */167export async function getSystemInfo(168  context: SystemInfoContext,169): Promise<SystemInfo> {170  const osPlatform = process.platform;171  const osArch = process.arch;172  const osRelease = os.release();173  const nodeVersion = process.version;174  const npmVersion = await getNpmVersion();175  const sandboxEnv = getSandboxEnv();176  const modelVersion = context.services.config?.getModel() || 'Unknown';177  const cliVersion = await getCliVersion();178  const selectedAuthType = context.services.config?.getAuthType() || '';179  const ideClient = await getIdeClientName(context);180  const sessionId = context.services.config?.getSessionId() || 'unknown';181  const proxy = context.services.config?.getProxy();182 183  return {184    cliVersion,185    osPlatform,186    osArch,187    osRelease,188    nodeVersion,189    npmVersion,190    sandboxEnv,191    modelVersion,192    selectedAuthType,193    ideClient,194    sessionId,195    proxy,196  };197}198 199/**200 * Collects extended system information for bug reports.201 * Includes all standard system info plus memory usage and optional base URL.202 *203 * @param context - Command context containing config and settings204 * @returns Promise resolving to ExtendedSystemInfo object205 */206export async function getExtendedSystemInfo(207  context: SystemInfoContext,208): Promise<ExtendedSystemInfo> {209  const baseInfo = await getSystemInfo(context);210  const memoryUsage = formatMemoryUsage(process.memoryUsage().rss);211 212  // For bug reports, use sandbox name without prefix213  const sandboxEnv = getSandboxEnv(true);214 215  // Get base URL and apiKeyEnvKey if using OpenAI or Anthropic auth216  const contentGeneratorConfig =217    baseInfo.selectedAuthType === AuthType.USE_OPENAI ||218    baseInfo.selectedAuthType === AuthType.USE_ANTHROPIC219      ? context.services.config?.getContentGeneratorConfig()220      : undefined;221  const baseUrl = contentGeneratorConfig?.baseUrl;222  const apiKeyEnvKey = contentGeneratorConfig?.apiKeyEnvKey;223 224  // Get git commit info225  const gitCommit =226    GIT_COMMIT_INFO && !['N/A'].includes(GIT_COMMIT_INFO)227      ? GIT_COMMIT_INFO228      : undefined;229 230  // Get fast model from settings231  const fastModel = context.services.settings?.merged?.fastModel || undefined;232  const lspStatus = getLspStatus(context);233 234  return {235    ...baseInfo,236    sandboxEnv,237    memoryUsage,238    baseUrl,239    apiKeyEnvKey,240    gitCommit,241    fastModel,242    lspStatus,243  };244}245 246function getLspStatus(context: SystemInfoContext): string | undefined {247  try {248    const snapshot = context.services.config?.getLspStatusSnapshot?.();249    if (!snapshot) {250      return undefined;251    }252 253    if (context.services.config?.getDebugMode?.()) {254      debugLogger.debug('LSP status snapshot for /status:', snapshot);255    }256 257    return formatLspStatusSnapshot(snapshot);258  } catch (error) {259    if (context.services.config?.getDebugMode?.()) {260      debugLogger.debug(261        'Unable to read LSP status snapshot for /status:',262        error,263      );264    }265    return undefined;266  }267}268 269function formatLspStatusSnapshot(snapshot: LspStatusSnapshot): string {270  if (!snapshot.enabled) {271    return 'disabled';272  }273 274  if (snapshot.initializationError) {275    return `enabled, initialization failed: ${snapshot.initializationError}`;276  }277 278  if (snapshot.statusUnavailable) {279    return 'enabled, status unavailable';280  }281 282  if (snapshot.configuredServers === 0) {283    return 'enabled, no servers configured';284  }285 286  const details = [287    snapshot.failedServers > 0 ? `${snapshot.failedServers} failed` : '',288    snapshot.inProgressServers > 0289      ? `${snapshot.inProgressServers} starting`290      : '',291    snapshot.notStartedServers > 0292      ? `${snapshot.notStartedServers} not started`293      : '',294  ].filter(Boolean);295 296  const detailText = details.length > 0 ? ` (${details.join(', ')})` : '';297  return `enabled, ${snapshot.readyServers}/${snapshot.configuredServers} ready${detailText}`;298}299 
basant307/AI_Governance_Project · CoolFace