jishushell 0.4.24 → 0.4.30

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.
Files changed (167) hide show
  1. package/INSTALL-NOTICE +11 -0
  2. package/apps/browserless-chromium-container.yaml +78 -0
  3. package/apps/hermes-container.yaml +36 -2
  4. package/apps/ollama-binary.yaml +91 -90
  5. package/apps/ollama-cpu-container.yaml +8 -1
  6. package/apps/ollama-with-hollama-binary.yaml +91 -90
  7. package/apps/openclaw-binary.yaml +30 -1
  8. package/apps/openclaw-container.yaml +37 -2
  9. package/apps/openclaw-with-ollama-container.yaml +11 -2
  10. package/apps/openclaw-with-searxng-container.yaml +22 -2
  11. package/apps/openwebui-container.yaml +45 -1
  12. package/apps/playwright-container.yaml +7 -1
  13. package/apps/searxng-container.yaml +54 -4
  14. package/dist/cli/app.js +79 -9
  15. package/dist/cli/app.js.map +1 -1
  16. package/dist/cli/doctor.d.ts +12 -12
  17. package/dist/cli/doctor.js +242 -55
  18. package/dist/cli/doctor.js.map +1 -1
  19. package/dist/cli/llm.d.ts +4 -3
  20. package/dist/cli/llm.js +4 -3
  21. package/dist/cli/llm.js.map +1 -1
  22. package/dist/cli/panel.d.ts +6 -5
  23. package/dist/cli/panel.js +10 -9
  24. package/dist/cli/panel.js.map +1 -1
  25. package/dist/control.d.ts +7 -6
  26. package/dist/control.js +7 -6
  27. package/dist/control.js.map +1 -1
  28. package/dist/routes/agent-apps.d.ts +1 -1
  29. package/dist/routes/agent-apps.js +1 -1
  30. package/dist/routes/apps.js +44 -11
  31. package/dist/routes/apps.js.map +1 -1
  32. package/dist/routes/auth.js +3 -0
  33. package/dist/routes/auth.js.map +1 -1
  34. package/dist/routes/instances.js +787 -16
  35. package/dist/routes/instances.js.map +1 -1
  36. package/dist/routes/llm.js +24 -35
  37. package/dist/routes/llm.js.map +1 -1
  38. package/dist/routes/setup.js +1 -1
  39. package/dist/routes/setup.js.map +1 -1
  40. package/dist/server.d.ts +9 -0
  41. package/dist/server.js +410 -17
  42. package/dist/server.js.map +1 -1
  43. package/dist/services/agent-apps/catalog.js +4 -3
  44. package/dist/services/agent-apps/catalog.js.map +1 -1
  45. package/dist/services/agent-apps/index.d.ts +1 -1
  46. package/dist/services/agent-apps/index.js +1 -1
  47. package/dist/services/agent-apps/installers/adapter.d.ts +1 -1
  48. package/dist/services/agent-apps/installers/adapter.js +1 -1
  49. package/dist/services/agent-apps/installers/shell-script.d.ts +1 -1
  50. package/dist/services/agent-apps/installers/shell-script.js +3 -3
  51. package/dist/services/agent-apps/installers/shell-script.js.map +1 -1
  52. package/dist/services/agent-apps/types.d.ts +2 -2
  53. package/dist/services/agent-apps/types.js +1 -1
  54. package/dist/services/app/app-manager.d.ts +24 -1
  55. package/dist/services/app/app-manager.js +664 -116
  56. package/dist/services/app/app-manager.js.map +1 -1
  57. package/dist/services/app/hermes-agent-manager.js +6 -4
  58. package/dist/services/app/hermes-agent-manager.js.map +1 -1
  59. package/dist/services/app/provide-resolver.d.ts +29 -0
  60. package/dist/services/app/provide-resolver.js +112 -0
  61. package/dist/services/app/provide-resolver.js.map +1 -0
  62. package/dist/services/capability-endpoint-validator.d.ts +41 -0
  63. package/dist/services/capability-endpoint-validator.js +104 -0
  64. package/dist/services/capability-endpoint-validator.js.map +1 -0
  65. package/dist/services/capability-health.d.ts +16 -0
  66. package/dist/services/capability-health.js +121 -0
  67. package/dist/services/capability-health.js.map +1 -0
  68. package/dist/services/capability-registry.d.ts +106 -0
  69. package/dist/services/capability-registry.js +313 -0
  70. package/dist/services/capability-registry.js.map +1 -0
  71. package/dist/services/connection-apply.d.ts +89 -0
  72. package/dist/services/connection-apply.js +421 -0
  73. package/dist/services/connection-apply.js.map +1 -0
  74. package/dist/services/connection-resolver.d.ts +65 -0
  75. package/dist/services/connection-resolver.js +281 -0
  76. package/dist/services/connection-resolver.js.map +1 -0
  77. package/dist/services/connection-transactor.d.ts +37 -0
  78. package/dist/services/connection-transactor.js +341 -0
  79. package/dist/services/connection-transactor.js.map +1 -0
  80. package/dist/services/instance-manager.d.ts +13 -0
  81. package/dist/services/instance-manager.js +137 -23
  82. package/dist/services/instance-manager.js.map +1 -1
  83. package/dist/services/llm-proxy/index.d.ts +16 -2
  84. package/dist/services/llm-proxy/index.js +48 -44
  85. package/dist/services/llm-proxy/index.js.map +1 -1
  86. package/dist/services/llm-proxy/probe.d.ts +6 -0
  87. package/dist/services/llm-proxy/probe.js +85 -0
  88. package/dist/services/llm-proxy/probe.js.map +1 -0
  89. package/dist/services/llm-proxy/ssrf.d.ts +1 -0
  90. package/dist/services/llm-proxy/ssrf.js +18 -7
  91. package/dist/services/llm-proxy/ssrf.js.map +1 -1
  92. package/dist/services/nomad-manager.js +375 -16
  93. package/dist/services/nomad-manager.js.map +1 -1
  94. package/dist/services/process-manager.js +1 -1
  95. package/dist/services/process-manager.js.map +1 -1
  96. package/dist/services/runtime/adapters/hermes.d.ts +30 -1
  97. package/dist/services/runtime/adapters/hermes.js +218 -5
  98. package/dist/services/runtime/adapters/hermes.js.map +1 -1
  99. package/dist/services/runtime/adapters/openclaw-mcporter.d.ts +45 -0
  100. package/dist/services/runtime/adapters/openclaw-mcporter.js +108 -0
  101. package/dist/services/runtime/adapters/openclaw-mcporter.js.map +1 -0
  102. package/dist/services/runtime/adapters/openclaw.d.ts +87 -0
  103. package/dist/services/runtime/adapters/openclaw.js +250 -2
  104. package/dist/services/runtime/adapters/openclaw.js.map +1 -1
  105. package/dist/services/runtime/mcp-shims/firewall.d.ts +26 -0
  106. package/dist/services/runtime/mcp-shims/firewall.js +129 -0
  107. package/dist/services/runtime/mcp-shims/firewall.js.map +1 -0
  108. package/dist/services/runtime/mcp-shims/searxng-shim.d.ts +27 -0
  109. package/dist/services/runtime/mcp-shims/searxng-shim.js +125 -0
  110. package/dist/services/runtime/mcp-shims/searxng-shim.js.map +1 -0
  111. package/dist/services/runtime/mcp-shims/write-mcp-entry.d.ts +83 -0
  112. package/dist/services/runtime/mcp-shims/write-mcp-entry.js +127 -0
  113. package/dist/services/runtime/mcp-shims/write-mcp-entry.js.map +1 -0
  114. package/dist/services/runtime/migrations.d.ts +8 -0
  115. package/dist/services/runtime/migrations.js +100 -0
  116. package/dist/services/runtime/migrations.js.map +1 -1
  117. package/dist/services/runtime/types.d.ts +15 -0
  118. package/dist/services/setup-manager.js +6 -6
  119. package/dist/services/setup-manager.js.map +1 -1
  120. package/dist/services/suggestions.d.ts +27 -0
  121. package/dist/services/suggestions.js +133 -0
  122. package/dist/services/suggestions.js.map +1 -0
  123. package/dist/services/task-registry.js +4 -2
  124. package/dist/services/task-registry.js.map +1 -1
  125. package/dist/services/telemetry/device-fingerprint.d.ts +1 -1
  126. package/dist/services/telemetry/device-fingerprint.js +1 -1
  127. package/dist/services/types-shim.d.ts +16 -0
  128. package/dist/services/types-shim.js +2 -0
  129. package/dist/services/types-shim.js.map +1 -0
  130. package/dist/types.d.ts +171 -1
  131. package/dist/utils/instance-lock.d.ts +22 -0
  132. package/dist/utils/instance-lock.js +48 -0
  133. package/dist/utils/instance-lock.js.map +1 -0
  134. package/dist/utils/safe-json.js +55 -22
  135. package/dist/utils/safe-json.js.map +1 -1
  136. package/install/jishu-install.sh +323 -27
  137. package/install/jishu-uninstall.sh +353 -20
  138. package/package.json +3 -1
  139. package/public/assets/Dashboard-rkWp-CXd.js +1 -0
  140. package/public/assets/{HermesChatPanel-mFSureyc.js → HermesChatPanel-_GHoklgo.js} +1 -1
  141. package/public/assets/HermesConfigForm-anDnwUp_.js +4 -0
  142. package/public/assets/{InitPassword-CVA8wQA6.js → InitPassword-ZU9_-hDr.js} +1 -1
  143. package/public/assets/InstanceDetail-CN0FH1aw.js +92 -0
  144. package/public/assets/{Login-BWsZH2mu.js → Login-BItXqYAJ.js} +1 -1
  145. package/public/assets/NewInstance-BousE6kY.js +1 -0
  146. package/public/assets/ProviderRecommendations-DFYj7Fb6.js +1 -0
  147. package/public/assets/Settings-Bttc6QmM.js +1 -0
  148. package/public/assets/Setup-Bsxx1zgj.js +1 -0
  149. package/public/assets/{WeixinLoginPanel-CnjR8xMu.js → WeixinLoginPanel-DPZpAKgO.js} +2 -2
  150. package/public/assets/index-8xZy1z5k.css +1 -0
  151. package/public/assets/index-Dw3HhUYE.js +19 -0
  152. package/public/assets/providers-DtNXh9JD.js +1 -0
  153. package/public/assets/registry-5s2UB6is.js +2 -0
  154. package/public/index.html +2 -2
  155. package/scripts/check-app-spec.mjs +443 -0
  156. package/scripts/check-i18n.mjs +154 -0
  157. package/scripts/run.sh +4 -4
  158. package/public/assets/Dashboard-B-JoOjBQ.js +0 -1
  159. package/public/assets/HermesConfigForm-DvR05LK1.js +0 -4
  160. package/public/assets/InstanceDetail-DcZW2QGO.js +0 -91
  161. package/public/assets/NewInstance-BCIrAd86.js +0 -1
  162. package/public/assets/Settings-xkDcduFz.js +0 -1
  163. package/public/assets/Setup-Cfuwj4gV.js +0 -1
  164. package/public/assets/index-CPhVFEsx.css +0 -1
  165. package/public/assets/index-DQsM6Joa.js +0 -19
  166. package/public/assets/providers-V-vwrExZ.js +0 -1
  167. package/public/assets/registry-B4UFJdpA.js +0 -2
