@@ -157,6 +157,8 @@ const AUTH_URL_TIMEOUT_MS = 90_000
157157export interface ExecNpmOptions {
158158 otp ?: string
159159 silent ?: boolean
160+ /** Working directory for the npm command. */
161+ cwd ?: string
160162 /** When true, use PTY-based interactive execution instead of execFile. */
161163 interactive ?: boolean
162164 /** When true, npm opens auth URLs in the user's browser.
@@ -214,6 +216,7 @@ async function execNpmInteractive(
214216 name : 'xterm-256color' ,
215217 cols : 120 ,
216218 rows : 30 ,
219+ cwd : options . cwd ,
217220 env,
218221 } )
219222
@@ -337,6 +340,7 @@ async function execNpm(args: string[], options: ExecNpmOptions = {}): Promise<Np
337340 const { command, args : processArgs } = resolveNpmProcessCommand ( npmArgs )
338341 const { stdout, stderr } = await execFileAsync ( command , processArgs , {
339342 timeout : 60000 ,
343+ cwd : options . cwd ,
340344 env : createNpmEnv ( ) ,
341345 } )
342346
@@ -559,12 +563,12 @@ export async function listUserPackages(user: string): Promise<NpmExecResult> {
559563 * Creates a minimal package.json in a temp directory and publishes it.
560564 * @param name Package name to claim
561565 * @param author npm username of the publisher (for author field)
562- * @param otp Optional OTP for 2FA
566+ * @param options Execution options (otp, interactive, etc.)
563567 */
564568export async function packageInit (
565569 name : string ,
566570 author ?: string ,
567- otp ?: string ,
571+ options ?: ExecNpmOptions ,
568572) : Promise < NpmExecResult > {
569573 validatePackageName ( name )
570574
@@ -600,52 +604,22 @@ export async function packageInit(
600604 args . push ( '--access' , access )
601605 }
602606
603- // Run npm publish from the temp directory
604- const npmArgs = otp ? [ ...args , '--otp' , otp ] : args
605-
606- // Log the command being run (hide OTP value for security)
607- const displayCmd = otp ? `npm ${ args . join ( ' ' ) } --otp ******` : `npm ${ args . join ( ' ' ) } `
607+ const displayCmd = options ?. otp
608+ ? [ 'npm' , ...args , '--otp' , '******' ] . join ( ' ' )
609+ : [ 'npm' , ...args ] . join ( ' ' )
608610 logCommand ( `${ displayCmd } (in temp dir for ${ name } )` )
609611
610- try {
611- const { command, args : processArgs } = resolveNpmProcessCommand ( npmArgs )
612- const { stdout, stderr } = await execFileAsync ( command , processArgs , {
613- timeout : 60000 ,
614- cwd : tempDir . path ,
615- env : createNpmEnv ( ) ,
616- } )
612+ const result = await execNpm ( args , { ...options , cwd : tempDir . path , silent : true } )
617613
614+ if ( result . exitCode === 0 ) {
618615 logSuccess ( `Published ${ name } @0.0.0` )
619-
620- return {
621- stdout : stdout . trim ( ) ,
622- stderr : filterNpmWarnings ( stderr ) ,
623- exitCode : 0 ,
624- }
625- } catch ( error ) {
626- const err = error as { stdout ?: string ; stderr ?: string ; code ?: number }
627- const stderr = err . stderr ?. trim ( ) ?? String ( error )
628- const requiresOtp = detectOtpRequired ( stderr )
629- const authFailure = detectAuthFailure ( stderr )
630-
631- if ( requiresOtp ) {
632- logError ( 'OTP required' )
633- } else if ( authFailure ) {
634- logError ( 'Authentication required - please run "npm login" and restart the connector' )
635- } else {
636- logError ( filterNpmWarnings ( stderr ) . split ( '\n' ) [ 0 ] || 'Command failed' )
637- }
638-
639- return {
640- stdout : err . stdout ?. trim ( ) ?? '' ,
641- stderr : requiresOtp
642- ? 'This operation requires a one-time password (OTP).'
643- : authFailure
644- ? 'Authentication failed. Please run "npm login" and restart the connector.'
645- : filterNpmWarnings ( stderr ) ,
646- exitCode : err . code ?? 1 ,
647- requiresOtp,
648- authFailure,
649- }
616+ } else if ( result . requiresOtp ) {
617+ logError ( 'OTP required' )
618+ } else if ( result . authFailure ) {
619+ logError ( 'Authentication required - please run "npm login" and restart the connector' )
620+ } else {
621+ logError ( result . stderr . split ( '\n' ) [ 0 ] || 'Command failed' )
650622 }
623+
624+ return result
651625}
0 commit comments