-
Notifications
You must be signed in to change notification settings - Fork 127
Expand file tree
/
Copy pathinstall-dev.sh
More file actions
executable file
·262 lines (225 loc) · 10.8 KB
/
Copy pathinstall-dev.sh
File metadata and controls
executable file
·262 lines (225 loc) · 10.8 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
#!/usr/bin/env bash
set -euo pipefail
COMPOSE_FILE="docker-compose-dev.yml"
OPENCODE_ENV_FILE=".opencode.env"
BIND_PATHS=(
"./data"
"./darkmoon-settings"
"./workflows"
)
# Colors
CYAN="\033[1;36m"
BLUE="\033[1;34m"
GREEN="\033[1;32m"
YELLOW="\033[1;33m"
RED="\033[1;31m"
MAGENTA="\033[1;35m"
BOLD="\033[1m"
RESET="\033[0m"
# ─────────────────────────────────────────────────────────────────
# Parse args
# ─────────────────────────────────────────────────────────────────
FORCE_RESET=false
for ARG in "$@"; do
case "${ARG}" in
--help|-h)
echo "Usage: ./install-dev.sh [OPTIONS]"
echo ""
echo "Options:"
echo " --init Force LLM provider reconfiguration (ignores existing .opencode.env)"
echo " --help Show this help"
echo ""
echo "Examples:"
echo " ./install-dev.sh # rebuild, skip provider form if already configured"
echo " ./install-dev.sh --init # rebuild and reconfigure LLM provider"
exit 0 ;;
--reset|--init) FORCE_RESET=true ;;
esac
done
SCRIPT_DIR="$(pwd)"
echo -e "${CYAN}"
cat << "EOF"
____ _
| _ \ __ _ _ __| | ___ __ ___ ___ ___ _ __
| | | |/ _` | '__| |/ / '_ ` _ \ / _ \ / _ \| '_ \
| |_| | (_| | | | <| | | | | | (_) | (_) | | | |
|____/ \__,_|_| |_|\_\_| |_| |_|\___/ \___/|_| |_|
EOF
echo -e "${RESET}"
echo -e "${BLUE}🔎 Checking prerequisites...${RESET}"
# Check Docker binary
if ! command -v docker >/dev/null 2>&1; then
echo -e "${RED}❌ Docker is not installed.${RESET}"
echo -e "${YELLOW}Install Docker before running this script:${RESET}"
echo "https://docs.docker.com/engine/install/"
exit 1
fi
# Check Docker daemon
if ! docker info >/dev/null 2>&1; then
echo -e "${RED}❌ Docker daemon is not running.${RESET}"
echo -e "${YELLOW}Start Docker and retry.${RESET}"
exit 1
fi
# Check Docker Compose v2
if ! docker compose version >/dev/null 2>&1; then
echo -e "${RED}❌ Docker Compose v2 is not available.${RESET}"
echo -e "${YELLOW}Install Docker Compose plugin:${RESET}"
echo "https://docs.docker.com/compose/install/"
exit 1
fi
echo -e "${GREEN}✔ Docker and Docker Compose detected${RESET}"
# ─────────────────────────────────────────────────────────────────
# Save .opencode.env BEFORE purge (unless --init)
# ─────────────────────────────────────────────────────────────────
SAVED_OPENCODE_ENV=""
if [ "${FORCE_RESET}" = false ] && [ -f "${SCRIPT_DIR}/${OPENCODE_ENV_FILE}" ]; then
SAVED_OPENCODE_ENV="$(cat "${SCRIPT_DIR}/${OPENCODE_ENV_FILE}")"
fi
# ─────────────────────────────────────────────────────────────────
# Detect if provider already configured
# ─────────────────────────────────────────────────────────────────
SKIP_PROVIDER_FORM=false
if [ -n "${SAVED_OPENCODE_ENV}" ]; then
set +u
eval "$(echo "${SAVED_OPENCODE_ENV}" | sed 's/:[[:space:]]\+/=/g' | sed 's/\r//' | grep -E '^[A-Z_]+=.+')" 2>/dev/null || true
set -u
if [ -n "${OPENROUTER_PROVIDER:-}" ] && \
[ -n "${OPENROUTER_API_KEY:-}" ] && \
[ -n "${OPENCODE_MODEL:-}" ]; then
SKIP_PROVIDER_FORM=true
elif [ "${OPENCODE_LOCAL_MODE:-}" = "true" ] && \
[ -n "${OPENCODE_LOCAL_PROVIDER_ID:-}" ] && \
[ -n "${OPENCODE_LOCAL_BASE_URL:-}" ] && \
[ -n "${OPENCODE_LOCAL_MODEL:-}" ]; then
SKIP_PROVIDER_FORM=true
fi
fi
# ─────────────────────────────────────────────────────────────────
# LLM Provider configuration
# ─────────────────────────────────────────────────────────────────
if [ "${SKIP_PROVIDER_FORM}" = true ]; then
echo -e "${GREEN}✔ LLM provider already configured — skipping${RESET}"
printf '%s\n' "${SAVED_OPENCODE_ENV}" > "${SCRIPT_DIR}/${OPENCODE_ENV_FILE}"
else
echo ""
echo -e "${BOLD}${MAGENTA}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo -e " 🤖 LLM PROVIDER CONFIGURATION"
echo -e "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}"
echo ""
echo -e " ${CYAN}[1]${RESET} ${BOLD}Cloud provider${RESET} (Anthropic, OpenRouter, OpenAI, etc.)"
echo -e " ${CYAN}[2]${RESET} ${BOLD}Local model${RESET} (Ollama, llama.cpp / llama-server)"
echo ""
while true; do
read -r -p "$(echo -e "${YELLOW}Your choice [1/2]: ${RESET}")" PROVIDER_TYPE
case "${PROVIDER_TYPE}" in
1|cloud|Cloud) PROVIDER_TYPE="cloud"; break ;;
2|local|Local) PROVIDER_TYPE="local"; break ;;
*) echo -e "${RED} Please enter 1 (cloud) or 2 (local).${RESET}" ;;
esac
done
# ── Cloud ────────────────────────────────────────────────────────
if [ "${PROVIDER_TYPE}" = "cloud" ]; then
echo ""
echo -e "${CYAN}Available cloud providers (examples):${RESET}"
echo -e " ${YELLOW}anthropic${RESET} → claude-opus-4-6, claude-sonnet-4-5"
echo -e " ${YELLOW}openai${RESET} → gpt-4o, o3"
echo -e " ${YELLOW}openrouter${RESET} → any model via openrouter.ai"
echo ""
read -r -p "$(echo -e "${YELLOW}Provider name (e.g. anthropic): ${RESET}")" CLOUD_PROVIDER
while [ -z "${CLOUD_PROVIDER}" ]; do
echo -e "${RED} Provider name cannot be empty.${RESET}"
read -r -p "$(echo -e "${YELLOW}Provider name: ${RESET}")" CLOUD_PROVIDER
done
read -r -p "$(echo -e "${YELLOW}Model name (e.g. claude-opus-4-6): ${RESET}")" CLOUD_MODEL
while [ -z "${CLOUD_MODEL}" ]; do
echo -e "${RED} Model name cannot be empty.${RESET}"
read -r -p "$(echo -e "${YELLOW}Model name: ${RESET}")" CLOUD_MODEL
done
read -r -s -p "$(echo -e "${YELLOW}API key: ${RESET}")" CLOUD_API_KEY
echo ""
while [ -z "${CLOUD_API_KEY}" ]; do
echo -e "${RED} API key cannot be empty.${RESET}"
read -r -s -p "$(echo -e "${YELLOW}API key: ${RESET}")" CLOUD_API_KEY
echo ""
done
cat > "${SCRIPT_DIR}/${OPENCODE_ENV_FILE}" <<EOF
# Darkmoon — LLM cloud provider config
# Generated by install-dev.sh on $(date '+%Y-%m-%d %H:%M:%S')
OPENROUTER_PROVIDER=${CLOUD_PROVIDER}
OPENCODE_MODEL=${CLOUD_MODEL}
OPENROUTER_API_KEY=${CLOUD_API_KEY}
EOF
# ── Local ─────────────────────────────────────────────────────────
else
echo ""
echo -e "${CYAN}Local provider options:${RESET}"
echo -e " ${YELLOW}[1]${RESET} Ollama (default: http://localhost:11434/v1)"
echo -e " ${YELLOW}[2]${RESET} llama.cpp (llama-server, default: http://localhost:8080/v1)"
echo -e " ${YELLOW}[3]${RESET} Custom URL (any OpenAI-compatible endpoint)"
echo ""
while true; do
read -r -p "$(echo -e "${YELLOW}Local engine [1/2/3]: ${RESET}")" LOCAL_ENGINE
case "${LOCAL_ENGINE}" in
1|ollama|Ollama)
LOCAL_PROVIDER_ID="ollama"
LOCAL_PROVIDER_NAME="Ollama (local)"
DEFAULT_BASE_URL="http://localhost:11434/v1"
break ;;
2|llama*|llamacpp)
LOCAL_PROVIDER_ID="llama.cpp"
LOCAL_PROVIDER_NAME="llama-server (local)"
DEFAULT_BASE_URL="http://localhost:8080/v1"
break ;;
3|custom|Custom)
LOCAL_PROVIDER_ID="local"
LOCAL_PROVIDER_NAME="Local model"
DEFAULT_BASE_URL=""
break ;;
*) echo -e "${RED} Please enter 1, 2 or 3.${RESET}" ;;
esac
done
read -r -p "$(echo -e "${YELLOW}Base URL [${DEFAULT_BASE_URL}]: ${RESET}")" LOCAL_BASE_URL
LOCAL_BASE_URL="${LOCAL_BASE_URL:-${DEFAULT_BASE_URL}}"
while [ -z "${LOCAL_BASE_URL}" ]; do
echo -e "${RED} Base URL cannot be empty.${RESET}"
read -r -p "$(echo -e "${YELLOW}Base URL: ${RESET}")" LOCAL_BASE_URL
done
read -r -p "$(echo -e "${YELLOW}Model name (e.g. qwen2.5-coder:7b): ${RESET}")" LOCAL_MODEL
while [ -z "${LOCAL_MODEL}" ]; do
echo -e "${RED} Model name cannot be empty.${RESET}"
read -r -p "$(echo -e "${YELLOW}Model name: ${RESET}")" LOCAL_MODEL
done
cat > "${SCRIPT_DIR}/${OPENCODE_ENV_FILE}" <<EOF
# Darkmoon — LLM local provider config
# Generated by install-dev.sh on $(date '+%Y-%m-%d %H:%M:%S')
OPENCODE_LOCAL_MODE=true
OPENCODE_LOCAL_PROVIDER_ID=${LOCAL_PROVIDER_ID}
OPENCODE_LOCAL_PROVIDER_NAME=${LOCAL_PROVIDER_NAME}
OPENCODE_LOCAL_BASE_URL=${LOCAL_BASE_URL}
OPENCODE_LOCAL_MODEL=${LOCAL_MODEL}
EOF
fi # end PROVIDER_TYPE
fi # end SKIP_PROVIDER_FORM
chmod 644 "${SCRIPT_DIR}/${OPENCODE_ENV_FILE}"
echo -e "${GREEN}✔ ${OPENCODE_ENV_FILE} written${RESET}"
# ─────────────────────────────────────────────────────────────────
# Stop stack + purge bind mounts
# ─────────────────────────────────────────────────────────────────
echo -e "${BLUE}🛑 Stopping stack (containers + networks + volumes)...${RESET}"
docker compose -f $COMPOSE_FILE down --remove-orphans --volumes
echo -e "${BLUE}🧹 Purging bind mounts...${RESET}"
for path in "${BIND_PATHS[@]}"; do
if [ -d "$path" ]; then
echo -e "${YELLOW} - removing ${path}${RESET}"
rm -rf "$path"
else
echo -e "${YELLOW} - ${path} (absent)${RESET}"
fi
done
echo -e "${BLUE}🧽 Purging docker build cache...${RESET}"
docker builder prune -f
echo -e "${BLUE}📥 Pulling latest images from Docker Hub...${RESET}"
docker compose -f $COMPOSE_FILE pull
echo -e "${BLUE}🚀 Recreating containers...${RESET}"
docker compose -f $COMPOSE_FILE up -d --build --force-recreate
echo -e "${GREEN}✅ Darkmoon stack recreated CLEAN${RESET}"