@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/v2/lib/common.sh
ADDED
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
CLAWSPARK_DIR="${CLAWSPARK_DIR:-${HOME}/.clawspark-v2}"
|
|
5
|
+
CLAWSPARK_LOG="${CLAWSPARK_LOG:-${CLAWSPARK_DIR}/install.log}"
|
|
6
|
+
CLAWSPARK_V2_DIR="${CLAWSPARK_DIR}"
|
|
7
|
+
CLAWSPARK_V2_LOG="${CLAWSPARK_LOG}"
|
|
8
|
+
CLAWSPARK_DEFAULTS="${CLAWSPARK_DEFAULTS:-false}"
|
|
9
|
+
|
|
10
|
+
if [[ -t 1 ]] && command -v tput &>/dev/null && [[ "$(tput colors 2>/dev/null || echo 0)" -ge 8 ]]; then
|
|
11
|
+
RED=$(tput setaf 1)
|
|
12
|
+
GREEN=$(tput setaf 2)
|
|
13
|
+
YELLOW=$(tput setaf 3)
|
|
14
|
+
BLUE=$(tput setaf 4)
|
|
15
|
+
CYAN=$(tput setaf 6)
|
|
16
|
+
BOLD=$(tput bold)
|
|
17
|
+
RESET=$(tput sgr0)
|
|
18
|
+
else
|
|
19
|
+
RED=$'\033[0;31m'
|
|
20
|
+
GREEN=$'\033[0;32m'
|
|
21
|
+
YELLOW=$'\033[0;33m'
|
|
22
|
+
BLUE=$'\033[0;34m'
|
|
23
|
+
CYAN=$'\033[0;36m'
|
|
24
|
+
BOLD=$'\033[1m'
|
|
25
|
+
RESET=$'\033[0m'
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
to_lower() { echo "$1" | tr '[:upper:]' '[:lower:]'; }
|
|
29
|
+
|
|
30
|
+
_ts() { date '+%H:%M:%S'; }
|
|
31
|
+
|
|
32
|
+
_log_to_file() {
|
|
33
|
+
local level="$1"; shift
|
|
34
|
+
mkdir -p "${CLAWSPARK_DIR}"
|
|
35
|
+
printf '[%s] [%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "${level}" "$*" >> "${CLAWSPARK_LOG}" 2>/dev/null || true
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
log_info() {
|
|
39
|
+
printf '%s[%s]%s %s\n' "${BLUE}" "$(_ts)" "${RESET}" "$*"
|
|
40
|
+
_log_to_file "INFO" "$*"
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
log_warn() {
|
|
44
|
+
printf '%s[%s] WARNING:%s %s\n' "${YELLOW}" "$(_ts)" "${RESET}" "$*" >&2
|
|
45
|
+
_log_to_file "WARN" "$*"
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
log_error() {
|
|
49
|
+
printf '%s[%s] ERROR:%s %s\n' "${RED}" "$(_ts)" "${RESET}" "$*" >&2
|
|
50
|
+
_log_to_file "ERROR" "$*"
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
log_success() {
|
|
54
|
+
printf '%s[%s] OK:%s %s\n' "${GREEN}" "$(_ts)" "${RESET}" "$*"
|
|
55
|
+
_log_to_file "OK" "$*"
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
check_command() {
|
|
59
|
+
command -v "$1" &>/dev/null
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
prompt_choice() {
|
|
63
|
+
local question="$1"
|
|
64
|
+
local options_name="$2"
|
|
65
|
+
local default_idx="${3:-0}"
|
|
66
|
+
local count
|
|
67
|
+
eval "count=\${#${options_name}[@]}"
|
|
68
|
+
|
|
69
|
+
if [[ "${CLAWSPARK_DEFAULTS}" == "true" ]]; then
|
|
70
|
+
eval "printf '%s' \"\${${options_name}[${default_idx}]}\""
|
|
71
|
+
return 0
|
|
72
|
+
fi
|
|
73
|
+
|
|
74
|
+
printf '\n%s%s%s\n' "${BOLD}" "${question}" "${RESET}" >/dev/tty
|
|
75
|
+
local i opt marker
|
|
76
|
+
for i in $(seq 0 $(( count - 1 ))); do
|
|
77
|
+
marker=""
|
|
78
|
+
if [[ "${i}" -eq "${default_idx}" ]]; then
|
|
79
|
+
marker=" ${CYAN}(default)${RESET}"
|
|
80
|
+
fi
|
|
81
|
+
eval "opt=\${${options_name}[${i}]}"
|
|
82
|
+
printf ' %s%d)%s %s%s\n' "${GREEN}" $(( i + 1 )) "${RESET}" "${opt}" "${marker}" >/dev/tty
|
|
83
|
+
done
|
|
84
|
+
|
|
85
|
+
local selection
|
|
86
|
+
while true; do
|
|
87
|
+
printf '%s> %s' "${BOLD}" "${RESET}" >/dev/tty
|
|
88
|
+
read -r selection </dev/tty || selection=""
|
|
89
|
+
if [[ -z "${selection}" ]]; then
|
|
90
|
+
eval "printf '%s' \"\${${options_name}[${default_idx}]}\""
|
|
91
|
+
return 0
|
|
92
|
+
fi
|
|
93
|
+
if [[ "${selection}" =~ ^[0-9]+$ ]] && (( selection >= 1 && selection <= count )); then
|
|
94
|
+
eval "printf '%s' \"\${${options_name}[$(( selection - 1 ))]}\""
|
|
95
|
+
return 0
|
|
96
|
+
fi
|
|
97
|
+
printf ' %sPlease enter a number between 1 and %d%s\n' "${YELLOW}" "${count}" "${RESET}" >/dev/tty
|
|
98
|
+
done
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
prompt_input() {
|
|
102
|
+
local question="$1"
|
|
103
|
+
local default_value="${2:-}"
|
|
104
|
+
|
|
105
|
+
if [[ "${CLAWSPARK_DEFAULTS}" == "true" ]]; then
|
|
106
|
+
printf '%s' "${default_value}"
|
|
107
|
+
return 0
|
|
108
|
+
fi
|
|
109
|
+
|
|
110
|
+
if [[ -n "${default_value}" ]]; then
|
|
111
|
+
printf '\n%s%s%s [%s]: ' "${BOLD}" "${question}" "${RESET}" "${default_value}" >/dev/tty
|
|
112
|
+
else
|
|
113
|
+
printf '\n%s%s%s: ' "${BOLD}" "${question}" "${RESET}" >/dev/tty
|
|
114
|
+
fi
|
|
115
|
+
|
|
116
|
+
local value
|
|
117
|
+
read -r value </dev/tty || value=""
|
|
118
|
+
if [[ -z "${value}" ]]; then
|
|
119
|
+
value="${default_value}"
|
|
120
|
+
fi
|
|
121
|
+
printf '%s' "${value}"
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
prompt_secret() {
|
|
125
|
+
local question="$1"
|
|
126
|
+
|
|
127
|
+
if [[ "${CLAWSPARK_DEFAULTS}" == "true" ]]; then
|
|
128
|
+
printf '%s' "${FLAG_API_KEY:-}"
|
|
129
|
+
return 0
|
|
130
|
+
fi
|
|
131
|
+
|
|
132
|
+
printf '\n%s%s%s: ' "${BOLD}" "${question}" "${RESET}" >/dev/tty
|
|
133
|
+
local value=""
|
|
134
|
+
stty -echo </dev/tty
|
|
135
|
+
read -r value </dev/tty || value=""
|
|
136
|
+
stty echo </dev/tty
|
|
137
|
+
printf '\n' >/dev/tty
|
|
138
|
+
printf '%s' "${value}"
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
prompt_yn() {
|
|
142
|
+
local question="$1"
|
|
143
|
+
local default="${2:-y}"
|
|
144
|
+
|
|
145
|
+
if [[ "${CLAWSPARK_DEFAULTS}" == "true" ]]; then
|
|
146
|
+
[[ "${default}" == "y" ]] && return 0 || return 1
|
|
147
|
+
fi
|
|
148
|
+
|
|
149
|
+
local hint="[y/N]"
|
|
150
|
+
[[ "${default}" == "y" ]] && hint="[Y/n]"
|
|
151
|
+
printf '\n%s%s %s%s ' "${BOLD}" "${question}" "${hint}" "${RESET}" >/dev/tty
|
|
152
|
+
|
|
153
|
+
local answer
|
|
154
|
+
read -r answer </dev/tty || answer=""
|
|
155
|
+
answer=$(to_lower "${answer}")
|
|
156
|
+
if [[ -z "${answer}" ]]; then
|
|
157
|
+
[[ "${default}" == "y" ]] && return 0 || return 1
|
|
158
|
+
fi
|
|
159
|
+
[[ "${answer}" =~ ^y(es)?$ ]]
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
spinner() {
|
|
163
|
+
local pid="$1"
|
|
164
|
+
local msg="${2:-Working...}"
|
|
165
|
+
local frames=('⠋' '⠙' '⠹' '⠸' '⠼' '⠴' '⠦' '⠧' '⠇' '⠏')
|
|
166
|
+
local frame_count=${#frames[@]}
|
|
167
|
+
local i=0
|
|
168
|
+
|
|
169
|
+
if [[ ! -t 1 ]]; then
|
|
170
|
+
wait "${pid}" 2>/dev/null || true
|
|
171
|
+
return 0
|
|
172
|
+
fi
|
|
173
|
+
|
|
174
|
+
printf ' '
|
|
175
|
+
while kill -0 "${pid}" 2>/dev/null; do
|
|
176
|
+
printf '\r %s%s%s %s' "${CYAN}" "${frames[i % frame_count]}" "${RESET}" "${msg}"
|
|
177
|
+
i=$(( i + 1 ))
|
|
178
|
+
sleep 0.08
|
|
179
|
+
done
|
|
180
|
+
|
|
181
|
+
wait "${pid}" 2>/dev/null
|
|
182
|
+
local exit_code=$?
|
|
183
|
+
if [[ ${exit_code} -eq 0 ]]; then
|
|
184
|
+
printf '\r %s✓%s %s\n' "${GREEN}" "${RESET}" "${msg}"
|
|
185
|
+
else
|
|
186
|
+
printf '\r %s✗%s %s\n' "${RED}" "${RESET}" "${msg}"
|
|
187
|
+
fi
|
|
188
|
+
return 0
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
hr() {
|
|
192
|
+
local cols
|
|
193
|
+
cols=$(tput cols 2>/dev/null || echo 60)
|
|
194
|
+
printf '%s%*s%s\n' "${BLUE}" "${cols}" '' "${RESET}" | tr ' ' '─'
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
print_box() {
|
|
198
|
+
local lines=("$@")
|
|
199
|
+
local max_len=0
|
|
200
|
+
local line
|
|
201
|
+
|
|
202
|
+
for line in "${lines[@]}"; do
|
|
203
|
+
local stripped
|
|
204
|
+
stripped=$(printf '%s' "${line}" | sed 's/\x1b\[[0-9;]*m//g')
|
|
205
|
+
(( ${#stripped} > max_len )) && max_len=${#stripped}
|
|
206
|
+
done
|
|
207
|
+
|
|
208
|
+
local pad=$(( max_len + 2 ))
|
|
209
|
+
printf '%s┌%s┐%s\n' "${BLUE}" "$(printf '─%.0s' $(seq 1 "${pad}"))" "${RESET}"
|
|
210
|
+
for line in "${lines[@]}"; do
|
|
211
|
+
local stripped
|
|
212
|
+
stripped=$(printf '%s' "${line}" | sed 's/\x1b\[[0-9;]*m//g')
|
|
213
|
+
local spaces=$(( max_len - ${#stripped} ))
|
|
214
|
+
printf '%s│%s %s%*s %s│%s\n' "${BLUE}" "${RESET}" "${line}" "${spaces}" '' "${BLUE}" "${RESET}"
|
|
215
|
+
done
|
|
216
|
+
printf '%s└%s┘%s\n' "${BLUE}" "$(printf '─%.0s' $(seq 1 "${pad}"))" "${RESET}"
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
mask_value() {
|
|
220
|
+
local value="$1"
|
|
221
|
+
if [[ -z "${value}" ]]; then
|
|
222
|
+
printf '(empty)'
|
|
223
|
+
elif [[ ${#value} -le 8 ]]; then
|
|
224
|
+
printf '********'
|
|
225
|
+
else
|
|
226
|
+
printf '%s****%s' "${value:0:4}" "${value: -4}"
|
|
227
|
+
fi
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
write_key_value() {
|
|
231
|
+
local file="$1"
|
|
232
|
+
local key="$2"
|
|
233
|
+
local value="$3"
|
|
234
|
+
touch "${file}"
|
|
235
|
+
if grep -q "^${key}=" "${file}" 2>/dev/null; then
|
|
236
|
+
python3 - "$file" "$key" "$value" <<'PY'
|
|
237
|
+
import sys
|
|
238
|
+
path, key, value = sys.argv[1:4]
|
|
239
|
+
with open(path, 'r', encoding='utf-8') as fh:
|
|
240
|
+
lines = fh.readlines()
|
|
241
|
+
with open(path, 'w', encoding='utf-8') as fh:
|
|
242
|
+
for line in lines:
|
|
243
|
+
if line.startswith(f"{key}="):
|
|
244
|
+
fh.write(f"{key}={value}\n")
|
|
245
|
+
else:
|
|
246
|
+
fh.write(line)
|
|
247
|
+
PY
|
|
248
|
+
else
|
|
249
|
+
printf '%s=%s\n' "${key}" "${value}" >> "${file}"
|
|
250
|
+
fi
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
_parse_enabled_skills() {
|
|
254
|
+
local skills_file="$1"
|
|
255
|
+
[[ -f "${skills_file}" && -r "${skills_file}" ]] || return 1
|
|
256
|
+
local in_enabled=false
|
|
257
|
+
|
|
258
|
+
while IFS= read -r line; do
|
|
259
|
+
[[ "${line}" =~ ^[[:space:]]*# ]] && continue
|
|
260
|
+
[[ -z "${line// }" ]] && continue
|
|
261
|
+
|
|
262
|
+
if [[ "${line}" =~ enabled:[[:space:]]*$ ]]; then
|
|
263
|
+
in_enabled=true
|
|
264
|
+
continue
|
|
265
|
+
fi
|
|
266
|
+
if ${in_enabled} && [[ "${line}" =~ ^[[:space:]]{0,3}[a-zA-Z] ]] && [[ ! "${line}" =~ ^[[:space:]]*- ]]; then
|
|
267
|
+
in_enabled=false
|
|
268
|
+
continue
|
|
269
|
+
fi
|
|
270
|
+
if ${in_enabled} && [[ "${line}" =~ ^[[:space:]]*-[[:space:]]+name:[[:space:]]+(.*) ]]; then
|
|
271
|
+
local slug="${BASH_REMATCH[1]}"
|
|
272
|
+
slug="${slug## }"
|
|
273
|
+
slug="${slug%% }"
|
|
274
|
+
echo "${slug}"
|
|
275
|
+
continue
|
|
276
|
+
fi
|
|
277
|
+
if ${in_enabled} && [[ "${line}" =~ ^[[:space:]]*-[[:space:]]+(.*) ]]; then
|
|
278
|
+
local slug="${BASH_REMATCH[1]}"
|
|
279
|
+
slug="${slug## }"
|
|
280
|
+
slug="${slug%% }"
|
|
281
|
+
[[ "${slug}" =~ ^[a-zA-Z]+: ]] && continue
|
|
282
|
+
echo "${slug}"
|
|
283
|
+
fi
|
|
284
|
+
done < "${skills_file}"
|
|
285
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
detect_hardware_v2() {
|
|
5
|
+
log_info "Detecting hardware profile for v2..."
|
|
6
|
+
|
|
7
|
+
HW_CPU_ARCH=$(uname -m)
|
|
8
|
+
if [[ -f /proc/cpuinfo ]]; then
|
|
9
|
+
HW_CPU_CORES=$(grep -c '^processor' /proc/cpuinfo 2>/dev/null || echo 1)
|
|
10
|
+
elif check_command sysctl; then
|
|
11
|
+
HW_CPU_CORES=$(sysctl -n hw.ncpu 2>/dev/null || echo 1)
|
|
12
|
+
else
|
|
13
|
+
HW_CPU_CORES=1
|
|
14
|
+
fi
|
|
15
|
+
|
|
16
|
+
if [[ -f /proc/meminfo ]]; then
|
|
17
|
+
local mem_kb
|
|
18
|
+
mem_kb=$(awk '/^MemTotal:/ {print $2}' /proc/meminfo 2>/dev/null || echo 0)
|
|
19
|
+
HW_TOTAL_RAM_MB=$(( mem_kb / 1024 ))
|
|
20
|
+
elif check_command sysctl; then
|
|
21
|
+
local mem_bytes
|
|
22
|
+
mem_bytes=$(sysctl -n hw.memsize 2>/dev/null || echo 0)
|
|
23
|
+
HW_TOTAL_RAM_MB=$(( mem_bytes / 1024 / 1024 ))
|
|
24
|
+
else
|
|
25
|
+
HW_TOTAL_RAM_MB=0
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
HW_GPU_NAME="none"
|
|
29
|
+
HW_GPU_VRAM_MB=0
|
|
30
|
+
HW_DRIVER_VERSION="n/a"
|
|
31
|
+
HW_ACCELERATION="cpu"
|
|
32
|
+
HW_PLATFORM="cpu-generic"
|
|
33
|
+
|
|
34
|
+
if [[ -f /etc/nv_tegra_release ]] || uname -r 2>/dev/null | grep -qi tegra; then
|
|
35
|
+
HW_PLATFORM="jetson"
|
|
36
|
+
HW_GPU_NAME="NVIDIA Jetson (Tegra)"
|
|
37
|
+
HW_GPU_VRAM_MB="${HW_TOTAL_RAM_MB}"
|
|
38
|
+
HW_ACCELERATION="gpu"
|
|
39
|
+
fi
|
|
40
|
+
|
|
41
|
+
if [[ -f /sys/devices/virtual/dmi/id/product_name ]]; then
|
|
42
|
+
local product_name
|
|
43
|
+
product_name=$(cat /sys/devices/virtual/dmi/id/product_name 2>/dev/null || echo "")
|
|
44
|
+
if echo "${product_name}" | grep -qiE "DGX.Spark|DGX_Spark"; then
|
|
45
|
+
HW_PLATFORM="dgx-spark"
|
|
46
|
+
HW_GPU_NAME="NVIDIA GB10 / DGX Spark"
|
|
47
|
+
HW_GPU_VRAM_MB=131072
|
|
48
|
+
HW_TOTAL_RAM_MB=131072
|
|
49
|
+
HW_ACCELERATION="gpu"
|
|
50
|
+
fi
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
if check_command nvidia-smi; then
|
|
54
|
+
local gpu_info
|
|
55
|
+
gpu_info=$(nvidia-smi --query-gpu=name,memory.total,driver_version --format=csv,noheader,nounits 2>/dev/null || echo "")
|
|
56
|
+
if [[ -n "${gpu_info}" ]]; then
|
|
57
|
+
local first_gpu
|
|
58
|
+
first_gpu=$(echo "${gpu_info}" | head -n1)
|
|
59
|
+
HW_GPU_NAME=$(echo "${first_gpu}" | cut -d',' -f1 | xargs)
|
|
60
|
+
local vram_str
|
|
61
|
+
vram_str=$(echo "${first_gpu}" | cut -d',' -f2 | xargs)
|
|
62
|
+
if [[ "${vram_str}" =~ ^[0-9]+ ]]; then
|
|
63
|
+
HW_GPU_VRAM_MB="${vram_str%%.*}"
|
|
64
|
+
fi
|
|
65
|
+
HW_DRIVER_VERSION=$(echo "${first_gpu}" | cut -d',' -f3 | xargs)
|
|
66
|
+
HW_ACCELERATION="gpu"
|
|
67
|
+
if [[ "${HW_PLATFORM}" == "cpu-generic" ]]; then
|
|
68
|
+
if echo "${HW_GPU_NAME}" | grep -qiE "RTX|GeForce|Quadro"; then
|
|
69
|
+
HW_PLATFORM="rtx"
|
|
70
|
+
else
|
|
71
|
+
HW_PLATFORM="gpu-generic"
|
|
72
|
+
fi
|
|
73
|
+
fi
|
|
74
|
+
fi
|
|
75
|
+
fi
|
|
76
|
+
|
|
77
|
+
if [[ "${HW_PLATFORM}" == "cpu-generic" && "$(uname)" == "Darwin" ]]; then
|
|
78
|
+
local chip_info
|
|
79
|
+
chip_info=$(sysctl -n machdep.cpu.brand_string 2>/dev/null || echo "")
|
|
80
|
+
if echo "${chip_info}" | grep -qi "Apple"; then
|
|
81
|
+
HW_PLATFORM="mac"
|
|
82
|
+
HW_GPU_NAME="Apple Silicon (${chip_info})"
|
|
83
|
+
HW_GPU_VRAM_MB="${HW_TOTAL_RAM_MB}"
|
|
84
|
+
HW_DRIVER_VERSION="Metal"
|
|
85
|
+
HW_ACCELERATION="gpu"
|
|
86
|
+
fi
|
|
87
|
+
fi
|
|
88
|
+
|
|
89
|
+
if [[ "${HW_ACCELERATION}" == "cpu" ]]; then
|
|
90
|
+
if (( HW_TOTAL_RAM_MB >= 32768 )); then
|
|
91
|
+
HW_CPU_PROFILE="cpu-large"
|
|
92
|
+
elif (( HW_TOTAL_RAM_MB >= 16384 )); then
|
|
93
|
+
HW_CPU_PROFILE="cpu-medium"
|
|
94
|
+
else
|
|
95
|
+
HW_CPU_PROFILE="cpu-small"
|
|
96
|
+
fi
|
|
97
|
+
else
|
|
98
|
+
HW_CPU_PROFILE="with-gpu"
|
|
99
|
+
fi
|
|
100
|
+
|
|
101
|
+
export HW_CPU_ARCH HW_CPU_CORES HW_TOTAL_RAM_MB HW_GPU_NAME HW_GPU_VRAM_MB
|
|
102
|
+
export HW_DRIVER_VERSION HW_ACCELERATION HW_PLATFORM HW_CPU_PROFILE
|
|
103
|
+
|
|
104
|
+
local ram_gb=$(( HW_TOTAL_RAM_MB / 1024 ))
|
|
105
|
+
local vram_gb=$(( HW_GPU_VRAM_MB / 1024 ))
|
|
106
|
+
print_box \
|
|
107
|
+
"${BOLD}v2 Hardware Summary${RESET}" \
|
|
108
|
+
"" \
|
|
109
|
+
"Platform : ${CYAN}${HW_PLATFORM}${RESET}" \
|
|
110
|
+
"Acceleration : ${HW_ACCELERATION}" \
|
|
111
|
+
"GPU : ${HW_GPU_NAME}" \
|
|
112
|
+
"VRAM : ${vram_gb} GB" \
|
|
113
|
+
"System RAM : ${ram_gb} GB" \
|
|
114
|
+
"CPU Cores : ${HW_CPU_CORES} (${HW_CPU_ARCH})" \
|
|
115
|
+
"CPU Profile : ${HW_CPU_PROFILE}" \
|
|
116
|
+
"Driver : ${HW_DRIVER_VERSION}"
|
|
117
|
+
|
|
118
|
+
log_success "Hardware profile ready: ${HW_PLATFORM} / ${HW_ACCELERATION}"
|
|
119
|
+
}
|
|
@@ -0,0 +1,273 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
select_runtime_v2() {
|
|
5
|
+
log_info "Selecting runtime mode for v2..."
|
|
6
|
+
|
|
7
|
+
local runtime_options=()
|
|
8
|
+
local default_idx=0
|
|
9
|
+
|
|
10
|
+
if [[ "${HW_ACCELERATION:-cpu}" == "gpu" ]]; then
|
|
11
|
+
runtime_options=("local-gpu" "local-cpu" "hybrid" "api-only")
|
|
12
|
+
default_idx=0
|
|
13
|
+
else
|
|
14
|
+
runtime_options=("local-cpu" "api-only" "hybrid")
|
|
15
|
+
default_idx=0
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
if [[ -n "${FLAG_RUNTIME_MODE:-}" ]]; then
|
|
19
|
+
RUNTIME_MODE="${FLAG_RUNTIME_MODE}"
|
|
20
|
+
else
|
|
21
|
+
RUNTIME_MODE=$(prompt_choice "Choose deployment mode" runtime_options "${default_idx}")
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
RUNTIME_MODE=$(to_lower "${RUNTIME_MODE}")
|
|
25
|
+
export RUNTIME_MODE
|
|
26
|
+
log_success "Runtime mode: ${RUNTIME_MODE}"
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
select_provider_v2() {
|
|
30
|
+
log_info "Selecting inference provider..."
|
|
31
|
+
|
|
32
|
+
local provider_options=()
|
|
33
|
+
local default_idx=0
|
|
34
|
+
|
|
35
|
+
case "${RUNTIME_MODE}" in
|
|
36
|
+
local-gpu|local-cpu)
|
|
37
|
+
provider_options=("ollama")
|
|
38
|
+
default_idx=0
|
|
39
|
+
;;
|
|
40
|
+
api-only|hybrid)
|
|
41
|
+
provider_options=("openai" "anthropic" "openrouter" "google" "custom")
|
|
42
|
+
default_idx=0
|
|
43
|
+
;;
|
|
44
|
+
*)
|
|
45
|
+
provider_options=("ollama")
|
|
46
|
+
default_idx=0
|
|
47
|
+
;;
|
|
48
|
+
esac
|
|
49
|
+
|
|
50
|
+
if [[ -n "${FLAG_PROVIDER:-}" ]]; then
|
|
51
|
+
PRIMARY_PROVIDER="${FLAG_PROVIDER}"
|
|
52
|
+
else
|
|
53
|
+
PRIMARY_PROVIDER=$(prompt_choice "Choose primary provider" provider_options "${default_idx}")
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
PRIMARY_PROVIDER=$(to_lower "${PRIMARY_PROVIDER}")
|
|
57
|
+
export PRIMARY_PROVIDER
|
|
58
|
+
log_success "Primary provider: ${PRIMARY_PROVIDER}"
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
select_model_v2() {
|
|
62
|
+
log_info "Selecting default model..."
|
|
63
|
+
|
|
64
|
+
local model_options=()
|
|
65
|
+
local model_ids=()
|
|
66
|
+
local default_idx=0
|
|
67
|
+
|
|
68
|
+
case "${PRIMARY_PROVIDER}" in
|
|
69
|
+
ollama)
|
|
70
|
+
if [[ "${RUNTIME_MODE}" == "local-cpu" ]]; then
|
|
71
|
+
if [[ "${HW_CPU_PROFILE}" == "cpu-large" ]]; then
|
|
72
|
+
model_ids=("qwen2.5:14b-instruct-q4_K_M" "qwen2.5:7b-instruct-q4_K_M" "llama3.1:8b-instruct-q4_K_M")
|
|
73
|
+
model_options=(
|
|
74
|
+
"qwen2.5:14b-instruct-q4_K_M - best CPU quality for 32GB+ RAM"
|
|
75
|
+
"qwen2.5:7b-instruct-q4_K_M - balanced CPU default"
|
|
76
|
+
"llama3.1:8b-instruct-q4_K_M - stable general assistant"
|
|
77
|
+
)
|
|
78
|
+
default_idx=1
|
|
79
|
+
elif [[ "${HW_CPU_PROFILE}" == "cpu-medium" ]]; then
|
|
80
|
+
model_ids=("qwen2.5:7b-instruct-q4_K_M" "llama3.1:8b-instruct-q4_K_M" "phi4-mini")
|
|
81
|
+
model_options=(
|
|
82
|
+
"qwen2.5:7b-instruct-q4_K_M - recommended for 16GB CPUs"
|
|
83
|
+
"llama3.1:8b-instruct-q4_K_M - general purpose CPU model"
|
|
84
|
+
"phi4-mini - lightweight and responsive"
|
|
85
|
+
)
|
|
86
|
+
default_idx=0
|
|
87
|
+
else
|
|
88
|
+
model_ids=("phi4-mini" "qwen2.5:3b-instruct-q4_K_M" "llama3.2:3b")
|
|
89
|
+
model_options=(
|
|
90
|
+
"phi4-mini - lowest RAM recommendation"
|
|
91
|
+
"qwen2.5:3b-instruct-q4_K_M - compact instruct model"
|
|
92
|
+
"llama3.2:3b - small local fallback"
|
|
93
|
+
)
|
|
94
|
+
default_idx=0
|
|
95
|
+
fi
|
|
96
|
+
else
|
|
97
|
+
model_ids=("qwen3.5:35b-a3b" "qwen3-coder:30b" "glm-4.7-flash")
|
|
98
|
+
model_options=(
|
|
99
|
+
"qwen3.5:35b-a3b - default GPU local model"
|
|
100
|
+
"qwen3-coder:30b - coding focused"
|
|
101
|
+
"glm-4.7-flash - faster compact option"
|
|
102
|
+
)
|
|
103
|
+
default_idx=0
|
|
104
|
+
fi
|
|
105
|
+
;;
|
|
106
|
+
openai)
|
|
107
|
+
model_ids=("gpt-4.1" "gpt-4.1-mini" "gpt-5-mini")
|
|
108
|
+
model_options=(
|
|
109
|
+
"gpt-4.1 - strong general purpose API model"
|
|
110
|
+
"gpt-4.1-mini - cheaper fast default"
|
|
111
|
+
"gpt-5-mini - compact next-gen option"
|
|
112
|
+
)
|
|
113
|
+
default_idx=1
|
|
114
|
+
;;
|
|
115
|
+
anthropic)
|
|
116
|
+
model_ids=("claude-sonnet-4-20250514" "claude-3-5-haiku-latest")
|
|
117
|
+
model_options=(
|
|
118
|
+
"claude-sonnet-4-20250514 - balanced reasoning"
|
|
119
|
+
"claude-3-5-haiku-latest - fast and lower cost"
|
|
120
|
+
)
|
|
121
|
+
default_idx=0
|
|
122
|
+
;;
|
|
123
|
+
openrouter)
|
|
124
|
+
model_ids=("openai/gpt-4.1-mini" "anthropic/claude-3.5-haiku" "google/gemini-2.0-flash-001")
|
|
125
|
+
model_options=(
|
|
126
|
+
"openai/gpt-4.1-mini - interoperable default"
|
|
127
|
+
"anthropic/claude-3.5-haiku - Anthropic via OpenRouter"
|
|
128
|
+
"google/gemini-2.0-flash-001 - fast multimodal route"
|
|
129
|
+
)
|
|
130
|
+
default_idx=0
|
|
131
|
+
;;
|
|
132
|
+
google)
|
|
133
|
+
model_ids=("gemini-2.0-flash" "gemini-1.5-pro")
|
|
134
|
+
model_options=(
|
|
135
|
+
"gemini-2.0-flash - default fast API model"
|
|
136
|
+
"gemini-1.5-pro - higher reasoning quality"
|
|
137
|
+
)
|
|
138
|
+
default_idx=0
|
|
139
|
+
;;
|
|
140
|
+
custom)
|
|
141
|
+
if [[ -n "${FLAG_MODEL:-}" ]]; then
|
|
142
|
+
SELECTED_MODEL_ID="${FLAG_MODEL}"
|
|
143
|
+
SELECTED_MODEL_NAME="${FLAG_MODEL}"
|
|
144
|
+
export SELECTED_MODEL_ID SELECTED_MODEL_NAME
|
|
145
|
+
log_success "Selected model: ${SELECTED_MODEL_ID}"
|
|
146
|
+
return 0
|
|
147
|
+
fi
|
|
148
|
+
|
|
149
|
+
local custom_model_default="custom-model"
|
|
150
|
+
if [[ "${CLAWSPARK_DEFAULTS}" == "true" ]]; then
|
|
151
|
+
if [[ -z "${FLAG_MODEL:-}" ]]; then
|
|
152
|
+
log_error "Custom provider requires --model when --defaults is used."
|
|
153
|
+
return 1
|
|
154
|
+
fi
|
|
155
|
+
fi
|
|
156
|
+
|
|
157
|
+
SELECTED_MODEL_ID=$(prompt_input "Enter custom model ID" "${custom_model_default}")
|
|
158
|
+
SELECTED_MODEL_NAME="${SELECTED_MODEL_ID}"
|
|
159
|
+
export SELECTED_MODEL_ID SELECTED_MODEL_NAME
|
|
160
|
+
log_success "Selected model: ${SELECTED_MODEL_ID}"
|
|
161
|
+
return 0
|
|
162
|
+
;;
|
|
163
|
+
*)
|
|
164
|
+
model_ids=("qwen2.5:7b-instruct-q4_K_M")
|
|
165
|
+
model_options=("qwen2.5:7b-instruct-q4_K_M - safe default")
|
|
166
|
+
default_idx=0
|
|
167
|
+
;;
|
|
168
|
+
esac
|
|
169
|
+
|
|
170
|
+
local choice_label
|
|
171
|
+
choice_label=$(prompt_choice "Choose default model" model_options "${default_idx}")
|
|
172
|
+
|
|
173
|
+
local idx=0
|
|
174
|
+
for idx in $(seq 0 $(( ${#model_options[@]} - 1 ))); do
|
|
175
|
+
if [[ "${model_options[${idx}]}" == "${choice_label}" ]]; then
|
|
176
|
+
SELECTED_MODEL_ID="${model_ids[${idx}]}"
|
|
177
|
+
SELECTED_MODEL_NAME="${model_ids[${idx}]}"
|
|
178
|
+
break
|
|
179
|
+
fi
|
|
180
|
+
done
|
|
181
|
+
|
|
182
|
+
if [[ -z "${SELECTED_MODEL_ID:-}" ]]; then
|
|
183
|
+
SELECTED_MODEL_ID="${model_ids[${default_idx}]}"
|
|
184
|
+
SELECTED_MODEL_NAME="${model_ids[${default_idx}]}"
|
|
185
|
+
fi
|
|
186
|
+
|
|
187
|
+
export SELECTED_MODEL_ID SELECTED_MODEL_NAME
|
|
188
|
+
log_success "Selected model: ${SELECTED_MODEL_ID}"
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
collect_provider_credentials_v2() {
|
|
192
|
+
API_BASE_URL=""
|
|
193
|
+
API_KEY=""
|
|
194
|
+
CUSTOM_PROVIDER_NAME=""
|
|
195
|
+
FALLBACK_PROVIDER=""
|
|
196
|
+
FALLBACK_MODEL_ID=""
|
|
197
|
+
|
|
198
|
+
case "${PRIMARY_PROVIDER}" in
|
|
199
|
+
openai)
|
|
200
|
+
API_KEY=$(prompt_secret "Enter OPENAI_API_KEY")
|
|
201
|
+
API_BASE_URL=$(prompt_input "OpenAI base URL" "${FLAG_BASE_URL:-https://api.openai.com/v1}")
|
|
202
|
+
;;
|
|
203
|
+
anthropic)
|
|
204
|
+
API_KEY=$(prompt_secret "Enter ANTHROPIC_API_KEY")
|
|
205
|
+
API_BASE_URL=$(prompt_input "Anthropic base URL" "${FLAG_BASE_URL:-https://api.anthropic.com}")
|
|
206
|
+
;;
|
|
207
|
+
openrouter)
|
|
208
|
+
API_KEY=$(prompt_secret "Enter OPENROUTER_API_KEY")
|
|
209
|
+
API_BASE_URL=$(prompt_input "OpenRouter base URL" "${FLAG_BASE_URL:-https://openrouter.ai/api/v1}")
|
|
210
|
+
;;
|
|
211
|
+
google)
|
|
212
|
+
API_KEY=$(prompt_secret "Enter GOOGLE_API_KEY")
|
|
213
|
+
API_BASE_URL=$(prompt_input "Google AI Studio base URL" "${FLAG_BASE_URL:-https://generativelanguage.googleapis.com/v1beta/openai}")
|
|
214
|
+
;;
|
|
215
|
+
custom)
|
|
216
|
+
CUSTOM_PROVIDER_NAME="${FLAG_PROVIDER_NAME:-}"
|
|
217
|
+
if [[ -z "${CUSTOM_PROVIDER_NAME}" ]]; then
|
|
218
|
+
if [[ "${CLAWSPARK_DEFAULTS}" == "true" ]]; then
|
|
219
|
+
log_error "Custom provider requires --provider-name when --defaults is used."
|
|
220
|
+
return 1
|
|
221
|
+
fi
|
|
222
|
+
CUSTOM_PROVIDER_NAME=$(prompt_input "Custom provider label" "Custom AI")
|
|
223
|
+
fi
|
|
224
|
+
|
|
225
|
+
if [[ -n "${FLAG_API_KEY:-}" ]]; then
|
|
226
|
+
API_KEY="${FLAG_API_KEY}"
|
|
227
|
+
else
|
|
228
|
+
API_KEY=$(prompt_secret "Enter custom provider API key")
|
|
229
|
+
fi
|
|
230
|
+
|
|
231
|
+
if [[ -z "${FLAG_BASE_URL:-}" ]]; then
|
|
232
|
+
if [[ "${CLAWSPARK_DEFAULTS}" == "true" ]]; then
|
|
233
|
+
log_error "Custom provider requires --base-url when --defaults is used."
|
|
234
|
+
return 1
|
|
235
|
+
fi
|
|
236
|
+
API_BASE_URL=$(prompt_input "Custom provider base URL" "https://your-provider.example.com/v1")
|
|
237
|
+
else
|
|
238
|
+
API_BASE_URL="${FLAG_BASE_URL}"
|
|
239
|
+
fi
|
|
240
|
+
;;
|
|
241
|
+
ollama)
|
|
242
|
+
API_KEY="ollama"
|
|
243
|
+
API_BASE_URL=$(prompt_input "Ollama base URL" "${FLAG_BASE_URL:-http://127.0.0.1:11434/v1}")
|
|
244
|
+
;;
|
|
245
|
+
esac
|
|
246
|
+
|
|
247
|
+
if [[ "${PRIMARY_PROVIDER}" != "ollama" && -n "${FLAG_API_KEY:-}" ]]; then
|
|
248
|
+
API_KEY="${FLAG_API_KEY}"
|
|
249
|
+
fi
|
|
250
|
+
|
|
251
|
+
if [[ "${PRIMARY_PROVIDER}" != "ollama" && -z "${API_KEY}" ]]; then
|
|
252
|
+
log_error "API key is required for provider ${PRIMARY_PROVIDER}."
|
|
253
|
+
return 1
|
|
254
|
+
fi
|
|
255
|
+
|
|
256
|
+
if [[ -z "${API_BASE_URL}" ]]; then
|
|
257
|
+
log_error "Base URL is required for provider ${PRIMARY_PROVIDER}."
|
|
258
|
+
return 1
|
|
259
|
+
fi
|
|
260
|
+
|
|
261
|
+
if [[ "${RUNTIME_MODE}" == "hybrid" ]]; then
|
|
262
|
+
if [[ "${PRIMARY_PROVIDER}" == "ollama" ]]; then
|
|
263
|
+
FALLBACK_PROVIDER="openai"
|
|
264
|
+
FALLBACK_MODEL_ID="gpt-4.1-mini"
|
|
265
|
+
else
|
|
266
|
+
FALLBACK_PROVIDER="ollama"
|
|
267
|
+
FALLBACK_MODEL_ID="qwen2.5:7b-instruct-q4_K_M"
|
|
268
|
+
fi
|
|
269
|
+
fi
|
|
270
|
+
|
|
271
|
+
export API_BASE_URL API_KEY CUSTOM_PROVIDER_NAME FALLBACK_PROVIDER FALLBACK_MODEL_ID
|
|
272
|
+
log_info "Credential collection finished for ${PRIMARY_PROVIDER}"
|
|
273
|
+
}
|