Skip to content

Commit 017f023

Browse files
authored
fix(cli): enable web auth for package:init (#2415)
1 parent eb2e6bb commit 017f023

File tree

2 files changed

+20
-48
lines changed

2 files changed

+20
-48
lines changed

cli/src/npm-client.ts

Lines changed: 19 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,8 @@ const AUTH_URL_TIMEOUT_MS = 90_000
157157
export 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
*/
564568
export 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
}

cli/src/server.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -809,9 +809,7 @@ async function executeOperation(
809809
result = await ownerRemove(params.user, params.pkg, execOptions)
810810
break
811811
case 'package:init':
812-
// package:init has its own special execution path (temp dir + publish)
813-
// and does not support interactive mode
814-
result = await packageInit(params.name, params.author, options.otp)
812+
result = await packageInit(params.name, params.author, execOptions)
815813
break
816814
default:
817815
return {

0 commit comments

Comments
 (0)