(args: string[], options: ExecNpmOptions = {})
| 320 | } |
| 321 | |
| 322 | async function execNpm(args: string[], options: ExecNpmOptions = {}): Promise<NpmExecResult> { |
| 323 | if (options.interactive) { |
| 324 | return execNpmInteractive(args, options) |
| 325 | } |
| 326 | |
| 327 | // Build the full args array including OTP if provided |
| 328 | const npmArgs = options.otp ? [...args, '--otp', options.otp] : args |
| 329 | |
| 330 | // Log the command being run (hide OTP value for security) |
| 331 | if (!options.silent) { |
| 332 | const displayCmd = options.otp |
| 333 | ? ['npm', ...args, '--otp', '******'].join(' ') |
| 334 | : ['npm', ...args].join(' ') |
| 335 | logCommand(displayCmd) |
| 336 | } |
| 337 | |
| 338 | try { |
| 339 | logDebug('Executing npm command:', { command: 'npm', args: npmArgs }) |
| 340 | const { command, args: processArgs } = resolveNpmProcessCommand(npmArgs) |
| 341 | const { stdout, stderr } = await execFileAsync(command, processArgs, { |
| 342 | timeout: 60000, |
| 343 | cwd: options.cwd, |
| 344 | env: createNpmEnv(), |
| 345 | }) |
| 346 | |
| 347 | logDebug('Command succeeded:', { stdout, stderr }) |
| 348 | |
| 349 | if (!options.silent) { |
| 350 | logSuccess('Done') |
| 351 | } |
| 352 | |
| 353 | return { |
| 354 | stdout: stdout.trim(), |
| 355 | stderr: filterNpmWarnings(stderr), |
| 356 | exitCode: 0, |
| 357 | } |
| 358 | } catch (error) { |
| 359 | const err = error as { stdout?: string; stderr?: string; code?: number } |
| 360 | const stderr = err.stderr?.trim() ?? String(error) |
| 361 | logDebug('Command failed:', { error, stdout: err.stdout, stderr: err.stderr, code: err.code }) |
| 362 | const requiresOtp = detectOtpRequired(stderr) |
| 363 | const authFailure = detectAuthFailure(stderr) |
| 364 | |
| 365 | if (!options.silent) { |
| 366 | if (requiresOtp) { |
| 367 | logError('OTP required') |
| 368 | } else if (authFailure) { |
| 369 | logError('Authentication required - please run "npm login" and restart the connector') |
| 370 | } else { |
| 371 | logError(filterNpmWarnings(stderr).split('\n')[0] || 'Command failed') |
| 372 | } |
| 373 | } |
| 374 | |
| 375 | return { |
| 376 | stdout: err.stdout?.trim() ?? '', |
| 377 | stderr: requiresOtp |
| 378 | ? 'This operation requires a one-time password (OTP).' |
| 379 | : authFailure |
no test coverage detected