mcp-rubber-duck 1.1.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/.dockerignore +19 -0
- package/.env.desktop.example +145 -0
- package/.env.example +45 -0
- package/.env.pi.example +106 -0
- package/.env.template +165 -0
- package/.eslintrc.json +40 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +65 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +58 -0
- package/.github/ISSUE_TEMPLATE/question.md +67 -0
- package/.github/pull_request_template.md +111 -0
- package/.github/workflows/docker-build.yml +138 -0
- package/.github/workflows/release.yml +182 -0
- package/.github/workflows/security.yml +141 -0
- package/.github/workflows/semantic-release.yml +89 -0
- package/.prettierrc +10 -0
- package/.releaserc.json +66 -0
- package/CHANGELOG.md +95 -0
- package/CONTRIBUTING.md +242 -0
- package/Dockerfile +62 -0
- package/LICENSE +21 -0
- package/README.md +803 -0
- package/audit-ci.json +8 -0
- package/config/claude_desktop.json +14 -0
- package/config/config.example.json +91 -0
- package/dist/config/config.d.ts +51 -0
- package/dist/config/config.d.ts.map +1 -0
- package/dist/config/config.js +301 -0
- package/dist/config/config.js.map +1 -0
- package/dist/config/types.d.ts +356 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +41 -0
- package/dist/config/types.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +109 -0
- package/dist/index.js.map +1 -0
- package/dist/providers/duck-provider-enhanced.d.ts +29 -0
- package/dist/providers/duck-provider-enhanced.d.ts.map +1 -0
- package/dist/providers/duck-provider-enhanced.js +230 -0
- package/dist/providers/duck-provider-enhanced.js.map +1 -0
- package/dist/providers/enhanced-manager.d.ts +54 -0
- package/dist/providers/enhanced-manager.d.ts.map +1 -0
- package/dist/providers/enhanced-manager.js +217 -0
- package/dist/providers/enhanced-manager.js.map +1 -0
- package/dist/providers/manager.d.ts +28 -0
- package/dist/providers/manager.d.ts.map +1 -0
- package/dist/providers/manager.js +204 -0
- package/dist/providers/manager.js.map +1 -0
- package/dist/providers/provider.d.ts +29 -0
- package/dist/providers/provider.d.ts.map +1 -0
- package/dist/providers/provider.js +179 -0
- package/dist/providers/provider.js.map +1 -0
- package/dist/providers/types.d.ts +69 -0
- package/dist/providers/types.d.ts.map +1 -0
- package/dist/providers/types.js +2 -0
- package/dist/providers/types.js.map +1 -0
- package/dist/server.d.ts +24 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +501 -0
- package/dist/server.js.map +1 -0
- package/dist/services/approval.d.ts +44 -0
- package/dist/services/approval.d.ts.map +1 -0
- package/dist/services/approval.js +159 -0
- package/dist/services/approval.js.map +1 -0
- package/dist/services/cache.d.ts +21 -0
- package/dist/services/cache.d.ts.map +1 -0
- package/dist/services/cache.js +63 -0
- package/dist/services/cache.js.map +1 -0
- package/dist/services/conversation.d.ts +24 -0
- package/dist/services/conversation.d.ts.map +1 -0
- package/dist/services/conversation.js +108 -0
- package/dist/services/conversation.js.map +1 -0
- package/dist/services/function-bridge.d.ts +41 -0
- package/dist/services/function-bridge.d.ts.map +1 -0
- package/dist/services/function-bridge.js +259 -0
- package/dist/services/function-bridge.js.map +1 -0
- package/dist/services/health.d.ts +17 -0
- package/dist/services/health.d.ts.map +1 -0
- package/dist/services/health.js +77 -0
- package/dist/services/health.js.map +1 -0
- package/dist/services/mcp-client-manager.d.ts +49 -0
- package/dist/services/mcp-client-manager.d.ts.map +1 -0
- package/dist/services/mcp-client-manager.js +279 -0
- package/dist/services/mcp-client-manager.js.map +1 -0
- package/dist/tools/approve-mcp-request.d.ts +9 -0
- package/dist/tools/approve-mcp-request.d.ts.map +1 -0
- package/dist/tools/approve-mcp-request.js +111 -0
- package/dist/tools/approve-mcp-request.js.map +1 -0
- package/dist/tools/ask-duck.d.ts +9 -0
- package/dist/tools/ask-duck.d.ts.map +1 -0
- package/dist/tools/ask-duck.js +43 -0
- package/dist/tools/ask-duck.js.map +1 -0
- package/dist/tools/chat-duck.d.ts +9 -0
- package/dist/tools/chat-duck.d.ts.map +1 -0
- package/dist/tools/chat-duck.js +57 -0
- package/dist/tools/chat-duck.js.map +1 -0
- package/dist/tools/clear-conversations.d.ts +8 -0
- package/dist/tools/clear-conversations.d.ts.map +1 -0
- package/dist/tools/clear-conversations.js +17 -0
- package/dist/tools/clear-conversations.js.map +1 -0
- package/dist/tools/compare-ducks.d.ts +8 -0
- package/dist/tools/compare-ducks.d.ts.map +1 -0
- package/dist/tools/compare-ducks.js +49 -0
- package/dist/tools/compare-ducks.js.map +1 -0
- package/dist/tools/duck-council.d.ts +8 -0
- package/dist/tools/duck-council.d.ts.map +1 -0
- package/dist/tools/duck-council.js +69 -0
- package/dist/tools/duck-council.js.map +1 -0
- package/dist/tools/get-pending-approvals.d.ts +15 -0
- package/dist/tools/get-pending-approvals.d.ts.map +1 -0
- package/dist/tools/get-pending-approvals.js +74 -0
- package/dist/tools/get-pending-approvals.js.map +1 -0
- package/dist/tools/list-ducks.d.ts +9 -0
- package/dist/tools/list-ducks.d.ts.map +1 -0
- package/dist/tools/list-ducks.js +47 -0
- package/dist/tools/list-ducks.js.map +1 -0
- package/dist/tools/list-models.d.ts +8 -0
- package/dist/tools/list-models.d.ts.map +1 -0
- package/dist/tools/list-models.js +72 -0
- package/dist/tools/list-models.js.map +1 -0
- package/dist/tools/mcp-status.d.ts +17 -0
- package/dist/tools/mcp-status.d.ts.map +1 -0
- package/dist/tools/mcp-status.js +100 -0
- package/dist/tools/mcp-status.js.map +1 -0
- package/dist/utils/ascii-art.d.ts +19 -0
- package/dist/utils/ascii-art.d.ts.map +1 -0
- package/dist/utils/ascii-art.js +73 -0
- package/dist/utils/ascii-art.js.map +1 -0
- package/dist/utils/logger.d.ts +3 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +86 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/safe-logger.d.ts +23 -0
- package/dist/utils/safe-logger.d.ts.map +1 -0
- package/dist/utils/safe-logger.js +145 -0
- package/dist/utils/safe-logger.js.map +1 -0
- package/docker-compose.yml +161 -0
- package/jest.config.js +26 -0
- package/package.json +65 -0
- package/scripts/build-multiarch.sh +290 -0
- package/scripts/deploy-raspbian.sh +410 -0
- package/scripts/deploy.sh +322 -0
- package/scripts/gh-deploy.sh +343 -0
- package/scripts/setup-docker-raspbian.sh +530 -0
- package/server.json +8 -0
- package/src/config/config.ts +357 -0
- package/src/config/types.ts +89 -0
- package/src/index.ts +114 -0
- package/src/providers/duck-provider-enhanced.ts +294 -0
- package/src/providers/enhanced-manager.ts +290 -0
- package/src/providers/manager.ts +257 -0
- package/src/providers/provider.ts +207 -0
- package/src/providers/types.ts +78 -0
- package/src/server.ts +603 -0
- package/src/services/approval.ts +225 -0
- package/src/services/cache.ts +79 -0
- package/src/services/conversation.ts +146 -0
- package/src/services/function-bridge.ts +329 -0
- package/src/services/health.ts +107 -0
- package/src/services/mcp-client-manager.ts +362 -0
- package/src/tools/approve-mcp-request.ts +126 -0
- package/src/tools/ask-duck.ts +74 -0
- package/src/tools/chat-duck.ts +82 -0
- package/src/tools/clear-conversations.ts +24 -0
- package/src/tools/compare-ducks.ts +67 -0
- package/src/tools/duck-council.ts +88 -0
- package/src/tools/get-pending-approvals.ts +90 -0
- package/src/tools/list-ducks.ts +65 -0
- package/src/tools/list-models.ts +101 -0
- package/src/tools/mcp-status.ts +117 -0
- package/src/utils/ascii-art.ts +85 -0
- package/src/utils/logger.ts +116 -0
- package/src/utils/safe-logger.ts +165 -0
- package/systemd/mcp-rubber-duck-with-ollama.service +55 -0
- package/systemd/mcp-rubber-duck.service +58 -0
- package/test-functionality.js +147 -0
- package/test-mcp-interface.js +221 -0
- package/tests/ascii-art.test.ts +36 -0
- package/tests/config.test.ts +239 -0
- package/tests/conversation.test.ts +308 -0
- package/tests/mcp-bridge.test.ts +291 -0
- package/tests/providers.test.ts +269 -0
- package/tests/tools/clear-conversations.test.ts +163 -0
- package/tsconfig.json +26 -0
|
@@ -0,0 +1,530 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# MCP Rubber Duck - Raspberry Pi Docker Setup Script
|
|
4
|
+
# This script installs and configures Docker and Docker Compose on Raspberry Pi
|
|
5
|
+
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
# Colors for output
|
|
9
|
+
RED='\033[0;31m'
|
|
10
|
+
GREEN='\033[0;32m'
|
|
11
|
+
YELLOW='\033[1;33m'
|
|
12
|
+
BLUE='\033[0;34m'
|
|
13
|
+
NC='\033[0m' # No Color
|
|
14
|
+
|
|
15
|
+
# Configuration
|
|
16
|
+
DOCKER_COMPOSE_VERSION="${DOCKER_COMPOSE_VERSION:-2.24.0}"
|
|
17
|
+
SETUP_SWAP="${SETUP_SWAP:-true}"
|
|
18
|
+
SWAP_SIZE="${SWAP_SIZE:-1G}"
|
|
19
|
+
ENABLE_MEMORY_CGROUPS="${ENABLE_MEMORY_CGROUPS:-true}"
|
|
20
|
+
|
|
21
|
+
# Functions
|
|
22
|
+
log() {
|
|
23
|
+
echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')] $1${NC}"
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
warn() {
|
|
27
|
+
echo -e "${YELLOW}[$(date +'%Y-%m-%d %H:%M:%S')] WARNING: $1${NC}"
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
error() {
|
|
31
|
+
echo -e "${RED}[$(date +'%Y-%m-%d %H:%M:%S')] ERROR: $1${NC}"
|
|
32
|
+
exit 1
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
info() {
|
|
36
|
+
echo -e "${BLUE}[$(date +'%Y-%m-%d %H:%M:%S')] INFO: $1${NC}"
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
print_usage() {
|
|
40
|
+
cat << EOF
|
|
41
|
+
Usage: $0 [OPTIONS]
|
|
42
|
+
|
|
43
|
+
Setup Docker and Docker Compose on Raspberry Pi for MCP Rubber Duck
|
|
44
|
+
|
|
45
|
+
OPTIONS:
|
|
46
|
+
--no-swap Don't setup swap file
|
|
47
|
+
--swap-size SIZE Swap file size (default: 1G)
|
|
48
|
+
--no-memory-cgroups Don't enable memory cgroups
|
|
49
|
+
--docker-compose-version Docker Compose version (default: 2.24.0)
|
|
50
|
+
--dry-run Show what would be done without executing
|
|
51
|
+
-y, --yes Assume yes to all prompts
|
|
52
|
+
-h, --help Show this help message
|
|
53
|
+
|
|
54
|
+
EXAMPLES:
|
|
55
|
+
# Full setup with defaults
|
|
56
|
+
$0
|
|
57
|
+
|
|
58
|
+
# Setup without swap
|
|
59
|
+
$0 --no-swap
|
|
60
|
+
|
|
61
|
+
# Custom swap size
|
|
62
|
+
$0 --swap-size 2G
|
|
63
|
+
|
|
64
|
+
# Dry run to see what would be done
|
|
65
|
+
$0 --dry-run
|
|
66
|
+
|
|
67
|
+
REQUIREMENTS:
|
|
68
|
+
- Raspberry Pi running Raspberry Pi OS (Debian-based)
|
|
69
|
+
- Internet connection
|
|
70
|
+
- sudo privileges
|
|
71
|
+
|
|
72
|
+
EOF
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
# System detection
|
|
76
|
+
detect_system() {
|
|
77
|
+
log "Detecting system..."
|
|
78
|
+
|
|
79
|
+
# Check if we're on Raspberry Pi
|
|
80
|
+
if [[ -f /proc/device-tree/model ]] && grep -q "Raspberry Pi" /proc/device-tree/model; then
|
|
81
|
+
PI_MODEL=$(tr -d '\0' < /proc/device-tree/model)
|
|
82
|
+
log "Detected: $PI_MODEL"
|
|
83
|
+
else
|
|
84
|
+
warn "This doesn't appear to be a Raspberry Pi"
|
|
85
|
+
fi
|
|
86
|
+
|
|
87
|
+
# Check OS
|
|
88
|
+
if [[ ! -f /etc/debian_version ]]; then
|
|
89
|
+
error "This script requires a Debian-based OS (Raspberry Pi OS recommended)"
|
|
90
|
+
fi
|
|
91
|
+
|
|
92
|
+
# Check architecture
|
|
93
|
+
ARCH=$(uname -m)
|
|
94
|
+
case $ARCH in
|
|
95
|
+
armv7l|armv6l)
|
|
96
|
+
DOCKER_ARCH="armhf"
|
|
97
|
+
;;
|
|
98
|
+
aarch64|arm64)
|
|
99
|
+
DOCKER_ARCH="arm64"
|
|
100
|
+
;;
|
|
101
|
+
x86_64)
|
|
102
|
+
DOCKER_ARCH="amd64"
|
|
103
|
+
warn "Running on x86_64, this setup is optimized for ARM"
|
|
104
|
+
;;
|
|
105
|
+
*)
|
|
106
|
+
error "Unsupported architecture: $ARCH"
|
|
107
|
+
;;
|
|
108
|
+
esac
|
|
109
|
+
|
|
110
|
+
info "Architecture: $ARCH (Docker arch: $DOCKER_ARCH)"
|
|
111
|
+
|
|
112
|
+
# Check memory
|
|
113
|
+
TOTAL_MEM_KB=$(grep MemTotal /proc/meminfo | awk '{print $2}')
|
|
114
|
+
TOTAL_MEM_MB=$((TOTAL_MEM_KB / 1024))
|
|
115
|
+
info "Total memory: ${TOTAL_MEM_MB}MB"
|
|
116
|
+
|
|
117
|
+
# Recommendations based on memory
|
|
118
|
+
if [[ $TOTAL_MEM_MB -lt 1024 ]]; then
|
|
119
|
+
warn "Low memory detected. Consider enabling swap and limiting container memory."
|
|
120
|
+
fi
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
# Update system
|
|
124
|
+
update_system() {
|
|
125
|
+
log "Updating system packages..."
|
|
126
|
+
|
|
127
|
+
if [[ "${DRY_RUN:-false}" == "true" ]]; then
|
|
128
|
+
info "DRY RUN: Would update system packages"
|
|
129
|
+
return
|
|
130
|
+
fi
|
|
131
|
+
|
|
132
|
+
sudo apt-get update
|
|
133
|
+
sudo apt-get upgrade -y
|
|
134
|
+
|
|
135
|
+
# Install prerequisites
|
|
136
|
+
sudo apt-get install -y \
|
|
137
|
+
apt-transport-https \
|
|
138
|
+
ca-certificates \
|
|
139
|
+
curl \
|
|
140
|
+
gnupg \
|
|
141
|
+
lsb-release \
|
|
142
|
+
software-properties-common \
|
|
143
|
+
git \
|
|
144
|
+
htop \
|
|
145
|
+
nano
|
|
146
|
+
|
|
147
|
+
log "✅ System updated"
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
# Setup memory cgroups
|
|
151
|
+
setup_memory_cgroups() {
|
|
152
|
+
if [[ "${ENABLE_MEMORY_CGROUPS}" != "true" ]]; then
|
|
153
|
+
info "Skipping memory cgroups setup"
|
|
154
|
+
return
|
|
155
|
+
fi
|
|
156
|
+
|
|
157
|
+
log "Setting up memory cgroups..."
|
|
158
|
+
|
|
159
|
+
local cmdline_file="/boot/cmdline.txt"
|
|
160
|
+
local boot_config="/boot/config.txt"
|
|
161
|
+
|
|
162
|
+
# Check if running on newer Raspberry Pi OS with /boot/firmware
|
|
163
|
+
if [[ -f "/boot/firmware/cmdline.txt" ]]; then
|
|
164
|
+
cmdline_file="/boot/firmware/cmdline.txt"
|
|
165
|
+
boot_config="/boot/firmware/config.txt"
|
|
166
|
+
fi
|
|
167
|
+
|
|
168
|
+
if [[ ! -f "$cmdline_file" ]]; then
|
|
169
|
+
warn "Cannot find cmdline.txt, skipping cgroups setup"
|
|
170
|
+
return
|
|
171
|
+
fi
|
|
172
|
+
|
|
173
|
+
# Check if cgroups are already enabled
|
|
174
|
+
if grep -q "cgroup_enable=memory" "$cmdline_file"; then
|
|
175
|
+
info "Memory cgroups already enabled"
|
|
176
|
+
else
|
|
177
|
+
log "Enabling memory cgroups..."
|
|
178
|
+
|
|
179
|
+
if [[ "${DRY_RUN:-false}" == "true" ]]; then
|
|
180
|
+
info "DRY RUN: Would enable memory cgroups in $cmdline_file"
|
|
181
|
+
else
|
|
182
|
+
# Backup original file
|
|
183
|
+
sudo cp "$cmdline_file" "$cmdline_file.backup.$(date +%Y%m%d_%H%M%S)"
|
|
184
|
+
|
|
185
|
+
# Add cgroup parameters
|
|
186
|
+
sudo sed -i '$ s/$/ cgroup_enable=cpuset cgroup_enable=memory cgroup_memory=1/' "$cmdline_file"
|
|
187
|
+
|
|
188
|
+
info "Memory cgroups enabled. Reboot required for changes to take effect."
|
|
189
|
+
REBOOT_REQUIRED=true
|
|
190
|
+
fi
|
|
191
|
+
fi
|
|
192
|
+
|
|
193
|
+
log "✅ Memory cgroups configured"
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
# Setup swap
|
|
197
|
+
setup_swap() {
|
|
198
|
+
if [[ "${SETUP_SWAP}" != "true" ]]; then
|
|
199
|
+
info "Skipping swap setup"
|
|
200
|
+
return
|
|
201
|
+
fi
|
|
202
|
+
|
|
203
|
+
log "Setting up swap file ($SWAP_SIZE)..."
|
|
204
|
+
|
|
205
|
+
# Check if swap is already active
|
|
206
|
+
if swapon --show | grep -q "/swapfile"; then
|
|
207
|
+
info "Swap file already exists and is active"
|
|
208
|
+
return
|
|
209
|
+
fi
|
|
210
|
+
|
|
211
|
+
if [[ "${DRY_RUN:-false}" == "true" ]]; then
|
|
212
|
+
info "DRY RUN: Would create ${SWAP_SIZE} swap file"
|
|
213
|
+
return
|
|
214
|
+
fi
|
|
215
|
+
|
|
216
|
+
# Create swap file
|
|
217
|
+
if [[ ! -f /swapfile ]]; then
|
|
218
|
+
log "Creating swap file..."
|
|
219
|
+
sudo fallocate -l "$SWAP_SIZE" /swapfile
|
|
220
|
+
sudo chmod 600 /swapfile
|
|
221
|
+
sudo mkswap /swapfile
|
|
222
|
+
fi
|
|
223
|
+
|
|
224
|
+
# Enable swap
|
|
225
|
+
sudo swapon /swapfile
|
|
226
|
+
|
|
227
|
+
# Add to fstab for persistence
|
|
228
|
+
if ! grep -q "/swapfile" /etc/fstab; then
|
|
229
|
+
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
|
|
230
|
+
fi
|
|
231
|
+
|
|
232
|
+
# Configure swappiness (how aggressively to use swap)
|
|
233
|
+
echo 'vm.swappiness=10' | sudo tee -a /etc/sysctl.conf
|
|
234
|
+
|
|
235
|
+
log "✅ Swap configured: $(swapon --show)"
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
# Install Docker
|
|
239
|
+
install_docker() {
|
|
240
|
+
log "Installing Docker..."
|
|
241
|
+
|
|
242
|
+
if command -v docker &> /dev/null; then
|
|
243
|
+
DOCKER_VERSION=$(docker --version | cut -d' ' -f3 | sed 's/,//')
|
|
244
|
+
info "Docker already installed: $DOCKER_VERSION"
|
|
245
|
+
|
|
246
|
+
# Check if user is in docker group
|
|
247
|
+
if groups "$USER" | grep -q docker; then
|
|
248
|
+
info "User $USER is already in docker group"
|
|
249
|
+
else
|
|
250
|
+
log "Adding user $USER to docker group..."
|
|
251
|
+
if [[ "${DRY_RUN:-false}" != "true" ]]; then
|
|
252
|
+
sudo usermod -aG docker "$USER"
|
|
253
|
+
info "Please log out and log back in for group changes to take effect"
|
|
254
|
+
fi
|
|
255
|
+
fi
|
|
256
|
+
return
|
|
257
|
+
fi
|
|
258
|
+
|
|
259
|
+
if [[ "${DRY_RUN:-false}" == "true" ]]; then
|
|
260
|
+
info "DRY RUN: Would install Docker"
|
|
261
|
+
return
|
|
262
|
+
fi
|
|
263
|
+
|
|
264
|
+
# Install Docker using convenience script
|
|
265
|
+
log "Downloading Docker installation script..."
|
|
266
|
+
curl -fsSL https://get.docker.com -o get-docker.sh
|
|
267
|
+
|
|
268
|
+
log "Installing Docker..."
|
|
269
|
+
sudo sh get-docker.sh
|
|
270
|
+
|
|
271
|
+
# Add user to docker group
|
|
272
|
+
sudo usermod -aG docker "$USER"
|
|
273
|
+
|
|
274
|
+
# Enable and start Docker service
|
|
275
|
+
sudo systemctl enable docker
|
|
276
|
+
sudo systemctl start docker
|
|
277
|
+
|
|
278
|
+
# Cleanup
|
|
279
|
+
rm get-docker.sh
|
|
280
|
+
|
|
281
|
+
# Configure Docker for Raspberry Pi
|
|
282
|
+
configure_docker()
|
|
283
|
+
|
|
284
|
+
log "✅ Docker installed successfully"
|
|
285
|
+
info "Please log out and log back in for group changes to take effect"
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
# Configure Docker for Raspberry Pi
|
|
289
|
+
configure_docker() {
|
|
290
|
+
log "Configuring Docker for Raspberry Pi..."
|
|
291
|
+
|
|
292
|
+
# Create Docker daemon configuration
|
|
293
|
+
local docker_config="/etc/docker/daemon.json"
|
|
294
|
+
|
|
295
|
+
if [[ "${DRY_RUN:-false}" == "true" ]]; then
|
|
296
|
+
info "DRY RUN: Would configure Docker daemon"
|
|
297
|
+
return
|
|
298
|
+
fi
|
|
299
|
+
|
|
300
|
+
# Create Docker configuration
|
|
301
|
+
cat << 'EOF' | sudo tee "$docker_config"
|
|
302
|
+
{
|
|
303
|
+
"log-driver": "json-file",
|
|
304
|
+
"log-opts": {
|
|
305
|
+
"max-size": "10m",
|
|
306
|
+
"max-file": "3"
|
|
307
|
+
},
|
|
308
|
+
"storage-driver": "overlay2",
|
|
309
|
+
"default-ulimits": {
|
|
310
|
+
"nofile": {
|
|
311
|
+
"hard": 64000,
|
|
312
|
+
"soft": 32000
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
EOF
|
|
317
|
+
|
|
318
|
+
# Restart Docker to apply configuration
|
|
319
|
+
sudo systemctl restart docker
|
|
320
|
+
|
|
321
|
+
log "✅ Docker configured for Raspberry Pi"
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
# Install Docker Compose
|
|
325
|
+
install_docker_compose() {
|
|
326
|
+
log "Installing Docker Compose..."
|
|
327
|
+
|
|
328
|
+
if command -v docker-compose &> /dev/null; then
|
|
329
|
+
COMPOSE_VERSION=$(docker-compose --version | cut -d' ' -f4 | sed 's/,//')
|
|
330
|
+
info "Docker Compose already installed: $COMPOSE_VERSION"
|
|
331
|
+
return
|
|
332
|
+
fi
|
|
333
|
+
|
|
334
|
+
if [[ "${DRY_RUN:-false}" == "true" ]]; then
|
|
335
|
+
info "DRY RUN: Would install Docker Compose v$DOCKER_COMPOSE_VERSION"
|
|
336
|
+
return
|
|
337
|
+
fi
|
|
338
|
+
|
|
339
|
+
# Install Docker Compose
|
|
340
|
+
case $DOCKER_ARCH in
|
|
341
|
+
armhf|arm64)
|
|
342
|
+
# For ARM, install via pip
|
|
343
|
+
log "Installing Docker Compose via pip (ARM optimization)..."
|
|
344
|
+
sudo apt-get install -y python3-pip python3-dev libffi-dev
|
|
345
|
+
sudo pip3 install docker-compose=="$DOCKER_COMPOSE_VERSION"
|
|
346
|
+
;;
|
|
347
|
+
amd64)
|
|
348
|
+
# For x86_64, download binary
|
|
349
|
+
log "Downloading Docker Compose binary..."
|
|
350
|
+
sudo curl -L "https://github.com/docker/compose/releases/download/v${DOCKER_COMPOSE_VERSION}/docker-compose-$(uname -s)-$(uname -m)" \
|
|
351
|
+
-o /usr/local/bin/docker-compose
|
|
352
|
+
sudo chmod +x /usr/local/bin/docker-compose
|
|
353
|
+
;;
|
|
354
|
+
esac
|
|
355
|
+
|
|
356
|
+
# Verify installation
|
|
357
|
+
docker-compose --version
|
|
358
|
+
|
|
359
|
+
log "✅ Docker Compose installed successfully"
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
# Performance optimizations
|
|
363
|
+
optimize_system() {
|
|
364
|
+
log "Applying Raspberry Pi optimizations..."
|
|
365
|
+
|
|
366
|
+
if [[ "${DRY_RUN:-false}" == "true" ]]; then
|
|
367
|
+
info "DRY RUN: Would apply system optimizations"
|
|
368
|
+
return
|
|
369
|
+
fi
|
|
370
|
+
|
|
371
|
+
# GPU memory split (give more RAM to system)
|
|
372
|
+
echo 'gpu_mem=16' | sudo tee -a /boot/config.txt
|
|
373
|
+
|
|
374
|
+
# Disable unnecessary services to save memory
|
|
375
|
+
sudo systemctl disable bluetooth
|
|
376
|
+
sudo systemctl disable hciuart
|
|
377
|
+
|
|
378
|
+
# Configure log rotation
|
|
379
|
+
cat << 'EOF' | sudo tee /etc/logrotate.d/docker-containers
|
|
380
|
+
/var/lib/docker/containers/*/*.log {
|
|
381
|
+
rotate 3
|
|
382
|
+
daily
|
|
383
|
+
compress
|
|
384
|
+
size=10M
|
|
385
|
+
missingok
|
|
386
|
+
delaycompress
|
|
387
|
+
copytruncate
|
|
388
|
+
}
|
|
389
|
+
EOF
|
|
390
|
+
|
|
391
|
+
log "✅ System optimizations applied"
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
# Verification
|
|
395
|
+
verify_installation() {
|
|
396
|
+
log "Verifying installation..."
|
|
397
|
+
|
|
398
|
+
# Check Docker
|
|
399
|
+
if ! command -v docker &> /dev/null; then
|
|
400
|
+
error "Docker is not installed or not in PATH"
|
|
401
|
+
fi
|
|
402
|
+
|
|
403
|
+
# Check Docker Compose
|
|
404
|
+
if ! command -v docker-compose &> /dev/null; then
|
|
405
|
+
error "Docker Compose is not installed or not in PATH"
|
|
406
|
+
fi
|
|
407
|
+
|
|
408
|
+
# Test Docker (if not dry run)
|
|
409
|
+
if [[ "${DRY_RUN:-false}" != "true" ]]; then
|
|
410
|
+
log "Testing Docker installation..."
|
|
411
|
+
if ! docker run --rm hello-world &> /dev/null; then
|
|
412
|
+
warn "Docker test failed. You may need to log out and log back in."
|
|
413
|
+
else
|
|
414
|
+
log "Docker test successful"
|
|
415
|
+
fi
|
|
416
|
+
fi
|
|
417
|
+
|
|
418
|
+
log "✅ Installation verified"
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
# Summary and next steps
|
|
422
|
+
show_summary() {
|
|
423
|
+
log "🎉 Setup completed successfully!"
|
|
424
|
+
|
|
425
|
+
echo
|
|
426
|
+
info "Installation Summary:"
|
|
427
|
+
echo " • Docker: $(docker --version 2>/dev/null || echo 'Installed')"
|
|
428
|
+
echo " • Docker Compose: $(docker-compose --version 2>/dev/null || echo 'Installed')"
|
|
429
|
+
echo " • Memory cgroups: $(grep -q 'cgroup_enable=memory' /boot/cmdline.txt 2>/dev/null && echo 'Enabled' || echo 'Disabled')"
|
|
430
|
+
echo " • Swap: $(swapon --show | grep -q '/swapfile' && echo 'Enabled' || echo 'Disabled')"
|
|
431
|
+
|
|
432
|
+
echo
|
|
433
|
+
log "Next steps:"
|
|
434
|
+
echo " 1. Log out and log back in to apply group changes"
|
|
435
|
+
if [[ "${REBOOT_REQUIRED:-false}" == "true" ]]; then
|
|
436
|
+
echo " 2. Reboot to enable memory cgroups: sudo reboot"
|
|
437
|
+
echo " 3. After reboot, deploy MCP Rubber Duck:"
|
|
438
|
+
else
|
|
439
|
+
echo " 2. Deploy MCP Rubber Duck:"
|
|
440
|
+
fi
|
|
441
|
+
echo " ./scripts/deploy-raspbian.sh"
|
|
442
|
+
echo " 4. Edit .env file with your API keys"
|
|
443
|
+
echo " 5. Monitor with: docker stats"
|
|
444
|
+
|
|
445
|
+
echo
|
|
446
|
+
log "Useful commands:"
|
|
447
|
+
echo " • Check Docker status: sudo systemctl status docker"
|
|
448
|
+
echo " • View Docker logs: sudo journalctl -u docker.service"
|
|
449
|
+
echo " • Monitor resources: htop"
|
|
450
|
+
echo " • Check memory: free -h"
|
|
451
|
+
echo " • Check swap: swapon --show"
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
# Parse command line arguments
|
|
455
|
+
while [[ $# -gt 0 ]]; do
|
|
456
|
+
case $1 in
|
|
457
|
+
--no-swap)
|
|
458
|
+
SETUP_SWAP="false"
|
|
459
|
+
shift
|
|
460
|
+
;;
|
|
461
|
+
--swap-size)
|
|
462
|
+
SWAP_SIZE="$2"
|
|
463
|
+
shift 2
|
|
464
|
+
;;
|
|
465
|
+
--no-memory-cgroups)
|
|
466
|
+
ENABLE_MEMORY_CGROUPS="false"
|
|
467
|
+
shift
|
|
468
|
+
;;
|
|
469
|
+
--docker-compose-version)
|
|
470
|
+
DOCKER_COMPOSE_VERSION="$2"
|
|
471
|
+
shift 2
|
|
472
|
+
;;
|
|
473
|
+
--dry-run)
|
|
474
|
+
DRY_RUN="true"
|
|
475
|
+
shift
|
|
476
|
+
;;
|
|
477
|
+
-y|--yes)
|
|
478
|
+
ASSUME_YES="true"
|
|
479
|
+
shift
|
|
480
|
+
;;
|
|
481
|
+
-h|--help)
|
|
482
|
+
print_usage
|
|
483
|
+
exit 0
|
|
484
|
+
;;
|
|
485
|
+
*)
|
|
486
|
+
error "Unknown option: $1"
|
|
487
|
+
;;
|
|
488
|
+
esac
|
|
489
|
+
done
|
|
490
|
+
|
|
491
|
+
# Check if running as root
|
|
492
|
+
if [[ $EUID -eq 0 ]]; then
|
|
493
|
+
error "This script should not be run as root. Run as your regular user."
|
|
494
|
+
fi
|
|
495
|
+
|
|
496
|
+
# Check for sudo access
|
|
497
|
+
if ! sudo -n true 2>/dev/null; then
|
|
498
|
+
error "This script requires sudo access. Please ensure you can run sudo commands."
|
|
499
|
+
fi
|
|
500
|
+
|
|
501
|
+
# Confirmation prompt
|
|
502
|
+
if [[ "${ASSUME_YES:-false}" != "true" && "${DRY_RUN:-false}" != "true" ]]; then
|
|
503
|
+
echo "This script will:"
|
|
504
|
+
echo " • Update system packages"
|
|
505
|
+
echo " • Install Docker and Docker Compose"
|
|
506
|
+
echo " • Configure memory cgroups (requires reboot)"
|
|
507
|
+
echo " • Setup swap file ($SWAP_SIZE)"
|
|
508
|
+
echo " • Apply Raspberry Pi optimizations"
|
|
509
|
+
echo
|
|
510
|
+
read -p "Continue? (y/N): " -r
|
|
511
|
+
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
|
512
|
+
log "Setup cancelled by user"
|
|
513
|
+
exit 0
|
|
514
|
+
fi
|
|
515
|
+
fi
|
|
516
|
+
|
|
517
|
+
# Main execution
|
|
518
|
+
log "Starting Raspberry Pi Docker setup for MCP Rubber Duck"
|
|
519
|
+
|
|
520
|
+
detect_system
|
|
521
|
+
update_system
|
|
522
|
+
setup_memory_cgroups
|
|
523
|
+
setup_swap
|
|
524
|
+
install_docker
|
|
525
|
+
install_docker_compose
|
|
526
|
+
optimize_system
|
|
527
|
+
verify_installation
|
|
528
|
+
show_summary
|
|
529
|
+
|
|
530
|
+
log "🚀 Setup completed! Happy ducking! 🦆"
|