@@ -138,7 +138,9 @@ describe("install.sh", () => {
138138 expect ( script ) . toContain (
139139 'run_quiet_step "Installing Node.js" sudo apk add --no-cache nodejs npm' ,
140140 ) ;
141- expect ( script ) . toContain ( 'run_quiet_step "Installing nodejs-current" apk add --no-cache nodejs-current npm' ) ;
141+ expect ( script ) . toContain (
142+ 'run_quiet_step "Installing nodejs-current" apk add --no-cache nodejs-current npm' ,
143+ ) ;
142144 expect ( script ) . toContain ( "if ! node_is_at_least_required; then" ) ;
143145
144146 const apkIndex = script . indexOf ( "if command -v apk &> /dev/null && is_alpine_linux; then" ) ;
@@ -208,7 +210,9 @@ describe("install.sh", () => {
208210 expect ( result . status ) . toBe ( 0 ) ;
209211 expect ( result . stdout ) . toContain ( "step:Installing Node.js|apk add --no-cache nodejs npm" ) ;
210212 expect ( result . stdout ) . toContain ( "warn:Alpine nodejs package installed v20.15.1" ) ;
211- expect ( result . stdout ) . toContain ( "step:Installing nodejs-current|apk add --no-cache nodejs-current npm" ) ;
213+ expect ( result . stdout ) . toContain (
214+ "step:Installing nodejs-current|apk add --no-cache nodejs-current npm" ,
215+ ) ;
212216 expect ( result . stdout ) . toContain ( "finish-linux-node" ) ;
213217 } ) ;
214218
@@ -247,8 +251,12 @@ describe("install.sh", () => {
247251
248252 expect ( result . status ) . toBe ( 1 ) ;
249253 expect ( result . stdout ) . toContain ( "warn:Alpine nodejs package installed v20.15.1" ) ;
250- expect ( result . stdout ) . toContain ( "step:Installing nodejs-current|apk add --no-cache nodejs-current npm" ) ;
251- expect ( result . stdout ) . toContain ( "error:Alpine apk repositories did not provide Node.js v22.19+" ) ;
254+ expect ( result . stdout ) . toContain (
255+ "step:Installing nodejs-current|apk add --no-cache nodejs-current npm" ,
256+ ) ;
257+ expect ( result . stdout ) . toContain (
258+ "error:Alpine apk repositories did not provide Node.js v22.19+" ,
259+ ) ;
252260 expect ( result . stdout ) . toContain ( "Use Alpine 3.21+ or install Node.js 24 manually" ) ;
253261 } ) ;
254262
@@ -758,6 +766,64 @@ describe("install.sh", () => {
758766 expect ( result . stdout ) . not . toContain ( "[4/3] Verifying installation" ) ;
759767 } ) ;
760768
769+ it ( "bounds installer npm prefix probes during finalization helpers" , ( ) => {
770+ const result = runInstallShell (
771+ [
772+ `source ${ JSON . stringify ( SCRIPT_PATH ) } ` ,
773+ "npm() {" ,
774+ ' if [[ "$1" == "prefix" && "$2" == "-g" ]]; then sleep 2; return 0; fi' ,
775+ ' if [[ "$1" == "config" && "$2" == "get" && "$3" == "prefix" ]]; then printf "/tmp/openclaw-npm\\n"; return 0; fi' ,
776+ " return 1" ,
777+ "}" ,
778+ "npm_global_bin_dir" ,
779+ ] . join ( "\n" ) ,
780+ { OPENCLAW_INSTALL_PROBE_TIMEOUT_SECONDS : "0.1" } ,
781+ ) ;
782+
783+ expect ( result . status ) . toBe ( 0 ) ;
784+ expect ( result . stdout . trim ( ) ) . toBe ( "/tmp/openclaw-npm/bin" ) ;
785+ expect ( result . stderr ) . toContain ( "timed out during installer finalization probe: npm prefix -g" ) ;
786+ } ) ;
787+
788+ it ( "bounds daemon status probes during finalization helpers" , ( ) => {
789+ const tmp = mkdtempSync ( join ( tmpdir ( ) , "openclaw-install-probe-" ) ) ;
790+ const claw = join ( tmp , "openclaw" ) ;
791+ writeFileSync (
792+ claw ,
793+ [
794+ "#!/usr/bin/env bash" ,
795+ 'if [[ "$1" == "daemon" && "$2" == "status" && "$3" == "--json" ]]; then' ,
796+ " sleep 2" ,
797+ " exit 0" ,
798+ "fi" ,
799+ "exit 1" ,
800+ "" ,
801+ ] . join ( "\n" ) ,
802+ ) ;
803+ chmodSync ( claw , 0o755 ) ;
804+ try {
805+ const result = runInstallShell (
806+ [
807+ `source ${ JSON . stringify ( SCRIPT_PATH ) } ` ,
808+ `if is_gateway_daemon_loaded ${ JSON . stringify ( claw ) } ; then` ,
809+ ' printf "loaded\\n"' ,
810+ "else" ,
811+ ' printf "not-loaded\\n"' ,
812+ "fi" ,
813+ ] . join ( "\n" ) ,
814+ { OPENCLAW_INSTALL_PROBE_TIMEOUT_SECONDS : "0.1" } ,
815+ ) ;
816+
817+ expect ( result . status ) . toBe ( 0 ) ;
818+ expect ( result . stdout . trim ( ) ) . toBe ( "not-loaded" ) ;
819+ expect ( result . stderr ) . toContain (
820+ "timed out during installer finalization probe: openclaw daemon status --json" ,
821+ ) ;
822+ } finally {
823+ rmSync ( tmp , { force : true , recursive : true } ) ;
824+ }
825+ } ) ;
826+
761827 it ( "loads nvm before checking Node.js so stale system Node does not win" , ( ) => {
762828 expect ( script ) . toMatch (
763829 / # S t e p 2 : N o d e \. j s \s + l o a d _ n v m _ f o r _ n o d e _ d e t e c t i o n \s + i f ! c h e c k _ n o d e ; t h e n / ,
0 commit comments