CoolFace
Datasetpublic

basant307/AI_Governance_Project

sourceHugging Faceapache-2.0updated 2mo agoView on Hugging Face
0likes48downloads
cli.ts479 linesDownload Raw Back to src
1/**2 * @license3 * Copyright 2026 Qwen Team4 * SPDX-License-Identifier: Apache-2.05 */6 7import { pathToFileURL } from 'node:url';8import type { ArgumentsCamelCase, Argv, Options } from 'yargs';9import { normalizeServeFastPathArgv } from './serve/fast-path-argv.js';10import { initStartupProfiler } from './utils/startupProfiler.js';11import { initCpuProfiler } from './utils/cpuProfiler.js';12 13// Preserve the old entrypoint's profiling baseline before route-specific14// dynamic imports or command handling shift startup measurements.15initStartupProfiler();16initCpuProfiler();17 18type BootstrapRoute = 'serve' | 'mcp' | 'help' | 'version' | 'default';19 20export const TOP_LEVEL_COMMANDS = [21  ['auth', 'Configure authentication (removed)'],22  ['channel <command>', 'Manage messaging channels (Telegram, Discord, etc.)'],23  ['extensions <command>', 'Manage Qwen Code extensions.'],24  ['hooks', 'Manage Qwen Code hooks (use /hooks in interactive mode).'],25  ['mcp', 'Manage MCP servers'],26  [27    'review <command>',28    'Internal helpers used by the /review skill (PR worktree setup, context fetch, rules loading, presubmit checks, cleanup)',29  ],30  [31    'serve',32    'Run Qwen Code as a local HTTP daemon (Stage 1 experimental: --http-bridge)',33  ],34  ['sessions <command>', 'Manage Qwen Code sessions'],35] as const;36 37export const MCP_COMMANDS = [38  ['add <name> <commandOrUrl> [args...]', 'Add a server'],39  ['remove <name>', 'Remove a server'],40  ['list', 'List all configured MCP servers'],41  ['reconnect [server-name]', 'Reconnect to MCP servers'],42  ['approve [name]', 'Approve a pending MCP server'],43  ['reject [name]', 'Reject a pending MCP server'],44] as const;45 46const TOP_LEVEL_HELP_OPTIONS = [47  ['model', { alias: 'm', type: 'string', description: 'Model' }],48  [49    'fallback-model',50    {51      type: 'array',52      description:53        'Fallback model(s) for capacity errors, repeatable or comma-separated (max 3)',54    },55  ],56  [57    'prompt',58    {59      alias: 'p',60      type: 'string',61      description: 'Prompt. Appended to input on stdin (if any).',62    },63  ],64  [65    'prompt-interactive',66    {67      alias: 'i',68      type: 'string',69      description:70        'Execute the provided prompt and continue in interactive mode',71    },72  ],73  [74    'safe-mode',75    {76      type: 'boolean',77      description:78        'Disable all customizations (context files, hooks, extensions, skills, MCP servers) for troubleshooting.',79    },80  ],81  [82    'sandbox',83    {84      alias: 's',85      type: 'boolean',86      description: 'Run in sandbox?',87    },88  ],89  [90    'output-format',91    {92      alias: 'o',93      type: 'string',94      choices: ['text', 'json', 'stream-json'],95      description: 'The format of the CLI output.',96    },97  ],98  [99    'continue',100    {101      alias: 'c',102      type: 'boolean',103      description: 'Resume the most recent session for the current project.',104    },105  ],106  [107    'resume',108    {109      alias: 'r',110      type: 'string',111      description:112        'Resume a specific session by its ID. Use without an ID to show session picker.',113    },114  ],115] as const satisfies ReadonlyArray<readonly [string, Options]>;116 117const VALUE_FLAGS = new Set([118  '--model',119  '-m',120  '--fallback-model',121  '--prompt',122  '-p',123  '--prompt-interactive',124  '-i',125  '--output-format',126  '-o',127  '--resume',128  '-r',129]);130 131function writeStdoutLine(line: string): void {132  process.stdout.write(line.endsWith('\n') ? line : `${line}\n`);133}134 135function hasFlag(136  argv: readonly string[],137  long: string,138  short: string,139): boolean {140  for (let i = 0; i < argv.length; i++) {141    const arg = argv[i]!;142    if (arg === '--') {143      return false;144    }145    if (VALUE_FLAGS.has(arg)) {146      i++;147      continue;148    }149    if (arg === long || arg === short) {150      return true;151    }152  }153  return false;154}155 156async function buildTopLevelHelpParser() {157  const { default: yargs } = await import('yargs');158  const parser = yargs([])159    .scriptName('qwen')160    .usage(161      'Usage: qwen [options] [command]\n\nQwen Code - Launch an interactive CLI, use -p/--prompt for non-interactive mode',162    )163    .version(process.env['CLI_VERSION'] || 'unknown')164    .alias('v', 'version')165    .help()166    .alias('h', 'help')167    .strict()168    .demandCommand(0, 0);169 170  for (const [option, config] of TOP_LEVEL_HELP_OPTIONS) {171    parser.option(option, config);172  }173 174  for (const [command, description] of TOP_LEVEL_COMMANDS) {175    parser.command(command, description);176  }177 178  return parser;179}180 181function firstPositionalArg(argv: readonly string[]): string | undefined {182  for (let i = 0; i < argv.length; i++) {183    const arg = argv[i]!;184    if (arg === '--') {185      return undefined;186    }187    if (VALUE_FLAGS.has(arg)) {188      i++;189      continue;190    }191    if (!arg.startsWith('-')) {192      return arg;193    }194  }195  return undefined;196}197 198function normalizeMcpFastPathArgv(argv: readonly string[]): readonly string[] {199  if (argv[0] === 'mcp' && argv[1] === '--') {200    return [argv[0], ...argv.slice(2)];201  }202  return argv;203}204 205export function resolveBootstrapRoute(206  rawArgv: readonly string[],207): BootstrapRoute {208  const argv = normalizeServeFastPathArgv(rawArgv);209 210  if (hasFlag(argv, '--version', '-v')) {211    return 'version';212  }213 214  const firstArg = argv[0];215  if (firstArg === 'serve') {216    return 'serve';217  }218  if (firstArg === 'mcp') {219    return 'mcp';220  }221 222  const firstPositional = firstPositionalArg(argv);223  if (hasFlag(argv, '--help', '-h') && firstPositional === undefined) {224    return 'help';225  }226 227  return 'default';228}229 230async function printTopLevelHelp(): Promise<void> {231  const help = await (await buildTopLevelHelpParser()).getHelp();232  writeStdoutLine(help);233}234 235function printMcpHelp(): void {236  const lines = [237    'Usage: qwen mcp <command>',238    '',239    'Manage MCP servers',240    '',241    'Commands:',242    ...MCP_COMMANDS.map(243      ([command, description]) => `  qwen mcp ${command}  ${description}`,244    ),245  ];246  writeStdoutLine(lines.join('\n'));247}248 249async function printBootstrapVersion(): Promise<void> {250  if (process.env['CLI_VERSION']) {251    writeStdoutLine(process.env['CLI_VERSION']);252    return;253  }254 255  const { getCliVersion } = await import('./utils/version.js');256  writeStdoutLine(await getCliVersion());257}258 259async function runMcpFastPath(rawArgv: readonly string[]): Promise<void> {260  const argv = normalizeMcpFastPathArgv(normalizeServeFastPathArgv(rawArgv));261  const hasSubcommand = argv.length > 1 && !argv[1]!.startsWith('-');262  if (!hasSubcommand) {263    printMcpHelp();264    return;265  }266 267  const [{ default: yargsInstance }, { mcpCommand }] = await Promise.all([268    import('yargs'),269    import('./commands/mcp.js'),270  ]);271 272  const parser = yargsInstance([])273    .scriptName('qwen')274    .command(mcpCommand)275    .version(false)276    .help()277    .alias('h', 'help')278    .strict()279    .strictCommands()280    .demandCommand(1, 'You need at least one command before continuing.')281    .fail((message: string | null, error: Error | undefined, yargs: Argv) => {282      writeStderrLine(message || error?.message || 'Unknown argument error');283      yargs.showHelp();284      process.exitCode = 1;285    })286    .exitProcess(false);287 288  if (hasFlag(argv.slice(2), '--help', '-h')) {289    await parseYargsHelp(parser, argv);290    return;291  }292 293  await parseYargsCommand(parser, argv);294}295 296async function parseYargsHelp(297  parser: Argv,298  argv: readonly string[],299): Promise<void> {300  await new Promise<void>((resolve, reject) => {301    parser.parse(302      argv,303      (error: Error | undefined, _argv: ArgumentsCamelCase, output: string) => {304        if (output) {305          writeStdoutLine(output);306        }307        if (error) {308          reject(error);309          return;310        }311        resolve();312      },313    );314  });315}316 317async function parseYargsCommand(318  parser: Argv,319  argv: readonly string[],320): Promise<void> {321  await new Promise<void>((resolve) => {322    parser.parse(323      argv,324      (error: Error | undefined, _argv: ArgumentsCamelCase, output: string) => {325        if (output) {326          writeStdoutLine(output);327        }328        if (error) {329          writeStderrLine(error.message);330          process.exitCode = 1;331        }332        resolve();333      },334    );335  });336}337 338export async function runCliEntry(339  rawArgv: readonly string[] = process.argv.slice(2),340): Promise<void> {341  const argv = normalizeServeFastPathArgv(rawArgv);342  const route = resolveBootstrapRoute(argv);343 344  if (route === 'version') {345    await printBootstrapVersion();346    return;347  }348 349  if (route === 'serve') {350    const { tryRunServeFastPath } = await import('./serve/fast-path.js');351    if (await tryRunServeFastPath(argv)) {352      return;353    }354  } else if (route === 'mcp') {355    await runMcpFastPath(argv);356    return;357  } else if (route === 'help') {358    await printTopLevelHelp();359    return;360  }361 362  const { main } = await import('./gemini.js');363  await main();364}365 366function getErrnoCode(error: unknown): string | undefined {367  if (!error || typeof error !== 'object') {368    return undefined;369  }370  const code = (error as { code?: unknown }).code;371  return typeof code === 'string' ? code : undefined;372}373 374export function isExpectedPtyRaceError(error: unknown): boolean {375  if (!(error instanceof Error)) {376    return false;377  }378 379  const message = error.message;380  const code = getErrnoCode(error);381 382  if (383    (code === 'EIO' && message.includes('read')) ||384    message.includes('read EIO')385  ) {386    return true;387  }388 389  if (390    (code === 'EAGAIN' && message.includes('read')) ||391    message.includes('read EAGAIN')392  ) {393    return true;394  }395 396  return (397    message.includes('ioctl(2) failed, EBADF') ||398    message.includes('Cannot resize a pty that has already exited')399  );400}401 402export async function handleCriticalError(error: unknown): Promise<void> {403  const [{ FatalError }, { AlreadyReportedError }] = await Promise.all([404    import('@qwen-code/qwen-code-core'),405    import('./utils/errors.js'),406  ]);407 408  if (error instanceof FatalError) {409    let errorMessage = error.message;410    if (!process.env['NO_COLOR']) {411      errorMessage = `\x1b[31m${errorMessage}\x1b[0m`;412    }413    writeStderrLine(errorMessage);414    process.exit(error.exitCode);415  }416  if (error instanceof AlreadyReportedError) {417    process.exit(error.exitCode);418  }419  writeStderrLine('An unexpected critical error occurred:');420  if (error instanceof Error) {421    writeStderrLine(error.stack ?? error.message);422  } else {423    writeStderrLine(String(error));424  }425  process.exit(1);426}427 428function writeStderrLine(line: string): void {429  process.stderr.write(line.endsWith('\n') ? line : `${line}\n`);430}431 432export async function runCliEntryPoint(433  run: () => Promise<void> = runCliEntry,434  handleError: (error: unknown) => Promise<void> = handleCriticalError,435): Promise<void> {436  process.on('uncaughtException', (error) => {437    if (isExpectedPtyRaceError(error)) {438      return;439    }440 441    if (error instanceof Error) {442      writeStderrLine(error.stack ?? error.message);443    } else {444      writeStderrLine(String(error));445    }446    process.exit(1);447  });448 449  try {450    await run();451  } catch (error) {452    try {453      await handleError(error);454    } catch (handlerError) {455      writeStderrLine('An unexpected critical error occurred:');456      writeStderrLine('Original error:');457      if (error instanceof Error) {458        writeStderrLine(error.stack ?? error.message);459      } else {460        writeStderrLine(String(error));461      }462      writeStderrLine('Error handler failed:');463      if (handlerError instanceof Error) {464        writeStderrLine(handlerError.stack ?? handlerError.message);465      } else {466        writeStderrLine(String(handlerError));467      }468      process.exit(1);469    }470  }471}472 473if (474  process.argv[1] !== undefined &&475  import.meta.url === pathToFileURL(process.argv[1]).href476) {477  void runCliEntryPoint();478}479 
basant307/AI_Governance_Project · CoolFace