-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathroadwarrior-ollama-peer-host.sh
More file actions
341 lines (282 loc) · 8.94 KB
/
Copy pathroadwarrior-ollama-peer-host.sh
File metadata and controls
341 lines (282 loc) · 8.94 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
#!/usr/bin/env bash
set -euo pipefail
# Roadwarrior bootstrap for the peer Ubuntu host that consumes Ollama from
# a private GPU host over a DigitalOcean VPC/private network.
#
# Intended usage:
# curl -fsSL https://raw.githubusercontent.com/vektort13/CDLP-OlamaUncensored/main/roadwarrior-ollama-peer-host.sh | bash
# or:
# bash roadwarrior-ollama-peer-host.sh
SCRIPT_NAME="$(basename "$0")"
TMPFILES=()
SUDO=""
cleanup() {
local path
for path in "${TMPFILES[@]:-}"; do
rm -f "$path" 2>/dev/null || true
done
}
trap cleanup EXIT
if [[ ! -r /dev/tty ]]; then
echo "$SCRIPT_NAME requires an interactive TTY." >&2
echo "Run it from a terminal instead of a non-interactive pipeline." >&2
exit 1
fi
if [[ -t 1 ]]; then
BOLD='\033[1m'
BLUE='\033[34m'
GREEN='\033[32m'
YELLOW='\033[33m'
RED='\033[31m'
NC='\033[0m'
else
BOLD=''
BLUE=''
GREEN=''
YELLOW=''
RED=''
NC=''
fi
say() {
printf '%b\n' "$1"
}
section() {
say ""
say "${BOLD}${BLUE}==>${NC} ${BOLD}$1${NC}"
}
info() {
say "${BLUE}[info]${NC} $1"
}
success() {
say "${GREEN}[ok]${NC} $1"
}
warn() {
say "${YELLOW}[warn]${NC} $1"
}
die() {
say "${RED}[error]${NC} $1" >&2
exit 1
}
need_cmd() {
command -v "$1" >/dev/null 2>&1 || die "Missing required command: $1"
}
prompt() {
local label="$1"
local default_value="${2-}"
local value
if [[ -n "$default_value" ]]; then
printf '%b' "${BOLD}${label}${NC} [${default_value}]: " > /dev/tty
else
printf '%b' "${BOLD}${label}${NC}: " > /dev/tty
fi
IFS= read -r value < /dev/tty
if [[ -z "$value" ]]; then
value="$default_value"
fi
printf '%s' "$value"
}
confirm() {
local label="$1"
local default_answer="${2:-Y}"
local suffix="[Y/n]"
local answer
if [[ "$default_answer" == "N" ]]; then
suffix="[y/N]"
fi
while true; do
printf '%b' "${BOLD}${label}${NC} ${suffix}: " > /dev/tty
IFS= read -r answer < /dev/tty
answer="${answer:-$default_answer}"
case "$answer" in
Y|y|yes|YES) return 0 ;;
N|n|no|NO) return 1 ;;
esac
warn "Please answer yes or no."
done
}
run_root() {
if [[ -n "$SUDO" ]]; then
"$SUDO" "$@"
return
fi
"$@"
}
metadata_get() {
local path="$1"
curl -fsS "http://169.254.169.254/metadata/v1/${path}"
}
detect_privilege_model() {
if [[ "$EUID" -eq 0 ]]; then
SUDO=""
return
fi
need_cmd sudo
SUDO="sudo"
}
ensure_supported_os() {
[[ -r /etc/os-release ]] || die "Cannot detect OS. /etc/os-release is missing."
# shellcheck disable=SC1091
source /etc/os-release
if [[ "${ID:-}" != "ubuntu" ]]; then
warn "This script is tuned for Ubuntu on a private peer host."
warn "Detected OS: ${PRETTY_NAME:-unknown}"
confirm "Continue anyway" "N" || exit 1
fi
}
valid_ipv4() {
local value="$1"
if [[ ! "$value" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
return 1
fi
local octet
IFS='.' read -r -a octets <<< "$value"
for octet in "${octets[@]}"; do
if (( octet < 0 || octet > 255 )); then
return 1
fi
done
return 0
}
is_private_ipv4() {
local value="$1"
local a=""
local b=""
valid_ipv4 "$value" || return 1
IFS='.' read -r a b _ <<< "$value"
if (( a == 10 )); then
return 0
fi
if (( a == 172 && b >= 16 && b <= 31 )); then
return 0
fi
if (( a == 192 && b == 168 )); then
return 0
fi
return 1
}
extract_model_names() {
grep -o '"name":"[^"]*"' <<< "$1" | sed -E 's/"name":"([^"]*)"/\1/' | awk '!seen[$0]++'
}
show_prereqs() {
section "Prerequisites"
say "This script assumes:"
say " 1. This host is the non-GPU peer Ubuntu host."
say " 2. The Ollama GPU host already exists and is configured."
say " 3. Both hosts are in the same private network or VPC."
say " 4. You want to confirm private-network reachability before wiring OpenClaw to it."
}
main() {
local detected_private_ip=""
local detected_public_ip=""
local gpu_private_ip=""
local base_url=""
local route_hint=""
local tags_response=""
local models_list=""
local selected_model=""
local onboard_model=""
section "Roadwarrior Ollama Peer Host"
say "This bootstrap verifies that a peer Ubuntu host can reach Ollama"
say "over the private network and prints the next OpenClaw command."
detect_privilege_model
ensure_supported_os
if ! command -v curl >/dev/null 2>&1; then
if confirm "curl is missing. Install it now" "Y"; then
run_root apt update
run_root apt install -y ca-certificates curl
else
die "curl is required to verify private-network access to Ollama."
fi
fi
need_cmd curl
show_prereqs
confirm "Continue" "Y" || exit 0
section "Detect network"
detected_private_ip="$(metadata_get interfaces/private/0/ipv4/address 2>/dev/null || true)"
detected_public_ip="$(metadata_get interfaces/public/0/ipv4/address 2>/dev/null || true)"
if [[ -n "$detected_private_ip" ]]; then
info "Detected peer private IP: $detected_private_ip"
else
warn "Could not read the peer private IP from the DigitalOcean metadata API."
fi
if [[ -n "$detected_public_ip" ]]; then
info "Detected peer public IP: $detected_public_ip"
fi
while true; do
gpu_private_ip="$(prompt "GPU host private IP running Ollama" "")"
valid_ipv4 "$gpu_private_ip" && break
warn "Enter a single IPv4 address, for example 10.10.0.10"
done
if ! is_private_ipv4 "$gpu_private_ip"; then
warn "The selected address is not RFC1918 private space. Make sure you are not targeting a public Ollama endpoint by mistake."
confirm "Continue with this address" "N" || exit 1
fi
base_url="http://${gpu_private_ip}:11434"
if command -v ip >/dev/null 2>&1; then
route_hint="$(ip route get "$gpu_private_ip" 2>/dev/null || true)"
fi
section "Reachability check"
info "Testing ${base_url}/api/tags"
if ! tags_response="$(curl -fsS --connect-timeout 5 --max-time 20 "${base_url}/api/tags")"; then
warn "The peer host could not reach ${base_url}."
warn "Check UFW on the GPU host, any attached DigitalOcean Cloud Firewall, and confirm both hosts are on the same private network."
exit 1
fi
success "Remote /api/tags check passed."
if [[ -n "$route_hint" ]]; then
info "Route: $route_hint"
fi
models_list="$(extract_model_names "$tags_response" || true)"
selected_model="$(printf '%s\n' "$models_list" | sed -n '1p')"
if [[ -n "$models_list" ]]; then
section "Remote models"
printf '%s\n' "$models_list" | while IFS= read -r model_name; do
[[ -n "$model_name" ]] || continue
say " - $model_name"
done
if [[ -n "$selected_model" ]] && confirm "Run a remote chat smoke test with ${selected_model}" "Y"; then
curl -fsS --connect-timeout 5 --max-time 30 "${base_url}/api/chat" \
-H 'Content-Type: application/json' \
-d "{\"model\":\"${selected_model}\",\"messages\":[{\"role\":\"user\",\"content\":\"Reply with only PEER_OK\"}]}" >/dev/null
success "Remote chat smoke test passed."
fi
else
warn "The GPU host is reachable, but /api/tags returned no pulled local models."
warn "Pull a model on the GPU host first if you want an end-to-end chat smoke test."
fi
section "OpenClaw next step"
if [[ -n "$selected_model" ]]; then
onboard_model="$selected_model"
else
onboard_model="<model-id>"
fi
say "Use the native Ollama base URL without /v1:"
say ""
say " openclaw onboard --non-interactive \\
--auth-choice ollama \\
--custom-base-url \"${base_url}\" \\
--custom-model-id \"${onboard_model}\" \\
--accept-risk"
say ""
if command -v openclaw >/dev/null 2>&1 && confirm "Run OpenClaw onboarding against this remote Ollama host now" "N"; then
if [[ "$onboard_model" == "<model-id>" ]]; then
onboard_model="$(prompt "Remote Ollama model id for OpenClaw" "")"
[[ -n "$onboard_model" ]] || die "A model id is required to run OpenClaw onboarding."
fi
openclaw onboard --non-interactive \
--auth-choice ollama \
--custom-base-url "$base_url" \
--custom-model-id "$onboard_model" \
--accept-risk
success "OpenClaw onboarding completed."
fi
section "Peer ready"
say "Peer private IP : ${detected_private_ip:-unknown}"
say "GPU private IP : $gpu_private_ip"
say "Ollama base URL : $base_url"
if [[ -n "$selected_model" ]]; then
say "Smoke-test model: $selected_model"
fi
warn "Use the native Ollama API URL only. Do not append /v1."
}
main "$@"