@@ -361,12 +361,8 @@ detect_os() {
361
361
  OS_ID="macos"
362
362
  OS_VERSION="$(sw_vers -productVersion 2>/dev/null || echo "unknown")"
363
363
  OS_NAME="macOS ${OS_VERSION}"
364
- if command -v brew &>/dev/null; then
365
- PKG_MANAGER="brew"
366
- else
367
- PKG_MANAGER="none"
368
- ui_warn "Homebrew not found — some optional installs may be skipped"
369
- fi
364
+ # macOS bootstrap always targets Homebrew even if it is not installed yet.
365
+ PKG_MANAGER="brew"
370
366
  ui_success "OS: ${OS_NAME} (package manager: ${PKG_MANAGER})"
371
367
  return 0
372
368
  fi
@@ -461,7 +457,7 @@ check_sudo() {
461
457
  ui_error "Failed to obtain sudo privileges (no interactive TTY available)"
462
458
  exit 1
463
459
  fi
464
- if ! sudo -v </dev/tty; then
460
+ if ! sudo -p "Password: " -v </dev/tty; then
465
461
  ui_error "Failed to obtain sudo privileges"
466
462
  exit 1
467
463
  fi
@@ -640,6 +636,138 @@ _pkg_is_installed() {
640
636
  esac
641
637
  }
642
638
 
639
+ _load_homebrew_shellenv() {
640
+ local brew_bin=""
641
+
642
+ if command -v brew >/dev/null 2>&1; then
643
+ brew_bin="$(command -v brew)"
644
+ elif [[ -x "/opt/homebrew/bin/brew" ]]; then
645
+ brew_bin="/opt/homebrew/bin/brew"
646
+ elif [[ -x "/usr/local/bin/brew" ]]; then
647
+ brew_bin="/usr/local/bin/brew"
648
+ fi
649
+
650
+ if [[ -z "$brew_bin" ]]; then
651
+ return 1
652
+ fi
653
+
654
+ eval "$(${brew_bin} shellenv 2>/dev/null)"
655
+ command -v brew >/dev/null 2>&1
656
+ }
657
+
658
+ _run_as_real_user() {
659
+ if [[ $EUID -eq 0 && -n "${REAL_USER:-}" && "${REAL_USER}" != "root" ]]; then
660
+ sudo -u "${REAL_USER}" -H env HOME="${REAL_HOME}" "$@"
661
+ return $?
662
+ fi
663
+ "$@"
664
+ }
665
+
666
+ _has_macos_command_line_tools() {
667
+ local dev_dir=""
668
+ dev_dir="$(xcode-select -p 2>/dev/null || true)"
669
+
670
+ if [[ -z "$dev_dir" || ! -d "$dev_dir" ]]; then
671
+ return 1
672
+ fi
673
+
674
+ if [[ -x "${dev_dir}/usr/bin/clang" ]]; then
675
+ return 0
676
+ fi
677
+
678
+ if [[ -x "${dev_dir}/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang" ]]; then
679
+ return 0
680
+ fi
681
+
682
+ return 1
683
+ }
684
+
685
+ ensure_macos_command_line_tools() {
686
+ if [[ "$OS" != "macos" ]]; then
687
+ return 0
688
+ fi
689
+
690
+ if _has_macos_command_line_tools; then
691
+ ui_success "${ACCENT}${BOLD}Xcode Command Line Tools${NC} is ready"
692
+ return 0
693
+ fi
694
+
695
+ if [[ "$DRY_RUN" == "1" ]]; then
696
+ ui_info "[dry-run] Would run: ${ACCENT}${BOLD}xcode-select --install${NC}"
697
+ ui_info "[dry-run] Would stop here until ${ACCENT}${BOLD}Command Line Tools${NC} installation completes"
698
+ return 0
699
+ fi
700
+
701
+ ui_warn "${ACCENT}${BOLD}Xcode Command Line Tools${NC} not found — requesting installation dialog..."
702
+ xcode-select --install >/dev/null 2>&1 || true
703
+ ui_warn "Watch for the ${ACCENT}${BOLD}installer popup${NC} or the ${ACCENT}${BOLD}Software Update${NC} indicator in the ${ACCENT}${BOLD}Dock${NC}."
704
+ ui_warn "If nothing appears, run ${ACCENT}${BOLD}xcode-select --install${NC} manually in Terminal."
705
+ ui_info "After ${ACCENT}${BOLD}Command Line Tools${NC} finish installing, run this installer again."
706
+ exit 1
707
+ }
708
+
709
+ install_homebrew() {
710
+ local brew_install_script=""
711
+
712
+ if [[ "$DRY_RUN" == "1" ]]; then
713
+ ui_info "[dry-run] Would install Homebrew"
714
+ PKG_MANAGER="brew"
715
+ return 0
716
+ fi
717
+
718
+ brew_install_script="$(mktemp)"
719
+ trap "rm -f '$brew_install_script'" RETURN
720
+
721
+ ui_info "Installing Homebrew..."
722
+ if ! retry_net "Download Homebrew install script" 3 curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh -o "$brew_install_script"; then
723
+ ui_error "Failed to download Homebrew installer"
724
+ return 1
725
+ fi
726
+
727
+ if ! _run_as_real_user env NONINTERACTIVE=1 CI=1 bash "$brew_install_script"; then
728
+ ui_error "Homebrew installation failed"
729
+ return 1
730
+ fi
731
+
732
+ rm -f "$brew_install_script"
733
+ trap - RETURN
734
+
735
+ if ! _load_homebrew_shellenv; then
736
+ ui_error "Homebrew installed but could not be loaded into PATH"
737
+ ui_error "Try running one of these, then re-run the installer:"
738
+ ui_error " eval \"\$(/opt/homebrew/bin/brew shellenv)\""
739
+ ui_error " eval \"\$(/usr/local/bin/brew shellenv)\""
740
+ return 1
741
+ fi
742
+
743
+ PKG_MANAGER="brew"
744
+ ui_success "Homebrew installed: $(brew --version 2>/dev/null | head -n1)"
745
+ }
746
+
747
+ ensure_macos_homebrew() {
748
+ if [[ "$OS" != "macos" ]]; then
749
+ return 0
750
+ fi
751
+
752
+ if _load_homebrew_shellenv; then
753
+ PKG_MANAGER="brew"
754
+ ui_success "Homebrew is ready: $(brew --version 2>/dev/null | head -n1)"
755
+ return 0
756
+ fi
757
+
758
+ install_homebrew
759
+ }
760
+
761
+ ensure_macos_prerequisites() {
762
+ if [[ "$OS" != "macos" ]]; then
763
+ return 0
764
+ fi
765
+
766
+ ui_section "macOS prerequisites"
767
+ ensure_macos_command_line_tools
768
+ ensure_macos_homebrew
769
+ }
770
+
643
771
  # ═══════════════════════════════════════════════════════════════════════════════
644
772
  # npm install failure detection and auto-fix
645
773
  # ═══════════════════════════════════════════════════════════════════════════════
@@ -721,17 +849,11 @@ install_build_tools_linux() {
721
849
 
722
850
  install_build_tools_macos() {
723
851
  local ok=true
724
- if ! xcode-select -p >/dev/null 2>&1; then
725
- ui_info "Installing Xcode Command Line Tools (required for make/clang)"
726
- xcode-select --install >/dev/null 2>&1 || true
727
- if ! xcode-select -p >/dev/null 2>&1; then
728
- ui_warn "Xcode Command Line Tools are not ready yet"
729
- ui_info "Complete the installer dialog, then re-run this installer"
730
- ok=false
731
- fi
852
+ if ! ensure_macos_command_line_tools; then
853
+ ok=false
732
854
  fi
733
855
  if ! command -v cmake >/dev/null 2>&1; then
734
- if command -v brew >/dev/null 2>&1; then
856
+ if ensure_macos_homebrew; then
735
857
  run_quiet_step "Installing cmake" brew install cmake
736
858
  else
737
859
  ui_warn "Homebrew not available; cannot auto-install cmake"
@@ -846,6 +968,14 @@ _load_nvm() {
846
968
  install_node() {
847
969
  ui_stage "Node.js (via nvm)"
848
970
 
971
+ # On macOS, ensure Command Line Tools are installed BEFORE npm-gyp modules
972
+ # during nvm/Node.js build, not just before Homebrew
973
+ if [[ "${OS:-}" == "macos" ]]; then
974
+ if ! ensure_macos_command_line_tools; then
975
+ return 1
976
+ fi
977
+ fi
978
+
849
979
  _load_nvm 2>/dev/null || true
850
980
 
851
981
  if command -v node &>/dev/null; then
@@ -974,7 +1104,8 @@ install_docker() {
974
1104
  ui_info "Starting Colima VM (profile: ${_COLIMA_PROFILE})..."
975
1105
  mkdir -p "${_COLIMA_HOME}"
976
1106
  _colima start "${_COLIMA_PROFILE}" \
977
- --vm-type vz --mount-type virtiofs --network-address \
1107
+ --vm-type vz --mount-type virtiofs \
1108
+ --network-host-addresses --arch aarch64 \
978
1109
  --activate=false --cpu 2 --memory 4 --disk 60 >/dev/null \
979
1110
  || { ui_warn "colima start failed — run 'COLIMA_HOME=${_COLIMA_HOME} colima start ${_COLIMA_PROFILE}' manually"; return 1; }
980
1111
  export DOCKER_HOST="unix://${_COLIMA_SOCKET}"
@@ -1054,8 +1185,7 @@ _do_install_docker() {
1054
1185
 
1055
1186
  if [[ "$OS" == "macos" ]]; then
1056
1187
  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."
1188
+ if ! ensure_macos_homebrew; then
1059
1189
  return 1
1060
1190
  fi
1061
1191
  brew install -q docker colima || { ui_warn "brew install failed"; return 1; }
@@ -1064,7 +1194,8 @@ _do_install_docker() {
1064
1194
  mkdir -p "${_COLIMA_HOME}"
1065
1195
  ui_info "Starting Colima VM (profile: ${_COLIMA_PROFILE})..."
1066
1196
  _colima start "${_COLIMA_PROFILE}" \
1067
- --vm-type vz --mount-type virtiofs --network-address \
1197
+ --vm-type vz --mount-type virtiofs \
1198
+ --network-host-addresses --arch aarch64 \
1068
1199
  --activate=false --cpu 2 --memory 4 --disk 60 >/dev/null \
1069
1200
  || { ui_warn "colima start failed — run 'COLIMA_HOME=${_COLIMA_HOME} colima start ${_COLIMA_PROFILE}' manually"; return 1; }
1070
1201
  ui_success "Colima is running"
@@ -1230,7 +1361,8 @@ _ensure_docker_running() {
1230
1361
  ui_info "Starting Colima VM..."
1231
1362
  mkdir -p "${_COLIMA_HOME}"
1232
1363
  if ! _colima start "${_COLIMA_PROFILE}" \
1233
- --vm-type vz --mount-type virtiofs --network-address \
1364
+ --vm-type vz --mount-type virtiofs \
1365
+ --network-host-addresses --arch aarch64 \
1234
1366
  --activate=false --cpu 2 --memory 4 --disk 60 >/dev/null; then
1235
1367
  ui_warn "colima start failed"
1236
1368
  ui_info "Run manually: COLIMA_HOME=${_COLIMA_HOME} colima start ${_COLIMA_PROFILE}"
@@ -2051,6 +2183,12 @@ install_nomad_systemd() {
2051
2183
  local config_file="${nomad_config_dir}/nomad.hcl"
2052
2184
 
2053
2185
  if [[ "$OS" == "macos" ]]; then
2186
+ # Install colima's launchd agent first so it is started at the next
2187
+ # login alongside nomad (colima starts the VM, nomad's wrapper waits
2188
+ # for it). Order of installation here doesn't change runtime order
2189
+ # — both fire RunAtLoad — but we install colima first so the VM is
2190
+ # up before we kickstart nomad below in run_install_components.
2191
+ _install_colima_launchd
2054
2192
  _install_nomad_launchd
2055
2193
  return $?
2056
2194
  fi
@@ -2118,6 +2256,114 @@ WantedBy=multi-user.target"
2118
2256
  fi
2119
2257
  }
2120
2258
 
2259
+ # Colima launchd agent: auto-start the JishuShell Colima VM on user login.
2260
+ # Without this, after every Mac reboot the user has to run `colima start
2261
+ # jishushell` manually, and Nomad (started earlier by launchd) caches
2262
+ # docker.Healthy=False because docker is not yet reachable.
2263
+ _install_colima_launchd() {
2264
+ local plist_label="com.jishushell.colima"
2265
+ local plist_path="${HOME}/Library/LaunchAgents/${plist_label}.plist"
2266
+ local log_path="${JISHUSHELL_HOME}/colima/colima-launchd.log"
2267
+ local colima_bin
2268
+ colima_bin="$(command -v colima 2>/dev/null || echo /opt/homebrew/bin/colima)"
2269
+
2270
+ if [[ "$DRY_RUN" == "1" ]]; then
2271
+ ui_info "[dry-run] Would install launchd agent: ${plist_path}"
2272
+ return 0
2273
+ fi
2274
+
2275
+ mkdir -p "${HOME}/Library/LaunchAgents" "${JISHUSHELL_HOME}/colima"
2276
+
2277
+ # `colima start` exits cleanly once the VM is up, so KeepAlive must be
2278
+ # false — otherwise launchd restarts colima start in an infinite loop.
2279
+ # The flags here mirror the ones jishushell itself uses on first install
2280
+ # (network-host-addresses + arch aarch64), so a relaunched VM gets the
2281
+ # same network config; --activate=false keeps the user's docker context
2282
+ # selection in ~/.docker/config.json untouched.
2283
+ cat > "$plist_path" << PLIST
2284
+ <?xml version="1.0" encoding="UTF-8"?>
2285
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
2286
+ <plist version="1.0">
2287
+ <dict>
2288
+ <key>Label</key>
2289
+ <string>${plist_label}</string>
2290
+ <key>ProgramArguments</key>
2291
+ <array>
2292
+ <string>${colima_bin}</string>
2293
+ <string>start</string>
2294
+ <string>${_COLIMA_PROFILE}</string>
2295
+ <string>--vm-type</string><string>vz</string>
2296
+ <string>--mount-type</string><string>virtiofs</string>
2297
+ <string>--network-host-addresses</string>
2298
+ <string>--arch</string><string>aarch64</string>
2299
+ <string>--activate=false</string>
2300
+ <string>--cpu</string><string>2</string>
2301
+ <string>--memory</string><string>4</string>
2302
+ <string>--disk</string><string>60</string>
2303
+ </array>
2304
+ <key>EnvironmentVariables</key>
2305
+ <dict>
2306
+ <key>COLIMA_HOME</key>
2307
+ <string>${_COLIMA_HOME}</string>
2308
+ <key>PATH</key>
2309
+ <string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin</string>
2310
+ </dict>
2311
+ <key>RunAtLoad</key>
2312
+ <true/>
2313
+ <key>KeepAlive</key>
2314
+ <false/>
2315
+ <key>StandardOutPath</key>
2316
+ <string>${log_path}</string>
2317
+ <key>StandardErrorPath</key>
2318
+ <string>${log_path}</string>
2319
+ </dict>
2320
+ </plist>
2321
+ PLIST
2322
+
2323
+ launchctl unload "$plist_path" 2>/dev/null || true
2324
+ if launchctl load -w "$plist_path" 2>/dev/null; then
2325
+ ui_success "Colima launchd agent installed (auto-start at login)"
2326
+ else
2327
+ ui_warn "Could not load colima launchd agent"
2328
+ fi
2329
+ }
2330
+
2331
+ # Wrapper script that nomad's launchd plist invokes. Polls the JishuShell
2332
+ # private docker socket (i.e. waits for Colima to be ready) before exec'ing
2333
+ # the real `nomad agent`. Without this gate, after a Mac reboot the OS
2334
+ # launches every LaunchAgent in parallel, nomad starts before colima
2335
+ # finishes booting, the docker driver fingerprint ends up Healthy=False,
2336
+ # and that bad cache survives until something restarts nomad — leaving
2337
+ # every job stuck in `pending` with `missing drivers: 1`.
2338
+ _install_nomad_wait_docker_wrapper() {
2339
+ local wrapper="${JISHUSHELL_BIN_DIR}/nomad-launchd-wrapper.sh"
2340
+ local nomad_bin="${JISHUSHELL_BIN_DIR}/nomad"
2341
+ local config_file="${JISHUSHELL_HOME}/nomad/nomad.hcl"
2342
+ local docker_sock="${_COLIMA_SOCKET}"
2343
+
2344
+ mkdir -p "${JISHUSHELL_BIN_DIR}"
2345
+ cat > "$wrapper" << WRAPPER
2346
+ #!/bin/bash
2347
+ # Auto-generated by jishu-install.sh — do not edit by hand.
2348
+ # Wait for the JishuShell Colima docker socket to come up, then exec nomad.
2349
+ export DOCKER_HOST="unix://${docker_sock}"
2350
+ export PATH="/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
2351
+
2352
+ deadline=\$(( \$(date +%s) + 180 ))
2353
+ while ! /usr/bin/env docker info >/dev/null 2>&1; do
2354
+ if [ \$(date +%s) -ge \$deadline ]; then
2355
+ echo "[nomad-launchd-wrapper] timed out waiting for docker after 180s; starting nomad anyway" >&2
2356
+ break
2357
+ fi
2358
+ sleep 2
2359
+ done
2360
+
2361
+ exec "${nomad_bin}" agent -config="${config_file}"
2362
+ WRAPPER
2363
+ chmod 755 "$wrapper"
2364
+ echo "$wrapper"
2365
+ }
2366
+
2121
2367
  _install_nomad_launchd() {
2122
2368
  local nomad_bin="${JISHUSHELL_BIN_DIR}/nomad"
2123
2369
  local nomad_config_dir="${JISHUSHELL_HOME}/nomad"
@@ -2138,6 +2384,11 @@ _install_nomad_launchd() {
2138
2384
  # pick the wrong socket (Docker Desktop or /var/run/docker.sock).
2139
2385
  local docker_sock="${_COLIMA_SOCKET}"
2140
2386
 
2387
+ # Wrap nomad in a docker-readiness gate so boot ordering doesn't poison
2388
+ # the docker driver fingerprint. See _install_nomad_wait_docker_wrapper.
2389
+ local nomad_wrapper
2390
+ nomad_wrapper="$(_install_nomad_wait_docker_wrapper)"
2391
+
2141
2392
  cat > "$plist_path" << PLIST
2142
2393
  <?xml version="1.0" encoding="UTF-8"?>
2143
2394
  <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
@@ -2147,9 +2398,7 @@ _install_nomad_launchd() {
2147
2398
  <string>${plist_label}</string>
2148
2399
  <key>ProgramArguments</key>
2149
2400
  <array>
2150
- <string>${nomad_bin}</string>
2151
- <string>agent</string>
2152
- <string>-config=${config_file}</string>
2401
+ <string>${nomad_wrapper}</string>
2153
2402
  </array>
2154
2403
  <key>EnvironmentVariables</key>
2155
2404
  <dict>
@@ -2454,6 +2703,9 @@ show_install_plan() {
2454
2703
  ui_kv "OS" "$OS_NAME"
2455
2704
  ui_kv "Package manager" "$PKG_MANAGER"
2456
2705
  ui_kv "Architecture" "$ARCH"
2706
+ if [[ "$OS" == "macos" ]]; then
2707
+ ui_kv "macOS bootstrap" "Xcode Command Line Tools + Homebrew"
2708
+ fi
2457
2709
  echo ""
2458
2710
  ui_kv "Node.js" "$(if [[ $SKIP_NODE -eq 1 ]]; then echo 'skip'; else echo "v${NODE_VERSION} via nvm v${NVM_VERSION}"; fi)"
2459
2711
  ui_kv "Docker" "$(if [[ $SKIP_DOCKER -eq 1 ]]; then echo 'skip'; else echo 'latest stable'; fi)"
@@ -2868,7 +3120,6 @@ Environment=HOME=${REAL_HOME}
2868
3120
  Environment=JISHUSHELL_HOME=${JISHUSHELL_HOME}
2869
3121
  ProtectSystem=strict
2870
3122
  PrivateTmp=true
2871
- NoNewPrivileges=true
2872
3123
  ReadWritePaths=${JISHUSHELL_HOME} /etc/jishushell
2873
3124
 
2874
3125
  [Install]
@@ -2970,6 +3221,26 @@ run_install_components() {
2970
3221
  ui_warn "Skipped — Docker is not available (installation failed or not installed)"
2971
3222
  fi
2972
3223
 
3224
+ # Once docker is verified working (Colima up + image pulled on macOS,
3225
+ # native daemon up on Linux), bounce Nomad so its docker driver
3226
+ # fingerprint is fresh. Without this, Nomad — which `install_nomad`
3227
+ # started above before docker was reachable on macOS — keeps a cached
3228
+ # `docker.Healthy=False` and rejects every job with `missing drivers:1`
3229
+ # until something restarts it. Belt-and-suspenders with the wrapper
3230
+ # script in _install_nomad_launchd: covers install-time as well as
3231
+ # the boot-time path.
3232
+ if [[ $docker_ok -eq 1 && $SKIP_NOMAD -eq 0 ]]; then
3233
+ if [[ "$OS" == "macos" ]]; then
3234
+ launchctl kickstart -k "gui/$(id -u)/com.jishushell.nomad" 2>/dev/null \
3235
+ && ui_info "Restarted Nomad to refresh docker driver fingerprint" \
3236
+ || true
3237
+ elif command -v systemctl &>/dev/null && systemctl is-enabled nomad &>/dev/null 2>&1; then
3238
+ ${SUDO} systemctl restart nomad 2>/dev/null \
3239
+ && ui_info "Restarted Nomad to refresh docker driver fingerprint" \
3240
+ || true
3241
+ fi
3242
+ fi
3243
+
2973
3244
  if [[ $with_jishushell -eq 1 ]]; then
2974
3245
  if [[ $SKIP_JISHUSHELL -eq 1 ]]; then
2975
3246
  ui_stage "JishuShell"
@@ -3001,8 +3272,15 @@ run_install_components() {
3001
3272
  ${SUDO} find "${JISHUSHELL_HOME}" -type f -exec chmod 644 {} + 2>/dev/null || true
3002
3273
  # Executables in bin/ must keep the execute bit
3003
3274
  ${SUDO} find "${JISHUSHELL_BIN_DIR}" -type f -exec chmod 755 {} + 2>/dev/null || true
3004
- # Sensitive files: owner-only read (600)
3005
- for f in auth.json jwt-secret panel.json nomad.env encryption-key; do
3275
+ # Sensitive files: owner-only read (600). The find above blanket-set
3276
+ # 644 on every file under JISHUSHELL_HOME, including secrets — restore
3277
+ # them here. MUST include the Colima/lima VM SSH private key:
3278
+ # OpenSSH refuses any key with group/world-readable perms ("bad
3279
+ # permissions"), so a 644 key file makes lima hostagent unable to
3280
+ # provision the VM (host-addresses + docker context never come up),
3281
+ # docker containers ship empty Ports{}, and the panel proxy 502s.
3282
+ for f in auth.json jwt-secret panel.json nomad.env encryption-key \
3283
+ colima/_lima/_config/user; do
3006
3284
  local fp="${JISHUSHELL_HOME}/${f}"
3007
3285
  [[ -f "$fp" ]] && ${SUDO} chmod 600 "$fp" 2>/dev/null || true
3008
3286
  done
@@ -3195,6 +3473,23 @@ _prompt_install_confirm() {
3195
3473
  echo -e " commercial use, competitive offerings, or redistribution."
3196
3474
  echo ""
3197
3475
 
3476
+ if [[ "$OS" == "macos" ]]; then
3477
+ echo -e " ${BOLD}Xcode Command Line Tools${NC}"
3478
+ echo -e " ${MUTED} Xcode Command Line Tools (build tools for macOS)${NC}"
3479
+ echo -e " ${MUTED} URL : https://www.apple.com/legal/sla/docs/xcode.pdf${NC}"
3480
+ echo -e " ${MUTED} License : Apple Xcode and Apple SDKs Agreement${NC}"
3481
+ echo -e " ${MUTED} Author : Apple Inc.${NC}"
3482
+ echo ""
3483
+
3484
+ echo -e " ${BOLD}Homebrew${NC}"
3485
+ echo -e " ${MUTED} Homebrew (package manager for macOS)${NC}"
3486
+ echo -e " ${MUTED} URL : https://github.com/Homebrew/brew${NC}"
3487
+ echo -e " ${MUTED} License : BSD 2-Clause License${NC}"
3488
+ echo -e " ${MUTED} https://github.com/Homebrew/brew/blob/HEAD/LICENSE.txt${NC}"
3489
+ echo -e " ${MUTED} Author : Homebrew contributors${NC}"
3490
+ echo ""
3491
+ fi
3492
+
3198
3493
  if [[ $SKIP_DOCKER -eq 0 && "$OS" == "linux" ]]; then
3199
3494
  echo -e " ${BOLD}Docker Engine${NC}"
3200
3495
  echo -e " ${MUTED} Docker Engine (container runtime — Linux)${NC}"
@@ -3374,6 +3669,7 @@ _jishu_install_main() {
3374
3669
  show_install_plan --with-jishushell
3375
3670
  _prompt_install_confirm
3376
3671
  check_sudo
3672
+ ensure_macos_prerequisites
3377
3673
  ensure_prerequisites
3378
3674
  run_install_components --with-jishushell
3379
3675
  local rc=$?