@@ -13,6 +13,10 @@ UPDATE_BASELINE_VERSION="${OPENCLAW_INSTALL_UPDATE_BASELINE:-latest}"
1313UPDATE_BASELINE_TAG_URL=" ${OPENCLAW_INSTALL_UPDATE_BASELINE_TAG_URL:- } "
1414UPDATE_EXPECT_VERSION=" ${OPENCLAW_INSTALL_UPDATE_EXPECT_VERSION:- } "
1515UPDATE_TAG_URL=" ${OPENCLAW_INSTALL_UPDATE_TAG_URL:- } "
16+ FRESHNESS_VERSION=" ${OPENCLAW_INSTALL_FRESHNESS_VERSION:- latest} "
17+ # npm min-release-age is days; 10000 keeps the control failure independent of normal release cadence.
18+ FRESHNESS_MIN_RELEASE_AGE=" ${OPENCLAW_INSTALL_FRESHNESS_MIN_RELEASE_AGE:- 10000} "
19+ FRESHNESS_NPM_VERSION=" ${OPENCLAW_INSTALL_FRESHNESS_NPM_VERSION:- 11.14.1} "
1620HEARTBEAT_INTERVAL=" ${OPENCLAW_INSTALL_SMOKE_HEARTBEAT_INTERVAL:- 60} "
1721INSTALL_COMMAND_TIMEOUT=" ${OPENCLAW_INSTALL_SMOKE_COMMAND_TIMEOUT:- 900} "
1822SCRIPT_DIR=" $( cd " $( dirname " $0 " ) " && pwd) "
@@ -391,6 +395,75 @@ run_npm_global_smoke() {
391395 echo " OK"
392396}
393397
398+ run_freshness_smoke () {
399+ local freshness_spec=" ${PACKAGE_NAME} @${FRESHNESS_VERSION} "
400+ local expected_version
401+ local current_npm_version
402+ local policy_home
403+ local plain_stdout_file
404+ local plain_stderr_file
405+ local plain_status
406+ policy_home=" $( mktemp -d) "
407+ plain_stdout_file=" $( mktemp) "
408+ plain_stderr_file=" $( mktemp) "
409+ printf " min-release-age=%s\n" " $FRESHNESS_MIN_RELEASE_AGE " > " ${policy_home} /.npmrc"
410+
411+ current_npm_version=" $( npm --version 2> /dev/null || true) "
412+ if [[ " $current_npm_version " != " $FRESHNESS_NPM_VERSION " ]]; then
413+ echo " ==> Install npm with min-release-age support: npm@$FRESHNESS_NPM_VERSION "
414+ npm_install_global " install npm freshness-capable release" " npm@${FRESHNESS_NPM_VERSION} "
415+ fi
416+
417+ expected_version=" $( quiet_npm view " $freshness_spec " version 2> /dev/null || true) "
418+ if [[ -z " $expected_version " ]]; then
419+ echo " ERROR: failed to resolve $freshness_spec " >&2
420+ return 1
421+ fi
422+
423+ echo " package=$PACKAGE_NAME version=$FRESHNESS_VERSION resolved=$expected_version npm=$( npm --version) min_release_age=$FRESHNESS_MIN_RELEASE_AGE "
424+ echo " ==> Verify user npm freshness policy blocks plain npm install"
425+ set +e
426+ HOME=" $policy_home " NPM_CONFIG_USERCONFIG=" ${policy_home} /.npmrc" \
427+ timeout --foreground " ${INSTALL_COMMAND_TIMEOUT} s" \
428+ npm \
429+ --loglevel=error \
430+ --logs-max=0 \
431+ --no-update-notifier \
432+ --no-fund \
433+ --no-audit \
434+ --no-progress \
435+ install -g " $freshness_spec " \
436+ > " $plain_stdout_file " 2> " $plain_stderr_file "
437+ plain_status=$?
438+ set -e
439+ if [[ " $plain_status " -eq 0 ]]; then
440+ echo " ERROR: plain npm install unexpectedly succeeded under min-release-age policy" >&2
441+ return 1
442+ fi
443+ if ! grep -Eiq " No matching version|No versions available|ETARGET|ENOVERSIONS|notarget|min-release-age|minimum release age|before" \
444+ " $plain_stdout_file " " $plain_stderr_file " ; then
445+ echo " ERROR: plain npm install failed without expected freshness evidence" >&2
446+ cat " $plain_stdout_file "
447+ cat " $plain_stderr_file " >&2
448+ return 1
449+ fi
450+
451+ echo " ==> Run installer with same npm freshness policy"
452+ env \
453+ HOME=" $policy_home " \
454+ NPM_CONFIG_USERCONFIG=" ${policy_home} /.npmrc" \
455+ OPENCLAW_NO_ONBOARD=1 \
456+ OPENCLAW_NO_PROMPT=1 \
457+ bash -c ' curl -fsSL "$1" | bash -s -- --install-method npm --version "$2" --no-prompt --no-onboard' \
458+ _ " $INSTALL_URL " " $FRESHNESS_VERSION "
459+
460+ echo " ==> Verify installed version"
461+ print_install_audit " freshness install"
462+ verify_installed_cli " $PACKAGE_NAME " " $expected_version "
463+
464+ echo " OK"
465+ }
466+
394467case " $SMOKE_MODE " in
395468 install)
396469 run_install_smoke
@@ -401,6 +474,9 @@ case "$SMOKE_MODE" in
401474 npm-global)
402475 run_npm_global_smoke
403476 ;;
477+ freshness)
478+ run_freshness_smoke
479+ ;;
404480 * )
405481 echo " ERROR: unsupported OPENCLAW_INSTALL_SMOKE_MODE=$SMOKE_MODE " >&2
406482 exit 1
0 commit comments