@@ -1056,8 +1056,8 @@ function commandNeedsAwsMacosPackageManager(commandArgs, options = {}) {
10561056 return true ;
10571057 }
10581058 if ( commandArgs . length === 1 ) {
1059- return shellCommandWordCandidates ( commandArgs [ 0 ] ) . some (
1060- ( words ) => commandWordsNeedAwsMacosPackageManager ( words , options ) ,
1059+ return shellCommandWordCandidates ( commandArgs [ 0 ] ) . some ( ( words ) =>
1060+ commandWordsNeedAwsMacosPackageManager ( words , options ) ,
10611061 ) ;
10621062 }
10631063 return commandWordsNeedAwsMacosPackageManager ( normalizedCommandWords ( commandArgs ) , options ) ;
@@ -1964,7 +1964,7 @@ function remoteGitBootstrapForChangedGate(changedGateBase) {
19641964}
19651965
19661966function injectRemoteChangedGateEnvironment ( commandArgs ) {
1967- if ( commandArgs [ 0 ] !== "run" || isWindowsRemoteTarget ( commandArgs ) ) {
1967+ if ( commandArgs [ 0 ] !== "run" || isNativeWindowsRemoteTarget ( commandArgs ) ) {
19681968 return commandArgs ;
19691969 }
19701970
@@ -2055,6 +2055,16 @@ function isAwsMacosRemoteTarget(commandArgs, providerName) {
20552055 ) ;
20562056}
20572057
2058+ function isBrokeredWsl2RemoteTarget ( commandArgs , providerName ) {
2059+ const canonicalProvider = providerAliases . get ( providerName ) ?? providerName ;
2060+ return (
2061+ commandArgs [ 0 ] === "run" &&
2062+ ( canonicalProvider === "aws" || canonicalProvider === "azure" ) &&
2063+ isWindowsRemoteTarget ( commandArgs ) &&
2064+ optionValue ( commandArgs , "--windows-mode" ) === "wsl2"
2065+ ) ;
2066+ }
2067+
20582068function isHydratedNativeWindowsProvider ( providerName ) {
20592069 return providerName === "aws" || providerName === "azure" ;
20602070}
@@ -2141,6 +2151,31 @@ function injectRemoteChangedGateGitBootstrap(commandArgs, changedGateBase) {
21412151 return normalizedArgs ;
21422152}
21432153
2154+ function remotePosixJsEnvBootstrap ( ) {
2155+ return [
2156+ "openclaw_crabbox_env() {" ,
2157+ "openclaw_env_args=();" ,
2158+ "openclaw_env_ignore=0;" ,
2159+ "openclaw_env_path_seen=0;" ,
2160+ 'while [ "$#" -gt 0 ]; do' ,
2161+ 'case "$1" in' ,
2162+ '-i|--ignore-environment) openclaw_env_ignore=1; openclaw_env_args+=("$1"); shift ;;' ,
2163+ '-S|--split-string|-S*|--split-string=*) command env "${openclaw_env_args[@]}" "$@"; return ;;' ,
2164+ '-[!-]*i*) openclaw_env_ignore=1; openclaw_env_args+=("$1"); shift ;;' ,
2165+ '-u|--unset|-C|--chdir) openclaw_env_args+=("$1"); shift; if [ "$#" -gt 0 ]; then openclaw_env_args+=("$1"); shift; fi ;;' ,
2166+ '--unset=*|--chdir=*) openclaw_env_args+=("$1"); shift ;;' ,
2167+ 'PATH=*) if [ "$openclaw_env_ignore" = "1" ]; then openclaw_env_args+=("PATH=${OPENCLAW_CRABBOX_BOOTSTRAP_PATH:-$PATH}:${1#PATH=}"); else openclaw_env_args+=("$1"); fi; openclaw_env_path_seen=1; shift ;;' ,
2168+ '[A-Za-z_]*=*) openclaw_env_args+=("$1"); shift ;;' ,
2169+ '--) openclaw_env_args+=("--"); shift; break ;;' ,
2170+ "*) break ;;" ,
2171+ "esac;" ,
2172+ "done;" ,
2173+ 'if [ "$openclaw_env_ignore" = "1" ] && [ "$openclaw_env_path_seen" = "0" ]; then openclaw_env_args+=("PATH=${OPENCLAW_CRABBOX_BOOTSTRAP_PATH:-$PATH}"); fi;' ,
2174+ 'command env "${openclaw_env_args[@]}" "$@";' ,
2175+ "};" ,
2176+ ] ;
2177+ }
2178+
21442179function remoteAwsMacosJsBootstrap ( { packageManager = false , bun = false } = { } ) {
21452180 const nodeVersion = process . env . OPENCLAW_CRABBOX_MACOS_NODE_VERSION ?. trim ( ) || "24.15.0" ;
21462181 const bootstrap = [
@@ -2192,26 +2227,7 @@ function remoteAwsMacosJsBootstrap({ packageManager = false, bun = false } = {})
21922227 "release_install_lock;" ,
21932228 "fi;" ,
21942229 "node --version >&2 || return 1;" ,
2195- "openclaw_crabbox_env() {" ,
2196- "openclaw_env_args=();" ,
2197- "openclaw_env_ignore=0;" ,
2198- "openclaw_env_path_seen=0;" ,
2199- 'while [ "$#" -gt 0 ]; do' ,
2200- 'case "$1" in' ,
2201- '-i|--ignore-environment) openclaw_env_ignore=1; openclaw_env_args+=("$1"); shift ;;' ,
2202- '-S|--split-string|-S*|--split-string=*) command env "${openclaw_env_args[@]}" "$@"; return ;;' ,
2203- '-[!-]*i*) openclaw_env_ignore=1; openclaw_env_args+=("$1"); shift ;;' ,
2204- '-u|--unset|-C|--chdir) openclaw_env_args+=("$1"); shift; if [ "$#" -gt 0 ]; then openclaw_env_args+=("$1"); shift; fi ;;' ,
2205- '--unset=*|--chdir=*) openclaw_env_args+=("$1"); shift ;;' ,
2206- 'PATH=*) if [ "$openclaw_env_ignore" = "1" ]; then openclaw_env_args+=("PATH=${OPENCLAW_CRABBOX_BOOTSTRAP_PATH:-$PATH}:${1#PATH=}"); else openclaw_env_args+=("$1"); fi; openclaw_env_path_seen=1; shift ;;' ,
2207- '[A-Za-z_]*=*) openclaw_env_args+=("$1"); shift ;;' ,
2208- '--) openclaw_env_args+=("--"); shift; break ;;' ,
2209- "*) break ;;" ,
2210- "esac;" ,
2211- "done;" ,
2212- 'if [ "$openclaw_env_ignore" = "1" ] && [ "$openclaw_env_path_seen" = "0" ]; then openclaw_env_args+=("PATH=${OPENCLAW_CRABBOX_BOOTSTRAP_PATH:-$PATH}"); fi;' ,
2213- 'command env "${openclaw_env_args[@]}" "$@";' ,
2214- "};" ,
2230+ ...remotePosixJsEnvBootstrap ( ) ,
22152231 ] ;
22162232 if ( packageManager ) {
22172233 bootstrap . push (
@@ -2264,6 +2280,71 @@ function remoteAwsMacosJsBootstrap({ packageManager = false, bun = false } = {})
22642280 return bootstrap . join ( " " ) ;
22652281}
22662282
2283+ function remoteWsl2JsBootstrap ( { packageManager = false } = { } ) {
2284+ const nodeVersion = process . env . OPENCLAW_CRABBOX_WSL2_NODE_VERSION ?. trim ( ) || "24.15.0" ;
2285+ const bootstrap = [
2286+ "openclaw_crabbox_bootstrap_wsl2_js() {" ,
2287+ 'tool_root="${OPENCLAW_CRABBOX_WSL2_TOOLCHAIN_DIR:-$HOME/.openclaw-crabbox-toolchain}";' ,
2288+ `node_version=${ shellQuote ( nodeVersion ) } ;` ,
2289+ 'arch="$(uname -m)";' ,
2290+ 'case "$arch" in arm64|aarch64) node_arch=arm64 ;; x86_64|amd64) node_arch=x64 ;; *) echo "unsupported WSL2 arch: $arch" >&2; return 2 ;; esac;' ,
2291+ 'if [ -z "${TMPDIR:-}" ]; then export TMPDIR="/tmp"; fi;' ,
2292+ 'if [ ! -d "$TMPDIR" ]; then mkdir -p "$TMPDIR" 2>/dev/null || export TMPDIR="/tmp"; fi;' ,
2293+ 'if [ ! -d "$TMPDIR" ]; then echo "usable TMPDIR not found: $TMPDIR" >&2; return 1; fi;' ,
2294+ 'node_dir="$tool_root/node-v${node_version}-linux-${node_arch}";' ,
2295+ 'ready_marker="$node_dir/.openclaw-crabbox-node-ready";' ,
2296+ 'export PATH="$node_dir/bin:$PATH";' ,
2297+ 'if [ ! -x "$node_dir/bin/node" ] || [ ! -f "$ready_marker" ]; then' ,
2298+ 'mkdir -p "$tool_root" || { status=$?; return "$status"; };' ,
2299+ 'install_lock="$tool_root/.node-${node_version}-${node_arch}.lock";' ,
2300+ "lock_acquired=0;" ,
2301+ "lock_deadline=$((SECONDS + 300));" ,
2302+ "while true; do" ,
2303+ 'if mkdir "$install_lock" 2>/dev/null; then lock_acquired=1; printf "%s\\n" "$$" >"$install_lock/pid" || { status=$?; rm -rf "$install_lock"; return "$status"; }; break; fi;' ,
2304+ 'if [ -x "$node_dir/bin/node" ] && [ -f "$ready_marker" ]; then break; fi;' ,
2305+ 'if [ "$SECONDS" -ge "$lock_deadline" ]; then' ,
2306+ 'lock_pid="$(cat "$install_lock/pid" 2>/dev/null || true)";' ,
2307+ 'if [ -n "$lock_pid" ] && kill -0 "$lock_pid" 2>/dev/null; then echo "timed out waiting for active WSL2 Node toolchain install lock: $install_lock pid=$lock_pid" >&2; return 1; fi;' ,
2308+ 'echo "reclaiming stale WSL2 Node toolchain install lock: $install_lock" >&2;' ,
2309+ 'rm -rf "$install_lock" || return 1;' ,
2310+ "lock_deadline=$((SECONDS + 300));" ,
2311+ "fi;" ,
2312+ "sleep 1;" ,
2313+ "done;" ,
2314+ 'release_install_lock() { if [ "$lock_acquired" = "1" ]; then rm -rf "$install_lock" 2>/dev/null || true; fi; };' ,
2315+ 'if [ ! -x "$node_dir/bin/node" ] || [ ! -f "$ready_marker" ]; then' ,
2316+ 'tmp_dir="$(mktemp -d)" || { release_install_lock; return 1; };' ,
2317+ 'pkg="node-v${node_version}-linux-${node_arch}.tar.gz";' ,
2318+ 'base_url="https://nodejs.org/dist/v${node_version}";' ,
2319+ 'curl -fsSL --connect-timeout 10 --max-time 300 --retry 2 --retry-delay 2 -o "$tmp_dir/$pkg" "$base_url/$pkg" || { status=$?; release_install_lock; rm -rf "$tmp_dir"; return "$status"; };' ,
2320+ 'curl -fsSL --connect-timeout 10 --max-time 60 --retry 2 --retry-delay 2 -o "$tmp_dir/SHASUMS256.txt" "$base_url/SHASUMS256.txt" || { status=$?; release_install_lock; rm -rf "$tmp_dir"; return "$status"; };' ,
2321+ '(cd "$tmp_dir" && grep " $pkg$" SHASUMS256.txt | sha256sum -c -) || { status=$?; release_install_lock; rm -rf "$tmp_dir"; return "$status"; };' ,
2322+ 'rm -rf "$node_dir" || { status=$?; release_install_lock; rm -rf "$tmp_dir"; return "$status"; };' ,
2323+ 'tar -xzf "$tmp_dir/$pkg" -C "$tool_root" || { status=$?; release_install_lock; rm -rf "$tmp_dir"; return "$status"; };' ,
2324+ 'touch "$ready_marker" || { status=$?; release_install_lock; rm -rf "$tmp_dir"; return "$status"; };' ,
2325+ 'rm -rf "$tmp_dir";' ,
2326+ "fi;" ,
2327+ "release_install_lock;" ,
2328+ "fi;" ,
2329+ "node --version >&2 || return 1;" ,
2330+ ...remotePosixJsEnvBootstrap ( ) ,
2331+ ] ;
2332+ if ( packageManager ) {
2333+ bootstrap . push (
2334+ 'export COREPACK_HOME="${COREPACK_HOME:-$tool_root/corepack}";' ,
2335+ 'export PNPM_HOME="${PNPM_HOME:-$tool_root/pnpm-home}";' ,
2336+ 'mkdir -p "$COREPACK_HOME" "$PNPM_HOME" || return 1;' ,
2337+ 'export PATH="$PNPM_HOME:$PATH";' ,
2338+ 'corepack enable --install-directory "$PNPM_HOME" || return 1;' ,
2339+ "pnpm --version >&2;" ,
2340+ "if [ -f pnpm-lock.yaml ] && [ ! -f node_modules/.modules.yaml ]; then pnpm install --frozen-lockfile || return 1; fi;" ,
2341+ ) ;
2342+ }
2343+ bootstrap . push ( 'export OPENCLAW_CRABBOX_BOOTSTRAP_PATH="$PATH";' ) ;
2344+ bootstrap . push ( "};" , "openclaw_crabbox_bootstrap_wsl2_js" ) ;
2345+ return bootstrap . join ( " " ) ;
2346+ }
2347+
22672348function scopedAwsMacosEnvCommand ( commandArgs ) {
22682349 if ( commandArgs . length <= 1 || ! isSupportedSystemEnvCommand ( commandArgs [ 0 ] ) ) {
22692350 return null ;
@@ -2280,11 +2361,7 @@ function scopedAwsMacosEnvCommand(commandArgs) {
22802361 commandWordsNeedAwsMacosPackageManager ( targetWords ) ;
22812362 const needsRuntime = jsRuntimeEntrypoints . has ( targetEntrypoint ) ;
22822363 const needsBun = awsMacosBunEntrypoints . has ( targetEntrypoint ) ;
2283- if (
2284- ! needsRuntime &&
2285- ! needsPackageManager &&
2286- ! needsBun
2287- ) {
2364+ if ( ! needsRuntime && ! needsPackageManager && ! needsBun ) {
22882365 return null ;
22892366 }
22902367
@@ -2521,6 +2598,73 @@ function readLeadingShellWord(command, start) {
25212598 return word ? { word, end : command . length } : null ;
25222599}
25232600
2601+ function remoteWsl2JsBootstrapRequirements ( commandArgs ) {
2602+ const runArgs = runCommandArgs ( commandArgs ) ;
2603+ const directScopedEnvCommand = hasOption ( commandArgs , "--shell" )
2604+ ? null
2605+ : scopedAwsMacosEnvCommand ( runArgs ) ;
2606+ const shellScopedEnvCommand =
2607+ hasOption ( commandArgs , "--shell" ) && runArgs . length === 1
2608+ ? scopedAwsMacosShellEnvCommand ( runArgs [ 0 ] )
2609+ : null ;
2610+ const scopedEnvCommand = directScopedEnvCommand ?? shellScopedEnvCommand ;
2611+ const packageManagerFallbackNeeded = scopedEnvCommand
2612+ ? commandNeedsAwsMacosPackageManager ( runArgs )
2613+ : commandNeedsAwsMacosPackageManager ( runArgs , { canShimIgnoreEnvironment : false } ) ;
2614+ const packageManagerNeeded = scopedEnvCommand ?. packageManager || packageManagerFallbackNeeded ;
2615+ const runtimeEntrypoint =
2616+ scopedEnvCommand ?. runtimeEntrypoint || commandRuntimeEntrypoint ( runArgs ) ;
2617+ const runtimeNeeded =
2618+ runtimeEntrypoint && ! awsMacosBunEntrypoints . has ( runtimeEntrypoint ) ? runtimeEntrypoint : "" ;
2619+
2620+ return {
2621+ scopedEnvCommand,
2622+ packageManager : packageManagerNeeded ,
2623+ runtimeEntrypoint : runtimeNeeded ,
2624+ } ;
2625+ }
2626+
2627+ function prepareRemoteWsl2JsBootstrapScript ( commandArgs , providerName ) {
2628+ const requirements = remoteWsl2JsBootstrapRequirements ( commandArgs ) ;
2629+ if (
2630+ ! isBrokeredWsl2RemoteTarget ( commandArgs , providerName ) ||
2631+ ( ! requirements . runtimeEntrypoint && ! requirements . packageManager )
2632+ ) {
2633+ return { args : commandArgs , cleanup : ( ) => { } , prepared : false } ;
2634+ }
2635+
2636+ const { start, optionEnd } = runCommandBounds ( commandArgs ) ;
2637+ if ( start < 0 ) {
2638+ return { args : commandArgs , cleanup : ( ) => { } , prepared : false } ;
2639+ }
2640+
2641+ const scriptRoot = mkdtempSync ( resolve ( tmpdir ( ) , "openclaw-crabbox-wsl2-script-" ) ) ;
2642+ const scriptPath = resolve ( scriptRoot , "script.sh" ) ;
2643+ const remoteCommand = commandArgs . slice ( start ) ;
2644+ const originalShellCommand =
2645+ requirements . scopedEnvCommand ?. shellCommand ??
2646+ ( hasOption ( commandArgs , "--shell" ) && remoteCommand . length === 1
2647+ ? remoteCommand [ 0 ]
2648+ : shellJoin ( remoteCommand ) ) ;
2649+ const script = `${ remoteWsl2JsBootstrap ( {
2650+ packageManager : requirements . packageManager ,
2651+ } ) } || exit $?\n{ ${ originalShellCommand } \n}\n`;
2652+ writeFileSync ( scriptPath , script , "utf8" ) ;
2653+ chmodSync ( scriptPath , 0o700 ) ;
2654+
2655+ const normalizedArgs = commandArgs . slice ( 0 , optionEnd ) ;
2656+ if ( ! hasOption ( normalizedArgs , "--no-hydrate" ) ) {
2657+ normalizedArgs . push ( "--no-hydrate" ) ;
2658+ }
2659+ normalizedArgs . push ( "--script" , scriptPath ) ;
2660+
2661+ return {
2662+ args : normalizedArgs ,
2663+ cleanup : ( ) => rmSync ( scriptRoot , { recursive : true , force : true } ) ,
2664+ prepared : true ,
2665+ } ;
2666+ }
2667+
25242668function injectRemoteAwsMacosJsBootstrap ( commandArgs , providerName ) {
25252669 const runArgs = runCommandArgs ( commandArgs ) ;
25262670 const directScopedEnvCommand = hasOption ( commandArgs , "--shell" )
@@ -2531,12 +2675,10 @@ function injectRemoteAwsMacosJsBootstrap(commandArgs, providerName) {
25312675 ? scopedAwsMacosShellEnvCommand ( runArgs [ 0 ] )
25322676 : null ;
25332677 const scopedEnvCommand = directScopedEnvCommand ?? shellScopedEnvCommand ;
2534- const packageManagerFallbackNeeded =
2535- scopedEnvCommand
2536- ? commandNeedsAwsMacosPackageManager ( runArgs )
2537- : commandNeedsAwsMacosPackageManager ( runArgs , { canShimIgnoreEnvironment : false } ) ;
2538- const packageManagerNeeded =
2539- scopedEnvCommand ?. packageManager || packageManagerFallbackNeeded ;
2678+ const packageManagerFallbackNeeded = scopedEnvCommand
2679+ ? commandNeedsAwsMacosPackageManager ( runArgs )
2680+ : commandNeedsAwsMacosPackageManager ( runArgs , { canShimIgnoreEnvironment : false } ) ;
2681+ const packageManagerNeeded = scopedEnvCommand ?. packageManager || packageManagerFallbackNeeded ;
25402682 const bunNeeded = scopedEnvCommand ?. bun || commandNeedsAwsMacosBun ( runArgs ) ;
25412683 const runtimeEntrypoint =
25422684 scopedEnvCommand ?. runtimeEntrypoint || commandRuntimeEntrypoint ( runArgs ) ;
@@ -3182,6 +3324,7 @@ let remoteChangedGateBase = "";
31823324const scriptBootstrap = prepareAwsMacosScriptStdinBootstrap ( normalizedArgs , provider ) ;
31833325normalizedArgs = scriptBootstrap . args ;
31843326const scriptStdinPrepared = scriptBootstrap . prepared ;
3327+ let wsl2ScriptBootstrap = { args : normalizedArgs , cleanup : ( ) => { } , prepared : false } ;
31853328try {
31863329 if ( shouldUseFullCheckoutForCleanRemoteSync ( normalizedArgs , provider ) ) {
31873330 const runWords = runCommandArgs ( normalizedArgs ) ;
@@ -3212,6 +3355,7 @@ function cleanupOnce() {
32123355 }
32133356 cleanupDone = true ;
32143357 stopFullCheckoutKeepalive ( ) ;
3358+ wsl2ScriptBootstrap . cleanup ( ) ;
32153359 scriptBootstrap . cleanup ( ) ;
32163360 preserveTemporaryCrabboxRuns ( ) ;
32173361 cleanupChildCwd ( ) ;
@@ -3237,6 +3381,14 @@ if (
32373381 ) ;
32383382 }
32393383}
3384+ if ( normalizedArgs [ 0 ] === "run" && isBrokeredWsl2RemoteTarget ( normalizedArgs , provider ) ) {
3385+ const wsl2Requirements = remoteWsl2JsBootstrapRequirements ( normalizedArgs ) ;
3386+ if ( wsl2Requirements . runtimeEntrypoint || wsl2Requirements . packageManager ) {
3387+ console . error (
3388+ `[crabbox] provider=${ provider } WSL2 raw boxes may lack Node/Corepack/pnpm for ${ wsl2Requirements . runtimeEntrypoint || "package-manager" } ; using no-hydrate pinned user-local JavaScript tooling before the command` ,
3389+ ) ;
3390+ }
3391+ }
32403392
32413393const childEnv = { ...process . env } ;
32423394if (
@@ -3265,11 +3417,20 @@ const remoteMarkedArgs = injectRemoteChangedGateEnvironment(normalizedArgs);
32653417const remoteMarkedNeedsAwsMacosSwift =
32663418 isAwsMacosRemoteTarget ( remoteMarkedArgs , provider ) &&
32673419 commandNeedsAwsMacosSwiftToolchain ( runCommandArgs ( remoteMarkedArgs ) ) ;
3420+ try {
3421+ wsl2ScriptBootstrap = prepareRemoteWsl2JsBootstrapScript (
3422+ childCwd === repoRoot ? remoteMarkedArgs : absolutizeLocalRunPaths ( remoteMarkedArgs ) ,
3423+ provider ,
3424+ ) ;
3425+ } catch ( error ) {
3426+ cleanupOnce ( ) ;
3427+ throw error ;
3428+ }
32683429const childArgs =
32693430 childCwd === repoRoot
32703431 ? injectRemoteWindowsHydratedNodeModulesBootstrap (
32713432 injectRemoteAwsMacosSwiftBootstrap (
3272- injectRemoteAwsMacosJsBootstrap ( remoteMarkedArgs , provider ) ,
3433+ injectRemoteAwsMacosJsBootstrap ( wsl2ScriptBootstrap . args , provider ) ,
32733434 provider ,
32743435 remoteMarkedNeedsAwsMacosSwift ,
32753436 ) ,
@@ -3278,7 +3439,7 @@ const childArgs =
32783439 : injectRemoteChangedGateGitBootstrap (
32793440 injectRemoteWindowsHydratedNodeModulesBootstrap (
32803441 injectRemoteAwsMacosSwiftBootstrap (
3281- injectRemoteAwsMacosJsBootstrap ( absolutizeLocalRunPaths ( remoteMarkedArgs ) , provider ) ,
3442+ injectRemoteAwsMacosJsBootstrap ( wsl2ScriptBootstrap . args , provider ) ,
32823443 provider ,
32833444 remoteMarkedNeedsAwsMacosSwift ,
32843445 ) ,
0 commit comments