@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.
- package/CHANGELOG.md +35 -0
- package/LICENSE +21 -0
- package/README.md +378 -0
- package/clawspark +2715 -0
- package/configs/models.yaml +108 -0
- package/configs/skill-packs.yaml +44 -0
- package/configs/skills.yaml +37 -0
- package/install.sh +387 -0
- package/lib/common.sh +249 -0
- package/lib/detect-hardware.sh +156 -0
- package/lib/diagnose.sh +636 -0
- package/lib/render-diagram.sh +47 -0
- package/lib/sandbox-commands.sh +415 -0
- package/lib/secure.sh +244 -0
- package/lib/select-model.sh +442 -0
- package/lib/setup-browser.sh +138 -0
- package/lib/setup-dashboard.sh +228 -0
- package/lib/setup-inference.sh +128 -0
- package/lib/setup-mcp.sh +142 -0
- package/lib/setup-messaging.sh +242 -0
- package/lib/setup-models.sh +121 -0
- package/lib/setup-openclaw.sh +808 -0
- package/lib/setup-sandbox.sh +188 -0
- package/lib/setup-skills.sh +113 -0
- package/lib/setup-systemd.sh +224 -0
- package/lib/setup-tailscale.sh +188 -0
- package/lib/setup-voice.sh +101 -0
- package/lib/skill-audit.sh +449 -0
- package/lib/verify.sh +177 -0
- package/package.json +57 -0
- package/scripts/release.sh +133 -0
- package/uninstall.sh +161 -0
- package/v2/README.md +50 -0
- package/v2/configs/providers.yaml +79 -0
- package/v2/configs/skills.yaml +36 -0
- package/v2/install.sh +116 -0
- package/v2/lib/common.sh +285 -0
- package/v2/lib/detect-hardware.sh +119 -0
- package/v2/lib/select-runtime.sh +273 -0
- package/v2/lib/setup-extras.sh +95 -0
- package/v2/lib/setup-openclaw.sh +187 -0
- package/v2/lib/setup-provider.sh +131 -0
- package/v2/lib/verify.sh +133 -0
- package/web/index.html +1835 -0
- package/web/install.sh +387 -0
- package/web/logo-hero.svg +11 -0
- package/web/logo-icon.svg +12 -0
- package/web/logo.svg +17 -0
- package/web/vercel.json +8 -0
package/lib/common.sh
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# lib/common.sh — Shared utilities sourced by all clawspark scripts.
|
|
3
|
+
# Provides logging, prompts, color constants, spinner, and global paths.
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
# ── Global paths ────────────────────────────────────────────────────────────
|
|
7
|
+
CLAWSPARK_DIR="${CLAWSPARK_DIR:-${HOME}/.clawspark}"
|
|
8
|
+
CLAWSPARK_LOG="${CLAWSPARK_LOG:-${CLAWSPARK_DIR}/install.log}"
|
|
9
|
+
CLAWSPARK_DEFAULTS="${CLAWSPARK_DEFAULTS:-false}"
|
|
10
|
+
|
|
11
|
+
# ── Color constants ─────────────────────────────────────────────────────────
|
|
12
|
+
if [[ -t 1 ]] && command -v tput &>/dev/null && [[ "$(tput colors 2>/dev/null || echo 0)" -ge 8 ]]; then
|
|
13
|
+
RED=$(tput setaf 1)
|
|
14
|
+
GREEN=$(tput setaf 2)
|
|
15
|
+
YELLOW=$(tput setaf 3)
|
|
16
|
+
BLUE=$(tput setaf 4)
|
|
17
|
+
CYAN=$(tput setaf 6)
|
|
18
|
+
BOLD=$(tput bold)
|
|
19
|
+
RESET=$(tput sgr0)
|
|
20
|
+
else
|
|
21
|
+
RED=$'\033[0;31m'
|
|
22
|
+
GREEN=$'\033[0;32m'
|
|
23
|
+
YELLOW=$'\033[0;33m'
|
|
24
|
+
BLUE=$'\033[0;34m'
|
|
25
|
+
CYAN=$'\033[0;36m'
|
|
26
|
+
BOLD=$'\033[1m'
|
|
27
|
+
RESET=$'\033[0m'
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
# ── Helpers (Bash 3.2 compatible) ───────────────────────────────────────────
|
|
31
|
+
to_lower() { echo "$1" | tr '[:upper:]' '[:lower:]'; }
|
|
32
|
+
|
|
33
|
+
# ── Logging ─────────────────────────────────────────────────────────────────
|
|
34
|
+
_ts() { date '+%H:%M:%S'; }
|
|
35
|
+
|
|
36
|
+
log_info() {
|
|
37
|
+
printf '%s[%s]%s %s\n' "${BLUE}" "$(_ts)" "${RESET}" "$*"
|
|
38
|
+
_log_to_file "INFO" "$*"
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
log_warn() {
|
|
42
|
+
printf '%s[%s] WARNING:%s %s\n' "${YELLOW}" "$(_ts)" "${RESET}" "$*" >&2
|
|
43
|
+
_log_to_file "WARN" "$*"
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
log_error() {
|
|
47
|
+
printf '%s[%s] ERROR:%s %s\n' "${RED}" "$(_ts)" "${RESET}" "$*" >&2
|
|
48
|
+
_log_to_file "ERROR" "$*"
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
log_success() {
|
|
52
|
+
printf '%s[%s] OK:%s %s\n' "${GREEN}" "$(_ts)" "${RESET}" "$*"
|
|
53
|
+
_log_to_file "OK" "$*"
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
_log_to_file() {
|
|
57
|
+
local level="$1"; shift
|
|
58
|
+
if [[ -d "${CLAWSPARK_DIR}" ]]; then
|
|
59
|
+
printf '[%s] [%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "${level}" "$*" \
|
|
60
|
+
>> "${CLAWSPARK_LOG}" 2>/dev/null || true
|
|
61
|
+
fi
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
# ── Prompt helpers ──────────────────────────────────────────────────────────
|
|
65
|
+
|
|
66
|
+
# prompt_choice QUESTION OPTIONS_ARRAY DEFAULT_INDEX
|
|
67
|
+
# Displays numbered options and returns the selected value.
|
|
68
|
+
# If CLAWSPARK_DEFAULTS=true, returns the default without prompting.
|
|
69
|
+
#
|
|
70
|
+
# Usage:
|
|
71
|
+
# options=("Option A" "Option B" "Option C")
|
|
72
|
+
# result=$(prompt_choice "Pick one:" options 0)
|
|
73
|
+
prompt_choice() {
|
|
74
|
+
local question="$1"
|
|
75
|
+
local options_name="$2" # array name (Bash 3.2 compatible: no nameref)
|
|
76
|
+
local default_idx="${3:-0}"
|
|
77
|
+
local count
|
|
78
|
+
eval "count=\${#${options_name}[@]}"
|
|
79
|
+
|
|
80
|
+
# If running in defaults mode, return default immediately
|
|
81
|
+
if [[ "${CLAWSPARK_DEFAULTS}" == "true" ]]; then
|
|
82
|
+
eval "printf '%s' \"\${${options_name}[$default_idx]}\""
|
|
83
|
+
return 0
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
# Print menu to /dev/tty so it's visible even inside $(...) capture
|
|
87
|
+
printf '\n%s%s%s\n' "${BOLD}" "${question}" "${RESET}" >/dev/tty
|
|
88
|
+
local i
|
|
89
|
+
for i in $(seq 0 $(( count - 1 ))); do
|
|
90
|
+
local marker=""
|
|
91
|
+
if [[ "$i" -eq "$default_idx" ]]; then
|
|
92
|
+
marker=" ${CYAN}(default)${RESET}"
|
|
93
|
+
fi
|
|
94
|
+
local opt
|
|
95
|
+
eval "opt=\${${options_name}[$i]}"
|
|
96
|
+
printf ' %s%d)%s %s%s\n' "${GREEN}" $(( i + 1 )) "${RESET}" "${opt}" "${marker}" >/dev/tty
|
|
97
|
+
done
|
|
98
|
+
|
|
99
|
+
local selection
|
|
100
|
+
while true; do
|
|
101
|
+
printf '%s> %s' "${BOLD}" "${RESET}" >/dev/tty
|
|
102
|
+
read -r selection </dev/tty || selection=""
|
|
103
|
+
# Empty input → default
|
|
104
|
+
if [[ -z "${selection}" ]]; then
|
|
105
|
+
eval "printf '%s' \"\${${options_name}[$default_idx]}\""
|
|
106
|
+
return 0
|
|
107
|
+
fi
|
|
108
|
+
# Validate numeric input
|
|
109
|
+
if [[ "${selection}" =~ ^[0-9]+$ ]] && (( selection >= 1 && selection <= count )); then
|
|
110
|
+
eval "printf '%s' \"\${${options_name}[$(( selection - 1 ))]}\""
|
|
111
|
+
return 0
|
|
112
|
+
fi
|
|
113
|
+
printf ' %sPlease enter a number between 1 and %d%s\n' "${YELLOW}" "${count}" "${RESET}" >/dev/tty
|
|
114
|
+
done
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
# prompt_yn QUESTION DEFAULT(y/n)
|
|
118
|
+
# Returns 0 for yes, 1 for no.
|
|
119
|
+
prompt_yn() {
|
|
120
|
+
local question="$1"
|
|
121
|
+
local default="${2:-y}"
|
|
122
|
+
|
|
123
|
+
if [[ "${CLAWSPARK_DEFAULTS}" == "true" ]]; then
|
|
124
|
+
[[ "${default}" == "y" ]] && return 0 || return 1
|
|
125
|
+
fi
|
|
126
|
+
|
|
127
|
+
local hint
|
|
128
|
+
if [[ "${default}" == "y" ]]; then hint="[Y/n]"; else hint="[y/N]"; fi
|
|
129
|
+
|
|
130
|
+
printf '\n%s%s %s%s ' "${BOLD}" "${question}" "${hint}" "${RESET}" >/dev/tty
|
|
131
|
+
local answer
|
|
132
|
+
read -r answer </dev/tty || answer=""
|
|
133
|
+
answer=$(to_lower "${answer}")
|
|
134
|
+
|
|
135
|
+
if [[ -z "${answer}" ]]; then
|
|
136
|
+
[[ "${default}" == "y" ]] && return 0 || return 1
|
|
137
|
+
fi
|
|
138
|
+
[[ "${answer}" =~ ^y(es)?$ ]] && return 0 || return 1
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
# ── check_command ───────────────────────────────────────────────────────────
|
|
142
|
+
# Returns 0 if the command exists on PATH, 1 otherwise.
|
|
143
|
+
check_command() {
|
|
144
|
+
command -v "$1" &>/dev/null
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
# ── Spinner ─────────────────────────────────────────────────────────────────
|
|
148
|
+
# spinner PID "message"
|
|
149
|
+
# Shows a spinner while a background process runs.
|
|
150
|
+
# Usage:
|
|
151
|
+
# long_running_command &
|
|
152
|
+
# spinner $! "Installing widgets..."
|
|
153
|
+
spinner() {
|
|
154
|
+
local pid="$1"
|
|
155
|
+
local msg="${2:-Working...}"
|
|
156
|
+
local frames=('⠋' '⠙' '⠹' '⠸' '⠼' '⠴' '⠦' '⠧' '⠇' '⠏')
|
|
157
|
+
local frame_count=${#frames[@]}
|
|
158
|
+
local i=0
|
|
159
|
+
|
|
160
|
+
# Only show spinner if stdout is a terminal
|
|
161
|
+
if [[ ! -t 1 ]]; then
|
|
162
|
+
wait "${pid}" 2>/dev/null || true
|
|
163
|
+
return 0
|
|
164
|
+
fi
|
|
165
|
+
|
|
166
|
+
printf ' '
|
|
167
|
+
while kill -0 "${pid}" 2>/dev/null; do
|
|
168
|
+
printf '\r %s%s%s %s' "${CYAN}" "${frames[i % frame_count]}" "${RESET}" "${msg}"
|
|
169
|
+
i=$(( i + 1 ))
|
|
170
|
+
sleep 0.08
|
|
171
|
+
done
|
|
172
|
+
|
|
173
|
+
# Capture exit status of the background process
|
|
174
|
+
wait "${pid}" 2>/dev/null
|
|
175
|
+
local exit_code=$?
|
|
176
|
+
|
|
177
|
+
if [[ ${exit_code} -eq 0 ]]; then
|
|
178
|
+
printf '\r %s✓%s %s\n' "${GREEN}" "${RESET}" "${msg}"
|
|
179
|
+
else
|
|
180
|
+
printf '\r %s✗%s %s\n' "${RED}" "${RESET}" "${msg}"
|
|
181
|
+
fi
|
|
182
|
+
# Always return 0 so set -e does not abort the installer.
|
|
183
|
+
# Callers should validate success themselves (e.g. check_command).
|
|
184
|
+
return 0
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
# ── Horizontal rule ────────────────────────────────────────────────────────
|
|
188
|
+
hr() {
|
|
189
|
+
local cols
|
|
190
|
+
cols=$(tput cols 2>/dev/null || echo 60)
|
|
191
|
+
printf '%s%*s%s\n' "${BLUE}" "${cols}" '' "${RESET}" | tr ' ' '─'
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
# ── Box drawing ─────────────────────────────────────────────────────────────
|
|
195
|
+
# print_box "line1" "line2" ...
|
|
196
|
+
# Draws a bordered box around the given lines.
|
|
197
|
+
print_box() {
|
|
198
|
+
local lines=("$@")
|
|
199
|
+
local max_len=0
|
|
200
|
+
local line
|
|
201
|
+
|
|
202
|
+
for line in "${lines[@]}"; do
|
|
203
|
+
# Strip ANSI codes when measuring length
|
|
204
|
+
local stripped
|
|
205
|
+
stripped=$(printf '%s' "${line}" | sed 's/\x1b\[[0-9;]*m//g')
|
|
206
|
+
(( ${#stripped} > max_len )) && max_len=${#stripped}
|
|
207
|
+
done
|
|
208
|
+
|
|
209
|
+
local pad=$(( max_len + 2 ))
|
|
210
|
+
printf '%s┌%s┐%s\n' "${BLUE}" "$(printf '─%.0s' $(seq 1 "${pad}"))" "${RESET}"
|
|
211
|
+
for line in "${lines[@]}"; do
|
|
212
|
+
local stripped
|
|
213
|
+
stripped=$(printf '%s' "${line}" | sed 's/\x1b\[[0-9;]*m//g')
|
|
214
|
+
local spaces=$(( max_len - ${#stripped} ))
|
|
215
|
+
printf '%s│%s %s%*s %s│%s\n' "${BLUE}" "${RESET}" "${line}" "${spaces}" '' "${BLUE}" "${RESET}"
|
|
216
|
+
done
|
|
217
|
+
printf '%s└%s┘%s\n' "${BLUE}" "$(printf '─%.0s' $(seq 1 "${pad}"))" "${RESET}"
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
# ── Skills YAML parser ────────────────────────────────────────────────────
|
|
221
|
+
# _parse_enabled_skills FILE
|
|
222
|
+
# Prints one skill slug per line from the "enabled:" section.
|
|
223
|
+
# Handles both "- name: slug" and "- slug" formats.
|
|
224
|
+
_parse_enabled_skills() {
|
|
225
|
+
local skills_file="$1"
|
|
226
|
+
[[ -f "${skills_file}" && -r "${skills_file}" ]] || return 1
|
|
227
|
+
local in_enabled=false
|
|
228
|
+
|
|
229
|
+
while IFS= read -r line; do
|
|
230
|
+
[[ "${line}" =~ ^[[:space:]]*# ]] && continue
|
|
231
|
+
[[ -z "${line// }" ]] && continue
|
|
232
|
+
|
|
233
|
+
if [[ "${line}" =~ enabled:[[:space:]]*$ ]]; then
|
|
234
|
+
in_enabled=true; continue
|
|
235
|
+
fi
|
|
236
|
+
if ${in_enabled} && [[ "${line}" =~ ^[[:space:]]{0,3}[a-zA-Z] ]] && [[ ! "${line}" =~ ^[[:space:]]*- ]]; then
|
|
237
|
+
in_enabled=false; continue
|
|
238
|
+
fi
|
|
239
|
+
if ${in_enabled} && [[ "${line}" =~ ^[[:space:]]*-[[:space:]]+name:[[:space:]]+(.*) ]]; then
|
|
240
|
+
local slug="${BASH_REMATCH[1]}"; slug="${slug## }"; slug="${slug%% }"
|
|
241
|
+
echo "${slug}"; continue
|
|
242
|
+
fi
|
|
243
|
+
if ${in_enabled} && [[ "${line}" =~ ^[[:space:]]*-[[:space:]]+(.*) ]]; then
|
|
244
|
+
local slug="${BASH_REMATCH[1]}"; slug="${slug## }"; slug="${slug%% }"
|
|
245
|
+
[[ "${slug}" =~ ^[a-zA-Z]+: ]] && continue
|
|
246
|
+
echo "${slug}"
|
|
247
|
+
fi
|
|
248
|
+
done < "${skills_file}"
|
|
249
|
+
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# lib/detect-hardware.sh — Detects GPU, CPU, and memory for platform classification.
|
|
3
|
+
# Exports: HW_PLATFORM, HW_GPU_NAME, HW_GPU_VRAM_MB, HW_TOTAL_RAM_MB,
|
|
4
|
+
# HW_CPU_CORES, HW_CPU_ARCH, HW_DRIVER_VERSION
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
detect_hardware() {
|
|
8
|
+
log_info "Detecting hardware..."
|
|
9
|
+
|
|
10
|
+
# ── CPU ─────────────────────────────────────────────────────────────────
|
|
11
|
+
HW_CPU_ARCH=$(uname -m)
|
|
12
|
+
|
|
13
|
+
if [[ -f /proc/cpuinfo ]]; then
|
|
14
|
+
HW_CPU_CORES=$(grep -c '^processor' /proc/cpuinfo 2>/dev/null || nproc 2>/dev/null || echo 1)
|
|
15
|
+
elif check_command sysctl; then
|
|
16
|
+
HW_CPU_CORES=$(sysctl -n hw.ncpu 2>/dev/null || echo 1)
|
|
17
|
+
else
|
|
18
|
+
HW_CPU_CORES=1
|
|
19
|
+
fi
|
|
20
|
+
|
|
21
|
+
# ── Memory ──────────────────────────────────────────────────────────────
|
|
22
|
+
if [[ -f /proc/meminfo ]]; then
|
|
23
|
+
local mem_kb
|
|
24
|
+
mem_kb=$(awk '/^MemTotal:/ {print $2}' /proc/meminfo 2>/dev/null || echo 0)
|
|
25
|
+
HW_TOTAL_RAM_MB=$(( mem_kb / 1024 ))
|
|
26
|
+
elif check_command sysctl; then
|
|
27
|
+
local mem_bytes
|
|
28
|
+
mem_bytes=$(sysctl -n hw.memsize 2>/dev/null || echo 0)
|
|
29
|
+
HW_TOTAL_RAM_MB=$(( mem_bytes / 1024 / 1024 ))
|
|
30
|
+
else
|
|
31
|
+
HW_TOTAL_RAM_MB=0
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
# ── GPU / Platform detection ────────────────────────────────────────────
|
|
35
|
+
HW_GPU_NAME="none"
|
|
36
|
+
HW_GPU_VRAM_MB=0
|
|
37
|
+
HW_DRIVER_VERSION="n/a"
|
|
38
|
+
HW_PLATFORM="generic"
|
|
39
|
+
|
|
40
|
+
# Check for Jetson (Tegra) first — it may not have nvidia-smi
|
|
41
|
+
if [[ -f /etc/nv_tegra_release ]] || uname -r 2>/dev/null | grep -qi tegra; then
|
|
42
|
+
HW_PLATFORM="jetson"
|
|
43
|
+
HW_GPU_NAME="NVIDIA Jetson (Tegra)"
|
|
44
|
+
# Jetson shares system memory; VRAM = total RAM
|
|
45
|
+
HW_GPU_VRAM_MB="${HW_TOTAL_RAM_MB}"
|
|
46
|
+
log_info "Jetson platform detected via Tegra signature."
|
|
47
|
+
fi
|
|
48
|
+
|
|
49
|
+
# Check for DGX Spark via DMI product name
|
|
50
|
+
if [[ -f /sys/devices/virtual/dmi/id/product_name ]]; then
|
|
51
|
+
local product_name
|
|
52
|
+
product_name=$(cat /sys/devices/virtual/dmi/id/product_name 2>/dev/null || echo "")
|
|
53
|
+
if echo "${product_name}" | grep -qiE "DGX.Spark|DGX_Spark"; then
|
|
54
|
+
HW_PLATFORM="dgx-spark"
|
|
55
|
+
log_info "DGX Spark detected via DMI product name."
|
|
56
|
+
fi
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
# nvidia-smi based detection
|
|
60
|
+
if check_command nvidia-smi; then
|
|
61
|
+
local gpu_info
|
|
62
|
+
gpu_info=$(nvidia-smi --query-gpu=name,memory.total,driver_version \
|
|
63
|
+
--format=csv,noheader,nounits 2>/dev/null || echo "")
|
|
64
|
+
|
|
65
|
+
if [[ -n "${gpu_info}" ]]; then
|
|
66
|
+
# Take the first GPU line
|
|
67
|
+
local first_gpu
|
|
68
|
+
first_gpu=$(echo "${gpu_info}" | head -n1)
|
|
69
|
+
|
|
70
|
+
HW_GPU_NAME=$(echo "${first_gpu}" | cut -d',' -f1 | xargs)
|
|
71
|
+
local vram_str
|
|
72
|
+
vram_str=$(echo "${first_gpu}" | cut -d',' -f2 | xargs)
|
|
73
|
+
# Handle [N/A] or "Not Supported" (DGX Spark unified memory)
|
|
74
|
+
if [[ "${vram_str}" =~ ^[0-9]+ ]]; then
|
|
75
|
+
HW_GPU_VRAM_MB="${vram_str%%.*}"
|
|
76
|
+
else
|
|
77
|
+
HW_GPU_VRAM_MB=0
|
|
78
|
+
fi
|
|
79
|
+
HW_DRIVER_VERSION=$(echo "${first_gpu}" | cut -d',' -f3 | xargs)
|
|
80
|
+
|
|
81
|
+
# Detect DGX Spark by GPU name (GB10 / Grace-Blackwell)
|
|
82
|
+
if echo "${HW_GPU_NAME}" | grep -qiE "GB10|DGX.*Spark|Grace.*Blackwell"; then
|
|
83
|
+
HW_PLATFORM="dgx-spark"
|
|
84
|
+
fi
|
|
85
|
+
|
|
86
|
+
# Detect RTX cards
|
|
87
|
+
if [[ "${HW_PLATFORM}" == "generic" ]]; then
|
|
88
|
+
if echo "${HW_GPU_NAME}" | grep -qiE "RTX|GeForce|Quadro"; then
|
|
89
|
+
HW_PLATFORM="rtx"
|
|
90
|
+
fi
|
|
91
|
+
fi
|
|
92
|
+
fi
|
|
93
|
+
fi
|
|
94
|
+
|
|
95
|
+
# macOS with Apple Silicon: detect GPU via system_profiler
|
|
96
|
+
if [[ "${HW_PLATFORM}" == "generic" && "$(uname)" == "Darwin" ]]; then
|
|
97
|
+
local chip_info
|
|
98
|
+
chip_info=$(sysctl -n machdep.cpu.brand_string 2>/dev/null || echo "")
|
|
99
|
+
if echo "${chip_info}" | grep -qi "Apple"; then
|
|
100
|
+
HW_PLATFORM="mac"
|
|
101
|
+
HW_GPU_NAME="Apple Silicon (${chip_info})"
|
|
102
|
+
# Apple Silicon uses unified memory; GPU VRAM = system RAM
|
|
103
|
+
HW_GPU_VRAM_MB="${HW_TOTAL_RAM_MB}"
|
|
104
|
+
HW_DRIVER_VERSION="Metal"
|
|
105
|
+
fi
|
|
106
|
+
fi
|
|
107
|
+
|
|
108
|
+
# Unified memory platforms: VRAM = system RAM (nvidia-smi reports N/A)
|
|
109
|
+
# This must come AFTER nvidia-smi parsing which may have reset VRAM to 0
|
|
110
|
+
if [[ "${HW_PLATFORM}" == "dgx-spark" ]]; then
|
|
111
|
+
HW_TOTAL_RAM_MB=131072 # 128 * 1024
|
|
112
|
+
HW_GPU_VRAM_MB=131072
|
|
113
|
+
elif [[ "${HW_PLATFORM}" == "jetson" && "${HW_GPU_VRAM_MB}" -eq 0 ]]; then
|
|
114
|
+
# Jetson uses unified memory; nvidia-smi returns N/A for VRAM
|
|
115
|
+
HW_GPU_VRAM_MB="${HW_TOTAL_RAM_MB}"
|
|
116
|
+
fi
|
|
117
|
+
|
|
118
|
+
# ── Validate NVIDIA driver version (Ollama needs >= 531 for GPU) ────────
|
|
119
|
+
if [[ "${HW_DRIVER_VERSION}" != "n/a" && "${HW_DRIVER_VERSION}" != "Metal" ]]; then
|
|
120
|
+
local driver_major
|
|
121
|
+
driver_major=$(echo "${HW_DRIVER_VERSION}" | cut -d. -f1)
|
|
122
|
+
if [[ "${driver_major}" =~ ^[0-9]+$ ]] && (( driver_major > 0 && driver_major < 531 )); then
|
|
123
|
+
log_warn "NVIDIA driver ${HW_DRIVER_VERSION} is too old for GPU inference (need >= 531)."
|
|
124
|
+
log_warn "Ollama will fall back to CPU-only mode. Update driver: sudo apt install nvidia-driver-535"
|
|
125
|
+
fi
|
|
126
|
+
fi
|
|
127
|
+
|
|
128
|
+
# ── Export globals ──────────────────────────────────────────────────────
|
|
129
|
+
export HW_PLATFORM HW_GPU_NAME HW_GPU_VRAM_MB HW_TOTAL_RAM_MB
|
|
130
|
+
export HW_CPU_CORES HW_CPU_ARCH HW_DRIVER_VERSION
|
|
131
|
+
|
|
132
|
+
# ── Pretty summary ──────────────────────────────────────────────────────
|
|
133
|
+
local ram_gb=$(( HW_TOTAL_RAM_MB / 1024 ))
|
|
134
|
+
local vram_gb=$(( HW_GPU_VRAM_MB / 1024 ))
|
|
135
|
+
|
|
136
|
+
local platform_label
|
|
137
|
+
case "${HW_PLATFORM}" in
|
|
138
|
+
dgx-spark) platform_label="NVIDIA DGX Spark" ;;
|
|
139
|
+
jetson) platform_label="NVIDIA Jetson" ;;
|
|
140
|
+
rtx) platform_label="NVIDIA RTX Desktop" ;;
|
|
141
|
+
mac) platform_label="macOS Apple Silicon" ;;
|
|
142
|
+
*) platform_label="Generic / Unknown GPU" ;;
|
|
143
|
+
esac
|
|
144
|
+
|
|
145
|
+
print_box \
|
|
146
|
+
"${BOLD}Hardware Summary${RESET}" \
|
|
147
|
+
"" \
|
|
148
|
+
"Platform : ${CYAN}${platform_label}${RESET}" \
|
|
149
|
+
"GPU : ${HW_GPU_NAME}" \
|
|
150
|
+
"VRAM : ${vram_gb} GB" \
|
|
151
|
+
"System RAM : ${ram_gb} GB" \
|
|
152
|
+
"CPU Cores : ${HW_CPU_CORES} (${HW_CPU_ARCH})" \
|
|
153
|
+
"Driver : ${HW_DRIVER_VERSION}"
|
|
154
|
+
|
|
155
|
+
log_success "Hardware detection complete — platform: ${HW_PLATFORM}"
|
|
156
|
+
}
|