@hitechclaw/clawspark 2.0.0

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 (49) hide show
  1. package/CHANGELOG.md +35 -0
  2. package/LICENSE +21 -0
  3. package/README.md +378 -0
  4. package/clawspark +2715 -0
  5. package/configs/models.yaml +108 -0
  6. package/configs/skill-packs.yaml +44 -0
  7. package/configs/skills.yaml +37 -0
  8. package/install.sh +387 -0
  9. package/lib/common.sh +249 -0
  10. package/lib/detect-hardware.sh +156 -0
  11. package/lib/diagnose.sh +636 -0
  12. package/lib/render-diagram.sh +47 -0
  13. package/lib/sandbox-commands.sh +415 -0
  14. package/lib/secure.sh +244 -0
  15. package/lib/select-model.sh +442 -0
  16. package/lib/setup-browser.sh +138 -0
  17. package/lib/setup-dashboard.sh +228 -0
  18. package/lib/setup-inference.sh +128 -0
  19. package/lib/setup-mcp.sh +142 -0
  20. package/lib/setup-messaging.sh +242 -0
  21. package/lib/setup-models.sh +121 -0
  22. package/lib/setup-openclaw.sh +808 -0
  23. package/lib/setup-sandbox.sh +188 -0
  24. package/lib/setup-skills.sh +113 -0
  25. package/lib/setup-systemd.sh +224 -0
  26. package/lib/setup-tailscale.sh +188 -0
  27. package/lib/setup-voice.sh +101 -0
  28. package/lib/skill-audit.sh +449 -0
  29. package/lib/verify.sh +177 -0
  30. package/package.json +57 -0
  31. package/scripts/release.sh +133 -0
  32. package/uninstall.sh +161 -0
  33. package/v2/README.md +50 -0
  34. package/v2/configs/providers.yaml +79 -0
  35. package/v2/configs/skills.yaml +36 -0
  36. package/v2/install.sh +116 -0
  37. package/v2/lib/common.sh +285 -0
  38. package/v2/lib/detect-hardware.sh +119 -0
  39. package/v2/lib/select-runtime.sh +273 -0
  40. package/v2/lib/setup-extras.sh +95 -0
  41. package/v2/lib/setup-openclaw.sh +187 -0
  42. package/v2/lib/setup-provider.sh +131 -0
  43. package/v2/lib/verify.sh +133 -0
  44. package/web/index.html +1835 -0
  45. package/web/install.sh +387 -0
  46. package/web/logo-hero.svg +11 -0
  47. package/web/logo-icon.svg +12 -0
  48. package/web/logo.svg +17 -0
  49. package/web/vercel.json +8 -0
