jishushell 0.0.1 → 0.4.2-beta2

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 (139) hide show
  1. package/INSTALL-NOTICE +41 -0
  2. package/LICENSE +202 -0
  3. package/README.md +36 -0
  4. package/THIRD-PARTY-NOTICES +387 -0
  5. package/dist/auth.d.ts +6 -0
  6. package/dist/auth.js +88 -0
  7. package/dist/auth.js.map +1 -0
  8. package/dist/cli.d.ts +2 -0
  9. package/dist/cli.js +290 -0
  10. package/dist/cli.js.map +1 -0
  11. package/dist/config.d.ts +24 -0
  12. package/dist/config.js +226 -0
  13. package/dist/config.js.map +1 -0
  14. package/dist/constants.d.ts +3 -0
  15. package/dist/constants.js +15 -0
  16. package/dist/constants.js.map +1 -0
  17. package/dist/control.d.ts +44 -0
  18. package/dist/control.js +1359 -0
  19. package/dist/control.js.map +1 -0
  20. package/dist/crypto-shim.d.ts +1 -0
  21. package/dist/crypto-shim.js +2 -0
  22. package/dist/crypto-shim.js.map +1 -0
  23. package/dist/doctor.d.ts +46 -0
  24. package/dist/doctor.js +937 -0
  25. package/dist/doctor.js.map +1 -0
  26. package/dist/install.d.ts +27 -0
  27. package/dist/install.js +570 -0
  28. package/dist/install.js.map +1 -0
  29. package/dist/routes/auth.d.ts +4 -0
  30. package/dist/routes/auth.js +151 -0
  31. package/dist/routes/auth.js.map +1 -0
  32. package/dist/routes/instances.d.ts +2 -0
  33. package/dist/routes/instances.js +1303 -0
  34. package/dist/routes/instances.js.map +1 -0
  35. package/dist/routes/setup.d.ts +2 -0
  36. package/dist/routes/setup.js +139 -0
  37. package/dist/routes/setup.js.map +1 -0
  38. package/dist/routes/system.d.ts +2 -0
  39. package/dist/routes/system.js +102 -0
  40. package/dist/routes/system.js.map +1 -0
  41. package/dist/server.d.ts +6 -0
  42. package/dist/server.js +392 -0
  43. package/dist/server.js.map +1 -0
  44. package/dist/services/instance-manager.d.ts +67 -0
  45. package/dist/services/instance-manager.js +1319 -0
  46. package/dist/services/instance-manager.js.map +1 -0
  47. package/dist/services/llm-proxy/adapters.d.ts +3 -0
  48. package/dist/services/llm-proxy/adapters.js +309 -0
  49. package/dist/services/llm-proxy/adapters.js.map +1 -0
  50. package/dist/services/llm-proxy/circuit-breaker.d.ts +9 -0
  51. package/dist/services/llm-proxy/circuit-breaker.js +73 -0
  52. package/dist/services/llm-proxy/circuit-breaker.js.map +1 -0
  53. package/dist/services/llm-proxy/encryption.d.ts +6 -0
  54. package/dist/services/llm-proxy/encryption.js +61 -0
  55. package/dist/services/llm-proxy/encryption.js.map +1 -0
  56. package/dist/services/llm-proxy/index.d.ts +24 -0
  57. package/dist/services/llm-proxy/index.js +708 -0
  58. package/dist/services/llm-proxy/index.js.map +1 -0
  59. package/dist/services/llm-proxy/rate-limiter.d.ts +1 -0
  60. package/dist/services/llm-proxy/rate-limiter.js +39 -0
  61. package/dist/services/llm-proxy/rate-limiter.js.map +1 -0
  62. package/dist/services/llm-proxy/sse.d.ts +10 -0
  63. package/dist/services/llm-proxy/sse.js +378 -0
  64. package/dist/services/llm-proxy/sse.js.map +1 -0
  65. package/dist/services/llm-proxy/ssrf.d.ts +16 -0
  66. package/dist/services/llm-proxy/ssrf.js +185 -0
  67. package/dist/services/llm-proxy/ssrf.js.map +1 -0
  68. package/dist/services/llm-proxy/types.d.ts +52 -0
  69. package/dist/services/llm-proxy/types.js +2 -0
  70. package/dist/services/llm-proxy/types.js.map +1 -0
  71. package/dist/services/llm-proxy/usage.d.ts +12 -0
  72. package/dist/services/llm-proxy/usage.js +108 -0
  73. package/dist/services/llm-proxy/usage.js.map +1 -0
  74. package/dist/services/nomad-manager.d.ts +22 -0
  75. package/dist/services/nomad-manager.js +828 -0
  76. package/dist/services/nomad-manager.js.map +1 -0
  77. package/dist/services/plugin-installer.d.ts +22 -0
  78. package/dist/services/plugin-installer.js +102 -0
  79. package/dist/services/plugin-installer.js.map +1 -0
  80. package/dist/services/process-manager.d.ts +25 -0
  81. package/dist/services/process-manager.js +531 -0
  82. package/dist/services/process-manager.js.map +1 -0
  83. package/dist/services/setup-manager.d.ts +93 -0
  84. package/dist/services/setup-manager.js +1922 -0
  85. package/dist/services/setup-manager.js.map +1 -0
  86. package/dist/services/system-monitor.d.ts +1 -0
  87. package/dist/services/system-monitor.js +79 -0
  88. package/dist/services/system-monitor.js.map +1 -0
  89. package/dist/services/telemetry/activation.d.ts +12 -0
  90. package/dist/services/telemetry/activation.js +78 -0
  91. package/dist/services/telemetry/activation.js.map +1 -0
  92. package/dist/services/telemetry/client.d.ts +21 -0
  93. package/dist/services/telemetry/client.js +36 -0
  94. package/dist/services/telemetry/client.js.map +1 -0
  95. package/dist/services/telemetry/device-fingerprint.d.ts +18 -0
  96. package/dist/services/telemetry/device-fingerprint.js +123 -0
  97. package/dist/services/telemetry/device-fingerprint.js.map +1 -0
  98. package/dist/services/telemetry/heartbeat.d.ts +13 -0
  99. package/dist/services/telemetry/heartbeat.js +87 -0
  100. package/dist/services/telemetry/heartbeat.js.map +1 -0
  101. package/dist/services/telemetry/index.d.ts +3 -0
  102. package/dist/services/telemetry/index.js +4 -0
  103. package/dist/services/telemetry/index.js.map +1 -0
  104. package/dist/types.d.ts +51 -0
  105. package/dist/types.js +2 -0
  106. package/dist/types.js.map +1 -0
  107. package/dist/utils/safe-json.d.ts +2 -0
  108. package/dist/utils/safe-json.js +80 -0
  109. package/dist/utils/safe-json.js.map +1 -0
  110. package/dist/utils/ttl-cache.d.ts +29 -0
  111. package/dist/utils/ttl-cache.js +77 -0
  112. package/dist/utils/ttl-cache.js.map +1 -0
  113. package/install/jishu-install.sh +2920 -0
  114. package/install/jishu-uninstall.sh +811 -0
  115. package/install/post-install.sh +124 -0
  116. package/install/post-uninstall.sh +46 -0
  117. package/package.json +57 -8
  118. package/public/assets/Dashboard-Dxsq690N.js +1 -0
  119. package/public/assets/InitPassword-CslWYy8G.js +1 -0
  120. package/public/assets/InstanceDetail-DmEkMj-t.js +14 -0
  121. package/public/assets/Login-d45wtgVA.js +1 -0
  122. package/public/assets/NewInstance-Czp5-AJe.js +1 -0
  123. package/public/assets/Settings-BKMGck05.js +1 -0
  124. package/public/assets/Setup-D3rfLWjZ.js +1 -0
  125. package/public/assets/index-77Ug7feY.css +1 -0
  126. package/public/assets/index-DkDnIohs.js +16 -0
  127. package/public/assets/logo-black-theme-DywLAtFy.png +0 -0
  128. package/public/assets/logo-white-theme-DXffFAWw.png +0 -0
  129. package/public/assets/providers-lBSOjUWy.js +1 -0
  130. package/public/assets/usePolling-CqQ8hrNc.js +1 -0
  131. package/public/assets/vendor-i18n-Bvxxh8Di.js +9 -0
  132. package/public/assets/vendor-react-DONn7uBV.js +59 -0
  133. package/public/index.html +15 -0
  134. package/scripts/build-image.sh +55 -0
  135. package/scripts/run.sh +310 -0
  136. package/scripts/setup-pi.sh +80 -0
  137. package/scripts/start-feishu1.js +46 -0
  138. package/index.js +0 -0
  139. package/jishushell-0.0.1.tgz +0 -0
