




















@@ -53,6 +53,12 @@ OPENCLAW_EFFECTIVE_HOME="$(resolve_openclaw_effective_home)"
5353PREFIX="${OPENCLAW_PREFIX:-${HOME}/.openclaw}"
5454OPENCLAW_VERSION="${OPENCLAW_VERSION:-latest}"
5555NODE_VERSION="${OPENCLAW_NODE_VERSION:-22.22.0}"
56+NODE_VERSION_REQUESTED=0
57+if [[ -n "${OPENCLAW_NODE_VERSION:-}" ]]; then
58+ NODE_VERSION_REQUESTED=1
59+fi
60+MIN_NODE_VERSION="22.19.0"
61+APK_NODE_BIN_DIR="/usr/bin"
5662SHARP_IGNORE_GLOBAL_LIBVIPS="${SHARP_IGNORE_GLOBAL_LIBVIPS:-1}"
5763NPM_LOGLEVEL="${OPENCLAW_NPM_LOGLEVEL:-error}"
5864INSTALL_METHOD="${OPENCLAW_INSTALL_METHOD:-npm}"
@@ -215,6 +221,14 @@ ensure_git() {
215221else
216222 fail "Git missing and sudo unavailable. Install git and retry."
217223fi
224+elif command -v apk >/dev/null 2>&1; then
225+if is_root; then
226+ apk add --no-cache git
227+elif has_sudo; then
228+ sudo apk add --no-cache git
229+else
230+ fail "Git missing and sudo unavailable. Install git and retry."
231+fi
218232else
219233 fail "Git missing and package manager not found. Install git and retry."
220234fi
@@ -252,6 +266,7 @@ parse_args() {
252266 ;;
253267 --node-version)
254268 NODE_VERSION="$2"
269+ NODE_VERSION_REQUESTED=1
255270shift 2
256271 ;;
257272 --install-method|--method)
@@ -329,6 +344,177 @@ npm_bin() {
329344echo "$(node_dir)/bin/npm"
330345}
331346347+command_path_without_node_prefix() {
348+local name="$1"
349+local path_entry
350+local prefix_bin
351+local filtered_path=""
352+local separator=""
353+local -a path_entries=()
354+355+ prefix_bin="$(node_dir)/bin"
356+ IFS=: read -r -a path_entries <<<"$PATH"
357+for path_entry in "${path_entries[@]}"; do
358+if [[ "$path_entry" == "$prefix_bin" ]]; then
359+continue
360+fi
361+ filtered_path="${filtered_path}${separator}${path_entry}"
362+ separator=":"
363+done
364+365+ PATH="$filtered_path" command -v "$name" 2>/dev/null
366+}
367+368+is_musl_linux() {
369+if [[ "$(os_detect)" != "linux" ]]; then
370+return 1
371+fi
372+if [[ -f /etc/alpine-release ]]; then
373+return 0
374+fi
375+ ldd --version 2>&1 | grep -qi musl
376+}
377+378+link_node_runtime_paths() {
379+local node_path="$1"
380+local npm_path="$2"
381+local dir
382+local runtime_bin
383+local resolved
384+ dir="$(node_dir)"
385+ runtime_bin="${node_path%/*}"
386+387+ mkdir -p "${dir}/bin" "${PREFIX}/tools"
388+ ln -sfn "$node_path" "${dir}/bin/node"
389+ ln -sfn "$npm_path" "${dir}/bin/npm"
390+for name in npx corepack; do
391+if [[ -x "${runtime_bin}/${name}" ]]; then
392+ ln -sfn "${runtime_bin}/${name}" "${dir}/bin/${name}"
393+continue
394+fi
395+ resolved="$(command_path_without_node_prefix "$name" || true)"
396+if [[ -n "$resolved" && "$resolved" != "${dir}/bin/${name}" ]]; then
397+ ln -sfn "$resolved" "${dir}/bin/${name}"
398+fi
399+done
400+ ln -sfn "$dir" "${PREFIX}/tools/node"
401+}
402+403+linked_node_is_usable() {
404+local current_version
405+local required_version
406+407+if [[ ! -x "$(node_bin)" || ! -x "$(npm_bin)" ]]; then
408+return 1
409+fi
410+411+ current_version="$("$(node_bin)" -v 2>/dev/null || echo "")"
412+ required_version="$(required_node_version)"
413+if ! semver_at_least "$current_version" "$required_version"; then
414+return 1
415+fi
416+417+"$(node_bin)" -e "require('node:sqlite')" >/dev/null 2>&1
418+}
419+420+semver_at_least() {
421+local version="${1#v}"
422+local required="${2#v}"
423+local version_major version_minor version_patch
424+local required_major required_minor required_patch
425+426+ IFS=. read -r version_major version_minor version_patch <<<"$version"
427+ IFS=. read -r required_major required_minor required_patch <<<"$required"
428+ version_minor="${version_minor:-0}"
429+ version_patch="${version_patch:-0}"
430+ required_minor="${required_minor:-0}"
431+ required_patch="${required_patch:-0}"
432+433+for part in "$version_major" "$version_minor" "$version_patch" "$required_major" "$required_minor" "$required_patch"; do
434+if [[ ! "$part" =~ ^[0-9]+$ ]]; then
435+return 1
436+fi
437+done
438+439+if ((version_major != required_major)); then
440+((version_major > required_major))
441+return
442+fi
443+if ((version_minor != required_minor)); then
444+((version_minor > required_minor))
445+return
446+fi
447+((version_patch >= required_patch))
448+}
449+450+required_node_version() {
451+if [[ "$NODE_VERSION_REQUESTED" == "1" ]] && semver_at_least "$NODE_VERSION" "$MIN_NODE_VERSION"; then
452+printf '%s\n' "$NODE_VERSION"
453+return
454+fi
455+printf '%s\n' "$MIN_NODE_VERSION"
456+}
457+458+try_link_usable_node_runtime_from_path() {
459+local path_entry
460+local prefix_bin
461+local -a path_entries=()
462+463+ prefix_bin="$(node_dir)/bin"
464+ IFS=: read -r -a path_entries <<<"$PATH"
465+for path_entry in "${path_entries[@]}"; do
466+if [[ -z "$path_entry" ]]; then
467+ path_entry="."
468+fi
469+if [[ "$path_entry" == "$prefix_bin" ]]; then
470+continue
471+fi
472+if [[ -x "${path_entry}/node" && -x "${path_entry}/npm" ]]; then
473+ link_node_runtime_paths "${path_entry}/node" "${path_entry}/npm"
474+if linked_node_is_usable; then
475+return 0
476+fi
477+fi
478+done
479+return 1
480+}
481+482+install_alpine_node() {
483+local installed_version
484+local required_version
485+486+ emit_json "{\"event\":\"step\",\"name\":\"node\",\"status\":\"start\",\"method\":\"apk\"}"
487+if try_link_usable_node_runtime_from_path; then
488+ installed_version="$("$(node_bin)" -v 2>/dev/null || echo unknown)"
489+ emit_json "{\"event\":\"step\",\"name\":\"node\",\"status\":\"ok\",\"method\":\"system\",\"version\":\"${installed_version}\"}"
490+return
491+fi
492+493+ log "Installing Node via apk (Alpine Linux detected)..."
494+if is_root; then
495+ apk add --no-cache nodejs npm
496+elif has_sudo; then
497+ sudo apk add --no-cache nodejs npm
498+else
499+ fail "Alpine Linux detected, but Node musl tarballs are unavailable and sudo is unavailable. Install nodejs and npm with apk, then retry."
500+fi
501+502+if [[ -x "${APK_NODE_BIN_DIR}/node" && -x "${APK_NODE_BIN_DIR}/npm" ]]; then
503+ link_node_runtime_paths "${APK_NODE_BIN_DIR}/node" "${APK_NODE_BIN_DIR}/npm"
504+elif ! try_link_usable_node_runtime_from_path; then
505+ fail "apk Node install failed. Install nodejs and npm manually, then retry."
506+fi
507+508+if ! linked_node_is_usable; then
509+ installed_version="$("$(node_bin)" -v 2>/dev/null || echo unknown)"
510+ required_version="$(required_node_version)"
511+ fail "Alpine Node package must provide Node >= ${required_version} with node:sqlite; found ${installed_version}."
512+fi
513+514+ installed_version="$("$(node_bin)" -v 2>/dev/null || echo unknown)"
515+ emit_json "{\"event\":\"step\",\"name\":\"node\",\"status\":\"ok\",\"method\":\"apk\",\"version\":\"${installed_version}\"}"
516+}
517+332518set_pnpm_cmd() {
333519 PNPM_CMD=("$@")
334520}
@@ -560,6 +746,11 @@ install_node() {
560746 arch="$(arch_detect)"
561747 dir="$(node_dir)"
562748749+if [[ "$os" == "linux" ]] && command -v apk >/dev/null 2>&1 && is_musl_linux; then
750+ install_alpine_node
751+return
752+fi
753+563754if [[ -x "$(node_bin)" ]]; then
564755 current_major="$("$(node_bin)" -v 2>/dev/null | tr -d 'v' | cut -d'.' -f1 || echo "")"
565756if [[ -n "$current_major" && "$current_major" -ge 22 ]]; then
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。