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,290 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# MCP Rubber Duck - Multi-Architecture Build Script
|
|
4
|
+
# This script builds Docker images for multiple architectures including 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
|
+
IMAGE_NAME="${IMAGE_NAME:-mcp-rubber-duck}"
|
|
17
|
+
IMAGE_TAG="${IMAGE_TAG:-latest}"
|
|
18
|
+
DOCKER_REGISTRY="${DOCKER_REGISTRY:-}"
|
|
19
|
+
PUSH_IMAGE="${PUSH_IMAGE:-false}"
|
|
20
|
+
USE_GITHUB="${USE_GITHUB:-false}"
|
|
21
|
+
|
|
22
|
+
# Supported platforms for Raspberry Pi (Pi 3+ supports ARM64)
|
|
23
|
+
PLATFORMS="linux/amd64,linux/arm64"
|
|
24
|
+
|
|
25
|
+
# Functions
|
|
26
|
+
log() {
|
|
27
|
+
echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')] $1${NC}"
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
warn() {
|
|
31
|
+
echo -e "${YELLOW}[$(date +'%Y-%m-%d %H:%M:%S')] WARNING: $1${NC}"
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
error() {
|
|
35
|
+
echo -e "${RED}[$(date +'%Y-%m-%d %H:%M:%S')] ERROR: $1${NC}"
|
|
36
|
+
exit 1
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
info() {
|
|
40
|
+
echo -e "${BLUE}[$(date +'%Y-%m-%d %H:%M:%S')] INFO: $1${NC}"
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
print_usage() {
|
|
44
|
+
cat << EOF
|
|
45
|
+
Usage: $0 [OPTIONS]
|
|
46
|
+
|
|
47
|
+
Build multi-architecture Docker images for MCP Rubber Duck
|
|
48
|
+
|
|
49
|
+
OPTIONS:
|
|
50
|
+
-n, --name NAME Docker image name (default: mcp-rubber-duck)
|
|
51
|
+
-t, --tag TAG Docker image tag (default: latest)
|
|
52
|
+
-r, --registry REGISTRY Docker registry (e.g., your-username, ghcr.io/username)
|
|
53
|
+
-p, --push Push image to registry after build
|
|
54
|
+
--gh, --github Use GitHub Container Registry (ghcr.io) with gh CLI
|
|
55
|
+
--local Build for local architecture only (faster)
|
|
56
|
+
--arm-only Build for ARM architectures only (Pi optimized)
|
|
57
|
+
-h, --help Show this help message
|
|
58
|
+
|
|
59
|
+
EXAMPLES:
|
|
60
|
+
# Build for all architectures locally
|
|
61
|
+
$0 --name mcp-rubber-duck --tag v1.0.0
|
|
62
|
+
|
|
63
|
+
# Build and push to Docker Hub
|
|
64
|
+
$0 --name yourusername/mcp-rubber-duck --push
|
|
65
|
+
|
|
66
|
+
# Build and push to GitHub Container Registry
|
|
67
|
+
$0 --registry ghcr.io/yourusername --push
|
|
68
|
+
|
|
69
|
+
# Build and push to GitHub (auto-detects username)
|
|
70
|
+
$0 --github --push
|
|
71
|
+
|
|
72
|
+
# Build only for Raspberry Pi (ARM)
|
|
73
|
+
$0 --arm-only --name mcp-rubber-duck-arm
|
|
74
|
+
|
|
75
|
+
# Build for local development (current architecture only)
|
|
76
|
+
$0 --local
|
|
77
|
+
|
|
78
|
+
ENVIRONMENT VARIABLES:
|
|
79
|
+
IMAGE_NAME Override default image name
|
|
80
|
+
IMAGE_TAG Override default tag
|
|
81
|
+
DOCKER_REGISTRY Override default registry
|
|
82
|
+
PUSH_IMAGE Set to 'true' to push after build
|
|
83
|
+
USE_GITHUB Set to 'true' to use GitHub Container Registry
|
|
84
|
+
|
|
85
|
+
EOF
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
# Parse command line arguments
|
|
89
|
+
while [[ $# -gt 0 ]]; do
|
|
90
|
+
case $1 in
|
|
91
|
+
-n|--name)
|
|
92
|
+
IMAGE_NAME="$2"
|
|
93
|
+
shift 2
|
|
94
|
+
;;
|
|
95
|
+
-t|--tag)
|
|
96
|
+
IMAGE_TAG="$2"
|
|
97
|
+
shift 2
|
|
98
|
+
;;
|
|
99
|
+
-r|--registry)
|
|
100
|
+
DOCKER_REGISTRY="$2"
|
|
101
|
+
shift 2
|
|
102
|
+
;;
|
|
103
|
+
-p|--push)
|
|
104
|
+
PUSH_IMAGE="true"
|
|
105
|
+
shift
|
|
106
|
+
;;
|
|
107
|
+
--gh|--github)
|
|
108
|
+
USE_GITHUB="true"
|
|
109
|
+
shift
|
|
110
|
+
;;
|
|
111
|
+
--local)
|
|
112
|
+
PLATFORMS=""
|
|
113
|
+
shift
|
|
114
|
+
;;
|
|
115
|
+
--arm-only)
|
|
116
|
+
PLATFORMS="linux/arm64,linux/arm/v7"
|
|
117
|
+
shift
|
|
118
|
+
;;
|
|
119
|
+
-h|--help)
|
|
120
|
+
print_usage
|
|
121
|
+
exit 0
|
|
122
|
+
;;
|
|
123
|
+
*)
|
|
124
|
+
error "Unknown option: $1"
|
|
125
|
+
;;
|
|
126
|
+
esac
|
|
127
|
+
done
|
|
128
|
+
|
|
129
|
+
# GitHub CLI integration
|
|
130
|
+
if [[ "$USE_GITHUB" == "true" ]]; then
|
|
131
|
+
# Check if gh is installed
|
|
132
|
+
if ! command -v gh &> /dev/null; then
|
|
133
|
+
error "GitHub CLI (gh) is not installed. Install from: https://cli.github.com/"
|
|
134
|
+
fi
|
|
135
|
+
|
|
136
|
+
# Check if user is authenticated
|
|
137
|
+
if ! gh auth status &> /dev/null; then
|
|
138
|
+
error "Not authenticated with GitHub. Run: gh auth login"
|
|
139
|
+
fi
|
|
140
|
+
|
|
141
|
+
# Get GitHub username
|
|
142
|
+
GITHUB_USER=$(gh api user --jq .login 2>/dev/null)
|
|
143
|
+
if [[ -z "$GITHUB_USER" ]]; then
|
|
144
|
+
error "Could not get GitHub username. Check your gh authentication."
|
|
145
|
+
fi
|
|
146
|
+
|
|
147
|
+
# Get repository name (fallback to mcp-rubber-duck)
|
|
148
|
+
REPO_NAME=$(gh repo view --json name --jq .name 2>/dev/null || echo "mcp-rubber-duck")
|
|
149
|
+
|
|
150
|
+
# Set up GitHub Container Registry
|
|
151
|
+
DOCKER_REGISTRY="ghcr.io/$GITHUB_USER"
|
|
152
|
+
IMAGE_NAME="$REPO_NAME"
|
|
153
|
+
PUSH_IMAGE="true" # Auto-enable push for GitHub
|
|
154
|
+
|
|
155
|
+
log "Using GitHub Container Registry: ghcr.io/$GITHUB_USER/$REPO_NAME"
|
|
156
|
+
|
|
157
|
+
# Authenticate Docker with ghcr.io using gh
|
|
158
|
+
info "Authenticating Docker with GitHub Container Registry..."
|
|
159
|
+
if ! gh auth token | docker login ghcr.io -u "$GITHUB_USER" --password-stdin; then
|
|
160
|
+
error "Failed to authenticate Docker with ghcr.io"
|
|
161
|
+
fi
|
|
162
|
+
fi
|
|
163
|
+
|
|
164
|
+
# Build full image name
|
|
165
|
+
if [[ -n "$DOCKER_REGISTRY" ]]; then
|
|
166
|
+
FULL_IMAGE_NAME="${DOCKER_REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}"
|
|
167
|
+
else
|
|
168
|
+
FULL_IMAGE_NAME="${IMAGE_NAME}:${IMAGE_TAG}"
|
|
169
|
+
fi
|
|
170
|
+
|
|
171
|
+
# Validate Docker is installed
|
|
172
|
+
if ! command -v docker &> /dev/null; then
|
|
173
|
+
error "Docker is not installed or not in PATH"
|
|
174
|
+
fi
|
|
175
|
+
|
|
176
|
+
# Check if buildx is available
|
|
177
|
+
if ! docker buildx version &> /dev/null; then
|
|
178
|
+
error "Docker buildx is not available. Please update Docker to a newer version."
|
|
179
|
+
fi
|
|
180
|
+
|
|
181
|
+
# Change to project root directory
|
|
182
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
183
|
+
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
|
|
184
|
+
cd "$PROJECT_ROOT"
|
|
185
|
+
|
|
186
|
+
log "Starting multi-architecture build for MCP Rubber Duck"
|
|
187
|
+
info "Image name: $FULL_IMAGE_NAME"
|
|
188
|
+
info "Platforms: ${PLATFORMS:-$(uname -m)}"
|
|
189
|
+
info "Push to registry: $PUSH_IMAGE"
|
|
190
|
+
|
|
191
|
+
# Check if Dockerfile exists
|
|
192
|
+
if [[ ! -f "Dockerfile" ]]; then
|
|
193
|
+
error "Dockerfile not found in project root"
|
|
194
|
+
fi
|
|
195
|
+
|
|
196
|
+
# Create buildx builder if it doesn't exist
|
|
197
|
+
BUILDER_NAME="mcp-multiarch"
|
|
198
|
+
if ! docker buildx inspect "$BUILDER_NAME" &> /dev/null; then
|
|
199
|
+
log "Creating buildx builder: $BUILDER_NAME"
|
|
200
|
+
docker buildx create --name "$BUILDER_NAME" --platform "$PLATFORMS" --use
|
|
201
|
+
else
|
|
202
|
+
log "Using existing buildx builder: $BUILDER_NAME"
|
|
203
|
+
docker buildx use "$BUILDER_NAME"
|
|
204
|
+
fi
|
|
205
|
+
|
|
206
|
+
# Ensure builder is running
|
|
207
|
+
log "Starting buildx builder"
|
|
208
|
+
docker buildx inspect --bootstrap
|
|
209
|
+
|
|
210
|
+
# Build command
|
|
211
|
+
BUILD_ARGS=(
|
|
212
|
+
"buildx" "build"
|
|
213
|
+
"--builder" "$BUILDER_NAME"
|
|
214
|
+
"-t" "$FULL_IMAGE_NAME"
|
|
215
|
+
"--progress" "plain"
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
# Add platform specification if not local build
|
|
219
|
+
if [[ -n "$PLATFORMS" ]]; then
|
|
220
|
+
BUILD_ARGS+=("--platform" "$PLATFORMS")
|
|
221
|
+
fi
|
|
222
|
+
|
|
223
|
+
# Add push flag if requested
|
|
224
|
+
if [[ "$PUSH_IMAGE" == "true" ]]; then
|
|
225
|
+
BUILD_ARGS+=("--push")
|
|
226
|
+
info "Image will be pushed to registry after build"
|
|
227
|
+
else
|
|
228
|
+
BUILD_ARGS+=("--load")
|
|
229
|
+
info "Image will be loaded to local Docker daemon"
|
|
230
|
+
fi
|
|
231
|
+
|
|
232
|
+
# Add context
|
|
233
|
+
BUILD_ARGS+=(".")
|
|
234
|
+
|
|
235
|
+
# Print build command for debugging
|
|
236
|
+
info "Build command: docker ${BUILD_ARGS[*]}"
|
|
237
|
+
|
|
238
|
+
# Run the build
|
|
239
|
+
log "Building Docker image..."
|
|
240
|
+
if docker "${BUILD_ARGS[@]}"; then
|
|
241
|
+
log "â
Build completed successfully!"
|
|
242
|
+
|
|
243
|
+
if [[ "$PUSH_IMAGE" == "true" ]]; then
|
|
244
|
+
log "â
Image pushed to registry: $FULL_IMAGE_NAME"
|
|
245
|
+
|
|
246
|
+
# Show GitHub package info if using GitHub
|
|
247
|
+
if [[ "$USE_GITHUB" == "true" ]]; then
|
|
248
|
+
log "đĻ GitHub Package Information:"
|
|
249
|
+
if gh api "user/packages/container/$IMAGE_NAME" &> /dev/null; then
|
|
250
|
+
echo " Package URL: https://github.com/$GITHUB_USER/packages/container/package/$IMAGE_NAME"
|
|
251
|
+
echo " Visibility: $(gh api "user/packages/container/$IMAGE_NAME" --jq .visibility)"
|
|
252
|
+
echo " Downloads: $(gh api "user/packages/container/$IMAGE_NAME/versions" --jq 'map(.metadata.container.tags | length) | add')"
|
|
253
|
+
|
|
254
|
+
# Show how to make package public if it's private
|
|
255
|
+
VISIBILITY=$(gh api "user/packages/container/$IMAGE_NAME" --jq .visibility 2>/dev/null)
|
|
256
|
+
if [[ "$VISIBILITY" == "private" ]]; then
|
|
257
|
+
info "To make package public: gh api --method PATCH user/packages/container/$IMAGE_NAME --field visibility=public"
|
|
258
|
+
fi
|
|
259
|
+
else
|
|
260
|
+
warn "Package info not yet available (may take a moment to appear)"
|
|
261
|
+
fi
|
|
262
|
+
fi
|
|
263
|
+
else
|
|
264
|
+
log "â
Image loaded locally: $FULL_IMAGE_NAME"
|
|
265
|
+
|
|
266
|
+
# Show image info
|
|
267
|
+
if [[ -z "$PLATFORMS" ]]; then
|
|
268
|
+
info "Image details:"
|
|
269
|
+
docker images "$FULL_IMAGE_NAME" | head -2
|
|
270
|
+
fi
|
|
271
|
+
fi
|
|
272
|
+
|
|
273
|
+
# Show next steps
|
|
274
|
+
echo
|
|
275
|
+
log "đ Next steps for Raspberry Pi deployment:"
|
|
276
|
+
echo " 1. Copy this image to your Raspberry Pi:"
|
|
277
|
+
if [[ "$PUSH_IMAGE" == "true" ]]; then
|
|
278
|
+
echo " docker pull $FULL_IMAGE_NAME"
|
|
279
|
+
else
|
|
280
|
+
echo " docker save $FULL_IMAGE_NAME | ssh pi@your-pi-ip 'docker load'"
|
|
281
|
+
fi
|
|
282
|
+
echo " 2. Use docker-compose.yml to deploy (works on all platforms)"
|
|
283
|
+
echo " 3. Configure your .env file with API keys"
|
|
284
|
+
|
|
285
|
+
else
|
|
286
|
+
error "â Build failed!"
|
|
287
|
+
fi
|
|
288
|
+
|
|
289
|
+
# Cleanup
|
|
290
|
+
log "Build process completed"
|
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
|
|
3
|
+
# MCP Rubber Duck - Raspberry Pi Deployment Script
|
|
4
|
+
# This script deploys MCP Rubber Duck on Raspberry Pi using Docker Compose
|
|
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
|
+
PROJECT_NAME="${PROJECT_NAME:-mcp-rubber-duck}"
|
|
17
|
+
COMPOSE_FILE="${COMPOSE_FILE:-docker-compose.raspbian.yml}"
|
|
18
|
+
ENV_FILE="${ENV_FILE:-.env}"
|
|
19
|
+
DATA_DIR="${DATA_DIR:-./data}"
|
|
20
|
+
CONFIG_DIR="${CONFIG_DIR:-./config}"
|
|
21
|
+
|
|
22
|
+
# Functions
|
|
23
|
+
log() {
|
|
24
|
+
echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')] $1${NC}"
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
warn() {
|
|
28
|
+
echo -e "${YELLOW}[$(date +'%Y-%m-%d %H:%M:%S')] WARNING: $1${NC}"
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
error() {
|
|
32
|
+
echo -e "${RED}[$(date +'%Y-%m-%d %H:%M:%S')] ERROR: $1${NC}"
|
|
33
|
+
exit 1
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
info() {
|
|
37
|
+
echo -e "${BLUE}[$(date +'%Y-%m-%d %H:%M:%S')] INFO: $1${NC}"
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
print_usage() {
|
|
41
|
+
cat << EOF
|
|
42
|
+
Usage: $0 [OPTIONS] [COMMAND]
|
|
43
|
+
|
|
44
|
+
Deploy and manage MCP Rubber Duck on Raspberry Pi
|
|
45
|
+
|
|
46
|
+
COMMANDS:
|
|
47
|
+
deploy Deploy the application (default)
|
|
48
|
+
update Update the application (pull + restart)
|
|
49
|
+
stop Stop the application
|
|
50
|
+
start Start the application
|
|
51
|
+
restart Restart the application
|
|
52
|
+
logs Show application logs
|
|
53
|
+
status Show application status
|
|
54
|
+
clean Clean up unused Docker resources
|
|
55
|
+
health Check application health
|
|
56
|
+
backup Backup configuration and data
|
|
57
|
+
|
|
58
|
+
OPTIONS:
|
|
59
|
+
-f, --compose-file FILE Docker compose file (default: docker-compose.raspbian.yml)
|
|
60
|
+
-e, --env-file FILE Environment file (default: .env)
|
|
61
|
+
--pull Pull latest images before deploy
|
|
62
|
+
--build Build image locally before deploy
|
|
63
|
+
--with-ollama Include Ollama service
|
|
64
|
+
-v, --verbose Verbose output
|
|
65
|
+
-h, --help Show this help message
|
|
66
|
+
|
|
67
|
+
EXAMPLES:
|
|
68
|
+
# Deploy with default settings
|
|
69
|
+
$0 deploy
|
|
70
|
+
|
|
71
|
+
# Deploy with Ollama support
|
|
72
|
+
$0 --with-ollama deploy
|
|
73
|
+
|
|
74
|
+
# Update to latest version
|
|
75
|
+
$0 update
|
|
76
|
+
|
|
77
|
+
# View logs in real-time
|
|
78
|
+
$0 logs -f
|
|
79
|
+
|
|
80
|
+
# Check health status
|
|
81
|
+
$0 health
|
|
82
|
+
|
|
83
|
+
EOF
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
# System checks
|
|
87
|
+
check_system() {
|
|
88
|
+
log "Performing system checks..."
|
|
89
|
+
|
|
90
|
+
# Check if we're on ARM (Raspberry Pi)
|
|
91
|
+
ARCH=$(uname -m)
|
|
92
|
+
if [[ "$ARCH" != "armv7l" && "$ARCH" != "aarch64" && "$ARCH" != "arm64" ]]; then
|
|
93
|
+
warn "This script is optimized for Raspberry Pi (ARM). Current architecture: $ARCH"
|
|
94
|
+
fi
|
|
95
|
+
|
|
96
|
+
# Check available memory
|
|
97
|
+
TOTAL_MEM_KB=$(grep MemTotal /proc/meminfo | awk '{print $2}')
|
|
98
|
+
TOTAL_MEM_MB=$((TOTAL_MEM_KB / 1024))
|
|
99
|
+
|
|
100
|
+
if [[ $TOTAL_MEM_MB -lt 1024 ]]; then
|
|
101
|
+
warn "Low memory detected: ${TOTAL_MEM_MB}MB. Consider adjusting memory limits."
|
|
102
|
+
fi
|
|
103
|
+
|
|
104
|
+
info "System: $ARCH, Memory: ${TOTAL_MEM_MB}MB"
|
|
105
|
+
|
|
106
|
+
# Check Docker
|
|
107
|
+
if ! command -v docker &> /dev/null; then
|
|
108
|
+
error "Docker is not installed. Run setup-docker-raspbian.sh first."
|
|
109
|
+
fi
|
|
110
|
+
|
|
111
|
+
# Check Docker Compose
|
|
112
|
+
if ! command -v docker-compose &> /dev/null; then
|
|
113
|
+
error "Docker Compose is not installed. Run setup-docker-raspbian.sh first."
|
|
114
|
+
fi
|
|
115
|
+
|
|
116
|
+
# Check if Docker daemon is running
|
|
117
|
+
if ! docker info &> /dev/null; then
|
|
118
|
+
error "Docker daemon is not running. Try: sudo systemctl start docker"
|
|
119
|
+
fi
|
|
120
|
+
|
|
121
|
+
log "â
System checks passed"
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
# Environment setup
|
|
125
|
+
setup_environment() {
|
|
126
|
+
log "Setting up environment..."
|
|
127
|
+
|
|
128
|
+
# Create directories
|
|
129
|
+
mkdir -p "$DATA_DIR" "$CONFIG_DIR"
|
|
130
|
+
|
|
131
|
+
# Create .env file if it doesn't exist
|
|
132
|
+
if [[ ! -f "$ENV_FILE" ]]; then
|
|
133
|
+
if [[ -f ".env.raspbian.template" ]]; then
|
|
134
|
+
log "Creating $ENV_FILE from template"
|
|
135
|
+
cp .env.raspbian.template "$ENV_FILE"
|
|
136
|
+
warn "Please edit $ENV_FILE and add your API keys before proceeding"
|
|
137
|
+
|
|
138
|
+
# Check if nano is available for editing
|
|
139
|
+
if command -v nano &> /dev/null; then
|
|
140
|
+
read -p "Would you like to edit $ENV_FILE now? (y/N): " -r
|
|
141
|
+
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
|
142
|
+
nano "$ENV_FILE"
|
|
143
|
+
fi
|
|
144
|
+
fi
|
|
145
|
+
else
|
|
146
|
+
error "No $ENV_FILE found and no .env.raspbian.template available"
|
|
147
|
+
fi
|
|
148
|
+
fi
|
|
149
|
+
|
|
150
|
+
# Validate .env file has required keys
|
|
151
|
+
if ! grep -q "OPENAI_API_KEY=sk-" "$ENV_FILE" 2>/dev/null; then
|
|
152
|
+
warn "OPENAI_API_KEY not found in $ENV_FILE. Please configure your API keys."
|
|
153
|
+
fi
|
|
154
|
+
|
|
155
|
+
log "â
Environment setup complete"
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
# Deployment functions
|
|
159
|
+
deploy() {
|
|
160
|
+
log "Starting deployment of MCP Rubber Duck..."
|
|
161
|
+
|
|
162
|
+
# Stop existing containers
|
|
163
|
+
if docker-compose -f "$COMPOSE_FILE" ps -q 2>/dev/null | grep -q .; then
|
|
164
|
+
log "Stopping existing containers..."
|
|
165
|
+
docker-compose -f "$COMPOSE_FILE" down
|
|
166
|
+
fi
|
|
167
|
+
|
|
168
|
+
# Pull images if requested
|
|
169
|
+
if [[ "${PULL_IMAGES:-false}" == "true" ]]; then
|
|
170
|
+
log "Pulling latest Docker images..."
|
|
171
|
+
docker-compose -f "$COMPOSE_FILE" pull
|
|
172
|
+
fi
|
|
173
|
+
|
|
174
|
+
# Build locally if requested
|
|
175
|
+
if [[ "${BUILD_LOCAL:-false}" == "true" ]]; then
|
|
176
|
+
log "Building Docker image locally..."
|
|
177
|
+
docker-compose -f "$COMPOSE_FILE" build
|
|
178
|
+
fi
|
|
179
|
+
|
|
180
|
+
# Start services
|
|
181
|
+
log "Starting services..."
|
|
182
|
+
if [[ "${WITH_OLLAMA:-false}" == "true" ]]; then
|
|
183
|
+
docker-compose -f "$COMPOSE_FILE" --profile with-ollama up -d
|
|
184
|
+
else
|
|
185
|
+
docker-compose -f "$COMPOSE_FILE" up -d
|
|
186
|
+
fi
|
|
187
|
+
|
|
188
|
+
# Wait for services to be ready
|
|
189
|
+
log "Waiting for services to start..."
|
|
190
|
+
sleep 10
|
|
191
|
+
|
|
192
|
+
# Check health
|
|
193
|
+
check_health
|
|
194
|
+
|
|
195
|
+
log "â
Deployment completed successfully!"
|
|
196
|
+
show_status
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
update() {
|
|
200
|
+
log "Updating MCP Rubber Duck..."
|
|
201
|
+
PULL_IMAGES=true deploy
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
stop() {
|
|
205
|
+
log "Stopping MCP Rubber Duck..."
|
|
206
|
+
docker-compose -f "$COMPOSE_FILE" down
|
|
207
|
+
log "â
Services stopped"
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
start() {
|
|
211
|
+
log "Starting MCP Rubber Duck..."
|
|
212
|
+
if [[ "${WITH_OLLAMA:-false}" == "true" ]]; then
|
|
213
|
+
docker-compose -f "$COMPOSE_FILE" --profile with-ollama up -d
|
|
214
|
+
else
|
|
215
|
+
docker-compose -f "$COMPOSE_FILE" up -d
|
|
216
|
+
fi
|
|
217
|
+
log "â
Services started"
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
restart() {
|
|
221
|
+
log "Restarting MCP Rubber Duck..."
|
|
222
|
+
docker-compose -f "$COMPOSE_FILE" restart
|
|
223
|
+
log "â
Services restarted"
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
show_logs() {
|
|
227
|
+
docker-compose -f "$COMPOSE_FILE" logs "$@"
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
show_status() {
|
|
231
|
+
log "Service Status:"
|
|
232
|
+
docker-compose -f "$COMPOSE_FILE" ps
|
|
233
|
+
|
|
234
|
+
echo
|
|
235
|
+
log "Resource Usage:"
|
|
236
|
+
docker stats --no-stream --format "table {{.Name}}\t{{.CPUPerc}}\t{{.MemUsage}}\t{{.MemPerc}}"
|
|
237
|
+
|
|
238
|
+
echo
|
|
239
|
+
log "Health Status:"
|
|
240
|
+
check_health
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
check_health() {
|
|
244
|
+
local container_name="mcp-rubber-duck"
|
|
245
|
+
|
|
246
|
+
if docker ps --format "table {{.Names}}" | grep -q "$container_name"; then
|
|
247
|
+
local health_status=$(docker inspect --format='{{.State.Health.Status}}' "$container_name" 2>/dev/null || echo "unknown")
|
|
248
|
+
|
|
249
|
+
case "$health_status" in
|
|
250
|
+
"healthy")
|
|
251
|
+
log "â
Application is healthy"
|
|
252
|
+
return 0
|
|
253
|
+
;;
|
|
254
|
+
"unhealthy")
|
|
255
|
+
warn "â Application is unhealthy"
|
|
256
|
+
return 1
|
|
257
|
+
;;
|
|
258
|
+
"starting")
|
|
259
|
+
info "đ Application is starting..."
|
|
260
|
+
return 0
|
|
261
|
+
;;
|
|
262
|
+
*)
|
|
263
|
+
info "âšī¸ Health status: $health_status"
|
|
264
|
+
return 0
|
|
265
|
+
;;
|
|
266
|
+
esac
|
|
267
|
+
else
|
|
268
|
+
warn "â Container is not running"
|
|
269
|
+
return 1
|
|
270
|
+
fi
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
clean() {
|
|
274
|
+
log "Cleaning up Docker resources..."
|
|
275
|
+
|
|
276
|
+
# Remove stopped containers
|
|
277
|
+
docker container prune -f
|
|
278
|
+
|
|
279
|
+
# Remove unused images
|
|
280
|
+
docker image prune -f
|
|
281
|
+
|
|
282
|
+
# Remove unused volumes (ask for confirmation)
|
|
283
|
+
read -p "Remove unused volumes? This may delete data! (y/N): " -r
|
|
284
|
+
if [[ $REPLY =~ ^[Yy]$ ]]; then
|
|
285
|
+
docker volume prune -f
|
|
286
|
+
fi
|
|
287
|
+
|
|
288
|
+
# Remove unused networks
|
|
289
|
+
docker network prune -f
|
|
290
|
+
|
|
291
|
+
log "â
Cleanup completed"
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
backup() {
|
|
295
|
+
local backup_dir="backup_$(date +%Y%m%d_%H%M%S)"
|
|
296
|
+
log "Creating backup in $backup_dir..."
|
|
297
|
+
|
|
298
|
+
mkdir -p "$backup_dir"
|
|
299
|
+
|
|
300
|
+
# Backup configuration
|
|
301
|
+
if [[ -f "$ENV_FILE" ]]; then
|
|
302
|
+
cp "$ENV_FILE" "$backup_dir/"
|
|
303
|
+
fi
|
|
304
|
+
|
|
305
|
+
if [[ -d "$CONFIG_DIR" ]]; then
|
|
306
|
+
cp -r "$CONFIG_DIR" "$backup_dir/"
|
|
307
|
+
fi
|
|
308
|
+
|
|
309
|
+
# Backup data
|
|
310
|
+
if [[ -d "$DATA_DIR" ]]; then
|
|
311
|
+
cp -r "$DATA_DIR" "$backup_dir/"
|
|
312
|
+
fi
|
|
313
|
+
|
|
314
|
+
# Create archive
|
|
315
|
+
tar -czf "${backup_dir}.tar.gz" "$backup_dir"
|
|
316
|
+
rm -rf "$backup_dir"
|
|
317
|
+
|
|
318
|
+
log "â
Backup created: ${backup_dir}.tar.gz"
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
# Parse command line arguments
|
|
322
|
+
COMMAND="deploy"
|
|
323
|
+
while [[ $# -gt 0 ]]; do
|
|
324
|
+
case $1 in
|
|
325
|
+
deploy|update|stop|start|restart|logs|status|clean|health|backup)
|
|
326
|
+
COMMAND="$1"
|
|
327
|
+
shift
|
|
328
|
+
;;
|
|
329
|
+
-f|--compose-file)
|
|
330
|
+
COMPOSE_FILE="$2"
|
|
331
|
+
shift 2
|
|
332
|
+
;;
|
|
333
|
+
-e|--env-file)
|
|
334
|
+
ENV_FILE="$2"
|
|
335
|
+
shift 2
|
|
336
|
+
;;
|
|
337
|
+
--pull)
|
|
338
|
+
PULL_IMAGES="true"
|
|
339
|
+
shift
|
|
340
|
+
;;
|
|
341
|
+
--build)
|
|
342
|
+
BUILD_LOCAL="true"
|
|
343
|
+
shift
|
|
344
|
+
;;
|
|
345
|
+
--with-ollama)
|
|
346
|
+
WITH_OLLAMA="true"
|
|
347
|
+
shift
|
|
348
|
+
;;
|
|
349
|
+
-v|--verbose)
|
|
350
|
+
set -x
|
|
351
|
+
shift
|
|
352
|
+
;;
|
|
353
|
+
-h|--help)
|
|
354
|
+
print_usage
|
|
355
|
+
exit 0
|
|
356
|
+
;;
|
|
357
|
+
*)
|
|
358
|
+
# Pass remaining args to docker-compose logs
|
|
359
|
+
if [[ "$COMMAND" == "logs" ]]; then
|
|
360
|
+
break
|
|
361
|
+
fi
|
|
362
|
+
error "Unknown option: $1"
|
|
363
|
+
;;
|
|
364
|
+
esac
|
|
365
|
+
done
|
|
366
|
+
|
|
367
|
+
# Validate compose file exists
|
|
368
|
+
if [[ ! -f "$COMPOSE_FILE" ]]; then
|
|
369
|
+
error "Compose file not found: $COMPOSE_FILE"
|
|
370
|
+
fi
|
|
371
|
+
|
|
372
|
+
# Main execution
|
|
373
|
+
case "$COMMAND" in
|
|
374
|
+
deploy)
|
|
375
|
+
check_system
|
|
376
|
+
setup_environment
|
|
377
|
+
deploy
|
|
378
|
+
;;
|
|
379
|
+
update)
|
|
380
|
+
check_system
|
|
381
|
+
update
|
|
382
|
+
;;
|
|
383
|
+
stop)
|
|
384
|
+
stop
|
|
385
|
+
;;
|
|
386
|
+
start)
|
|
387
|
+
start
|
|
388
|
+
;;
|
|
389
|
+
restart)
|
|
390
|
+
restart
|
|
391
|
+
;;
|
|
392
|
+
logs)
|
|
393
|
+
show_logs "$@"
|
|
394
|
+
;;
|
|
395
|
+
status)
|
|
396
|
+
show_status
|
|
397
|
+
;;
|
|
398
|
+
clean)
|
|
399
|
+
clean
|
|
400
|
+
;;
|
|
401
|
+
health)
|
|
402
|
+
check_health
|
|
403
|
+
;;
|
|
404
|
+
backup)
|
|
405
|
+
backup
|
|
406
|
+
;;
|
|
407
|
+
*)
|
|
408
|
+
error "Unknown command: $COMMAND"
|
|
409
|
+
;;
|
|
410
|
+
esac
|