···7676}
77777878/**
7979+ * Show authentication required error in a box
8080+ */
8181+export function showAuthRequired(): void {
8282+ p.note(
8383+ [
8484+ pc.red('Not logged in to npm'),
8585+ '',
8686+ 'Please run the following command to log in:',
8787+ '',
8888+ ` ${pc.cyan('npm login')}`,
8989+ '',
9090+ 'Then restart the connector.',
9191+ ].join('\n'),
9292+ 'Authentication required',
9393+ )
9494+}
9595+9696+/**
7997 * Create a spinner for async operations
8098 */
8199export function createSpinner() {
+28-2
cli/src/npm-client.ts
···1010 exitCode: number
1111 /** True if the operation failed due to missing/invalid OTP */
1212 requiresOtp?: boolean
1313+ /** True if the operation failed due to authentication failure (not logged in or token expired) */
1414+ authFailure?: boolean
1315}
14161517function detectOtpRequired(stderr: string): boolean {
···1820 'one-time password',
1921 'This operation requires a one-time password',
2022 '--otp=<code>',
2121- 'OTP',
2223 ]
2324 const lowerStderr = stderr.toLowerCase()
2425 return otpPatterns.some(pattern => lowerStderr.includes(pattern.toLowerCase()))
2526}
26272828+function detectAuthFailure(stderr: string): boolean {
2929+ const authPatterns = [
3030+ 'ENEEDAUTH',
3131+ 'You must be logged in',
3232+ 'authentication error',
3333+ 'Unable to authenticate',
3434+ 'code E401',
3535+ 'code E403',
3636+ '401 Unauthorized',
3737+ '403 Forbidden',
3838+ 'not logged in',
3939+ 'npm login',
4040+ 'npm adduser',
4141+ ]
4242+ const lowerStderr = stderr.toLowerCase()
4343+ return authPatterns.some(pattern => lowerStderr.includes(pattern.toLowerCase()))
4444+}
4545+2746function filterNpmWarnings(stderr: string): string {
2847 return stderr
2948 .split('\n')
···7089 const err = error as { stdout?: string, stderr?: string, code?: number }
7190 const stderr = err.stderr?.trim() ?? String(error)
7291 const requiresOtp = detectOtpRequired(stderr)
9292+ const authFailure = detectAuthFailure(stderr)
73937494 if (!options.silent) {
7595 if (requiresOtp) {
7696 logError('OTP required')
9797+ }
9898+ else if (authFailure) {
9999+ logError('Authentication required - please run "npm login" and restart the connector')
77100 }
78101 else {
79102 logError(filterNpmWarnings(stderr).split('\n')[0] || 'Command failed')
···84107 stdout: err.stdout?.trim() ?? '',
85108 stderr: requiresOtp
86109 ? 'This operation requires a one-time password (OTP).'
8787- : filterNpmWarnings(stderr),
110110+ : authFailure
111111+ ? 'Authentication failed. Please run "npm login" and restart the connector.'
112112+ : filterNpmWarnings(stderr),
88113 exitCode: err.code ?? 1,
89114 requiresOtp,
115115+ authFailure,
90116 }
91117 }
92118}
+4
cli/src/server.ts
···353353 await Promise.all(runningOps)
354354 }
355355356356+ // Check if any operation had an auth failure
357357+ const authFailure = results.some(r => r.result.authFailure)
358358+356359 return {
357360 success: true,
358361 data: {
359362 results,
360363 otpRequired,
364364+ authFailure,
361365 },
362366 } as ApiResponse
363367 }),
+2
cli/src/types.ts
···3636 exitCode: number
3737 /** True if the operation failed due to missing/invalid OTP */
3838 requiresOtp?: boolean
3939+ /** True if the operation failed due to authentication failure (not logged in or token expired) */
4040+ authFailure?: boolean
3941}
40424143export interface PendingOperation {