jishushell 0.4.2 → 0.4.17
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Dockerfile.openclaw-slim +58 -0
- package/INSTALL-NOTICE +45 -0
- package/dist/auth.js +3 -3
- package/dist/auth.js.map +1 -1
- package/dist/cli/app.d.ts +3 -0
- package/dist/cli/app.js +156 -0
- package/dist/cli/app.js.map +1 -0
- package/dist/{doctor.d.ts → cli/doctor.d.ts} +6 -1
- package/dist/{doctor.js → cli/doctor.js} +389 -27
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/helpers.d.ts +4 -0
- package/dist/cli/helpers.js +32 -0
- package/dist/cli/helpers.js.map +1 -0
- package/dist/cli/job.d.ts +3 -0
- package/dist/cli/job.js +260 -0
- package/dist/cli/job.js.map +1 -0
- package/dist/cli/llm.d.ts +24 -0
- package/dist/cli/llm.js +593 -0
- package/dist/cli/llm.js.map +1 -0
- package/dist/cli/openclaw.d.ts +12 -0
- package/dist/cli/openclaw.js +156 -0
- package/dist/cli/openclaw.js.map +1 -0
- package/dist/cli/panel.d.ts +25 -0
- package/dist/cli/panel.js +734 -0
- package/dist/cli/panel.js.map +1 -0
- package/dist/cli.js +476 -219
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +22 -4
- package/dist/config.js +96 -55
- package/dist/config.js.map +1 -1
- package/dist/control.d.ts +13 -41
- package/dist/control.js +12 -1355
- package/dist/control.js.map +1 -1
- package/dist/install.d.ts +1 -1
- package/dist/install.js +15 -29
- package/dist/install.js.map +1 -1
- package/dist/routes/apps.d.ts +3 -0
- package/dist/routes/apps.js +99 -0
- package/dist/routes/apps.js.map +1 -0
- package/dist/routes/backup.d.ts +2 -0
- package/dist/routes/backup.js +370 -0
- package/dist/routes/backup.js.map +1 -0
- package/dist/routes/instances.d.ts +1 -0
- package/dist/routes/instances.js +61 -15
- package/dist/routes/instances.js.map +1 -1
- package/dist/routes/llm.d.ts +15 -0
- package/dist/routes/llm.js +246 -0
- package/dist/routes/llm.js.map +1 -0
- package/dist/routes/setup.js +32 -7
- package/dist/routes/setup.js.map +1 -1
- package/dist/routes/system.js +31 -6
- package/dist/routes/system.js.map +1 -1
- package/dist/server.js +69 -5
- package/dist/server.js.map +1 -1
- package/dist/services/app-compiler.d.ts +15 -0
- package/dist/services/app-compiler.js +169 -0
- package/dist/services/app-compiler.js.map +1 -0
- package/dist/services/app-manager.d.ts +17 -0
- package/dist/services/app-manager.js +168 -0
- package/dist/services/app-manager.js.map +1 -0
- package/dist/services/backup-manager.d.ts +253 -0
- package/dist/services/backup-manager.js +2014 -0
- package/dist/services/backup-manager.js.map +1 -0
- package/dist/services/backup-verify.d.ts +26 -0
- package/dist/services/backup-verify.js +240 -0
- package/dist/services/backup-verify.js.map +1 -0
- package/dist/services/instance-manager.d.ts +73 -5
- package/dist/services/instance-manager.js +446 -74
- package/dist/services/instance-manager.js.map +1 -1
- package/dist/services/job-manager.d.ts +22 -0
- package/dist/services/job-manager.js +102 -0
- package/dist/services/job-manager.js.map +1 -0
- package/dist/services/llm-proxy/adapters.js +5 -1
- package/dist/services/llm-proxy/adapters.js.map +1 -1
- package/dist/services/llm-proxy/index.d.ts +30 -0
- package/dist/services/llm-proxy/index.js +71 -1
- package/dist/services/llm-proxy/index.js.map +1 -1
- package/dist/services/llm-proxy/ssrf.js +1 -1
- package/dist/services/llm-proxy/ssrf.js.map +1 -1
- package/dist/services/nomad-manager.js +263 -159
- package/dist/services/nomad-manager.js.map +1 -1
- package/dist/services/panel-manager.d.ts +40 -0
- package/dist/services/panel-manager.js +346 -0
- package/dist/services/panel-manager.js.map +1 -0
- package/dist/services/process-manager.js +24 -10
- package/dist/services/process-manager.js.map +1 -1
- package/dist/services/setup-manager.d.ts +4 -2
- package/dist/services/setup-manager.js +578 -154
- package/dist/services/setup-manager.js.map +1 -1
- package/dist/services/telemetry/activation.js +10 -7
- package/dist/services/telemetry/activation.js.map +1 -1
- package/dist/services/telemetry/client.js +7 -18
- package/dist/services/telemetry/client.js.map +1 -1
- package/dist/services/telemetry/heartbeat.js +12 -6
- package/dist/services/telemetry/heartbeat.js.map +1 -1
- package/dist/services/update-manager.d.ts +47 -0
- package/dist/services/update-manager.js +305 -0
- package/dist/services/update-manager.js.map +1 -0
- package/dist/types.d.ts +62 -0
- package/dist/utils/fs.d.ts +85 -0
- package/dist/utils/fs.js +111 -0
- package/dist/utils/fs.js.map +1 -0
- package/dist/utils/safe-json.d.ts +2 -0
- package/dist/utils/safe-json.js +22 -16
- package/dist/utils/safe-json.js.map +1 -1
- package/install/jishu-install.sh +582 -138
- package/install/jishu-uninstall.sh +276 -391
- package/install/post-install.sh +85 -3
- package/openclaw-entry.sh +15 -0
- package/package.json +12 -5
- package/public/assets/Dashboard-CQsp1Mr9.js +1 -0
- package/public/assets/InitPassword-BEC8SE4A.js +1 -0
- package/public/assets/InstanceDetail-B5wTgNEg.js +17 -0
- package/public/assets/{Login-RkjzTNWg.js → Login-D1Bt-Lyk.js} +1 -1
- package/public/assets/NewInstance-GQzm3K9D.js +1 -0
- package/public/assets/Settings-ByjGlqhP.js +1 -0
- package/public/assets/Setup-cMF21Y-8.js +1 -0
- package/public/assets/index-B6qQP4mH.css +1 -0
- package/public/assets/index-BuTQtuNy.js +16 -0
- package/public/assets/logo-black-theme-DywLAtFy.png +0 -0
- package/public/assets/logo-white-theme-DXffFAWw.png +0 -0
- package/public/assets/{providers-lBSOjUWy.js → providers-V-vwrExZ.js} +1 -1
- package/public/assets/{usePolling-CqQ8hrNc.js → usePolling-CK0DfI4h.js} +1 -1
- package/public/assets/{vendor-i18n-Bvxxh8Di.js → vendor-i18n-CfW0RvgE.js} +1 -1
- package/public/assets/vendor-react-B1-3Yrt-.js +59 -0
- package/public/index.html +4 -4
- package/dist/doctor.js.map +0 -1
- package/public/assets/Dashboard-CAOQDYDR.js +0 -1
- package/public/assets/InitPassword-CkehIkJG.js +0 -1
- package/public/assets/InstanceDetail-CzW2S95J.js +0 -14
- package/public/assets/NewInstance-DdbErdjA.js +0 -1
- package/public/assets/Settings-BUD7zwv9.js +0 -1
- package/public/assets/Setup-RRTIERGG.js +0 -1
- package/public/assets/index-77Ug7feY.css +0 -1
- package/public/assets/index-DfRnVUQR.js +0 -16
- package/public/assets/vendor-react-DONn7uBV.js +0 -59
package/install/jishu-install.sh
CHANGED
|
@@ -118,7 +118,11 @@ is_promptable() {
|
|
|
118
118
|
if [[ "${NO_PROMPT:-0}" == "1" ]]; then
|
|
119
119
|
return 1
|
|
120
120
|
fi
|
|
121
|
-
|
|
121
|
+
# Web-triggered upgrades never have an interactive TTY
|
|
122
|
+
if [[ "${JISHUSHELL_WEB_UPDATE:-0}" == "1" ]]; then
|
|
123
|
+
return 1
|
|
124
|
+
fi
|
|
125
|
+
if ( : <> /dev/tty ) 2>/dev/null; then
|
|
122
126
|
return 0
|
|
123
127
|
fi
|
|
124
128
|
return 1
|
|
@@ -161,7 +165,7 @@ JISHU_SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd 2>/dev/null |
|
|
|
161
165
|
# ──── BEGIN VERSIONS ────
|
|
162
166
|
NODE_VERSION="${JISHU_NODE_VERSION:-22}"
|
|
163
167
|
NVM_VERSION="${JISHU_NVM_VERSION:-0.40.4}"
|
|
164
|
-
NOMAD_VERSION="${JISHU_NOMAD_VERSION:-1.
|
|
168
|
+
NOMAD_VERSION="${JISHU_NOMAD_VERSION:-1.6.5}"
|
|
165
169
|
JISHUSHELL_PORT="${JISHUSHELL_PORT:-8090}"
|
|
166
170
|
|
|
167
171
|
# ──── NPM Registry Configuration ────
|
|
@@ -223,8 +227,13 @@ SKIP_NOMAD="${SKIP_NOMAD:-0}"
|
|
|
223
227
|
SKIP_OPENCLAW="${SKIP_OPENCLAW:-0}" # default=0 (install); use --skip 4 or --skip-openclaw to skip
|
|
224
228
|
SKIP_JISHUSHELL="${SKIP_JISHUSHELL:-0}" # 1=skip install_jishushell
|
|
225
229
|
SKIP_JISHUSHELL_SERVICE="${SKIP_JISHUSHELL_SERVICE:-0}" # 1=skip service registration
|
|
230
|
+
JISHUSHELL_NPM_VERSION="${JISHUSHELL_NPM_VERSION:-latest}" # jishushell npm package version
|
|
231
|
+
JISHUSHELL_VERSION_OVERRIDE=0
|
|
232
|
+
if [[ "${JISHUSHELL_NPM_VERSION}" != "latest" ]]; then
|
|
233
|
+
JISHUSHELL_VERSION_OVERRIDE=1
|
|
234
|
+
fi
|
|
226
235
|
OPENCLAW_NPM_VERSION="${OPENCLAW_NPM_VERSION:-latest}" # openclaw npm package version
|
|
227
|
-
OPENCLAW_DOCKER_TAG="${OPENCLAW_DOCKER_TAG:-
|
|
236
|
+
OPENCLAW_DOCKER_TAG="${OPENCLAW_DOCKER_TAG:-ghcr.io/x-aijishu/openclaw-runtime:latest}" # pre-built image from registry
|
|
228
237
|
OPENCLAW_IMAGE="" # set dynamically after pull/build
|
|
229
238
|
AUTO_YES="${AUTO_YES:-0}"
|
|
230
239
|
DOCKER_CMD_PREFIX="" # Set to "sg docker -c" when group activated via sg
|
|
@@ -405,18 +414,33 @@ detect_os() {
|
|
|
405
414
|
ui_success "OS: ${OS_NAME} (package manager: ${PKG_MANAGER})"
|
|
406
415
|
}
|
|
407
416
|
|
|
408
|
-
# Detect CPU architecture. Sets: ARCH (
|
|
417
|
+
# Detect CPU architecture. Sets: ARCH (arm64)
|
|
418
|
+
# Only Arm-family (aarch64, arm64, armv7l) and Apple Silicon (Darwin/arm64)
|
|
419
|
+
# are supported. x86_64, i686, riscv, mips, s390x, ppc, etc. are rejected.
|
|
409
420
|
detect_arch() {
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
aarch64|arm64)
|
|
421
|
+
local raw_arch
|
|
422
|
+
raw_arch="$(uname -m)"
|
|
423
|
+
case "$raw_arch" in
|
|
424
|
+
aarch64|arm64)
|
|
425
|
+
ARCH="arm64"
|
|
426
|
+
;;
|
|
427
|
+
armv7l|armv8l|armhf)
|
|
428
|
+
# 32-bit Arm — may work but not officially tested
|
|
429
|
+
ARCH="arm64"
|
|
430
|
+
ui_warn "32-bit Arm detected (${raw_arch}). 64-bit OS on a 64-bit board is strongly recommended."
|
|
431
|
+
;;
|
|
414
432
|
*)
|
|
415
|
-
ui_error "Unsupported CPU architecture: $
|
|
433
|
+
ui_error "Unsupported CPU architecture: ${raw_arch}"
|
|
434
|
+
ui_error ""
|
|
435
|
+
ui_error "JishuShell runs exclusively on Arm-based devices (aarch64 / arm64)."
|
|
436
|
+
ui_error "Supported examples: Raspberry Pi 4/5, Orange Pi 5, Jetson Orin,"
|
|
437
|
+
ui_error " Rockchip RK3588, Apple Silicon Mac (arm64 macOS)."
|
|
438
|
+
ui_error ""
|
|
439
|
+
ui_error "x86_64 / i686 / RISC-V / MIPS / s390x / PowerPC are not supported."
|
|
416
440
|
exit 1
|
|
417
441
|
;;
|
|
418
442
|
esac
|
|
419
|
-
ui_success "Architecture: ${ARCH}"
|
|
443
|
+
ui_success "Architecture: ${ARCH} (${raw_arch})"
|
|
420
444
|
}
|
|
421
445
|
|
|
422
446
|
# Verify sudo access. Sets: SUDO ("" if root, "sudo" otherwise)
|
|
@@ -433,6 +457,10 @@ check_sudo() {
|
|
|
433
457
|
|
|
434
458
|
if ! sudo -n true 2>/dev/null; then
|
|
435
459
|
ui_info "Some steps require sudo — you may be prompted for your password."
|
|
460
|
+
if ! is_promptable; then
|
|
461
|
+
ui_error "Failed to obtain sudo privileges (no interactive TTY available)"
|
|
462
|
+
exit 1
|
|
463
|
+
fi
|
|
436
464
|
if ! sudo -v </dev/tty; then
|
|
437
465
|
ui_error "Failed to obtain sudo privileges"
|
|
438
466
|
exit 1
|
|
@@ -446,7 +474,7 @@ check_sudo() {
|
|
|
446
474
|
# This ensures 'sudo docker' works even after a long Docker install step
|
|
447
475
|
# without prompting for the password again.
|
|
448
476
|
if [[ -z "${_SUDO_KEEPALIVE_PID:-}" ]]; then
|
|
449
|
-
( while true; do sudo -n true 2>/dev/null; sleep 60; done ) &
|
|
477
|
+
( while true; do sudo -n true 2>/dev/null; sleep 60; done ) &>/dev/null &
|
|
450
478
|
_SUDO_KEEPALIVE_PID=$!
|
|
451
479
|
disown "$_SUDO_KEEPALIVE_PID" 2>/dev/null || true
|
|
452
480
|
fi
|
|
@@ -925,6 +953,41 @@ _ensure_nvm_shell_config() {
|
|
|
925
953
|
install_docker() {
|
|
926
954
|
ui_stage "Docker"
|
|
927
955
|
|
|
956
|
+
# ── macOS: use private Colima instance ─────────────────────────────────────
|
|
957
|
+
if [[ "$OS" == "macos" ]]; then
|
|
958
|
+
local need_brew=0
|
|
959
|
+
local need_profile=0
|
|
960
|
+
|
|
961
|
+
if ! command -v docker &>/dev/null || ! command -v colima &>/dev/null; then
|
|
962
|
+
need_brew=1
|
|
963
|
+
need_profile=1
|
|
964
|
+
elif ! _colima list 2>/dev/null | grep -q "${_COLIMA_PROFILE}"; then
|
|
965
|
+
need_profile=1
|
|
966
|
+
fi
|
|
967
|
+
|
|
968
|
+
if [[ $need_brew -eq 1 ]]; then
|
|
969
|
+
if ! _do_install_docker; then
|
|
970
|
+
ui_error "Colima installation failed"
|
|
971
|
+
return 1
|
|
972
|
+
fi
|
|
973
|
+
elif [[ $need_profile -eq 1 ]]; then
|
|
974
|
+
ui_info "Starting Colima VM (profile: ${_COLIMA_PROFILE})..."
|
|
975
|
+
mkdir -p "${_COLIMA_HOME}"
|
|
976
|
+
_colima start "${_COLIMA_PROFILE}" \
|
|
977
|
+
--vm-type vz --mount-type virtiofs --network-address \
|
|
978
|
+
--activate=false --cpu 2 --memory 4 --disk 60 >/dev/null \
|
|
979
|
+
|| { ui_warn "colima start failed — run 'COLIMA_HOME=${_COLIMA_HOME} colima start ${_COLIMA_PROFILE}' manually"; return 1; }
|
|
980
|
+
export DOCKER_HOST="unix://${_COLIMA_SOCKET}"
|
|
981
|
+
ui_success "Colima is running"
|
|
982
|
+
else
|
|
983
|
+
ui_success "Docker and Colima already configured"
|
|
984
|
+
fi
|
|
985
|
+
|
|
986
|
+
_ensure_docker_running
|
|
987
|
+
return 0
|
|
988
|
+
fi
|
|
989
|
+
|
|
990
|
+
# ── Linux: standard Docker Engine ──────────────────────────────────────────
|
|
928
991
|
local need_install_docker=0
|
|
929
992
|
local need_install_compose=0
|
|
930
993
|
|
|
@@ -948,8 +1011,6 @@ install_docker() {
|
|
|
948
1011
|
:
|
|
949
1012
|
elif command -v docker-compose &>/dev/null; then
|
|
950
1013
|
:
|
|
951
|
-
elif [[ "$OS" == "macos" ]]; then
|
|
952
|
-
:
|
|
953
1014
|
else
|
|
954
1015
|
need_install_compose=1
|
|
955
1016
|
fi
|
|
@@ -962,8 +1023,6 @@ install_docker() {
|
|
|
962
1023
|
|
|
963
1024
|
if [[ $need_install_docker -eq 1 ]]; then
|
|
964
1025
|
ui_info "Docker not found — installing..."
|
|
965
|
-
# _do_install_docker installs docker-compose-plugin in the same apt command,
|
|
966
|
-
# so Compose V2 will be available immediately after — no separate step needed.
|
|
967
1026
|
if ! _do_install_docker; then
|
|
968
1027
|
ui_warn "Official Docker install script failed — trying system package manager fallback..."
|
|
969
1028
|
if ! _do_install_docker_apt_fallback; then
|
|
@@ -971,7 +1030,6 @@ install_docker() {
|
|
|
971
1030
|
return 1
|
|
972
1031
|
fi
|
|
973
1032
|
fi
|
|
974
|
-
# Compose is bundled; skip the separate install step
|
|
975
1033
|
need_install_compose=0
|
|
976
1034
|
fi
|
|
977
1035
|
|
|
@@ -995,13 +1053,23 @@ _do_install_docker() {
|
|
|
995
1053
|
fi
|
|
996
1054
|
|
|
997
1055
|
if [[ "$OS" == "macos" ]]; then
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1056
|
+
ui_info "Installing docker and colima via Homebrew..."
|
|
1057
|
+
if ! command -v brew &>/dev/null; then
|
|
1058
|
+
ui_warn "Homebrew not found. Install it from https://brew.sh then re-run this script."
|
|
1059
|
+
return 1
|
|
1002
1060
|
fi
|
|
1003
|
-
|
|
1004
|
-
|
|
1061
|
+
brew install -q docker colima || { ui_warn "brew install failed"; return 1; }
|
|
1062
|
+
ui_success "docker and colima installed"
|
|
1063
|
+
|
|
1064
|
+
mkdir -p "${_COLIMA_HOME}"
|
|
1065
|
+
ui_info "Starting Colima VM (profile: ${_COLIMA_PROFILE})..."
|
|
1066
|
+
_colima start "${_COLIMA_PROFILE}" \
|
|
1067
|
+
--vm-type vz --mount-type virtiofs --network-address \
|
|
1068
|
+
--activate=false --cpu 2 --memory 4 --disk 60 >/dev/null \
|
|
1069
|
+
|| { ui_warn "colima start failed — run 'COLIMA_HOME=${_COLIMA_HOME} colima start ${_COLIMA_PROFILE}' manually"; return 1; }
|
|
1070
|
+
ui_success "Colima is running"
|
|
1071
|
+
export DOCKER_HOST="unix://${_COLIMA_SOCKET}"
|
|
1072
|
+
return 0
|
|
1005
1073
|
fi
|
|
1006
1074
|
|
|
1007
1075
|
# Step 1: download
|
|
@@ -1096,7 +1164,7 @@ _do_install_docker_apt_fallback() {
|
|
|
1096
1164
|
fi
|
|
1097
1165
|
|
|
1098
1166
|
if [[ "$OS" == "macos" ]]; then
|
|
1099
|
-
ui_warn "No apt/dnf fallback available on macOS —
|
|
1167
|
+
ui_warn "No apt/dnf fallback available on macOS — install via Homebrew: brew install docker colima"
|
|
1100
1168
|
return 1
|
|
1101
1169
|
fi
|
|
1102
1170
|
|
|
@@ -1157,17 +1225,30 @@ _ensure_docker_running() {
|
|
|
1157
1225
|
fi
|
|
1158
1226
|
|
|
1159
1227
|
if [[ "$OS" == "macos" ]]; then
|
|
1228
|
+
export DOCKER_HOST="unix://${_COLIMA_SOCKET}"
|
|
1229
|
+
if ! docker info &>/dev/null 2>&1; then
|
|
1230
|
+
ui_info "Starting Colima VM..."
|
|
1231
|
+
mkdir -p "${_COLIMA_HOME}"
|
|
1232
|
+
if ! _colima start "${_COLIMA_PROFILE}" \
|
|
1233
|
+
--vm-type vz --mount-type virtiofs --network-address \
|
|
1234
|
+
--activate=false --cpu 2 --memory 4 --disk 60 >/dev/null; then
|
|
1235
|
+
ui_warn "colima start failed"
|
|
1236
|
+
ui_info "Run manually: COLIMA_HOME=${_COLIMA_HOME} colima start ${_COLIMA_PROFILE}"
|
|
1237
|
+
return 1
|
|
1238
|
+
fi
|
|
1239
|
+
fi
|
|
1160
1240
|
local waited=0
|
|
1241
|
+
local timeout=120
|
|
1161
1242
|
while ! docker info &>/dev/null 2>&1; do
|
|
1162
|
-
if [[ $waited -ge
|
|
1163
|
-
ui_warn "Docker daemon did not become ready within
|
|
1164
|
-
ui_info "
|
|
1243
|
+
if [[ $waited -ge $timeout ]]; then
|
|
1244
|
+
ui_warn "Docker daemon did not become ready within ${timeout} seconds"
|
|
1245
|
+
ui_info "Run: COLIMA_HOME=${_COLIMA_HOME} colima status ${_COLIMA_PROFILE}"
|
|
1165
1246
|
return 1
|
|
1166
1247
|
fi
|
|
1167
|
-
sleep
|
|
1168
|
-
(( waited
|
|
1248
|
+
sleep 2
|
|
1249
|
+
(( waited += 2 )) || true
|
|
1169
1250
|
done
|
|
1170
|
-
|
|
1251
|
+
ui_success "Docker daemon is ready"
|
|
1171
1252
|
return 0
|
|
1172
1253
|
fi
|
|
1173
1254
|
|
|
@@ -1303,6 +1384,21 @@ docker_exec() {
|
|
|
1303
1384
|
fi
|
|
1304
1385
|
}
|
|
1305
1386
|
|
|
1387
|
+
# Private Colima wrapper — runs colima with COLIMA_HOME scoped to JishuShell's
|
|
1388
|
+
# data directory so the VM, socket, and all state are fully isolated from any
|
|
1389
|
+
# user-level Docker Desktop or default Colima installation.
|
|
1390
|
+
#
|
|
1391
|
+
# Usage: _colima start jishushell --vm-type vz ...
|
|
1392
|
+
# _colima stop jishushell
|
|
1393
|
+
# _colima status jishushell
|
|
1394
|
+
_COLIMA_HOME="${JISHUSHELL_HOME}/colima"
|
|
1395
|
+
_COLIMA_PROFILE="jishushell"
|
|
1396
|
+
_COLIMA_SOCKET="${_COLIMA_HOME}/${_COLIMA_PROFILE}/docker.sock"
|
|
1397
|
+
|
|
1398
|
+
_colima() {
|
|
1399
|
+
COLIMA_HOME="${_COLIMA_HOME}" command colima "$@"
|
|
1400
|
+
}
|
|
1401
|
+
|
|
1306
1402
|
# ─── 3. Nomad ────────────────────────────────────────────────────────────────
|
|
1307
1403
|
|
|
1308
1404
|
install_nomad() {
|
|
@@ -1330,12 +1426,24 @@ install_nomad() {
|
|
|
1330
1426
|
if [[ -z "$current_version" ]]; then
|
|
1331
1427
|
ui_warn "Nomad at ${local_bin} is not functional (wrong arch or corrupt) — reinstalling..."
|
|
1332
1428
|
rm -f "$local_bin"
|
|
1429
|
+
elif [[ "$current_version" == "$NOMAD_VERSION" ]]; then
|
|
1430
|
+
ui_success "Nomad already at target version: v${current_version} → ${local_bin}"
|
|
1431
|
+
_ensure_jishushell_bin_in_path
|
|
1432
|
+
return 0
|
|
1333
1433
|
elif version_gte "$current_version" "$NOMAD_VERSION"; then
|
|
1334
|
-
|
|
1434
|
+
# current > target (the == case was handled above). JishuShell
|
|
1435
|
+
# pins Nomad to a specific version on purpose (license downgrade
|
|
1436
|
+
# from BSL 1.1 to MPL 2.0). Raft state is not backward compatible
|
|
1437
|
+
# across the jump, so we auto-migrate: download + verify the new
|
|
1438
|
+
# binary first (safe-first), then stop services, back up the old
|
|
1439
|
+
# data_dir, wipe it, clean orphaned containers, and swap the
|
|
1440
|
+
# binary. JishuShell re-bootstraps ACL and resubmits jobs from
|
|
1441
|
+
# on-disk instance configs on the next start.
|
|
1442
|
+
_migrate_nomad_to_target "$current_version" || return 1
|
|
1335
1443
|
_ensure_jishushell_bin_in_path
|
|
1336
1444
|
return 0
|
|
1337
1445
|
else
|
|
1338
|
-
ui_warn "Nomad version too old: v${current_version} (need
|
|
1446
|
+
ui_warn "Nomad version too old: v${current_version} (need v${NOMAD_VERSION}) — upgrading..."
|
|
1339
1447
|
rm -f "$local_bin"
|
|
1340
1448
|
fi
|
|
1341
1449
|
fi
|
|
@@ -1421,6 +1529,180 @@ REPO
|
|
|
1421
1529
|
return 1
|
|
1422
1530
|
}
|
|
1423
1531
|
|
|
1532
|
+
# Auto-migrate from a higher Nomad version (e.g. 1.11.3 BSL) back to the
|
|
1533
|
+
# jishushell target (1.6.5 MPL). Called when install_nomad detects a local
|
|
1534
|
+
# binary whose semver is > NOMAD_VERSION. The migration is destructive to
|
|
1535
|
+
# Nomad's raft state (schema is not backward compatible) but preserves
|
|
1536
|
+
# instance configs under ~/.jishushell/instances/*, which is what jishushell
|
|
1537
|
+
# uses to resubmit jobs after reboot. A single tar.gz snapshot of the old
|
|
1538
|
+
# data_dir is kept under ~/.jishushell/nomad/backups/ for forensic inspection
|
|
1539
|
+
# — it is not a user-recovery mechanism (the schema can't be replayed).
|
|
1540
|
+
_migrate_nomad_to_target() {
|
|
1541
|
+
local current_version="$1"
|
|
1542
|
+
local local_bin="${JISHUSHELL_BIN_DIR}/nomad"
|
|
1543
|
+
|
|
1544
|
+
if [[ "$DRY_RUN" == "1" ]]; then
|
|
1545
|
+
ui_info "[dry-run] Would migrate Nomad v${current_version} → v${NOMAD_VERSION}:"
|
|
1546
|
+
ui_info "[dry-run] 1. Stage + verify new binary in /tmp"
|
|
1547
|
+
ui_info "[dry-run] 2. Stop jishushell + nomad services"
|
|
1548
|
+
ui_info "[dry-run] 3. Tar backup ${JISHUSHELL_HOME}/nomad/data → nomad/backups/data-<ts>.tar.gz"
|
|
1549
|
+
ui_info "[dry-run] 4. Wipe raft state + nomad.env files (schema incompatible)"
|
|
1550
|
+
ui_info "[dry-run] 5. Remove orphaned gateway-<alloc> containers"
|
|
1551
|
+
ui_info "[dry-run] 6. Swap binary into ${local_bin}"
|
|
1552
|
+
return 0
|
|
1553
|
+
fi
|
|
1554
|
+
|
|
1555
|
+
ui_warn "Nomad v${current_version} > target v${NOMAD_VERSION} — auto-migrating (BSL → MPL)..."
|
|
1556
|
+
ui_info " Raft state is not backward-compatible; allocation history will be reset."
|
|
1557
|
+
ui_info " Instance configs under ${JISHUSHELL_HOME}/instances/ are preserved."
|
|
1558
|
+
|
|
1559
|
+
# ── Stage 1: download + verify new binary before touching anything ────
|
|
1560
|
+
local stage_dir
|
|
1561
|
+
stage_dir="$(mktemp -d)" || { ui_error "mktemp failed"; return 1; }
|
|
1562
|
+
# shellcheck disable=SC2064
|
|
1563
|
+
trap "rm -rf '$stage_dir'" RETURN
|
|
1564
|
+
|
|
1565
|
+
local platform
|
|
1566
|
+
platform="$(uname -s | tr '[:upper:]' '[:lower:]')"
|
|
1567
|
+
local download_url="https://releases.hashicorp.com/nomad/${NOMAD_VERSION}/nomad_${NOMAD_VERSION}_${platform}_${ARCH}.zip"
|
|
1568
|
+
local sums_url="https://releases.hashicorp.com/nomad/${NOMAD_VERSION}/nomad_${NOMAD_VERSION}_SHA256SUMS"
|
|
1569
|
+
|
|
1570
|
+
ui_info "Staging Nomad v${NOMAD_VERSION} (${platform}/${ARCH})..."
|
|
1571
|
+
if ! retry_net "Download Nomad binary" 3 curl -fsSL "$download_url" -o "${stage_dir}/nomad.zip"; then
|
|
1572
|
+
ui_error "Failed to download Nomad v${NOMAD_VERSION} — keeping existing v${current_version}"
|
|
1573
|
+
return 1
|
|
1574
|
+
fi
|
|
1575
|
+
if ! retry_net "Download Nomad checksums" 3 curl -fsSL "$sums_url" -o "${stage_dir}/SHA256SUMS"; then
|
|
1576
|
+
ui_error "Failed to download Nomad checksum file — aborting migration for security"
|
|
1577
|
+
return 1
|
|
1578
|
+
fi
|
|
1579
|
+
|
|
1580
|
+
local expected_hash actual_hash
|
|
1581
|
+
expected_hash="$(grep "nomad_${NOMAD_VERSION}_${platform}_${ARCH}.zip" "${stage_dir}/SHA256SUMS" | awk '{print $1}')"
|
|
1582
|
+
if [[ -z "$expected_hash" ]]; then
|
|
1583
|
+
ui_error "No checksum entry for nomad_${NOMAD_VERSION}_${platform}_${ARCH}.zip — aborting"
|
|
1584
|
+
return 1
|
|
1585
|
+
fi
|
|
1586
|
+
if command -v sha256sum &>/dev/null; then
|
|
1587
|
+
actual_hash="$(sha256sum "${stage_dir}/nomad.zip" | awk '{print $1}')"
|
|
1588
|
+
else
|
|
1589
|
+
actual_hash="$(shasum -a 256 "${stage_dir}/nomad.zip" | awk '{print $1}')"
|
|
1590
|
+
fi
|
|
1591
|
+
if [[ "$expected_hash" != "$actual_hash" ]]; then
|
|
1592
|
+
ui_error "Nomad checksum mismatch — download may have been tampered with!"
|
|
1593
|
+
ui_error " Expected: $expected_hash"
|
|
1594
|
+
ui_error " Got: $actual_hash"
|
|
1595
|
+
return 1
|
|
1596
|
+
fi
|
|
1597
|
+
ui_info "Checksum verified ✓"
|
|
1598
|
+
|
|
1599
|
+
if ! command -v unzip &>/dev/null; then
|
|
1600
|
+
ui_info "Installing unzip..."
|
|
1601
|
+
pkg_install unzip >/dev/null 2>&1
|
|
1602
|
+
fi
|
|
1603
|
+
if ! unzip -o "${stage_dir}/nomad.zip" nomad -d "${stage_dir}" >/dev/null 2>&1; then
|
|
1604
|
+
if ! unzip -o "${stage_dir}/nomad.zip" -d "${stage_dir}" >/dev/null 2>&1; then
|
|
1605
|
+
ui_error "Failed to extract staged Nomad archive"
|
|
1606
|
+
return 1
|
|
1607
|
+
fi
|
|
1608
|
+
fi
|
|
1609
|
+
chmod 755 "${stage_dir}/nomad" 2>/dev/null
|
|
1610
|
+
|
|
1611
|
+
local staged_version
|
|
1612
|
+
staged_version="$("${stage_dir}/nomad" version 2>/dev/null | head -n1 | extract_semver || echo "")"
|
|
1613
|
+
if [[ "$staged_version" != "$NOMAD_VERSION" ]]; then
|
|
1614
|
+
ui_error "Staged binary reports v${staged_version:-unknown}, expected v${NOMAD_VERSION} — aborting"
|
|
1615
|
+
return 1
|
|
1616
|
+
fi
|
|
1617
|
+
ui_success "Staged new Nomad binary v${staged_version}"
|
|
1618
|
+
|
|
1619
|
+
# ── Stage 2: destructive state changes begin ──────────────────────────
|
|
1620
|
+
ui_info "Stopping services..."
|
|
1621
|
+
${SUDO} systemctl stop jishushell 2>/dev/null || true
|
|
1622
|
+
${SUDO} systemctl stop nomad 2>/dev/null || true
|
|
1623
|
+
# pkill -f 'nomad agent' matches its own cmdline ("pkill -f nomad agent"
|
|
1624
|
+
# literally contains the pattern) and self-terminates before reaching the
|
|
1625
|
+
# real nomad process. Use pgrep -x nomad instead (exact proc-name match,
|
|
1626
|
+
# pgrep's own comm is "pgrep" not "nomad").
|
|
1627
|
+
local nomad_pids
|
|
1628
|
+
nomad_pids="$(pgrep -x nomad 2>/dev/null | tr '\n' ' ')"
|
|
1629
|
+
if [[ -n "$nomad_pids" ]]; then
|
|
1630
|
+
# shellcheck disable=SC2086
|
|
1631
|
+
${SUDO} kill -TERM $nomad_pids 2>/dev/null || kill -TERM $nomad_pids 2>/dev/null || true
|
|
1632
|
+
sleep 2
|
|
1633
|
+
nomad_pids="$(pgrep -x nomad 2>/dev/null | tr '\n' ' ')"
|
|
1634
|
+
if [[ -n "$nomad_pids" ]]; then
|
|
1635
|
+
# shellcheck disable=SC2086
|
|
1636
|
+
${SUDO} kill -KILL $nomad_pids 2>/dev/null || kill -KILL $nomad_pids 2>/dev/null || true
|
|
1637
|
+
fi
|
|
1638
|
+
fi
|
|
1639
|
+
|
|
1640
|
+
# ── Stage 3: tar backup (single snapshot, overwrite any previous) ─────
|
|
1641
|
+
local backup_file=""
|
|
1642
|
+
local backup_dir="${JISHUSHELL_HOME}/nomad/backups"
|
|
1643
|
+
if [[ -d "${JISHUSHELL_HOME}/nomad/data" ]]; then
|
|
1644
|
+
mkdir -p "$backup_dir"
|
|
1645
|
+
local ts
|
|
1646
|
+
ts="$(date +%Y%m%d-%H%M%S)"
|
|
1647
|
+
backup_file="${backup_dir}/data-${ts}.tar.gz"
|
|
1648
|
+
ui_info "Backing up raft state → ${backup_file}"
|
|
1649
|
+
if ! tar czf "$backup_file" -C "${JISHUSHELL_HOME}/nomad" data 2>/dev/null; then
|
|
1650
|
+
ui_warn "Backup tar failed — continuing (raft state will still be wiped)"
|
|
1651
|
+
backup_file=""
|
|
1652
|
+
else
|
|
1653
|
+
# Keep only the most recent snapshot to avoid unbounded disk growth
|
|
1654
|
+
ls -t "${backup_dir}"/data-*.tar.gz 2>/dev/null | tail -n +2 | xargs -r rm -f
|
|
1655
|
+
fi
|
|
1656
|
+
fi
|
|
1657
|
+
|
|
1658
|
+
# ── Stage 4: wipe raft state + env files ─────────────────────────────
|
|
1659
|
+
${SUDO} rm -rf "${JISHUSHELL_HOME}/nomad/data"
|
|
1660
|
+
rm -f "${JISHUSHELL_HOME}/nomad.env"
|
|
1661
|
+
${SUDO} rm -f /etc/jishushell/nomad.env
|
|
1662
|
+
|
|
1663
|
+
# ── Stage 5: orphaned gateway containers (alloc ids gone with raft) ──
|
|
1664
|
+
# sudo npm install -g runs postinstall as the invoking user (typically pi),
|
|
1665
|
+
# whose login shell may not have docker group access — the legacy install
|
|
1666
|
+
# only granted docker to the nomad.service via SupplementaryGroups, not to
|
|
1667
|
+
# the login shell. Try unprivileged first, fall back to sudo docker so this
|
|
1668
|
+
# step works regardless of group membership.
|
|
1669
|
+
if command -v docker &>/dev/null; then
|
|
1670
|
+
local _docker="docker"
|
|
1671
|
+
if ! docker ps >/dev/null 2>&1; then
|
|
1672
|
+
_docker="${SUDO} docker"
|
|
1673
|
+
fi
|
|
1674
|
+
local gw_containers
|
|
1675
|
+
gw_containers="$($_docker ps -a --format '{{.Names}}' 2>/dev/null | grep '^gateway-' || true)"
|
|
1676
|
+
if [[ -n "$gw_containers" ]]; then
|
|
1677
|
+
local gw_count
|
|
1678
|
+
gw_count="$(echo "$gw_containers" | wc -l)"
|
|
1679
|
+
echo "$gw_containers" | xargs -r $_docker rm -f >/dev/null 2>&1 || true
|
|
1680
|
+
ui_info "Removed ${gw_count} orphaned gateway container(s)"
|
|
1681
|
+
fi
|
|
1682
|
+
fi
|
|
1683
|
+
|
|
1684
|
+
# ── Stage 6: swap binary into place (atomic via temp name + rename) ──
|
|
1685
|
+
mkdir -p "${JISHUSHELL_BIN_DIR}"
|
|
1686
|
+
local dest_tmp="${local_bin}.tmp.$$"
|
|
1687
|
+
if ! cp "${stage_dir}/nomad" "$dest_tmp"; then
|
|
1688
|
+
ui_error "Failed to copy new Nomad binary into place"
|
|
1689
|
+
[[ -n "$backup_file" ]] && ui_error " Backup preserved at: ${backup_file}"
|
|
1690
|
+
return 1
|
|
1691
|
+
fi
|
|
1692
|
+
chmod 755 "$dest_tmp"
|
|
1693
|
+
if ! mv -f "$dest_tmp" "$local_bin"; then
|
|
1694
|
+
ui_error "Failed to swap Nomad binary"
|
|
1695
|
+
[[ -n "$backup_file" ]] && ui_error " Backup preserved at: ${backup_file}"
|
|
1696
|
+
rm -f "$dest_tmp"
|
|
1697
|
+
return 1
|
|
1698
|
+
fi
|
|
1699
|
+
|
|
1700
|
+
ui_success "Nomad migrated to v${NOMAD_VERSION}"
|
|
1701
|
+
[[ -n "$backup_file" ]] && ui_info " Backup (forensic, not self-recovery): ${backup_file}"
|
|
1702
|
+
ui_info " JishuShell will re-bootstrap ACL and resubmit jobs from instance configs on next start."
|
|
1703
|
+
return 0
|
|
1704
|
+
}
|
|
1705
|
+
|
|
1424
1706
|
_install_nomad_binary() {
|
|
1425
1707
|
local dest="${JISHUSHELL_BIN_DIR}/nomad"
|
|
1426
1708
|
|
|
@@ -1531,17 +1813,29 @@ _install_nomad_binary() {
|
|
|
1531
1813
|
ui_success "Nomad installed: v${installed_version} → ${dest}"
|
|
1532
1814
|
}
|
|
1533
1815
|
|
|
1534
|
-
# Add ~/.jishushell/bin to PATH in shell startup files and current session
|
|
1816
|
+
# Add ~/.jishushell/bin and npm global bin to PATH in shell startup files and current session
|
|
1535
1817
|
_ensure_jishushell_bin_in_path() {
|
|
1536
1818
|
local bin_dir="${JISHUSHELL_BIN_DIR}"
|
|
1537
1819
|
local marker="# jishushell-bin-path"
|
|
1820
|
+
|
|
1821
|
+
# Also ensure npm global bin is in PATH (for `npm install -g` with custom prefix)
|
|
1822
|
+
local npm_bin=""
|
|
1823
|
+
if command -v npm &>/dev/null; then
|
|
1824
|
+
npm_bin="$(npm config get prefix 2>/dev/null)/bin"
|
|
1825
|
+
fi
|
|
1826
|
+
|
|
1827
|
+
# Build PATH line: include npm global bin if it differs from jishushell bin
|
|
1538
1828
|
local init_line="export PATH=\"${bin_dir}:\$PATH\""
|
|
1829
|
+
if [[ -n "$npm_bin" && "$npm_bin" != "$bin_dir" && -d "$npm_bin" ]]; then
|
|
1830
|
+
init_line="export PATH=\"${bin_dir}:${npm_bin}:\$PATH\""
|
|
1831
|
+
export PATH="${npm_bin}:${PATH}"
|
|
1832
|
+
fi
|
|
1539
1833
|
|
|
1540
1834
|
# Export for the current running shell immediately
|
|
1541
1835
|
export PATH="${bin_dir}:${PATH}"
|
|
1542
1836
|
|
|
1543
1837
|
if [[ "$DRY_RUN" == "1" ]]; then
|
|
1544
|
-
ui_info "[dry-run] Would add
|
|
1838
|
+
ui_info "[dry-run] Would add PATH entries in shell startup files"
|
|
1545
1839
|
return 0
|
|
1546
1840
|
fi
|
|
1547
1841
|
|
|
@@ -1550,7 +1844,7 @@ _ensure_jishushell_bin_in_path() {
|
|
|
1550
1844
|
for rc in "${rc_files[@]}"; do
|
|
1551
1845
|
if [[ -f "$rc" ]] && ! grep -qF "$marker" "$rc" 2>/dev/null; then
|
|
1552
1846
|
printf '\n%s\n%s\n' "$marker" "$init_line" >> "$rc"
|
|
1553
|
-
ui_info "Added
|
|
1847
|
+
ui_info "Added PATH entries in ${rc}"
|
|
1554
1848
|
added=1
|
|
1555
1849
|
fi
|
|
1556
1850
|
done
|
|
@@ -1587,6 +1881,14 @@ _ensure_nomad_hcl() {
|
|
|
1587
1881
|
# Dirs are created by the current user — no sudo needed
|
|
1588
1882
|
chown -R "${REAL_USER}:${REAL_GID:-${REAL_USER}}" "${JISHUSHELL_HOME}" 2>/dev/null || true
|
|
1589
1883
|
|
|
1884
|
+
# Loopback interface name: lo0 on macOS, lo on Linux.
|
|
1885
|
+
# Forces Nomad to fingerprint 127.0.0.1 as the node IP so Docker port
|
|
1886
|
+
# publishing binds to loopback instead of the LAN IP. On macOS+Colima the
|
|
1887
|
+
# LAN IP doesn't exist inside the Lima VM, causing "cannot assign requested
|
|
1888
|
+
# address" when Docker tries to bind to it.
|
|
1889
|
+
local loopback_iface="lo"
|
|
1890
|
+
[[ "$OS" == "macos" ]] && loopback_iface="lo0"
|
|
1891
|
+
|
|
1590
1892
|
cat > "$config_file" << NOMAD_HCL
|
|
1591
1893
|
data_dir = "${nomad_data_dir}"
|
|
1592
1894
|
|
|
@@ -1608,6 +1910,7 @@ server {
|
|
|
1608
1910
|
client {
|
|
1609
1911
|
enabled = true
|
|
1610
1912
|
servers = ["127.0.0.1:4647"]
|
|
1913
|
+
network_interface = "${loopback_iface}"
|
|
1611
1914
|
alloc_dir = "${nomad_alloc_dir}"
|
|
1612
1915
|
|
|
1613
1916
|
drain_on_shutdown {
|
|
@@ -1745,14 +2048,17 @@ install_nomad_systemd() {
|
|
|
1745
2048
|
|
|
1746
2049
|
_ensure_nomad_hcl
|
|
1747
2050
|
|
|
2051
|
+
# Nomad 1.6.5's docker driver fingerprint requires euid==0 (PR #18197 lifted
|
|
2052
|
+
# the root requirement only in 1.7+, which is BSL). The panel stays as the
|
|
2053
|
+
# installing user via a separate unit; it talks to this agent over HTTP so
|
|
2054
|
+
# ~/.jishushell/nomad/data/ can be root-owned without breaking anything.
|
|
1748
2055
|
local service_content="[Unit]
|
|
1749
2056
|
Description=Nomad Agent
|
|
1750
2057
|
After=network-online.target docker.service
|
|
1751
2058
|
Wants=network-online.target
|
|
1752
2059
|
|
|
1753
2060
|
[Service]
|
|
1754
|
-
User
|
|
1755
|
-
SupplementaryGroups=docker
|
|
2061
|
+
User=root
|
|
1756
2062
|
Type=simple
|
|
1757
2063
|
EnvironmentFile=-/etc/jishushell/nomad.env
|
|
1758
2064
|
ExecStart=${nomad_bin} agent -config=${config_file}
|
|
@@ -1775,8 +2081,12 @@ WantedBy=multi-user.target"
|
|
|
1775
2081
|
need_reload=1
|
|
1776
2082
|
fi
|
|
1777
2083
|
|
|
1778
|
-
#
|
|
2084
|
+
# Keep the real user owning ~/.jishushell except for Nomad's own state,
|
|
2085
|
+
# which must be root-owned because the agent runs as root for driver fingerprinting.
|
|
1779
2086
|
chown -R "${REAL_USER}:${REAL_GID:-${REAL_USER}}" "${JISHUSHELL_HOME}" 2>/dev/null || true
|
|
2087
|
+
if [[ -d "${nomad_config_dir}/data" ]]; then
|
|
2088
|
+
${SUDO} chown -R root:root "${nomad_config_dir}/data" 2>/dev/null || true
|
|
2089
|
+
fi
|
|
1780
2090
|
|
|
1781
2091
|
if [[ $need_reload -eq 1 ]]; then
|
|
1782
2092
|
${SUDO} systemctl daemon-reload
|
|
@@ -1805,10 +2115,10 @@ _install_nomad_launchd() {
|
|
|
1805
2115
|
|
|
1806
2116
|
mkdir -p "${HOME}/Library/LaunchAgents"
|
|
1807
2117
|
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
2118
|
+
# Always use JishuShell's private Colima socket — hardcoded, not runtime-detected.
|
|
2119
|
+
# Colima may not be running yet when the plist is written; runtime fallback would
|
|
2120
|
+
# pick the wrong socket (Docker Desktop or /var/run/docker.sock).
|
|
2121
|
+
local docker_sock="${_COLIMA_SOCKET}"
|
|
1812
2122
|
|
|
1813
2123
|
cat > "$plist_path" << PLIST
|
|
1814
2124
|
<?xml version="1.0" encoding="UTF-8"?>
|
|
@@ -1870,6 +2180,54 @@ fs.writeFileSync(p, JSON.stringify(cfg, null, 2));
|
|
|
1870
2180
|
" 2>/dev/null || true
|
|
1871
2181
|
}
|
|
1872
2182
|
|
|
2183
|
+
_read_openclaw_image_from_panel() {
|
|
2184
|
+
local panel_file="${JISHUSHELL_HOME}/panel.json"
|
|
2185
|
+
node -e "
|
|
2186
|
+
const fs = require('fs');
|
|
2187
|
+
const p = '${panel_file}';
|
|
2188
|
+
try {
|
|
2189
|
+
const cfg = JSON.parse(fs.readFileSync(p, 'utf8'));
|
|
2190
|
+
if (typeof cfg.openclaw_image === 'string' && cfg.openclaw_image.trim()) {
|
|
2191
|
+
process.stdout.write(cfg.openclaw_image.trim());
|
|
2192
|
+
}
|
|
2193
|
+
} catch {}
|
|
2194
|
+
" 2>/dev/null || true
|
|
2195
|
+
}
|
|
2196
|
+
|
|
2197
|
+
_pin_openclaw_image_if_needed() {
|
|
2198
|
+
local image="$1"
|
|
2199
|
+
if [[ -z "${image}" ]]; then
|
|
2200
|
+
return 1
|
|
2201
|
+
fi
|
|
2202
|
+
if [[ ! "${image}" =~ :(latest|slim)$ ]]; then
|
|
2203
|
+
printf '%s' "${image}"
|
|
2204
|
+
return 0
|
|
2205
|
+
fi
|
|
2206
|
+
|
|
2207
|
+
local version=""
|
|
2208
|
+
version="$(docker_exec run --rm --entrypoint node "${image}" -p "require('/app/node_modules/openclaw/package.json').version" 2>/dev/null | tr -d '\r\n')"
|
|
2209
|
+
if [[ ! "${version}" =~ ^[0-9]+\.[0-9]+\.[0-9]+ ]]; then
|
|
2210
|
+
printf '%s' "${image}"
|
|
2211
|
+
return 0
|
|
2212
|
+
fi
|
|
2213
|
+
|
|
2214
|
+
local repo="${image%:*}"
|
|
2215
|
+
local pinned="${repo}:${version}"
|
|
2216
|
+
if docker_exec image inspect "${pinned}" &>/dev/null 2>&1; then
|
|
2217
|
+
printf '%s' "${pinned}"
|
|
2218
|
+
return 0
|
|
2219
|
+
fi
|
|
2220
|
+
|
|
2221
|
+
if docker_exec tag "${image}" "${pinned}" &>/dev/null 2>&1; then
|
|
2222
|
+
docker_exec rmi "${image}" &>/dev/null 2>&1 || true
|
|
2223
|
+
printf '%s' "${pinned}"
|
|
2224
|
+
return 0
|
|
2225
|
+
fi
|
|
2226
|
+
|
|
2227
|
+
printf '%s' "${image}"
|
|
2228
|
+
return 0
|
|
2229
|
+
}
|
|
2230
|
+
|
|
1873
2231
|
# Install OpenClaw npm package on the host (for process manager / raw_exec modes).
|
|
1874
2232
|
# Skipped when using official Docker image.
|
|
1875
2233
|
_install_openclaw_npm() {
|
|
@@ -1921,25 +2279,15 @@ install_openclaw() {
|
|
|
1921
2279
|
return 1
|
|
1922
2280
|
fi
|
|
1923
2281
|
|
|
2282
|
+
local docker_tag="${OPENCLAW_DOCKER_TAG}"
|
|
2283
|
+
local configured_tag=""
|
|
2284
|
+
|
|
1924
2285
|
if [[ "$DRY_RUN" == "1" ]]; then
|
|
1925
|
-
ui_info "[dry-run] Would:
|
|
1926
|
-
ui_info "[dry-run] Would: docker build -t jishushell-openclaw:<version> (npm package + Python)"
|
|
2286
|
+
ui_info "[dry-run] Would: docker pull ${docker_tag} (fallback: local build)"
|
|
1927
2287
|
return 0
|
|
1928
2288
|
fi
|
|
1929
2289
|
|
|
1930
|
-
# ── Step 1:
|
|
1931
|
-
_install_openclaw_npm || return 1
|
|
1932
|
-
|
|
1933
|
-
# Resolve versioned tag from installed package (e.g. jishushell-openclaw:2026.3.31)
|
|
1934
|
-
local pkg_dir="${JISHUSHELL_HOME}/packages/openclaw"
|
|
1935
|
-
local oc_ver
|
|
1936
|
-
oc_ver="$(node -p "require('${pkg_dir}/lib/node_modules/openclaw/package.json').version" 2>/dev/null || echo "")"
|
|
1937
|
-
local docker_tag="${OPENCLAW_DOCKER_TAG}"
|
|
1938
|
-
if [[ -n "$oc_ver" ]]; then
|
|
1939
|
-
docker_tag="jishushell-openclaw:${oc_ver}"
|
|
1940
|
-
fi
|
|
1941
|
-
|
|
1942
|
-
# ── Step 2: Ensure Docker daemon is accessible ────────────────────────────
|
|
2290
|
+
# ── Step 1: Ensure Docker daemon is accessible ────────────────────────────
|
|
1943
2291
|
if ! docker_exec info &>/dev/null 2>&1; then
|
|
1944
2292
|
if command -v sg &>/dev/null 2>/dev/null && sg docker -c "docker info" &>/dev/null 2>&1; then
|
|
1945
2293
|
DOCKER_CMD_PREFIX="sg docker -c"
|
|
@@ -1950,7 +2298,7 @@ install_openclaw() {
|
|
|
1950
2298
|
else
|
|
1951
2299
|
ui_warn "Docker daemon is not reachable"
|
|
1952
2300
|
if [[ "$OS" == "macos" ]]; then
|
|
1953
|
-
ui_warn "
|
|
2301
|
+
ui_warn "Run: COLIMA_HOME=${_COLIMA_HOME} colima start ${_COLIMA_PROFILE}"
|
|
1954
2302
|
else
|
|
1955
2303
|
ui_warn "Ensure Docker is running: sudo systemctl start docker"
|
|
1956
2304
|
fi
|
|
@@ -1958,49 +2306,90 @@ install_openclaw() {
|
|
|
1958
2306
|
fi
|
|
1959
2307
|
fi
|
|
1960
2308
|
|
|
1961
|
-
# ── Step
|
|
2309
|
+
# ── Step 2: Reuse the currently configured pinned image if it already
|
|
2310
|
+
# exists locally. This avoids re-pulling :latest on machines where the
|
|
2311
|
+
# running JishuShell service has already migrated panel.json from a mutable
|
|
2312
|
+
# tag (e.g. :latest) to an immutable version tag (e.g. :2026.4.9).
|
|
2313
|
+
configured_tag="$(_read_openclaw_image_from_panel)"
|
|
2314
|
+
if [[ -n "${configured_tag}" ]] && docker_exec image inspect "${configured_tag}" &>/dev/null 2>&1; then
|
|
2315
|
+
OPENCLAW_IMAGE="$(_pin_openclaw_image_if_needed "${configured_tag}")"
|
|
2316
|
+
_save_openclaw_image_to_panel "${OPENCLAW_IMAGE}"
|
|
2317
|
+
ui_success "Docker image ${OPENCLAW_IMAGE} already exists — reusing configured image"
|
|
2318
|
+
return 0
|
|
2319
|
+
fi
|
|
2320
|
+
|
|
2321
|
+
# ── Step 3: Skip if the requested install tag already exists ─────────────
|
|
1962
2322
|
if docker_exec image inspect "${docker_tag}" &>/dev/null 2>&1; then
|
|
1963
|
-
OPENCLAW_IMAGE="${docker_tag}"
|
|
1964
|
-
_save_openclaw_image_to_panel "${
|
|
1965
|
-
ui_success "Docker image ${
|
|
2323
|
+
OPENCLAW_IMAGE="$(_pin_openclaw_image_if_needed "${docker_tag}")"
|
|
2324
|
+
_save_openclaw_image_to_panel "${OPENCLAW_IMAGE}"
|
|
2325
|
+
ui_success "Docker image ${OPENCLAW_IMAGE} already exists — skipping"
|
|
1966
2326
|
return 0
|
|
1967
2327
|
fi
|
|
1968
2328
|
|
|
1969
|
-
# ── Step 4:
|
|
1970
|
-
|
|
2329
|
+
# ── Step 4: Pull from registry, fallback to local build ──────────────────
|
|
2330
|
+
ui_info "Pulling OpenClaw Docker image: ${docker_tag} ..."
|
|
2331
|
+
log_detail ""
|
|
2332
|
+
log_detail "[$(date '+%H:%M:%S')] docker pull ${docker_tag}"
|
|
2333
|
+
if log_cmd docker_exec pull "${docker_tag}"; then
|
|
2334
|
+
OPENCLAW_IMAGE="$(_pin_openclaw_image_if_needed "${docker_tag}")"
|
|
2335
|
+
_save_openclaw_image_to_panel "${OPENCLAW_IMAGE}"
|
|
2336
|
+
ui_success "OpenClaw Docker image pulled: ${OPENCLAW_IMAGE}"
|
|
2337
|
+
return 0
|
|
2338
|
+
fi
|
|
2339
|
+
|
|
2340
|
+
# ── Step 3b: Fallback — build locally using bundled Dockerfile ────
|
|
2341
|
+
ui_warn "Pull failed, falling back to local build..."
|
|
2342
|
+
|
|
2343
|
+
# Locate the bundled Dockerfile.openclaw-slim + openclaw-entry.sh.
|
|
2344
|
+
# Both ship at the npm package root, alongside the install/ directory,
|
|
2345
|
+
# so from this script's perspective they are one level up.
|
|
2346
|
+
local script_dir
|
|
2347
|
+
if [[ -n "${BASH_SOURCE[0]:-}" ]]; then
|
|
2348
|
+
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
2349
|
+
else
|
|
2350
|
+
script_dir="${PWD}"
|
|
2351
|
+
fi
|
|
2352
|
+
local dockerfile_src="${script_dir}/../Dockerfile.openclaw-slim"
|
|
2353
|
+
local entrypoint_src="${script_dir}/../openclaw-entry.sh"
|
|
2354
|
+
|
|
2355
|
+
if [[ ! -f "${dockerfile_src}" || ! -f "${entrypoint_src}" ]]; then
|
|
2356
|
+
ui_error "Bundled build files not found near ${script_dir}/.."
|
|
2357
|
+
ui_error "Expected: Dockerfile.openclaw-slim and openclaw-entry.sh"
|
|
2358
|
+
return 1
|
|
2359
|
+
fi
|
|
2360
|
+
|
|
2361
|
+
local build_ctx
|
|
2362
|
+
build_ctx="$(mktemp -d)"
|
|
2363
|
+
trap "rm -rf '${build_ctx}'" EXIT
|
|
1971
2364
|
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
USER node
|
|
1987
|
-
CMD ["node", "openclaw.mjs", "gateway", "--allow-unconfigured"]
|
|
1988
|
-
DOCKERFILE
|
|
1989
|
-
|
|
1990
|
-
ui_info "Building Docker image: ${docker_tag} (npm package + Python)..."
|
|
2365
|
+
cp "${dockerfile_src}" "${build_ctx}/Dockerfile.openclaw-slim"
|
|
2366
|
+
cp "${entrypoint_src}" "${build_ctx}/openclaw-entry.sh"
|
|
2367
|
+
|
|
2368
|
+
# Query current OpenClaw version from npm so the --build-arg busts the
|
|
2369
|
+
# Docker layer cache for the `RUN npm install openclaw@${ver}` step.
|
|
2370
|
+
# Fall back to "latest" if npm is unreachable.
|
|
2371
|
+
local openclaw_ver
|
|
2372
|
+
openclaw_ver="$(npm view openclaw version 2>/dev/null)"
|
|
2373
|
+
if [[ -z "${openclaw_ver}" ]]; then
|
|
2374
|
+
openclaw_ver="latest"
|
|
2375
|
+
fi
|
|
2376
|
+
log_detail "Resolved OpenClaw version for build: ${openclaw_ver}"
|
|
2377
|
+
|
|
2378
|
+
ui_info "Building OpenClaw Docker image locally: ${docker_tag} (openclaw@${openclaw_ver}) ..."
|
|
1991
2379
|
log_detail ""
|
|
1992
|
-
log_detail "[$(date '+%H:%M:%S')] docker build -t ${docker_tag} ${
|
|
1993
|
-
if log_cmd docker_exec build --network=host
|
|
2380
|
+
log_detail "[$(date '+%H:%M:%S')] docker build --network=host --build-arg OPENCLAW_VERSION=${openclaw_ver} -f Dockerfile.openclaw-slim -t ${docker_tag} ${build_ctx}"
|
|
2381
|
+
if log_cmd docker_exec build --network=host \
|
|
2382
|
+
--build-arg "OPENCLAW_VERSION=${openclaw_ver}" \
|
|
2383
|
+
-f "${build_ctx}/Dockerfile.openclaw-slim" \
|
|
2384
|
+
-t "${docker_tag}" "${build_ctx}"; then
|
|
1994
2385
|
OPENCLAW_IMAGE="${docker_tag}"
|
|
1995
2386
|
_save_openclaw_image_to_panel "${docker_tag}"
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
fi
|
|
2000
|
-
rm -f "${pkg_dir}/Dockerfile"
|
|
2001
|
-
ui_success "OpenClaw Docker image built: ${docker_tag} (with Python)"
|
|
2387
|
+
rm -rf "${build_ctx}"
|
|
2388
|
+
trap - EXIT
|
|
2389
|
+
ui_success "OpenClaw Docker image built: ${docker_tag}"
|
|
2002
2390
|
else
|
|
2003
|
-
rm -
|
|
2391
|
+
rm -rf "${build_ctx}"
|
|
2392
|
+
trap - EXIT
|
|
2004
2393
|
ui_error "Failed to build OpenClaw Docker image"
|
|
2005
2394
|
return 1
|
|
2006
2395
|
fi
|
|
@@ -2028,6 +2417,14 @@ _prompt_openclaw_skip() {
|
|
|
2028
2417
|
esac
|
|
2029
2418
|
}
|
|
2030
2419
|
|
|
2420
|
+
jishushell_package_spec() {
|
|
2421
|
+
if [[ "${JISHUSHELL_VERSION_OVERRIDE}" == "1" || "${JISHUSHELL_NPM_VERSION}" != "latest" ]]; then
|
|
2422
|
+
printf 'jishushell@%s' "${JISHUSHELL_NPM_VERSION}"
|
|
2423
|
+
return 0
|
|
2424
|
+
fi
|
|
2425
|
+
printf 'jishushell'
|
|
2426
|
+
}
|
|
2427
|
+
|
|
2031
2428
|
# show_install_plan [--with-jishushell]
|
|
2032
2429
|
show_install_plan() {
|
|
2033
2430
|
local with_jishushell=0
|
|
@@ -2043,17 +2440,19 @@ show_install_plan() {
|
|
|
2043
2440
|
ui_kv "Node.js" "$(if [[ $SKIP_NODE -eq 1 ]]; then echo 'skip'; else echo "v${NODE_VERSION} via nvm v${NVM_VERSION}"; fi)"
|
|
2044
2441
|
ui_kv "Docker" "$(if [[ $SKIP_DOCKER -eq 1 ]]; then echo 'skip'; else echo 'latest stable'; fi)"
|
|
2045
2442
|
ui_kv "Nomad" "$(if [[ $SKIP_NOMAD -eq 1 ]]; then echo 'skip'; else echo "v${NOMAD_VERSION}"; fi)"
|
|
2046
|
-
ui_kv "OpenClaw" "$(if [[
|
|
2443
|
+
ui_kv "OpenClaw" "$(if [[ "${SKIP_OPENCLAW}" == "1" ]]; then echo 'skip'; else echo "docker pull ${OPENCLAW_DOCKER_TAG}"; fi)"
|
|
2047
2444
|
if [[ $with_jishushell -eq 1 ]]; then
|
|
2048
2445
|
local _plan_jishu
|
|
2049
2446
|
if [[ $SKIP_JISHUSHELL -eq 1 ]]; then
|
|
2050
2447
|
_plan_jishu="skip"
|
|
2051
2448
|
else
|
|
2052
2449
|
local _plan_tgz=""
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2450
|
+
if [[ "${JISHUSHELL_VERSION_OVERRIDE}" != "1" && "${JISHUSHELL_NPM_VERSION}" == "latest" ]]; then
|
|
2451
|
+
for _c in "${JISHU_SCRIPT_DIR}"/jishushell-*.tgz; do
|
|
2452
|
+
[[ -f "$_c" ]] && { _plan_tgz="$(basename "$_c")"; break; }
|
|
2453
|
+
done
|
|
2454
|
+
fi
|
|
2455
|
+
_plan_jishu="${_plan_tgz:+npm install -g ${_plan_tgz} (local)}${_plan_tgz:-npm install -g $(jishushell_package_spec)}"
|
|
2057
2456
|
fi
|
|
2058
2457
|
ui_kv "JishuShell" "$_plan_jishu"
|
|
2059
2458
|
ui_kv "JishuShell service" "$(if [[ $SKIP_JISHUSHELL_SERVICE -eq 1 ]]; then echo 'skip'; else echo 'register autostart'; fi)"
|
|
@@ -2114,7 +2513,15 @@ show_summary() {
|
|
|
2114
2513
|
fi
|
|
2115
2514
|
|
|
2116
2515
|
if [[ "${SKIP_OPENCLAW}" != "1" ]]; then
|
|
2117
|
-
|
|
2516
|
+
local _summary_openclaw_image=""
|
|
2517
|
+
_summary_openclaw_image="$(_read_openclaw_image_from_panel)"
|
|
2518
|
+
if [[ -z "${_summary_openclaw_image}" && -n "${OPENCLAW_IMAGE}" ]]; then
|
|
2519
|
+
_summary_openclaw_image="${OPENCLAW_IMAGE}"
|
|
2520
|
+
fi
|
|
2521
|
+
|
|
2522
|
+
if [[ -n "${_summary_openclaw_image}" ]] && docker_exec image inspect "${_summary_openclaw_image}" &>/dev/null 2>&1; then
|
|
2523
|
+
ui_kv "OpenClaw" "✓ ${_summary_openclaw_image}"
|
|
2524
|
+
elif [[ -n "${OPENCLAW_IMAGE}" ]] && docker_exec image inspect "${OPENCLAW_IMAGE}" &>/dev/null 2>&1; then
|
|
2118
2525
|
ui_kv "OpenClaw" "✓ ${OPENCLAW_IMAGE}"
|
|
2119
2526
|
elif [[ "$DRY_RUN" == "1" ]]; then
|
|
2120
2527
|
ui_kv "OpenClaw" "- dry-run"
|
|
@@ -2210,16 +2617,20 @@ install_jishushell() {
|
|
|
2210
2617
|
fi
|
|
2211
2618
|
|
|
2212
2619
|
if [[ "$DRY_RUN" == "1" ]]; then
|
|
2620
|
+
local jishushell_pkg_spec
|
|
2621
|
+
jishushell_pkg_spec="$(jishushell_package_spec)"
|
|
2213
2622
|
local _dry_reg=""
|
|
2214
2623
|
[[ -n "${NPM_REGISTRY:-}" ]] && _dry_reg=" --registry ${NPM_REGISTRY}"
|
|
2215
2624
|
local _dry_tgz=""
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2625
|
+
if [[ "${JISHUSHELL_VERSION_OVERRIDE}" != "1" && "${JISHUSHELL_NPM_VERSION}" == "latest" ]]; then
|
|
2626
|
+
for _c in "${JISHU_SCRIPT_DIR}"/jishushell-*.tgz; do
|
|
2627
|
+
[[ -f "$_c" ]] && { _dry_tgz="$_c"; break; }
|
|
2628
|
+
done
|
|
2629
|
+
fi
|
|
2219
2630
|
if [[ -n "$_dry_tgz" ]]; then
|
|
2220
2631
|
ui_info "[dry-run] Would: npm install -g ${_dry_tgz} (local package)"
|
|
2221
2632
|
else
|
|
2222
|
-
ui_info "[dry-run] Would: npm install -g
|
|
2633
|
+
ui_info "[dry-run] Would: npm install -g ${jishushell_pkg_spec}${_dry_reg}"
|
|
2223
2634
|
fi
|
|
2224
2635
|
ui_info "[dry-run] Would write wrapper: ${JISHUSHELL_BIN_DIR}/jishushell-panel-start"
|
|
2225
2636
|
return 0
|
|
@@ -2250,6 +2661,8 @@ install_jishushell() {
|
|
|
2250
2661
|
return 1
|
|
2251
2662
|
fi
|
|
2252
2663
|
|
|
2664
|
+
local jishushell_pkg_spec
|
|
2665
|
+
jishushell_pkg_spec="$(jishushell_package_spec)"
|
|
2253
2666
|
local npm_registry_args=()
|
|
2254
2667
|
if [[ -n "${NPM_REGISTRY:-}" ]]; then
|
|
2255
2668
|
if [[ ! "$NPM_REGISTRY" =~ ^https?:// ]]; then
|
|
@@ -2257,9 +2670,9 @@ install_jishushell() {
|
|
|
2257
2670
|
return 1
|
|
2258
2671
|
fi
|
|
2259
2672
|
npm_registry_args=("--registry" "${NPM_REGISTRY}")
|
|
2260
|
-
ui_info "Installing
|
|
2673
|
+
ui_info "Installing ${jishushell_pkg_spec} from ${NPM_REGISTRY}..."
|
|
2261
2674
|
else
|
|
2262
|
-
ui_info "Installing
|
|
2675
|
+
ui_info "Installing ${jishushell_pkg_spec} from public npm registry..."
|
|
2263
2676
|
fi
|
|
2264
2677
|
|
|
2265
2678
|
# When jishushell is already installed (e.g. running as npm postinstall hook),
|
|
@@ -2268,12 +2681,14 @@ install_jishushell() {
|
|
|
2268
2681
|
# Prefer a local .tgz package in the same directory as this script.
|
|
2269
2682
|
local tgz_path=""
|
|
2270
2683
|
local _tgz_candidate
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2684
|
+
if [[ "${JISHUSHELL_VERSION_OVERRIDE}" != "1" && "${JISHUSHELL_NPM_VERSION}" == "latest" ]]; then
|
|
2685
|
+
for _tgz_candidate in "${JISHU_SCRIPT_DIR}"/jishushell-*.tgz; do
|
|
2686
|
+
if [[ -f "$_tgz_candidate" ]]; then
|
|
2687
|
+
tgz_path="$_tgz_candidate"
|
|
2688
|
+
break
|
|
2689
|
+
fi
|
|
2690
|
+
done
|
|
2691
|
+
fi
|
|
2277
2692
|
|
|
2278
2693
|
# Export a sentinel so post-install.sh (triggered by npm's postinstall
|
|
2279
2694
|
# lifecycle hook) knows it was launched from inside jishu-install.sh and
|
|
@@ -2289,10 +2704,10 @@ install_jishushell() {
|
|
|
2289
2704
|
return 1
|
|
2290
2705
|
fi
|
|
2291
2706
|
else
|
|
2292
|
-
log_detail "[$(date '+%H:%M:%S')] ${npm_bin} install -g
|
|
2293
|
-
if ! log_cmd "$npm_bin" install -g
|
|
2707
|
+
log_detail "[$(date '+%H:%M:%S')] ${npm_bin} install -g ${jishushell_pkg_spec} ${npm_registry_args[*]:-}"
|
|
2708
|
+
if ! log_cmd "$npm_bin" install -g "${jishushell_pkg_spec}" "${npm_registry_args[@]}"; then
|
|
2294
2709
|
unset JISHU_RUNNING_IN_INSTALLER
|
|
2295
|
-
ui_error "npm install -g
|
|
2710
|
+
ui_error "npm install -g ${jishushell_pkg_spec} failed"
|
|
2296
2711
|
return 1
|
|
2297
2712
|
fi
|
|
2298
2713
|
fi
|
|
@@ -2462,7 +2877,9 @@ WantedBy=multi-user.target"
|
|
|
2462
2877
|
${SUDO} systemctl restart jishushell 2>/dev/null || true
|
|
2463
2878
|
ui_success "JishuShell systemd service updated and restarted"
|
|
2464
2879
|
else
|
|
2465
|
-
|
|
2880
|
+
# Package may have been upgraded — always restart to pick up new code
|
|
2881
|
+
${SUDO} systemctl restart jishushell 2>/dev/null || true
|
|
2882
|
+
ui_success "JishuShell systemd service restarted"
|
|
2466
2883
|
fi
|
|
2467
2884
|
}
|
|
2468
2885
|
|
|
@@ -2594,8 +3011,17 @@ parse_args() {
|
|
|
2594
3011
|
shift
|
|
2595
3012
|
OPENCLAW_NPM_VERSION="${1:?--openclaw-version requires a version argument (e.g. 3.24)}"
|
|
2596
3013
|
;;
|
|
3014
|
+
--openclaw-docker-tag)
|
|
3015
|
+
shift
|
|
3016
|
+
OPENCLAW_DOCKER_TAG="${1:?--openclaw-docker-tag requires a tag argument (e.g. ghcr.io/x-aijishu/openclaw-runtime:2026.4.9)}"
|
|
3017
|
+
;;
|
|
2597
3018
|
--skip-jishushell) SKIP_JISHUSHELL=1 ;;
|
|
2598
3019
|
--skip-jishushell-service) SKIP_JISHUSHELL_SERVICE=1 ;;
|
|
3020
|
+
--jishushell-version)
|
|
3021
|
+
shift
|
|
3022
|
+
JISHUSHELL_NPM_VERSION="${1:?--jishushell-version requires a version argument (e.g. 0.4.9)}"
|
|
3023
|
+
JISHUSHELL_VERSION_OVERRIDE=1
|
|
3024
|
+
;;
|
|
2599
3025
|
--skip)
|
|
2600
3026
|
shift
|
|
2601
3027
|
IFS=',' read -ra _steps <<< "${1:-}"
|
|
@@ -2661,10 +3087,16 @@ Options:
|
|
|
2661
3087
|
--skip-docker Skip step 2: Docker installation
|
|
2662
3088
|
--skip-nomad Skip step 3: Nomad installation
|
|
2663
3089
|
--skip-openclaw Skip step 4: OpenClaw installation
|
|
3090
|
+
--openclaw-docker-tag <tag>
|
|
3091
|
+
Pull a specific OpenClaw image tag
|
|
3092
|
+
(e.g. --openclaw-docker-tag ghcr.io/x-aijishu/openclaw-runtime:2026.4.9)
|
|
2664
3093
|
--skip-jishushell Skip step 5: JishuShell installation
|
|
2665
3094
|
--skip-jishushell-service Skip step 6: JishuShell service registration
|
|
3095
|
+
--jishushell-version <ver>
|
|
3096
|
+
Install a specific jishushell version
|
|
3097
|
+
(e.g. --jishushell-version 0.4.9)
|
|
2666
3098
|
--registry <url> Use a custom npm registry for all installs
|
|
2667
|
-
(e.g. --registry http://
|
|
3099
|
+
(e.g. --registry http://127.0.0.1:4873/)
|
|
2668
3100
|
--yes, -y Skip all confirmation prompts
|
|
2669
3101
|
--help, -h Show this help message
|
|
2670
3102
|
|
|
@@ -2672,7 +3104,7 @@ Steps:
|
|
|
2672
3104
|
1 Node.js (via nvm)
|
|
2673
3105
|
2 Docker
|
|
2674
3106
|
3 Nomad
|
|
2675
|
-
4 OpenClaw (
|
|
3107
|
+
4 OpenClaw (docker pull / local build)
|
|
2676
3108
|
5 JishuShell
|
|
2677
3109
|
6 JishuShell service registration (autostart)
|
|
2678
3110
|
|
|
@@ -2680,12 +3112,16 @@ Environment variables:
|
|
|
2680
3112
|
JISHU_NODE_VERSION Specify Node.js major version (default: ${NODE_VERSION})
|
|
2681
3113
|
JISHU_NVM_VERSION Specify nvm version (default: ${NVM_VERSION})
|
|
2682
3114
|
JISHU_NOMAD_VERSION Specify Nomad version (default: ${NOMAD_VERSION})
|
|
3115
|
+
JISHUSHELL_NPM_VERSION
|
|
3116
|
+
Specify jishushell npm package version (default: latest)
|
|
2683
3117
|
OPENCLAW_NPM_VERSION Specify openclaw npm package version (default: latest)
|
|
2684
|
-
|
|
3118
|
+
OPENCLAW_DOCKER_TAG Override OpenClaw Docker image tag (default: ${OPENCLAW_DOCKER_TAG})
|
|
2685
3119
|
NPM_REGISTRY Custom npm registry URL (same as --registry flag)
|
|
2686
3120
|
|
|
2687
|
-
|
|
2688
|
-
|
|
3121
|
+
Version flags:
|
|
3122
|
+
--jishushell-version <ver> Install a specific jishushell version, e.g. --jishushell-version 0.4.9
|
|
3123
|
+
--openclaw-version <ver> Install a specific openclaw version, e.g. --openclaw-version 3.24
|
|
3124
|
+
--openclaw-docker-tag <tag> Pull a specific OpenClaw image tag, e.g. --openclaw-docker-tag ghcr.io/x-aijishu/openclaw-runtime:2026.4.9
|
|
2689
3125
|
NO_PROMPT Set to 1 to skip interactive prompts
|
|
2690
3126
|
VERBOSE Set to 1 for verbose output
|
|
2691
3127
|
|
|
@@ -2735,27 +3171,36 @@ _prompt_install_confirm() {
|
|
|
2735
3171
|
echo -e " commercial use, competitive offerings, or redistribution."
|
|
2736
3172
|
echo ""
|
|
2737
3173
|
|
|
2738
|
-
if [[ $SKIP_DOCKER -eq 0 ]]; then
|
|
3174
|
+
if [[ $SKIP_DOCKER -eq 0 && "$OS" == "linux" ]]; then
|
|
2739
3175
|
echo -e " ${BOLD}Docker Engine${NC}"
|
|
2740
|
-
echo -e " ${MUTED}
|
|
2741
|
-
echo -e " ${MUTED}
|
|
2742
|
-
echo -e " ${MUTED}
|
|
2743
|
-
echo -e " ${MUTED}
|
|
3176
|
+
echo -e " ${MUTED} Docker Engine (container runtime — Linux)${NC}"
|
|
3177
|
+
echo -e " ${MUTED} URL : https://github.com/moby/moby${NC}"
|
|
3178
|
+
echo -e " ${MUTED} License : Apache License, Version 2.0${NC}"
|
|
3179
|
+
echo -e " ${MUTED} Author : Docker, Inc.${NC}"
|
|
3180
|
+
echo -e " ${MUTED} https://github.com/moby/moby/blob/master/LICENSE${NC}"
|
|
3181
|
+
echo ""
|
|
3182
|
+
fi
|
|
3183
|
+
if [[ $SKIP_DOCKER -eq 0 && "$OS" == "macos" ]]; then
|
|
3184
|
+
echo -e " ${BOLD}Colima${NC}"
|
|
3185
|
+
echo -e " ${MUTED} Colima (container runtime — macOS)${NC}"
|
|
3186
|
+
echo -e " ${MUTED} URL : https://github.com/abiosoft/colima${NC}"
|
|
3187
|
+
echo -e " ${MUTED} License : MIT License${NC}"
|
|
3188
|
+
echo -e " ${MUTED} https://github.com/abiosoft/colima/blob/main/LICENSE${NC}"
|
|
3189
|
+
echo -e " ${MUTED} Author : Abiola Ibrahim${NC}"
|
|
2744
3190
|
echo ""
|
|
2745
3191
|
fi
|
|
2746
3192
|
if [[ $SKIP_NOMAD -eq 0 ]]; then
|
|
2747
|
-
echo -e " ${BOLD}Nomad
|
|
2748
|
-
echo -e " ${MUTED}
|
|
2749
|
-
echo -e " ${MUTED}
|
|
2750
|
-
echo -e " ${MUTED}
|
|
2751
|
-
echo -e " ${MUTED}
|
|
2752
|
-
echo -e " ${MUTED}
|
|
3193
|
+
echo -e " ${BOLD}Nomad${NC}"
|
|
3194
|
+
echo -e " ${MUTED} Nomad v${NOMAD_VERSION} (last MPL 2.0 release in the 1.6.x line)${NC}"
|
|
3195
|
+
echo -e " ${MUTED} URL : https://github.com/hashicorp/nomad/tree/v${NOMAD_VERSION}${NC}"
|
|
3196
|
+
echo -e " ${MUTED} License : Mozilla Public License 2.0${NC}"
|
|
3197
|
+
echo -e " ${MUTED} https://github.com/hashicorp/nomad/blob/v${NOMAD_VERSION}/LICENSE${NC}"
|
|
3198
|
+
echo -e " ${MUTED} Author : HashiCorp, Inc.${NC}"
|
|
2753
3199
|
echo ""
|
|
2754
3200
|
fi
|
|
2755
|
-
echo -e " ${
|
|
2756
|
-
echo -e "
|
|
2757
|
-
echo -e "
|
|
2758
|
-
echo -e " ${MUTED}sudo privileges are required to write to system directories.${NC}"
|
|
3201
|
+
echo -e " ${ACCENT}─────────────────────────────────────────────────────────${NC}"
|
|
3202
|
+
echo -e " By continuing you acknowledge that you have read the above"
|
|
3203
|
+
echo -e " notices and agree to each package's license terms."
|
|
2759
3204
|
echo ""
|
|
2760
3205
|
} >/dev/tty
|
|
2761
3206
|
|
|
@@ -2767,6 +3212,9 @@ _prompt_install_confirm() {
|
|
|
2767
3212
|
exit 0
|
|
2768
3213
|
;;
|
|
2769
3214
|
esac
|
|
3215
|
+
|
|
3216
|
+
echo "" >/dev/tty
|
|
3217
|
+
echo -e " ${INFO}sudo privileges are required to write to system directories.${NC}" >/dev/tty
|
|
2770
3218
|
echo "" >/dev/tty
|
|
2771
3219
|
}
|
|
2772
3220
|
|
|
@@ -2885,7 +3333,7 @@ _jishu_install_main() {
|
|
|
2885
3333
|
# so the keepalive's early sudo -n true calls are intentional no-ops.
|
|
2886
3334
|
if [[ $EUID -ne 0 ]] && command -v sudo &>/dev/null; then
|
|
2887
3335
|
if [[ -z "${_SUDO_KEEPALIVE_PID:-}" ]]; then
|
|
2888
|
-
( while true; do sudo -n true 2>/dev/null; sleep 60; done ) &
|
|
3336
|
+
( while true; do sudo -n true 2>/dev/null; sleep 60; done ) &>/dev/null &
|
|
2889
3337
|
_SUDO_KEEPALIVE_PID=$!
|
|
2890
3338
|
disown "$_SUDO_KEEPALIVE_PID" 2>/dev/null || true
|
|
2891
3339
|
fi
|
|
@@ -2900,10 +3348,6 @@ _jishu_install_main() {
|
|
|
2900
3348
|
detect_os
|
|
2901
3349
|
detect_arch
|
|
2902
3350
|
show_install_plan --with-jishushell
|
|
2903
|
-
if [[ "$OS" == "macos" ]]; then
|
|
2904
|
-
ui_warn "macOS is not supported yet — coming soon!"
|
|
2905
|
-
exit 0
|
|
2906
|
-
fi
|
|
2907
3351
|
_prompt_install_confirm
|
|
2908
3352
|
check_sudo
|
|
2909
3353
|
ensure_prerequisites
|