@@ -0,0 +1,108 @@
1
+ # clawspark model recommendations by hardware tier
2
+ # Model IDs are verified against the Ollama library
3
+
4
+ hardware_profiles:
5
+ dgx-spark:
6
+ memory_gb: 128
7
+ unified: true
8
+ arch: aarch64
9
+ note: "Ranked by llmfit score, verified on real hardware"
10
+ models:
11
+ default:
12
+ id: "qwen3.5:35b-a3b"
13
+ name: "Qwen 3.5 35B-A3B"
14
+ description: "General MoE, proven on Spark. ~59 tok/s measured."
15
+ size_gb: 18
16
+ context: 32768
17
+ expected_tps: 59
18
+ llmfit_score: 91.8
19
+ best-quality:
20
+ id: "qwen3.5:122b-a10b"
21
+ name: "Qwen 3.5 122B-A10B"
22
+ description: "MoE variant of 122B, 10B active. Best llmfit score."
23
+ size_gb: 33
24
+ context: 262144
25
+ expected_tps: 45
26
+ llmfit_score: 95.5
27
+ coding:
28
+ id: "qwen3-coder-next"
29
+ name: "Qwen3 Coder Next 80B"
30
+ description: "Coding/agentic MoE. Highest estimated throughput."
31
+ size_gb: 52
32
+ context: 262144
33
+ expected_tps: 109
34
+ llmfit_score: 93.6
35
+ chat:
36
+ id: "qwen3-next"
37
+ name: "Qwen3 Next 80B-A3B"
38
+ description: "Latest chat/instruct MoE model."
39
+ size_gb: 50
40
+ context: 262144
41
+ expected_tps: 59
42
+ llmfit_score: 92.2
43
+ coding-light:
44
+ id: "qwen3-coder:30b"
45
+ name: "Qwen3 Coder 30B-A3B"
46
+ description: "Coding-focused MoE, lightweight."
47
+ size_gb: 19
48
+ context: 262144
49
+ expected_tps: 58
50
+ llmfit_score: 94.1
51
+
52
+ jetson-agx:
53
+ memory_gb: 64
54
+ unified: true
55
+ arch: aarch64
56
+ models:
57
+ balanced:
58
+ id: "nemotron-3-nano"
59
+ name: "Nemotron 3 Nano 30B"
60
+ description: "NVIDIA optimized for Jetson hardware."
61
+ size_gb: 19
62
+ context: 32768
63
+ expected_tps: 25
64
+ lightweight:
65
+ id: "glm-4.7-flash"
66
+ name: "GLM 4.7 Flash"
67
+ description: "Compact and fast."
68
+ size_gb: 19
69
+ context: 32768
70
+ expected_tps: 35
71
+
72
+ rtx-high:
73
+ min_vram_gb: 24
74
+ arch: x86_64
75
+ models:
76
+ balanced:
77
+ id: "qwen3.5:35b-a3b-q4_K_M"
78
+ name: "Qwen 3.5 35B-A3B (Q4)"
79
+ description: "Quantized MoE, fits 24GB VRAM."
80
+ size_gb: 24
81
+ context: 32768
82
+ expected_tps: 40
83
+ lightweight:
84
+ id: "glm-4.7-flash"
85
+ name: "GLM 4.7 Flash"
86
+ description: "Compact and fast."
87
+ size_gb: 19
88
+ context: 32768
89
+ expected_tps: 50
90
+
91
+ rtx-standard:
92
+ min_vram_gb: 8
93
+ arch: x86_64
94
+ models:
95
+ balanced:
96
+ id: "glm-4.7-flash"
97
+ name: "GLM 4.7 Flash"
98
+ description: "Best balance for mid range GPUs."
99
+ size_gb: 19
100
+ context: 32768
101
+ expected_tps: 35
102
+ lightweight:
103
+ id: "qwen3:8b"
104
+ name: "Qwen3 8B"
105
+ description: "Small but capable."
106
+ size_gb: 5
107
+ context: 32768
108
+ expected_tps: 50
@@ -0,0 +1,44 @@
1
+ # skill-packs.yaml -- Curated skill bundles for common use cases.
2
+ # Install a pack: clawspark skills pack <name>
3
+
4
+ packs:
5
+ research:
6
+ description: "Deep research with multi-source analysis"
7
+ skills:
8
+ - deep-research-pro
9
+ - ddg-web-search
10
+ - local-web-search-skill
11
+ - second-brain
12
+
13
+ coding:
14
+ description: "Code generation, review, and testing"
15
+ skills:
16
+ - self-improvement
17
+ - agent-browser
18
+
19
+ productivity:
20
+ description: "Task management and knowledge organization"
21
+ skills:
22
+ - proactive-agent
23
+ - memory-setup
24
+ - second-brain
25
+
26
+ voice:
27
+ description: "Voice interaction and transcription"
28
+ skills:
29
+ - local-whisper
30
+ - whatsapp-voice-chat-integration-open-source
31
+
32
+ full:
33
+ description: "All verified skills (everything clawspark ships with)"
34
+ skills:
35
+ - local-whisper
36
+ - self-improvement
37
+ - memory-setup
38
+ - whatsapp-voice-chat-integration-open-source
39
+ - deep-research-pro
40
+ - agent-browser
41
+ - second-brain
42
+ - proactive-agent
43
+ - ddg-web-search
44
+ - local-web-search-skill
@@ -0,0 +1,37 @@
1
+ # clawspark default skills
2
+ # Edit this file and run 'clawspark skills sync' to apply changes
3
+
4
+ skills:
5
+ enabled:
6
+ # Core
7
+ - name: local-whisper
8
+ description: Offline voice transcription via Whisper
9
+ - name: self-improvement
10
+ description: Agent learns from your corrections
11
+ - name: memory-setup
12
+ description: Persistent memory across conversations
13
+
14
+ # Voice
15
+ - name: whatsapp-voice-chat-integration-open-source
16
+ description: Voice notes to conversational flow
17
+
18
+ # Productivity
19
+ - name: deep-research-pro
20
+ description: Multi-step research with planning
21
+ - name: agent-browser
22
+ description: Web automation and browsing
23
+
24
+ # Knowledge
25
+ - name: second-brain
26
+ description: Personal knowledge base
27
+ - name: proactive-agent
28
+ description: Proactive assistance and suggestions
29
+
30
+ # Web Search
31
+ - name: ddg-web-search
32
+ description: DuckDuckGo search (no API key needed)
33
+ - name: local-web-search-skill
34
+ description: Local web search fallback
35
+
36
+ # Add your own skills below
37
+ custom: []
package/install.sh ADDED
@@ -0,0 +1,387 @@
1
+ #!/usr/bin/env bash
2
+ # install.sh — clawspark one-click installer.
3
+ # Sets up OpenClaw with a local LLM on NVIDIA DGX Spark, Jetson, or RTX hardware.
4
+ #
5
+ # Quick start:
6
+ # curl -fsSL https://clawspark.hitechclaw.com/install.sh | bash
7
+ #
8
+ # Or clone and run:
9
+ # git clone https://github.com/thanhan92-f1/clawspark && cd clawspark && bash install.sh
10
+ set -euo pipefail
11
+
12
+ # ── Bootstrap: if piped from curl, clone the repo and re-exec ─────────────
13
+ SCRIPT_DIR=""
14
+ if [[ -n "${BASH_SOURCE[0]:-}" ]]; then
15
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd 2>/dev/null || true)"
16
+ fi
17
+
18
+ if [[ -z "${SCRIPT_DIR}" ]] || [[ ! -d "${SCRIPT_DIR}/lib" ]]; then
19
+ # Running via curl pipe -- no lib/ available, need to clone
20
+ CLONE_DIR="$(mktemp -d)/clawspark"
21
+ echo "Downloading clawspark..."
22
+ if command -v git &>/dev/null; then
23
+ git clone --depth 1 https://github.com/thanhan92-f1/clawspark.git "${CLONE_DIR}" 2>/dev/null
24
+ else
25
+ # Fallback: download tarball if git is not available
26
+ mkdir -p "${CLONE_DIR}"
27
+ curl -fsSL https://github.com/thanhan92-f1/clawspark/archive/refs/heads/main.tar.gz \
28
+ | tar xz --strip-components=1 -C "${CLONE_DIR}"
29
+ fi
30
+ # Re-exec from the cloned repo, reconnecting stdin to the terminal
31
+ # so interactive prompts work (curl pipe leaves stdin at EOF).
32
+ # Prefer Homebrew bash on macOS (system bash is 3.2, too old).
33
+ _REEXEC_BASH="bash"
34
+ if [[ "$(uname)" == "Darwin" ]]; then
35
+ if [[ -x /opt/homebrew/bin/bash ]]; then
36
+ _REEXEC_BASH=/opt/homebrew/bin/bash
37
+ elif [[ -x /usr/local/bin/bash ]]; then
38
+ _REEXEC_BASH=/usr/local/bin/bash
39
+ fi
40
+ fi
41
+ exec "${_REEXEC_BASH}" "${CLONE_DIR}/install.sh" "$@" </dev/tty
42
+ fi
43
+
44
+ # ── Parse command-line flags ────────────────────────────────────────────────
45
+ CLAWSPARK_DEFAULTS="${CLAWSPARK_DEFAULTS:-false}"
46
+ AIR_GAP="false"
47
+ FLAG_MODEL=""
48
+ FLAG_MESSAGING=""
49
+ DEPLOY_MODE=""
50
+
51
+ while [[ $# -gt 0 ]]; do
52
+ case "$1" in
53
+ --defaults)
54
+ CLAWSPARK_DEFAULTS="true"
55
+ shift ;;
56
+ --air-gap|--airgap)
57
+ AIR_GAP="true"
58
+ shift ;;
59
+ --model=*)
60
+ FLAG_MODEL="${1#*=}"
61
+ shift ;;
62
+ --messaging=*)
63
+ FLAG_MESSAGING="${1#*=}"
64
+ shift ;;
65
+ -h|--help)
66
+ cat <<HELP
67
+ Usage: install.sh [OPTIONS]
68
+
69
+ Options:
70
+ --defaults Skip all interactive prompts (use defaults)
71
+ --air-gap Enable air-gap mode after setup
72
+ --model=<id> Ollama model ID to use (e.g. qwen3.5:35b-a3b)
73
+ --messaging=<type> whatsapp | telegram | both | skip
74
+ -h, --help Show this help
75
+ HELP
76
+ exit 0 ;;
77
+ *)
78
+ echo "Unknown option: $1" >&2
79
+ exit 1 ;;
80
+ esac
81
+ done
82
+
83
+ export CLAWSPARK_DEFAULTS AIR_GAP FLAG_MODEL FLAG_MESSAGING DEPLOY_MODE
84
+
85
+ # ── Require bash 4.2+ (macOS ships 3.2 which lacks nameref, ${var,,}, etc.) ─
86
+ if [[ "${BASH_VERSINFO[0]}" -lt 4 ]] || { [[ "${BASH_VERSINFO[0]}" -eq 4 ]] && [[ "${BASH_VERSINFO[1]}" -lt 2 ]]; }; then
87
+ echo ""
88
+ echo "ERROR: Bash 4.2+ is required (you have ${BASH_VERSION})."
89
+ echo ""
90
+ if [[ "$(uname)" == "Darwin" ]]; then
91
+ echo "macOS ships with bash 3.2. Install modern bash with Homebrew:"
92
+ echo ""
93
+ echo " brew install bash"
94
+ echo ""
95
+ echo "Then re-run the installer with:"
96
+ echo ""
97
+ echo " /opt/homebrew/bin/bash <(curl -fsSL https://clawspark.hitechclaw.com/install.sh)"
98
+ echo ""
99
+ else
100
+ echo "Please install bash 4.2+ and re-run the installer."
101
+ fi
102
+ exit 1
103
+ fi
104
+
105
+ # ── Prepare clawspark directory ─────────────────────────────────────────────
106
+ CLAWSPARK_DIR="${HOME}/.clawspark"
107
+ mkdir -p "${CLAWSPARK_DIR}"
108
+ CLAWSPARK_LOG="${CLAWSPARK_DIR}/install.log"
109
+ : > "${CLAWSPARK_LOG}" # truncate / create log
110
+
111
+ # ── Source library modules ──────────────────────────────────────────────────
112
+ _source_lib() {
113
+ local lib_dir="${SCRIPT_DIR}/lib"
114
+
115
+ if [[ ! -d "${lib_dir}" ]]; then
116
+ echo "ERROR: Cannot find lib/ directory at ${lib_dir}" >&2
117
+ exit 1
118
+ fi
119
+
120
+ local f
121
+ for f in \
122
+ common.sh \
123
+ detect-hardware.sh \
124
+ select-model.sh \
125
+ setup-inference.sh \
126
+ setup-openclaw.sh \
127
+ setup-skills.sh \
128
+ setup-messaging.sh \
129
+ setup-voice.sh \
130
+ setup-tailscale.sh \
131
+ setup-dashboard.sh \
132
+ setup-models.sh \
133
+ setup-mcp.sh \
134
+ setup-browser.sh \
135
+ setup-sandbox.sh \
136
+ setup-systemd.sh \
137
+ secure.sh \
138
+ verify.sh \
139
+ ; do
140
+ if [[ -f "${lib_dir}/${f}" ]]; then
141
+ # shellcheck source=/dev/null
142
+ source "${lib_dir}/${f}"
143
+ else
144
+ echo "ERROR: Missing library file: ${lib_dir}/${f}" >&2
145
+ exit 1
146
+ fi
147
+ done
148
+
149
+ # Store lib location for the CLI tool later
150
+ CLAWSPARK_LIB_DIR="${lib_dir}"
151
+ }
152
+
153
+ _source_lib
154
+
155
+ # ── Error trap ──────────────────────────────────────────────────────────────
156
+ _on_error() {
157
+ local exit_code=$?
158
+ local line_no=$1
159
+ log_error "Installation failed at line ${line_no} (exit code ${exit_code})."
160
+ log_error "Check the log for details: ${CLAWSPARK_LOG}"
161
+ printf '\n %sIf this looks like a bug, please open an issue at:%s\n' "${YELLOW}" "${RESET}"
162
+ printf ' https://github.com/thanhan92-f1/clawspark/issues\n\n'
163
+ exit "${exit_code}"
164
+ }
165
+ trap '_on_error ${LINENO}' ERR
166
+
167
+ # ── Detect if stdin is a terminal (for curl-pipe support) ───────────────────
168
+ if [[ ! -t 0 ]]; then
169
+ # Piped from curl — force defaults mode if user can't interact
170
+ if [[ ! -t 1 ]]; then
171
+ CLAWSPARK_DEFAULTS="true"
172
+ fi
173
+ fi
174
+
175
+ # ── ASCII banner ────────────────────────────────────────────────────────────
176
+ _show_banner() {
177
+ printf '%s' "${RED}"
178
+ cat <<'BANNER'
179
+
180
+ ___ _ ___ __ __ ___ ___ ___ ___ _ __
181
+ / __|| | / \\ \ /\ / / / __|| _ \ / \ | _ \| |/ /
182
+ | (__ | |__ | (_) |\ V V / \__ \| _/ | (_) || /| <
183
+ \___||____| |\___/ \_/\_/ |___/|_| \___/ |_|_\|_|\_\
184
+ |_|
185
+ BANNER
186
+ printf '%s' "${RESET}"
187
+ printf '%s' "${CYAN}"
188
+ cat <<'CLAW'
189
+ _____
190
+ / \
191
+ / () () \
192
+ | .____. |
193
+ \ \__/ /
194
+ ___ \______/ ___
195
+ / '-..__ __..-'' \
196
+ / .--._ '' _.---. \
197
+ | / ___\ /___ \ |
198
+ | | ( \ / ) | |
199
+ \ \ '---'/\\'---' / /
200
+ '.\_ / \ _/.'
201
+ '---' '---'
202
+ CLAW
203
+ printf '%s' "${RESET}"
204
+ printf '\n'
205
+ printf ' %s%sOne-click AI agent setup for NVIDIA hardware%s\n' "${BOLD}" "${BLUE}" "${RESET}"
206
+ printf ' %sPowered by OpenClaw + Ollama%s\n\n' "${CYAN}" "${RESET}"
207
+ hr
208
+ }
209
+
210
+ # ── Step 1: Banner ─────────────────────────────────────────────────────────
211
+ log_info "Step 1/19: Welcome"
212
+ _show_banner
213
+
214
+ # ════════════════════════════════════════════════════════════════════════════
215
+ # INSTALLATION FLOW
216
+ # ════════════════════════════════════════════════════════════════════════════
217
+
218
+ # ── Cache sudo credentials upfront so the install never pauses for password ──
219
+ if sudo -v 2>/dev/null; then
220
+ log_info "sudo credentials cached."
221
+ # Keep sudo alive in background for the duration of the install
222
+ ( while true; do sudo -n true 2>/dev/null; sleep 50; done ) &
223
+ SUDO_KEEPALIVE_PID=$!
224
+ trap 'kill ${SUDO_KEEPALIVE_PID} 2>/dev/null; _on_error ${LINENO}' ERR
225
+ trap 'kill ${SUDO_KEEPALIVE_PID} 2>/dev/null' EXIT
226
+ else
227
+ log_warn "sudo not available. Some steps (firewall, systemd) will be skipped."
228
+ fi
229
+
230
+ # ── Step 2: Hardware detection ──────────────────────────────────────────────
231
+ log_info "Step 2/19: Detecting hardware"
232
+ detect_hardware
233
+
234
+ # ── Step 3: Model selection ─────────────────────────────────────────────────
235
+ log_info "Step 3/19: Selecting model"
236
+ select_model
237
+
238
+ # ── Step 4: Deployment mode ─────────────────────────────────────────────────
239
+ log_info "Step 4/19: Deployment mode"
240
+ if [[ -z "${DEPLOY_MODE}" ]]; then
241
+ if prompt_yn "Use cloud APIs as fallback? (requires API key)" "n"; then
242
+ DEPLOY_MODE="hybrid"
243
+ else
244
+ DEPLOY_MODE="local"
245
+ fi
246
+ fi
247
+ export DEPLOY_MODE
248
+ log_info "Deploy mode: ${DEPLOY_MODE}"
249
+
250
+ # ── Step 5: Messaging preference ───────────────────────────────────────────
251
+ log_info "Step 5/19: Messaging preference"
252
+ if [[ -z "${FLAG_MESSAGING}" ]]; then
253
+ msg_opts=("WhatsApp" "Telegram" "Both" "Skip")
254
+ FLAG_MESSAGING=$(prompt_choice "Connect a messaging platform? (Web UI is always available)" msg_opts 3)
255
+ fi
256
+ # Lowercase for consistent matching downstream
257
+ FLAG_MESSAGING=$(to_lower "${FLAG_MESSAGING}")
258
+ export FLAG_MESSAGING
259
+ log_info "Messaging: ${FLAG_MESSAGING}"
260
+
261
+ hr
262
+ printf '\n %s%sBeginning installation...%s\n\n' "${BOLD}" "${GREEN}" "${RESET}"
263
+
264
+ # ── Step 6: Inference engine ────────────────────────────────────────────────
265
+ log_info "Step 6/19: Setting up inference engine"
266
+ setup_inference
267
+
268
+ # ── Step 7: OpenClaw ────────────────────────────────────────────────────────
269
+ log_info "Step 7/19: Installing OpenClaw"
270
+ setup_openclaw
271
+
272
+ # ── Install the clawspark CLI early so it is available for troubleshooting ──
273
+ if [[ -f "${SCRIPT_DIR}/clawspark" ]]; then
274
+ sudo cp "${SCRIPT_DIR}/clawspark" /usr/local/bin/clawspark 2>/dev/null || {
275
+ cp "${SCRIPT_DIR}/clawspark" "${CLAWSPARK_DIR}/clawspark" 2>/dev/null || true
276
+ }
277
+ sudo chmod +x /usr/local/bin/clawspark 2>/dev/null || true
278
+ fi
279
+ if [[ -d "${SCRIPT_DIR}/lib" ]]; then
280
+ cp -r "${SCRIPT_DIR}/lib" "${CLAWSPARK_DIR}/"
281
+ fi
282
+ if [[ -d "${SCRIPT_DIR}/configs" ]]; then
283
+ cp -r "${SCRIPT_DIR}/configs" "${CLAWSPARK_DIR}/"
284
+ fi
285
+
286
+ # ── Step 8: Skills ──────────────────────────────────────────────────────────
287
+ log_info "Step 8/19: Installing skills"
288
+ setup_skills
289
+
290
+ # ── Step 9: Voice ──────────────────────────────────────────────────────────
291
+ log_info "Step 9/19: Setting up voice"
292
+ setup_voice
293
+
294
+ # ── Step 10: Messaging ─────────────────────────────────────────────────────
295
+ log_info "Step 10/19: Setting up messaging"
296
+ setup_messaging
297
+
298
+ # ── Step 11: Tailscale ─────────────────────────────────────────────────────
299
+ log_info "Step 11/19: Setting up Tailscale"
300
+ setup_tailscale
301
+
302
+ # ── Step 12: Dashboard ─────────────────────────────────────────────────────
303
+ log_info "Step 12/19: Setting up dashboard"
304
+ setup_dashboard || log_warn "Dashboard setup had issues -- continuing with install."
305
+
306
+ # ── Step 13: Vision & multi-model ──────────────────────────────────────────
307
+ log_info "Step 13/19: Configuring vision and multi-model support"
308
+ setup_models || log_warn "Model configuration had issues -- continuing."
309
+
310
+ # ── Step 14: MCP servers (diagrams, memory, reasoning) ───────────────────
311
+ log_info "Step 14/19: Setting up MCP servers"
312
+ setup_mcp || log_warn "MCP setup had issues -- continuing."
313
+
314
+ # ── Step 15: Browser automation ────────────────────────────────────────────
315
+ log_info "Step 15/19: Setting up browser automation"
316
+ setup_browser || log_warn "Browser setup had issues -- continuing."
317
+
318
+ # ── Step 16: Docker sandbox ────────────────────────────────────────────────
319
+ log_info "Step 16/19: Setting up Docker sandbox"
320
+ setup_sandbox || log_warn "Sandbox setup had issues -- continuing."
321
+
322
+ # ── Step 17: Systemd services ──────────────────────────────────────────────
323
+ log_info "Step 17/19: Creating systemd services for auto-start on boot"
324
+ setup_systemd_services || log_warn "Systemd setup had issues -- services will use PID management."
325
+
326
+ # ── Step 18: Security ──────────────────────────────────────────────────────
327
+ log_info "Step 18/19: Applying security"
328
+ secure_setup
329
+
330
+ # ── Start node host (after all config changes are done) ───────────────────
331
+ # Skip if systemd already started the node host (step 16)
332
+ if check_command systemctl && systemctl is-active --quiet clawspark-nodehost.service 2>/dev/null; then
333
+ log_info "Node host already running via systemd."
334
+ else
335
+ log_info "Starting node host..."
336
+ _start_node_host || log_warn "Node host failed to start -- you can start it with: clawspark start"
337
+ fi
338
+
339
+ # ── Step 18: Verification ──────────────────────────────────────────────────
340
+ log_info "Step 19/19: Verifying installation"
341
+ verify_installation
342
+
343
+ # ── Final CLI refresh (picks up any changes made during install) ──────────
344
+ if [[ -f "${SCRIPT_DIR}/clawspark" ]]; then
345
+ sudo cp "${SCRIPT_DIR}/clawspark" /usr/local/bin/clawspark 2>/dev/null || true
346
+ sudo chmod +x /usr/local/bin/clawspark 2>/dev/null || true
347
+ fi
348
+ [[ -d "${SCRIPT_DIR}/lib" ]] && cp -r "${SCRIPT_DIR}/lib" "${CLAWSPARK_DIR}/"
349
+ [[ -d "${SCRIPT_DIR}/configs" ]] && cp -r "${SCRIPT_DIR}/configs" "${CLAWSPARK_DIR}/"
350
+
351
+ # ── Final message ───────────────────────────────────────────────────────────
352
+ printf '\n'
353
+ hr
354
+ printf '\n'
355
+ printf ' %s%s' "${GREEN}" "${BOLD}"
356
+ cat <<'DONE'
357
+ __ __ _ _ _ _ _
358
+ \ \ / /___ _ _ ( )_ _ ___ __ _| | | ___ ___ | |_| |
359
+ \ V // _ \| || | |/| '_/ -_) / _` | | | (_-</ -_)| _|_|
360
+ |_| \___/ \_,_| |_| \___| \__,_|_|_| /__/\___| \__(_)
361
+ DONE
362
+ printf '%s\n' "${RESET}"
363
+
364
+ _final_urls=()
365
+ _final_urls+=("Chat UI: http://localhost:18789/__openclaw__/canvas/")
366
+ _final_urls+=("Dashboard: http://localhost:8900")
367
+ if [[ -f "${CLAWSPARK_DIR}/tailscale.url" ]]; then
368
+ _final_urls+=("Tailscale: $(cat "${CLAWSPARK_DIR}/tailscale.url")")
369
+ fi
370
+
371
+ print_box \
372
+ "${BOLD}Next Steps${RESET}" \
373
+ "" \
374
+ "${_final_urls[@]}" \
375
+ "" \
376
+ "1. Open the Chat UI in your browser to talk to your agent" \
377
+ "2. Or send a message via WhatsApp or Telegram" \
378
+ "3. Manage your setup: ${CYAN}clawspark status${RESET}" \
379
+ "4. Install skill packs: ${CYAN}clawspark skills pack research${RESET}" \
380
+ "5. Switch models: ${CYAN}clawspark model list${RESET}" \
381
+ "6. Enable sandbox: ${CYAN}clawspark sandbox on${RESET}" \
382
+ "7. View logs: ${CYAN}clawspark logs${RESET}" \
383
+ "" \
384
+ "Services auto-start on boot (systemd)." \
385
+ "Full log: ${CLAWSPARK_LOG}"
386
+
387
+ printf '\n %sHappy hacking!%s\n\n' "${CYAN}" "${RESET}"