diff --git a/doc/PUBLISHING.md b/doc/PUBLISHING.md index a7b6652157e..c430d3473a8 100644 --- a/doc/PUBLISHING.md +++ b/doc/PUBLISHING.md @@ -202,9 +202,6 @@ Example for `@paperclipai/adapter-acpx-local` from the repo root: pnpm run release:bootstrap-package -- @paperclipai/adapter-acpx-local # one-time first publish from an authenticated maintainer machine -pnpm run release:bootstrap-package -- @paperclipai/adapter-acpx-local --publish - -# if npm requires an authenticator-app code for publish pnpm run release:bootstrap-package -- @paperclipai/adapter-acpx-local --publish --otp 123456 ``` @@ -213,12 +210,12 @@ The helper script: - checks that the package does not already exist on npm - builds the target package unless `--skip-build` is passed - runs `npm pack --dry-run` in the package directory -- only runs the real `npm publish --access public` when `--publish` is provided +- only runs the real `npm publish --access public` when `--publish --otp ` is provided For the real `--publish` step, the maintainer machine must already be authenticated to npm. If `npm whoami` returns `401`, first run `npm logout --registry=https://registry.npmjs.org/` to clear any stale local auth, then run `npm login` or `npm adduser` locally as an npm org member, and finally rerun the helper. That local human auth is fine for the one-time bootstrap publish; we just do not want the same auth model inside CI. -If `npm publish` returns `EOTP`, complete the browser auth URL that npm prints or rerun the helper with `--otp ` if your npm account uses authenticator-app 2FA. +The helper now requires `--otp ` up front for `--publish`, so it fails before the real publish attempt if the one-time password is missing. After that first publish succeeds: diff --git a/scripts/bootstrap-npm-package.mjs b/scripts/bootstrap-npm-package.mjs index 8a7131552fd..9b1e01beb04 100644 --- a/scripts/bootstrap-npm-package.mjs +++ b/scripts/bootstrap-npm-package.mjs @@ -17,7 +17,7 @@ function usage() { process.stderr.write( [ "Usage:", - " node scripts/bootstrap-npm-package.mjs [--publish] [--skip-build]", + " node scripts/bootstrap-npm-package.mjs [--publish --otp ] [--skip-build]", "", "Examples:", " node scripts/bootstrap-npm-package.mjs @paperclipai/adapter-acpx-local", @@ -233,6 +233,10 @@ function main(argv) { const pkg = resolveTargetPackage(selector); process.stdout.write(`Selected ${pkg.name} (${pkg.dir})\n`); + if (publish && !otp) { + throw new Error("`--publish` requires `--otp `. Generate a fresh npm one-time password and rerun."); + } + const npmState = inspectNpmPackage(pkg.name); if (npmState.exists) { throw new Error(`${pkg.name} already exists on npm at version ${npmState.version}; bootstrap is only for first publish`); @@ -257,8 +261,8 @@ function main(argv) { process.stdout.write( [ "", - `Dry run complete. To perform the first publish from an authenticated maintainer machine, run:`, - `node scripts/bootstrap-npm-package.mjs ${pkg.name} --publish`, + "Dry run complete. To perform the first publish from an authenticated maintainer machine, run:", + `node scripts/bootstrap-npm-package.mjs ${pkg.name} --publish --otp `, "", ].join("\n"), ); diff --git a/scripts/bootstrap-npm-package.test.mjs b/scripts/bootstrap-npm-package.test.mjs index 254bcd89b2d..48deb739583 100644 --- a/scripts/bootstrap-npm-package.test.mjs +++ b/scripts/bootstrap-npm-package.test.mjs @@ -23,6 +23,16 @@ test("parseArgs accepts an explicit otp value", () => { }); }); +test("parseArgs leaves otp null when omitted", () => { + assert.deepEqual(parseArgs(["packages/adapters/acpx-local", "--publish"]), { + help: false, + selector: "packages/adapters/acpx-local", + publish: true, + skipBuild: false, + otp: null, + }); +}); + test("parseArgs returns help mode", () => { assert.deepEqual(parseArgs(["--help"]), { help: true,