pubz 0.2.5 → 0.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/cli.js +108 -7
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -308,13 +308,57 @@ async function multiSelect(message, options, allSelectedByDefault = true) {
308
308
  });
309
309
  }
310
310
 
311
- // src/publish.ts
311
+ // src/auth.ts
312
312
  import { spawn } from "node:child_process";
313
+ async function checkNpmAuth(registry) {
314
+ return new Promise((resolve2) => {
315
+ const proc = spawn("npm", ["whoami", "--registry", registry], {
316
+ stdio: ["inherit", "pipe", "pipe"]
317
+ });
318
+ let stdout = "";
319
+ let stderr = "";
320
+ proc.stdout?.on("data", (data) => {
321
+ stdout += data.toString();
322
+ });
323
+ proc.stderr?.on("data", (data) => {
324
+ stderr += data.toString();
325
+ });
326
+ proc.on("close", (code) => {
327
+ if (code === 0 && stdout.trim()) {
328
+ resolve2({ authenticated: true, username: stdout.trim() });
329
+ } else {
330
+ resolve2({ authenticated: false });
331
+ }
332
+ });
333
+ });
334
+ }
335
+ async function npmLogin(registry) {
336
+ return new Promise((resolve2) => {
337
+ const proc = spawn("npm", ["login", "--registry", registry], {
338
+ stdio: "inherit"
339
+ });
340
+ proc.on("close", (code) => {
341
+ if (code === 0) {
342
+ resolve2({ success: true });
343
+ } else {
344
+ resolve2({ success: false, error: "Login failed or was cancelled" });
345
+ }
346
+ });
347
+ });
348
+ }
349
+ async function promptForOtp() {
350
+ console.log("");
351
+ const code = await prompt(`${cyan("?")} Enter OTP code from your authenticator: `);
352
+ return code.trim();
353
+ }
354
+
355
+ // src/publish.ts
356
+ import { spawn as spawn2 } from "node:child_process";
313
357
  import { readFile as readFile2, stat as stat3 } from "node:fs/promises";
314
358
  import { join as join3 } from "node:path";
315
359
  function run(command, args, cwd) {
316
360
  return new Promise((resolve2) => {
317
- const proc = spawn(command, args, {
361
+ const proc = spawn2(command, args, {
318
362
  cwd,
319
363
  stdio: ["inherit", "pipe", "pipe"]
320
364
  });
@@ -381,18 +425,35 @@ async function verifyBuild(pkg) {
381
425
  }
382
426
  return { success: true };
383
427
  }
384
- async function publishPackage(pkg, registry, otp, dryRun) {
428
+ function isOtpError(output) {
429
+ return output.includes("EOTP") || output.includes("one-time password");
430
+ }
431
+ async function publishPackage(pkg, registry, context, dryRun) {
385
432
  if (dryRun) {
386
433
  console.log(` [DRY RUN] Would publish ${pkg.name}@${pkg.version} to ${registry}`);
387
434
  return { success: true };
388
435
  }
389
436
  console.log(`Publishing ${pkg.name}@${pkg.version}...`);
390
437
  const args = ["publish", "--registry", registry, "--access", "public"];
391
- if (otp) {
392
- args.push("--otp", otp);
438
+ if (context.otp) {
439
+ args.push("--otp", context.otp);
440
+ }
441
+ let result = await run("npm", args, pkg.path);
442
+ if (result.code !== 0 && isOtpError(result.output) && context.onOtpRequired) {
443
+ const otpCode = await context.onOtpRequired();
444
+ if (otpCode) {
445
+ context.otp = otpCode;
446
+ const retryArgs = ["publish", "--registry", registry, "--access", "public", "--otp", otpCode];
447
+ result = await run("npm", retryArgs, pkg.path);
448
+ }
393
449
  }
394
- const result = await run("npm", args, pkg.path);
395
450
  if (result.code !== 0) {
451
+ if (isOtpError(result.output)) {
452
+ return {
453
+ success: false,
454
+ error: `2FA OTP required. Use --otp flag or run interactively.`
455
+ };
456
+ }
396
457
  return { success: false, error: `Failed to publish ${pkg.name}` };
397
458
  }
398
459
  console.log(` ${pkg.name} published successfully`);
@@ -777,6 +838,42 @@ async function main() {
777
838
  console.log("");
778
839
  console.log(`Publishing to: ${cyan(registry)}`);
779
840
  console.log("");
841
+ if (!options.dryRun) {
842
+ console.log(cyan("Verifying npm authentication..."));
843
+ const authResult = await checkNpmAuth(registry);
844
+ if (!authResult.authenticated) {
845
+ if (options.ci) {
846
+ console.error(red(bold("Error:")) + " Not authenticated to npm.");
847
+ console.log("");
848
+ console.log(muted("In CI mode, you need to configure authentication:"));
849
+ console.log(muted(" - Set NPM_TOKEN environment variable"));
850
+ console.log(muted(" - Or configure .npmrc with auth token"));
851
+ console.log(muted(" - Or use OIDC Trusted Publishing"));
852
+ closePrompt();
853
+ process.exit(1);
854
+ }
855
+ console.log("");
856
+ console.log(yellow("Not logged in to npm.") + " Starting login...");
857
+ console.log("");
858
+ const loginResult = await npmLogin(registry);
859
+ if (!loginResult.success) {
860
+ console.error(red(bold("Login failed:")) + ` ${loginResult.error}`);
861
+ closePrompt();
862
+ process.exit(1);
863
+ }
864
+ const verifyAuth = await checkNpmAuth(registry);
865
+ if (!verifyAuth.authenticated) {
866
+ console.error(red(bold("Error:")) + " Login did not complete successfully.");
867
+ closePrompt();
868
+ process.exit(1);
869
+ }
870
+ console.log("");
871
+ console.log(green("Logged in as") + ` ${cyan(verifyAuth.username ?? "unknown")}`);
872
+ } else {
873
+ console.log(green("Authenticated as") + ` ${cyan(authResult.username ?? "unknown")}`);
874
+ }
875
+ console.log("");
876
+ }
780
877
  if (!options.skipBuild) {
781
878
  console.log(bold(cyan("Step 2:")) + " Building Packages");
782
879
  console.log(dim("─".repeat(30)));
@@ -838,8 +935,12 @@ async function main() {
838
935
  console.log("");
839
936
  console.log(cyan("Publishing packages..."));
840
937
  console.log("");
938
+ const publishContext = {
939
+ otp: options.otp,
940
+ onOtpRequired: options.ci ? undefined : promptForOtp
941
+ };
841
942
  for (const pkg of packages) {
842
- const result = await publishPackage(pkg, registry, options.otp, options.dryRun);
943
+ const result = await publishPackage(pkg, registry, publishContext, options.dryRun);
843
944
  if (!result.success) {
844
945
  console.error(red(bold("Failed to publish")) + ` ${cyan(pkg.name)}: ${result.error}`);
845
946
  console.log("");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pubz",
3
- "version": "0.2.5",
3
+ "version": "0.2.8",
4
4
  "description": "Interactive CLI for publishing npm packages (single or monorepo)",
5
5
  "type": "module",
6
6
  "bin": {