From 2e7b8f7945aeb3d2edb7833b38adebea4b31fe91 Mon Sep 17 00:00:00 2001 From: mikouaji Date: Wed, 4 Feb 2026 09:23:17 +0100 Subject: [PATCH] feat(npmx-connector): add debug mode with extra logs, add one extra OTP case --- cli/package.json | 1 + cli/src/npm-client.ts | 19 ++++++++++++++++--- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/cli/package.json b/cli/package.json index ca1bea454..0dc83ead5 100644 --- a/cli/package.json +++ b/cli/package.json @@ -28,6 +28,7 @@ "scripts": { "build": "tsdown", "dev": "NPMX_CLI_DEV=true node src/cli.ts", + "dev:debug": "DEBUG=npmx-connector NPMX_CLI_DEV=true node src/cli.ts", "test:types": "tsc --noEmit" }, "dependencies": { diff --git a/cli/src/npm-client.ts b/cli/src/npm-client.ts index 77ebcccf9..795ba4b24 100644 --- a/cli/src/npm-client.ts +++ b/cli/src/npm-client.ts @@ -7,7 +7,7 @@ import { tmpdir } from 'node:os' import { join } from 'node:path' import * as v from 'valibot' import { PackageNameSchema, UsernameSchema, OrgNameSchema, ScopeTeamSchema } from './schemas.ts' -import { logCommand, logSuccess, logError } from './logger.ts' +import { logCommand, logSuccess, logError, logDebug } from './logger.ts' const execFileAsync = promisify(execFile) @@ -75,10 +75,15 @@ function detectOtpRequired(stderr: string): boolean { 'EOTP', 'one-time password', 'This operation requires a one-time password', + 'OTP required for authentication', '--otp=', ] const lowerStderr = stderr.toLowerCase() - return otpPatterns.some(pattern => lowerStderr.includes(pattern.toLowerCase())) + logDebug('Checking for OTP requirement in stderr:', stderr) + logDebug('OTP patterns:', otpPatterns) + const result = otpPatterns.some(pattern => lowerStderr.includes(pattern.toLowerCase())) + logDebug('OTP required:', result) + return result } function detectAuthFailure(stderr: string): boolean { @@ -96,7 +101,11 @@ function detectAuthFailure(stderr: string): boolean { 'npm adduser', ] const lowerStderr = stderr.toLowerCase() - return authPatterns.some(pattern => lowerStderr.includes(pattern.toLowerCase())) + logDebug('Checking for auth failure in stderr:', stderr) + logDebug('Auth patterns:', authPatterns) + const result = authPatterns.some(pattern => lowerStderr.includes(pattern.toLowerCase())) + logDebug('Auth failure:', result) + return result } function filterNpmWarnings(stderr: string): string { @@ -123,6 +132,7 @@ async function execNpm( } try { + logDebug('Executing npm command:', { command: 'npm', args: npmArgs }) // Use execFile instead of exec to avoid shell injection vulnerabilities // execFile does not spawn a shell, so metacharacters are passed literally const { stdout, stderr } = await execFileAsync('npm', npmArgs, { @@ -130,6 +140,8 @@ async function execNpm( env: { ...process.env, FORCE_COLOR: '0' }, }) + logDebug('Command succeeded:', { stdout, stderr }) + if (!options.silent) { logSuccess('Done') } @@ -142,6 +154,7 @@ async function execNpm( } catch (error) { const err = error as { stdout?: string; stderr?: string; code?: number } const stderr = err.stderr?.trim() ?? String(error) + logDebug('Command failed:', { error, stdout: err.stdout, stderr: err.stderr, code: err.code }) const requiresOtp = detectOtpRequired(stderr) const authFailure = detectAuthFailure(stderr)