@@ -0,0 +1,811 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+
4
+ JISHU_SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
5
+ # shellcheck source=jishu-install.sh
6
+ source "${JISHU_SCRIPT_DIR}/jishu-install.sh"
7
+
8
+ DO_NODE=0
9
+ DO_DOCKER=0
10
+ DO_DOCKER_CLEAN=0
11
+ DO_NOMAD=0
12
+ DO_OPENCLAW=0
13
+ DO_DATA=0
14
+ AUTO_YES=0
15
+
16
+ # ─── Argument parsing ─────────────────────────────────────────────────────────
17
+ usage() {
18
+ cat <<EOF
19
+ Usage: bash jishu-uninstall.sh [options]
20
+
21
+ Run without options to stop all services and remove them from auto-start.
22
+
23
+ Options:
24
+ --node Remove Node.js versions managed by nvm, and nvm itself
25
+ --docker Remove Docker packages (Compose plugin, docker group)
26
+ --docker-clean Stop & remove all containers, images, volumes, then remove Docker
27
+ --nomad Remove Nomad binary and service registration
28
+ --openclaw Remove the OpenClaw npm package and its Docker image(s)
29
+ --data Delete the ~/.jishushell data directory (instances, config, logs)
30
+ --all Remove all of the above (full uninstall, implies --docker-clean)
31
+ --dry-run Print the removal plan only, do not execute anything
32
+ --yes, -y Skip all confirmation prompts (auto-yes)
33
+ --help, -h Show this help message
34
+
35
+ EOF
36
+ }
37
+
38
+ parse_args() {
39
+ while [[ $# -gt 0 ]]; do
40
+ case "$1" in
41
+ --node) DO_NODE=1 ;;
42
+ --docker) DO_DOCKER=1 ;;
43
+ --docker-clean) DO_DOCKER_CLEAN=1; DO_DOCKER=1 ;;
44
+ --nomad) DO_NOMAD=1 ;;
45
+ --openclaw) DO_OPENCLAW=1 ;;
46
+ --data) DO_DATA=1 ;;
47
+ --all) DO_NODE=1; DO_DOCKER_CLEAN=1; DO_DOCKER=1; DO_NOMAD=1; DO_OPENCLAW=1; DO_DATA=1; AUTO_YES=1 ;;
48
+ --dry-run) DRY_RUN=1 ;;
49
+ --yes|-y) AUTO_YES=1 ;;
50
+ --help|-h) usage; exit 0 ;;
51
+ *) ui_warn "Unknown argument: $1" ;;
52
+ esac
53
+ shift
54
+ done
55
+ }
56
+
57
+ # ─── Stop & deregister auto-start services ───────────────────────────────────
58
+ stop_services() {
59
+ ui_section "Stopping services and removing auto-start"
60
+
61
+ if [[ "$DRY_RUN" == "1" ]]; then
62
+ ui_info "[dry-run] Would stop and deregister: com.jishushell.panel, com.jishushell.nomad"
63
+ ui_info "[dry-run] Would: npm uninstall -g jishushell"
64
+ return 0
65
+ fi
66
+
67
+ if [[ "$(uname -s)" == "Darwin" ]]; then
68
+ local panel_plist="${HOME}/Library/LaunchAgents/com.jishushell.panel.plist"
69
+ local nomad_plist="${HOME}/Library/LaunchAgents/com.jishushell.nomad.plist"
70
+
71
+ if launchctl list 2>/dev/null | grep -q "com.jishushell.panel"; then
72
+ ui_info "Stopping JishuShell panel..."
73
+ launchctl unload -w "$panel_plist" 2>/dev/null || true
74
+ ui_success "JishuShell panel stopped and removed from auto-start"
75
+ else
76
+ ui_info "JishuShell panel is not running"
77
+ fi
78
+
79
+ if [[ -f "$panel_plist" ]]; then
80
+ rm -f "$panel_plist"
81
+ ui_success "Removed: ${panel_plist}"
82
+ fi
83
+
84
+ if launchctl list 2>/dev/null | grep -q "com.jishushell.nomad"; then
85
+ ui_info "Stopping Nomad..."
86
+ launchctl unload -w "$nomad_plist" 2>/dev/null || true
87
+ ui_success "Nomad stopped and removed from auto-start"
88
+ else
89
+ ui_info "Nomad launchd agent is not running"
90
+ fi
91
+
92
+ if [[ -f "$nomad_plist" ]]; then
93
+ rm -f "$nomad_plist"
94
+ ui_success "Removed: ${nomad_plist}"
95
+ fi
96
+ else
97
+ if command -v systemctl &>/dev/null; then
98
+ for svc in jishushell nomad; do
99
+ if systemctl is-active --quiet "$svc" 2>/dev/null; then
100
+ ui_info "Stopping ${svc}..."
101
+ ${SUDO} systemctl stop "$svc" 2>/dev/null || true
102
+ fi
103
+ if systemctl is-enabled --quiet "$svc" 2>/dev/null; then
104
+ ui_info "Disabling ${svc} from auto-start..."
105
+ ${SUDO} systemctl disable "$svc" 2>/dev/null || true
106
+ fi
107
+ ${SUDO} rm -f "/etc/systemd/system/${svc}.service" 2>/dev/null || true
108
+ done
109
+ ${SUDO} systemctl daemon-reload 2>/dev/null || true
110
+ ui_success "Services stopped and removed from auto-start"
111
+ fi
112
+ fi
113
+
114
+ if command -v npm &>/dev/null && npm list -g jishushell &>/dev/null 2>&1; then
115
+ ui_info "Uninstalling jishushell npm package..."
116
+ npm uninstall -g jishushell 2>/dev/null || true
117
+ ui_success "jishushell npm package removed"
118
+ fi
119
+
120
+ # Kill any nomad agent process started directly by jishushell (non-systemd/launchd)
121
+ if pgrep -f "nomad agent" &>/dev/null 2>&1; then
122
+ ui_info "Stopping orphaned Nomad agent process..."
123
+ pkill -TERM -f "nomad agent" 2>/dev/null || true
124
+ local _i=0
125
+ while pgrep -f "nomad agent" &>/dev/null 2>&1 && [[ $_i -lt 10 ]]; do
126
+ sleep 1
127
+ ((_i++)) || true
128
+ done
129
+ if pgrep -f "nomad agent" &>/dev/null 2>&1; then
130
+ pkill -KILL -f "nomad agent" 2>/dev/null || true
131
+ fi
132
+ ui_success "Nomad agent stopped"
133
+ fi
134
+
135
+ local wrapper="${HOME}/.jishushell/bin/jishushell-panel-start"
136
+ if [[ -f "$wrapper" ]]; then
137
+ rm -f "$wrapper"
138
+ ui_success "Removed wrapper: ${wrapper}"
139
+ fi
140
+ }
141
+
142
+ # ─── Node.js via nvm ──────────────────────────────────────────────────────────
143
+ uninstall_node() {
144
+ ui_section "Removing Node.js / nvm"
145
+
146
+ local nvm_dir="${NVM_DIR:-$HOME/.nvm}"
147
+
148
+ if [[ -s "$nvm_dir/nvm.sh" ]]; then
149
+ ui_info "Detected nvm installation: $nvm_dir"
150
+
151
+ # shellcheck source=/dev/null
152
+ \. "$nvm_dir/nvm.sh" 2>/dev/null || true
153
+
154
+ local installed_versions
155
+ installed_versions="$(nvm ls --no-colors 2>/dev/null | grep -oP 'v\d+\.\d+\.\d+' || true)"
156
+
157
+ if [[ -n "$installed_versions" ]]; then
158
+ ui_info "Installed Node.js versions:"
159
+ echo "$installed_versions" | while read -r ver; do
160
+ ui_info " $ver"
161
+ done
162
+ if confirm "Remove all listed Node.js versions?"; then
163
+ echo "$installed_versions" | while read -r ver; do
164
+ ui_info "Uninstalling Node.js $ver ..."
165
+ run_cmd nvm uninstall "$ver" 2>/dev/null || true
166
+ done
167
+ fi
168
+ else
169
+ ui_info "No Node.js versions found managed by nvm"
170
+ fi
171
+
172
+ if confirm "Delete nvm installation directory ($nvm_dir)?"; then
173
+ run_cmd rm -rf "$nvm_dir"
174
+ ui_success "nvm directory removed"
175
+ fi
176
+
177
+ local shell_configs=("$HOME/.bashrc" "$HOME/.bash_profile" "$HOME/.profile" "$HOME/.zshrc")
178
+ for cfg in "${shell_configs[@]}"; do
179
+ if [[ -f "$cfg" ]] && grep -q 'NVM_DIR' "$cfg" 2>/dev/null; then
180
+ ui_info "Cleaning nvm init lines from $cfg ..."
181
+ if [[ "$DRY_RUN" == "1" ]]; then
182
+ ui_info "[dry-run] Would remove NVM_DIR lines from $cfg"
183
+ else
184
+ cp "$cfg" "${cfg}.bak-jishu-uninstall"
185
+ sed -i '/NVM_DIR/d; /nvm\.sh/d; /bash_completion.*nvm/d' "$cfg"
186
+ ui_success "Cleaned $cfg (backup: ${cfg}.bak-jishu-uninstall)"
187
+ fi
188
+ fi
189
+ done
190
+ else
191
+ ui_info "nvm not found ($nvm_dir/nvm.sh does not exist)"
192
+ fi
193
+
194
+ if command -v node &>/dev/null && [[ "$DRY_RUN" != "1" || DO_NODE -eq 1 ]]; then
195
+ local node_path
196
+ node_path="$(command -v node 2>/dev/null || true)"
197
+ if [[ "$node_path" == /usr/* || "$node_path" == /bin/* ]]; then
198
+ ui_info "Detected system-level Node.js: $node_path"
199
+ if confirm "Uninstall system-level Node.js via package manager?"; then
200
+ case "$PKG_MANAGER" in
201
+ apt)
202
+ run_sudo apt-get remove -y nodejs npm 2>/dev/null || true
203
+ run_sudo apt-get autoremove -y 2>/dev/null || true
204
+ run_sudo rm -f /etc/apt/sources.list.d/nodesource.list \
205
+ /usr/share/keyrings/nodesource.gpg 2>/dev/null || true
206
+ ;;
207
+ dnf|yum)
208
+ run_sudo "$PKG_MANAGER" remove -y nodejs npm 2>/dev/null || true
209
+ ;;
210
+ esac
211
+ ui_success "System-level Node.js removed"
212
+ fi
213
+ fi
214
+ fi
215
+
216
+ if ! command -v node &>/dev/null 2>&1; then
217
+ ui_success "Node.js successfully removed"
218
+ else
219
+ ui_warn "node command still found (you may need to open a new terminal)"
220
+ fi
221
+ }
222
+
223
+ # ─── Docker containers / images / volumes cleanup ────────────────────────────
224
+ clean_docker_data() {
225
+ ui_section "Removing Docker containers, images and volumes"
226
+
227
+ if ! command -v docker &>/dev/null; then
228
+ ui_info "Docker is not installed, skipping cleanup"
229
+ return 0
230
+ fi
231
+
232
+ if [[ "$DRY_RUN" == "1" ]]; then
233
+ ui_info "[dry-run] Would: docker stop all running containers"
234
+ ui_info "[dry-run] Would: docker rm -f all containers"
235
+ ui_info "[dry-run] Would: docker rmi -f all images"
236
+ ui_info "[dry-run] Would: docker volume rm all volumes"
237
+ ui_info "[dry-run] Would: docker network prune -f"
238
+ return 0
239
+ fi
240
+
241
+ ui_warn "This will PERMANENTLY delete all Docker containers, images and volumes on this host."
242
+ if ! confirm "Remove ALL Docker containers, images and volumes?"; then
243
+ ui_info "Skipping Docker data cleanup"
244
+ return 0
245
+ fi
246
+
247
+ # Stop all running containers
248
+ local running_containers
249
+ running_containers="$(docker ps -q 2>/dev/null || true)"
250
+ if [[ -n "$running_containers" ]]; then
251
+ ui_info "Stopping all running containers..."
252
+ # shellcheck disable=SC2086
253
+ docker stop $running_containers 2>/dev/null || true
254
+ ui_success "All containers stopped"
255
+ else
256
+ ui_info "No running containers"
257
+ fi
258
+
259
+ # Remove all containers
260
+ local all_containers
261
+ all_containers="$(docker ps -aq 2>/dev/null || true)"
262
+ if [[ -n "$all_containers" ]]; then
263
+ ui_info "Removing all containers..."
264
+ # shellcheck disable=SC2086
265
+ docker rm -f $all_containers 2>/dev/null || true
266
+ ui_success "All containers removed"
267
+ else
268
+ ui_info "No containers to remove"
269
+ fi
270
+
271
+ # Remove all images
272
+ local all_images
273
+ all_images="$(docker images -q 2>/dev/null || true)"
274
+ if [[ -n "$all_images" ]]; then
275
+ ui_info "Removing all Docker images..."
276
+ # shellcheck disable=SC2086
277
+ docker rmi -f $all_images 2>/dev/null || true
278
+ ui_success "All images removed"
279
+ else
280
+ ui_info "No images to remove"
281
+ fi
282
+
283
+ # Remove all volumes
284
+ local all_volumes
285
+ all_volumes="$(docker volume ls -q 2>/dev/null || true)"
286
+ if [[ -n "$all_volumes" ]]; then
287
+ ui_info "Removing all Docker volumes..."
288
+ # shellcheck disable=SC2086
289
+ docker volume rm $all_volumes 2>/dev/null || true
290
+ ui_success "All volumes removed"
291
+ else
292
+ ui_info "No volumes to remove"
293
+ fi
294
+
295
+ # Prune custom networks
296
+ docker network prune -f 2>/dev/null || true
297
+ ui_success "Docker data cleanup complete"
298
+ }
299
+
300
+ # ─── Docker ───────────────────────────────────────────────────────────────────
301
+ uninstall_docker() {
302
+ ui_section "Removing Docker"
303
+
304
+ local _docker_via_snap=0
305
+ if command -v snap &>/dev/null && snap list docker &>/dev/null 2>&1; then
306
+ _docker_via_snap=1
307
+ fi
308
+ if ! command -v docker &>/dev/null \
309
+ && ! dpkg -l 2>/dev/null | grep -q 'docker\|containerd' \
310
+ && [[ $_docker_via_snap -eq 0 ]]; then
311
+ ui_info "Docker is not installed, skipping"
312
+ return 0
313
+ fi
314
+
315
+ if [[ "$DRY_RUN" == "1" ]]; then
316
+ ui_info "[dry-run] Would stop and disable docker.socket docker.service"
317
+ if [[ $_docker_via_snap -eq 1 ]]; then
318
+ ui_info "[dry-run] Would: snap remove docker"
319
+ else
320
+ ui_info "[dry-run] Would purge Docker packages (docker-ce, docker-ce-cli, containerd.io, etc.)"
321
+ ui_info "[dry-run] Would remove Docker APT/YUM repo and GPG key"
322
+ fi
323
+ ui_info "[dry-run] Would groupdel docker"
324
+ ui_info "[dry-run] Would remove /var/lib/docker /var/lib/containerd /etc/docker"
325
+ ui_info "[dry-run] Would remove ~/.docker"
326
+ ui_info "[dry-run] Would: systemctl daemon-reload"
327
+ return 0
328
+ fi
329
+
330
+ ui_warn "This will remove Docker packages and the docker group from this host."
331
+ if ! confirm "Confirm removal of Docker?"; then
332
+ ui_info "Cancelled"
333
+ return 0
334
+ fi
335
+
336
+ # Stop and disable service — reset failed state first so stop succeeds
337
+ if command -v systemctl &>/dev/null; then
338
+ ui_info "Stopping Docker service..."
339
+ ${SUDO} systemctl reset-failed docker.socket docker.service 2>/dev/null || true
340
+ ${SUDO} systemctl stop docker docker.socket 2>/dev/null || true
341
+ ${SUDO} systemctl disable docker docker.socket 2>/dev/null || true
342
+ fi
343
+
344
+ case "$PKG_MANAGER" in
345
+ apt)
346
+ ui_info "Removing Docker packages via apt purge..."
347
+ local purge_pkgs=(
348
+ docker-ce
349
+ docker-ce-cli
350
+ containerd.io
351
+ docker-buildx-plugin
352
+ docker-compose-plugin
353
+ docker-ce-rootless-extras
354
+ docker-model-plugin
355
+ )
356
+ # Only purge packages that are actually installed
357
+ local installed_purge_pkgs=()
358
+ for pkg in "${purge_pkgs[@]}"; do
359
+ if dpkg -l "$pkg" 2>/dev/null | grep -q '^ii'; then
360
+ installed_purge_pkgs+=("$pkg")
361
+ fi
362
+ done
363
+ if [[ ${#installed_purge_pkgs[@]} -gt 0 ]]; then
364
+ ui_info "Packages to purge: ${installed_purge_pkgs[*]}"
365
+ wait_for_apt_lock
366
+ ${SUDO} apt-get purge -y "${installed_purge_pkgs[@]}" || true
367
+ else
368
+ ui_warn "None of the expected Docker packages found via dpkg"
369
+ fi
370
+ wait_for_apt_lock
371
+ ${SUDO} apt-get autoremove -y || true
372
+ # Remove apt repository and GPG key added by installer
373
+ ${SUDO} rm -f /etc/apt/sources.list.d/docker.sources \
374
+ /etc/apt/sources.list.d/docker.list \
375
+ /etc/apt/keyrings/docker.asc \
376
+ /usr/share/keyrings/docker-archive-keyring.gpg \
377
+ /usr/share/keyrings/docker.gpg 2>/dev/null || true
378
+ ;;
379
+ dnf|yum)
380
+ ui_info "Removing Docker packages via $PKG_MANAGER..."
381
+ local docker_pkgs
382
+ docker_pkgs="$(rpm -qa 2>/dev/null | grep -E 'docker|containerd' | tr '\n' ' ')"
383
+ if [[ -n "$docker_pkgs" ]]; then
384
+ ui_info "Packages to remove: ${docker_pkgs}"
385
+ # shellcheck disable=SC2086
386
+ ${SUDO} "$PKG_MANAGER" remove -y $docker_pkgs || true
387
+ fi
388
+ ${SUDO} rm -f /etc/yum.repos.d/docker-ce.repo 2>/dev/null || true
389
+ ;;
390
+ esac
391
+
392
+ # Remove Docker installed via snap
393
+ if [[ $_docker_via_snap -eq 1 ]]; then
394
+ ui_info "Removing Docker snap package..."
395
+ ${SUDO} snap remove docker 2>/dev/null || true
396
+ if ! snap list docker &>/dev/null 2>&1; then
397
+ ui_success "Docker snap package removed"
398
+ else
399
+ ui_warn "snap remove docker failed — try manually: sudo snap remove docker"
400
+ fi
401
+ fi
402
+
403
+ # Verify packages actually removed — try a dynamic cleanup pass for any stragglers
404
+ local remaining_pkgs
405
+ remaining_pkgs="$(dpkg -l 2>/dev/null | awk '/^ii/ && /docker|containerd/ {print $2}' | tr '\n' ' ' || true)"
406
+ if [[ -n "$remaining_pkgs" ]]; then
407
+ ui_warn "Packages still installed after first pass: ${remaining_pkgs}"
408
+ ui_info "Attempting dynamic cleanup..."
409
+ wait_for_apt_lock
410
+ # shellcheck disable=SC2086
411
+ ${SUDO} apt-get purge -y $remaining_pkgs 2>/dev/null || true
412
+ wait_for_apt_lock
413
+ ${SUDO} apt-get autoremove -y 2>/dev/null || true
414
+ # Re-check
415
+ remaining_pkgs="$(dpkg -l 2>/dev/null | awk '/^ii/ && /docker|containerd/ {print $2}' | tr '\n' ' ' || true)"
416
+ if [[ -n "$remaining_pkgs" ]]; then
417
+ ui_warn "Packages still installed after removal: ${remaining_pkgs}"
418
+ ui_warn "Try manually: sudo apt-get purge -y ${remaining_pkgs}"
419
+ fi
420
+ fi
421
+
422
+ if getent group docker &>/dev/null 2>&1; then
423
+ ${SUDO} groupdel docker 2>/dev/null || true
424
+ ui_success "docker group removed"
425
+ fi
426
+
427
+ # Remove Docker daemon data and configuration directories
428
+ for dir in /var/lib/docker /var/lib/containerd /etc/docker; do
429
+ if [[ -d "$dir" ]]; then
430
+ local dir_size
431
+ dir_size="$(du -sh "$dir" 2>/dev/null | cut -f1 || echo '?')"
432
+ if confirm "Delete Docker directory ${dir} (${dir_size})?"; then
433
+ ${SUDO} rm -rf "$dir" 2>/dev/null || true
434
+ if [[ ! -d "$dir" ]]; then
435
+ ui_success "Removed: ${dir}"
436
+ else
437
+ ui_warn "Could not fully remove: ${dir}"
438
+ fi
439
+ else
440
+ ui_info "Kept: ${dir}"
441
+ fi
442
+ fi
443
+ done
444
+
445
+ # Remove user Docker config directory (~/.docker contains credentials and config)
446
+ if [[ -d "${HOME}/.docker" ]]; then
447
+ if confirm "Delete user Docker config directory (~/.docker)?"; then
448
+ rm -rf "${HOME}/.docker" 2>/dev/null || true
449
+ ui_success "Removed: ${HOME}/.docker"
450
+ else
451
+ ui_info "Kept: ${HOME}/.docker"
452
+ fi
453
+ fi
454
+
455
+ # Reload systemd unit database after removing service units
456
+ if command -v systemctl &>/dev/null; then
457
+ ${SUDO} systemctl daemon-reload 2>/dev/null || true
458
+ fi
459
+
460
+ # Final verification — flush shell command cache before checking
461
+ hash -r 2>/dev/null || true
462
+ local docker_path
463
+ docker_path="$(command -v docker 2>/dev/null || true)"
464
+ if [[ -n "$docker_path" && -f "$docker_path" ]]; then
465
+ ui_error "Docker binary still present at: ${docker_path}"
466
+ [[ -n "$remaining_pkgs" ]] && ui_error "Packages not removed: ${remaining_pkgs}"
467
+ return 1
468
+ fi
469
+ ui_success "Docker removed successfully"
470
+ }
471
+
472
+ # ─── Nomad ────────────────────────────────────────────────────────────────────
473
+ uninstall_nomad() {
474
+ ui_section "Removing Nomad"
475
+
476
+ local local_bin="${HOME}/.jishushell/bin/nomad"
477
+ local system_nomad
478
+ system_nomad="$(command -v nomad 2>/dev/null || true)"
479
+ # Ignore if system PATH resolves to our local bin
480
+ [[ "$system_nomad" == "$local_bin" ]] && system_nomad=""
481
+
482
+ local found_local=0
483
+ local found_system=0
484
+ [[ -f "$local_bin" ]] && found_local=1
485
+ [[ -n "$system_nomad" ]] && found_system=1
486
+
487
+ if [[ $found_local -eq 0 && $found_system -eq 0 ]]; then
488
+ ui_info "Nomad is not installed, skipping"
489
+ return 0
490
+ fi
491
+
492
+ if [[ $found_local -eq 1 ]]; then
493
+ local local_ver
494
+ local_ver="$("$local_bin" version 2>/dev/null | head -n1 || echo 'unknown')"
495
+ ui_info "Local Nomad: ${local_ver} → ${local_bin}"
496
+ fi
497
+ if [[ $found_system -eq 1 ]]; then
498
+ local sys_ver
499
+ sys_ver="$(nomad version 2>/dev/null | head -n1 || echo 'unknown')"
500
+ ui_info "System Nomad: ${sys_ver} → ${system_nomad}"
501
+ fi
502
+
503
+ if ! confirm "Confirm removal of Nomad?"; then
504
+ ui_info "Cancelled"
505
+ return 0
506
+ fi
507
+
508
+ # Stop systemd service if running
509
+ if command -v systemctl &>/dev/null; then
510
+ if systemctl is-active --quiet nomad 2>/dev/null; then
511
+ ui_info "Stopping Nomad service..."
512
+ run_sudo systemctl stop nomad 2>/dev/null || true
513
+ fi
514
+ if systemctl is-enabled --quiet nomad 2>/dev/null; then
515
+ ui_info "Disabling Nomad on startup..."
516
+ run_sudo systemctl disable nomad 2>/dev/null || true
517
+ fi
518
+ fi
519
+
520
+ # Remove local binary (no sudo needed)
521
+ if [[ $found_local -eq 1 ]]; then
522
+ ui_info "Removing local Nomad binary: ${local_bin}"
523
+ run_cmd rm -f "$local_bin"
524
+ ui_success "Local Nomad binary removed"
525
+ fi
526
+
527
+ # Remove PATH entry from shell configs written by jishu-install.sh
528
+ local marker="# jishushell-bin-path"
529
+ local shell_configs=("$HOME/.bashrc" "$HOME/.bash_profile" "$HOME/.profile" "$HOME/.zshrc")
530
+ for cfg in "${shell_configs[@]}"; do
531
+ if [[ -f "$cfg" ]] && grep -qF "$marker" "$cfg" 2>/dev/null; then
532
+ ui_info "Cleaning JishuShell PATH from ${cfg}..."
533
+ if [[ "$DRY_RUN" == "1" ]]; then
534
+ ui_info "[dry-run] Would remove jishushell-bin-path lines from ${cfg}"
535
+ else
536
+ cp "$cfg" "${cfg}.bak-jishu-uninstall"
537
+ sed -i "/${marker//\//\\/}/d; /jishushell.*bin.*PATH/d; /\.jishushell\/bin/d" "$cfg"
538
+ ui_success "Cleaned ${cfg} (backup: ${cfg}.bak-jishu-uninstall)"
539
+ fi
540
+ fi
541
+ done
542
+
543
+ # Remove system binary if found via package manager
544
+ if [[ $found_system -eq 1 ]]; then
545
+ local removed_via_pkg=0
546
+ case "$PKG_MANAGER" in
547
+ apt)
548
+ if dpkg -l nomad 2>/dev/null | grep -q '^ii'; then
549
+ ui_info "Removing Nomad via apt..."
550
+ run_sudo apt-get remove -y nomad 2>/dev/null || true
551
+ run_sudo apt-get autoremove -y 2>/dev/null || true
552
+ removed_via_pkg=1
553
+ fi
554
+ if [[ $removed_via_pkg -eq 1 ]]; then
555
+ local hashi_sources=(/etc/apt/sources.list.d/hashicorp.list)
556
+ for src in "${hashi_sources[@]}"; do
557
+ if [[ -f "$src" ]]; then
558
+ if confirm "Delete HashiCorp APT repository config ($src)?"; then
559
+ run_sudo rm -f "$src" \
560
+ /usr/share/keyrings/hashicorp-archive-keyring.gpg 2>/dev/null || true
561
+ fi
562
+ fi
563
+ done
564
+ fi
565
+ ;;
566
+ dnf|yum)
567
+ if rpm -q nomad &>/dev/null 2>&1; then
568
+ ui_info "Removing Nomad via $PKG_MANAGER..."
569
+ run_sudo "$PKG_MANAGER" remove -y nomad 2>/dev/null || true
570
+ removed_via_pkg=1
571
+ fi
572
+ if [[ $removed_via_pkg -eq 1 ]] && [[ -f /etc/yum.repos.d/hashicorp.repo ]]; then
573
+ if confirm "Delete HashiCorp YUM repository config (/etc/yum.repos.d/hashicorp.repo)?"; then
574
+ run_sudo rm -f /etc/yum.repos.d/hashicorp.repo 2>/dev/null || true
575
+ fi
576
+ fi
577
+ ;;
578
+ esac
579
+
580
+ # Remove binary directly if still present and not the local one
581
+ local remaining
582
+ remaining="$(command -v nomad 2>/dev/null || true)"
583
+ if [[ -n "$remaining" && "$remaining" != "$local_bin" ]]; then
584
+ ui_info "Removing remaining system binary: ${remaining}"
585
+ run_sudo rm -f "$remaining" 2>/dev/null || true
586
+ fi
587
+ fi
588
+
589
+ # Remove systemd service files
590
+ run_sudo rm -f /etc/systemd/system/nomad.service \
591
+ /usr/lib/systemd/system/nomad.service 2>/dev/null || true
592
+
593
+ # Nomad JishuShell config dir
594
+ # NOTE: Nomad runs as root and creates root-owned files under data/,
595
+ # so sudo is required to remove the entire tree.
596
+ local jishu_nomad_dir="${HOME}/.jishushell/nomad"
597
+ if [[ -d "$jishu_nomad_dir" ]]; then
598
+ if confirm "Delete Nomad config directory (${jishu_nomad_dir})?"; then
599
+ run_sudo rm -rf "$jishu_nomad_dir"
600
+ ui_success "Nomad config directory removed"
601
+ fi
602
+ fi
603
+
604
+ # System Nomad data dirs (optional)
605
+ if confirm "Delete Nomad system data directories (/etc/nomad.d /var/lib/nomad /var/log/nomad)?"; then
606
+ run_sudo rm -rf /etc/nomad.d /var/lib/nomad /var/log/nomad 2>/dev/null || true
607
+ ui_success "Nomad system data directories removed"
608
+ fi
609
+
610
+ if command -v systemctl &>/dev/null; then
611
+ run_sudo systemctl daemon-reload 2>/dev/null || true
612
+ fi
613
+
614
+ # Final verification
615
+ local nomad_ok=1
616
+ if [[ -f "${HOME}/.jishushell/bin/nomad" ]]; then
617
+ ui_warn "Local Nomad binary still present: ${HOME}/.jishushell/bin/nomad"
618
+ nomad_ok=0
619
+ fi
620
+ local nomad_remaining
621
+ nomad_remaining="$(command -v nomad 2>/dev/null || true)"
622
+ if [[ -n "$nomad_remaining" ]]; then
623
+ ui_warn "Nomad still found in PATH: ${nomad_remaining}"
624
+ nomad_ok=0
625
+ fi
626
+ if [[ $nomad_ok -eq 1 ]]; then
627
+ ui_success "Nomad removed successfully"
628
+ else
629
+ ui_error "Nomad removal incomplete"
630
+ return 1
631
+ fi
632
+ }
633
+
634
+ # ─── OpenClaw ─────────────────────────────────────────────────────────────────
635
+ # Removes: npm package dir + Docker images matching openclaw:* or jishushell-base:*
636
+ uninstall_openclaw() {
637
+ ui_section "Removing OpenClaw"
638
+
639
+ local pkg_dir="${HOME}/.jishushell/packages/openclaw"
640
+
641
+ # ── Remove Docker image(s) ────────────────────────────────────
642
+ if command -v docker &>/dev/null; then
643
+ # Find all local openclaw:* and jishushell-base:* images
644
+ local image_tags
645
+ image_tags="$(docker images --format '{{.Repository}}:{{.Tag}}' 2>/dev/null | grep -E '^openclaw:|^jishushell-base:' || true)"
646
+
647
+ if [[ -n "$image_tags" ]]; then
648
+ ui_info "Found OpenClaw/base Docker image(s):"
649
+ echo "$image_tags" | while read -r img; do
650
+ ui_info " $img"
651
+ done
652
+ if confirm "Remove the above OpenClaw Docker image(s)?"; then
653
+ echo "$image_tags" | while read -r img; do
654
+ if [[ "$DRY_RUN" == "1" ]]; then
655
+ ui_info "[dry-run] Would: docker rmi -f ${img}"
656
+ else
657
+ docker rmi -f "$img" 2>/dev/null || ${SUDO} docker rmi -f "$img" 2>/dev/null || true
658
+ if ! docker image inspect "$img" &>/dev/null 2>&1; then
659
+ ui_success "Removed: ${img}"
660
+ else
661
+ ui_warn "Could not remove: ${img}"
662
+ fi
663
+ fi
664
+ done
665
+ else
666
+ ui_info "Docker images kept"
667
+ fi
668
+ else
669
+ ui_info "No local openclaw:* or jishushell-base:* Docker images found"
670
+ fi
671
+ else
672
+ ui_info "Docker not installed — skipping image removal"
673
+ fi
674
+
675
+ # ── Remove npm package directory ──────────────────────────────
676
+ if [[ -d "$pkg_dir" ]]; then
677
+ local size
678
+ size="$(du -sh "$pkg_dir" 2>/dev/null | cut -f1 || echo "?")"
679
+ ui_info "OpenClaw npm package directory: ${pkg_dir} (${size})"
680
+ if confirm "Delete OpenClaw npm package directory (${pkg_dir})?"; then
681
+ if [[ "$DRY_RUN" == "1" ]]; then
682
+ ui_info "[dry-run] Would: rm -rf ${pkg_dir}"
683
+ else
684
+ rm -rf "$pkg_dir"
685
+ ui_success "OpenClaw npm package directory removed"
686
+ fi
687
+ else
688
+ ui_info "Kept ${pkg_dir}"
689
+ fi
690
+ else
691
+ ui_info "OpenClaw npm package directory not found (${pkg_dir})"
692
+ fi
693
+ }
694
+
695
+ # ─── Summary ──────────────────────────────────────────────────────────────────
696
+ show_plan() {
697
+ echo ""
698
+ echo -e "${ACCENT}${BOLD}Uninstall Plan${NC}"
699
+ echo -e "${MUTED}────────────────────────────────${NC}"
700
+ echo -e " ${WARN}·${NC} Stop all running services"
701
+ echo -e " ${WARN}·${NC} Remove services from auto-start (launchd / systemd)"
702
+ echo -e " ${WARN}·${NC} Uninstall jishushell npm package"
703
+ [[ $DO_NODE -eq 1 ]] && echo -e " ${WARN}·${NC} Node.js / nvm"
704
+ [[ $DO_DOCKER_CLEAN -eq 1 ]] && echo -e " ${WARN}·${NC} Remove all Docker containers, images and volumes"
705
+ [[ $DO_DOCKER -eq 1 ]] && echo -e " ${WARN}·${NC} Docker packages, docker group, /var/lib/docker, /var/lib/containerd, /etc/docker, ~/.docker"
706
+ [[ $DO_NOMAD -eq 1 ]] && echo -e " ${WARN}·${NC} Nomad binary and config"
707
+ [[ $DO_OPENCLAW -eq 1 ]] && echo -e " ${WARN}·${NC} OpenClaw npm package + Docker image(s) (openclaw:*, jishushell-base:*)"
708
+ if [[ $DO_DATA -eq 1 ]]; then
709
+ echo -e " ${WARN}·${NC} Data directory (~/.jishushell) — WILL BE DELETED"
710
+ else
711
+ echo -e " ${MUTED}·${NC} Data directory (~/.jishushell) — will ask, default: keep"
712
+ fi
713
+ if [[ "$DRY_RUN" == "1" ]]; then
714
+ echo ""
715
+ echo -e "${WARN} Dry-run mode: no changes will be made${NC}"
716
+ fi
717
+ echo ""
718
+ }
719
+
720
+ # ═══════════════════════════════════════════════════════════════════════════════
721
+ # Main
722
+ # ═══════════════════════════════════════════════════════════════════════════════
723
+ main() {
724
+ parse_args "$@"
725
+
726
+ echo -e "${ACCENT}${BOLD}"
727
+ echo " ╔═══════════════════════════════════╗"
728
+ echo " ║ JishuShell Uninstaller ║"
729
+ echo " ╚═══════════════════════════════════╝"
730
+ echo -e "${NC}"
731
+
732
+ detect_os
733
+ check_sudo
734
+
735
+ show_plan
736
+
737
+ if ! confirm "This will stop all JishuShell services and remove them from auto-start. Proceed?"; then
738
+ ui_info "Cancelled"
739
+ exit 0
740
+ fi
741
+
742
+ local uninstall_errors=0
743
+
744
+ stop_services || uninstall_errors=1
745
+
746
+ [[ $DO_NODE -eq 1 ]] && { uninstall_node || uninstall_errors=1; }
747
+ [[ $DO_OPENCLAW -eq 1 ]] && { uninstall_openclaw || uninstall_errors=1; }
748
+ [[ $DO_NOMAD -eq 1 ]] && { uninstall_nomad || uninstall_errors=1; }
749
+ [[ $DO_DOCKER_CLEAN -eq 1 ]] && { clean_docker_data || uninstall_errors=1; }
750
+ [[ $DO_DOCKER -eq 1 ]] && { uninstall_docker || uninstall_errors=1; }
751
+
752
+ local jishu_home="${HOME}/.jishushell"
753
+ if [[ -d "$jishu_home" ]]; then
754
+ local size
755
+ size="$(du -sh "$jishu_home" 2>/dev/null | cut -f1 || echo "?")"
756
+ echo ""
757
+ ui_info "Data directory: ${jishu_home} (${size})"
758
+ # Auto-delete when --data flag is set (non-interactive, e.g. postuninstall)
759
+ local _delete_data=0
760
+ if [[ $DO_DATA -eq 1 ]]; then
761
+ _delete_data=1
762
+ else
763
+ ui_info "This contains your instances, config, and Nomad data."
764
+ if confirm "Delete data directory? (default: keep)"; then
765
+ _delete_data=1
766
+ fi
767
+ fi
768
+ if [[ $_delete_data -eq 1 ]]; then
769
+ if [[ "$DRY_RUN" == "1" ]]; then
770
+ ui_info "[dry-run] Would: docker rm any containers bind-mounted from ${jishu_home}"
771
+ ui_info "[dry-run] Would: ${SUDO} rm -rf ${jishu_home}"
772
+ else
773
+ # On macOS, Docker Desktop keeps VirtioFS shares open for containers that are
774
+ # stopped but not removed (docker rm). Those open shares prevent rm -rf from
775
+ # deleting the bind-mounted directories. Force-remove any such containers first.
776
+ if command -v docker &>/dev/null; then
777
+ local _containers_to_rm
778
+ _containers_to_rm="$(docker ps -a --format '{{.ID}}' 2>/dev/null | xargs -I{} sh -c \
779
+ 'docker inspect {} --format "{{.ID}} {{range .Mounts}}{{.Source}} {{end}}" 2>/dev/null' \
780
+ | grep -F "$jishu_home" | awk '{print $1}' || true)"
781
+ if [[ -n "$_containers_to_rm" ]]; then
782
+ ui_info "Removing Docker containers with bind-mounts from ${jishu_home}..."
783
+ echo "$_containers_to_rm" | xargs docker rm -f 2>/dev/null || true
784
+ fi
785
+ fi
786
+ if [[ "$(uname -s)" == "Darwin" ]]; then
787
+ ${SUDO} chflags -R nouchg,noschg "$jishu_home" 2>/dev/null || true
788
+ fi
789
+ ${SUDO} rm -rf "$jishu_home" || true
790
+ if [[ ! -d "$jishu_home" ]]; then
791
+ ui_success "Data directory removed"
792
+ else
793
+ ui_warn "Could not remove ${jishu_home}"
794
+ uninstall_errors=1
795
+ fi
796
+ fi
797
+ else
798
+ ui_info "Data directory kept: ${jishu_home}"
799
+ fi
800
+ fi
801
+
802
+ echo ""
803
+ if [[ $uninstall_errors -eq 0 ]]; then
804
+ echo -e "${SUCCESS}${BOLD}Done.${NC}"
805
+ else
806
+ echo -e "${WARN}${BOLD}Some steps could not be completed — review the output above.${NC}"
807
+ fi
808
+ echo ""
809
+ }
810
+
811
+ main "$@"