Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
19 changes: 16 additions & 3 deletions cli/src/npm-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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=<code>',
]
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 {
Expand All @@ -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 {
Expand All @@ -123,6 +132,7 @@ async function execNpm(
}

try {
logDebug('Executing npm command:', { command: 'npm', args: npmArgs })
Comment thread
mikouaji marked this conversation as resolved.
// Use execFile instead of exec to avoid shell injection vulnerabilities
// On Windows, shell: true is required to execute .cmd files (like npm.cmd)
// On Unix, we keep it false for better security and performance
Expand All @@ -132,6 +142,8 @@ async function execNpm(
shell: process.platform === 'win32',
})

logDebug('Command succeeded:', { stdout, stderr })

if (!options.silent) {
logSuccess('Done')
}
Expand All @@ -144,6 +156,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)

Expand Down
Loading