diff --git a/scripts/brev-launchable-ci-cpu.sh b/scripts/brev-launchable-ci-cpu.sh index 8da9e73936..a218657948 100755 --- a/scripts/brev-launchable-ci-cpu.sh +++ b/scripts/brev-launchable-ci-cpu.sh @@ -249,16 +249,15 @@ npm run build 2>&1 | tail -3 cd "$NEMOCLAW_CLONE_DIR" info "Plugin built" -# Create the global nemoclaw symlink once during setup. This runs here -# (before any test) so the cold-path npm link cost is absorbed by the -# launchable's own readiness window rather than a later test's tight -# execSync timeout. When the test suite rsyncs PR branch code over this -# clone, a subsequent `npm link` is a fast no-op against the existing -# symlink. See PR #1888 regression commentary. -info "Linking nemoclaw CLI globally..." -sudo npm link 2>&1 | tail -3 -sudo chown -R "$TARGET_USER":"$TARGET_USER" "$NEMOCLAW_CLONE_DIR" -info "nemoclaw CLI linked" +# Expose the nemoclaw CLI on PATH. Earlier this was `sudo npm link`, but +# on cold CPU Brev that routinely hangs inside npm's global-prefix +# housekeeping and `sudo chown -R node_modules` traversal (≥20 min in +# CI). npm link just creates two symlinks in the end; do them directly +# so setup stays deterministic and fast. +info "Linking nemoclaw CLI (direct symlink)..." +sudo ln -sf "$NEMOCLAW_CLONE_DIR/bin/nemoclaw.js" /usr/local/bin/nemoclaw +sudo chmod +x "$NEMOCLAW_CLONE_DIR/bin/nemoclaw.js" +info "nemoclaw CLI linked at /usr/local/bin/nemoclaw" # ══════════════════════════════════════════════════════════════════════ # 6. Pre-pull Docker images diff --git a/test/e2e/brev-e2e.test.ts b/test/e2e/brev-e2e.test.ts index f9700e751c..7e57359345 100644 --- a/test/e2e/brev-e2e.test.ts +++ b/test/e2e/brev-e2e.test.ts @@ -423,17 +423,17 @@ function bootstrapLaunchable(elapsed) { ); console.log(`[${elapsed()}] Plugin built`); - // Install nemoclaw CLI. - // Use `sudo npm link` because Node.js is installed system-wide via - // nodesource (global prefix is /usr), so creating the global symlink - // requires elevated permissions. The launchable setup script already - // runs this once during its readiness window, so this invocation is a - // fast idempotent no-op against the existing symlink on Brev CPU runs. - console.log(`[${elapsed()}] Installing nemoclaw CLI (npm link)...`); + // Expose the nemoclaw CLI on PATH. The launchable setup script already + // creates /usr/local/bin/nemoclaw → $NEMOCLAW_CLONE_DIR/bin/nemoclaw.js + // as a direct symlink, and rsync above preserves that path, so this is + // an idempotent re-link to make local dev runs (that skip the launchable) + // still work. Avoid `sudo npm link` on cold CPU Brev — it routinely + // hangs inside npm's global-prefix housekeeping. + console.log(`[${elapsed()}] Linking nemoclaw CLI (direct symlink)...`); ssh( - `source ~/.nvm/nvm.sh 2>/dev/null || true && cd ${resolvedRemoteDir} && sudo npm link && sudo chown -R $(whoami):$(whoami) ${resolvedRemoteDir}`, + `sudo ln -sf ${resolvedRemoteDir}/bin/nemoclaw.js /usr/local/bin/nemoclaw && sudo chmod +x ${resolvedRemoteDir}/bin/nemoclaw.js`, { - timeout: 120_000, + timeout: 30_000, stream: true, }, );