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.
Files changed (184) hide show
  1. package/.dockerignore +19 -0
  2. package/.env.desktop.example +145 -0
  3. package/.env.example +45 -0
  4. package/.env.pi.example +106 -0
  5. package/.env.template +165 -0
  6. package/.eslintrc.json +40 -0
  7. package/.github/ISSUE_TEMPLATE/bug_report.md +65 -0
  8. package/.github/ISSUE_TEMPLATE/feature_request.md +58 -0
  9. package/.github/ISSUE_TEMPLATE/question.md +67 -0
  10. package/.github/pull_request_template.md +111 -0
  11. package/.github/workflows/docker-build.yml +138 -0
  12. package/.github/workflows/release.yml +182 -0
  13. package/.github/workflows/security.yml +141 -0
  14. package/.github/workflows/semantic-release.yml +89 -0
  15. package/.prettierrc +10 -0
  16. package/.releaserc.json +66 -0
  17. package/CHANGELOG.md +95 -0
  18. package/CONTRIBUTING.md +242 -0
  19. package/Dockerfile +62 -0
  20. package/LICENSE +21 -0
  21. package/README.md +803 -0
  22. package/audit-ci.json +8 -0
  23. package/config/claude_desktop.json +14 -0
  24. package/config/config.example.json +91 -0
  25. package/dist/config/config.d.ts +51 -0
  26. package/dist/config/config.d.ts.map +1 -0
  27. package/dist/config/config.js +301 -0
  28. package/dist/config/config.js.map +1 -0
  29. package/dist/config/types.d.ts +356 -0
  30. package/dist/config/types.d.ts.map +1 -0
  31. package/dist/config/types.js +41 -0
  32. package/dist/config/types.js.map +1 -0
  33. package/dist/index.d.ts +3 -0
  34. package/dist/index.d.ts.map +1 -0
  35. package/dist/index.js +109 -0
  36. package/dist/index.js.map +1 -0
  37. package/dist/providers/duck-provider-enhanced.d.ts +29 -0
  38. package/dist/providers/duck-provider-enhanced.d.ts.map +1 -0
  39. package/dist/providers/duck-provider-enhanced.js +230 -0
  40. package/dist/providers/duck-provider-enhanced.js.map +1 -0
  41. package/dist/providers/enhanced-manager.d.ts +54 -0
  42. package/dist/providers/enhanced-manager.d.ts.map +1 -0
  43. package/dist/providers/enhanced-manager.js +217 -0
  44. package/dist/providers/enhanced-manager.js.map +1 -0
  45. package/dist/providers/manager.d.ts +28 -0
  46. package/dist/providers/manager.d.ts.map +1 -0
  47. package/dist/providers/manager.js +204 -0
  48. package/dist/providers/manager.js.map +1 -0
  49. package/dist/providers/provider.d.ts +29 -0
  50. package/dist/providers/provider.d.ts.map +1 -0
  51. package/dist/providers/provider.js +179 -0
  52. package/dist/providers/provider.js.map +1 -0
  53. package/dist/providers/types.d.ts +69 -0
  54. package/dist/providers/types.d.ts.map +1 -0
  55. package/dist/providers/types.js +2 -0
  56. package/dist/providers/types.js.map +1 -0
  57. package/dist/server.d.ts +24 -0
  58. package/dist/server.d.ts.map +1 -0
  59. package/dist/server.js +501 -0
  60. package/dist/server.js.map +1 -0
  61. package/dist/services/approval.d.ts +44 -0
  62. package/dist/services/approval.d.ts.map +1 -0
  63. package/dist/services/approval.js +159 -0
  64. package/dist/services/approval.js.map +1 -0
  65. package/dist/services/cache.d.ts +21 -0
  66. package/dist/services/cache.d.ts.map +1 -0
  67. package/dist/services/cache.js +63 -0
  68. package/dist/services/cache.js.map +1 -0
  69. package/dist/services/conversation.d.ts +24 -0
  70. package/dist/services/conversation.d.ts.map +1 -0
  71. package/dist/services/conversation.js +108 -0
  72. package/dist/services/conversation.js.map +1 -0
  73. package/dist/services/function-bridge.d.ts +41 -0
  74. package/dist/services/function-bridge.d.ts.map +1 -0
  75. package/dist/services/function-bridge.js +259 -0
  76. package/dist/services/function-bridge.js.map +1 -0
  77. package/dist/services/health.d.ts +17 -0
  78. package/dist/services/health.d.ts.map +1 -0
  79. package/dist/services/health.js +77 -0
  80. package/dist/services/health.js.map +1 -0
  81. package/dist/services/mcp-client-manager.d.ts +49 -0
  82. package/dist/services/mcp-client-manager.d.ts.map +1 -0
  83. package/dist/services/mcp-client-manager.js +279 -0
  84. package/dist/services/mcp-client-manager.js.map +1 -0
  85. package/dist/tools/approve-mcp-request.d.ts +9 -0
  86. package/dist/tools/approve-mcp-request.d.ts.map +1 -0
  87. package/dist/tools/approve-mcp-request.js +111 -0
  88. package/dist/tools/approve-mcp-request.js.map +1 -0
  89. package/dist/tools/ask-duck.d.ts +9 -0
  90. package/dist/tools/ask-duck.d.ts.map +1 -0
  91. package/dist/tools/ask-duck.js +43 -0
  92. package/dist/tools/ask-duck.js.map +1 -0
  93. package/dist/tools/chat-duck.d.ts +9 -0
  94. package/dist/tools/chat-duck.d.ts.map +1 -0
  95. package/dist/tools/chat-duck.js +57 -0
  96. package/dist/tools/chat-duck.js.map +1 -0
  97. package/dist/tools/clear-conversations.d.ts +8 -0
  98. package/dist/tools/clear-conversations.d.ts.map +1 -0
  99. package/dist/tools/clear-conversations.js +17 -0
  100. package/dist/tools/clear-conversations.js.map +1 -0
  101. package/dist/tools/compare-ducks.d.ts +8 -0
  102. package/dist/tools/compare-ducks.d.ts.map +1 -0
  103. package/dist/tools/compare-ducks.js +49 -0
  104. package/dist/tools/compare-ducks.js.map +1 -0
  105. package/dist/tools/duck-council.d.ts +8 -0
  106. package/dist/tools/duck-council.d.ts.map +1 -0
  107. package/dist/tools/duck-council.js +69 -0
  108. package/dist/tools/duck-council.js.map +1 -0
  109. package/dist/tools/get-pending-approvals.d.ts +15 -0
  110. package/dist/tools/get-pending-approvals.d.ts.map +1 -0
  111. package/dist/tools/get-pending-approvals.js +74 -0
  112. package/dist/tools/get-pending-approvals.js.map +1 -0
  113. package/dist/tools/list-ducks.d.ts +9 -0
  114. package/dist/tools/list-ducks.d.ts.map +1 -0
  115. package/dist/tools/list-ducks.js +47 -0
  116. package/dist/tools/list-ducks.js.map +1 -0
  117. package/dist/tools/list-models.d.ts +8 -0
  118. package/dist/tools/list-models.d.ts.map +1 -0
  119. package/dist/tools/list-models.js +72 -0
  120. package/dist/tools/list-models.js.map +1 -0
  121. package/dist/tools/mcp-status.d.ts +17 -0
  122. package/dist/tools/mcp-status.d.ts.map +1 -0
  123. package/dist/tools/mcp-status.js +100 -0
  124. package/dist/tools/mcp-status.js.map +1 -0
  125. package/dist/utils/ascii-art.d.ts +19 -0
  126. package/dist/utils/ascii-art.d.ts.map +1 -0
  127. package/dist/utils/ascii-art.js +73 -0
  128. package/dist/utils/ascii-art.js.map +1 -0
  129. package/dist/utils/logger.d.ts +3 -0
  130. package/dist/utils/logger.d.ts.map +1 -0
  131. package/dist/utils/logger.js +86 -0
  132. package/dist/utils/logger.js.map +1 -0
  133. package/dist/utils/safe-logger.d.ts +23 -0
  134. package/dist/utils/safe-logger.d.ts.map +1 -0
  135. package/dist/utils/safe-logger.js +145 -0
  136. package/dist/utils/safe-logger.js.map +1 -0
  137. package/docker-compose.yml +161 -0
  138. package/jest.config.js +26 -0
  139. package/package.json +65 -0
  140. package/scripts/build-multiarch.sh +290 -0
  141. package/scripts/deploy-raspbian.sh +410 -0
  142. package/scripts/deploy.sh +322 -0
  143. package/scripts/gh-deploy.sh +343 -0
  144. package/scripts/setup-docker-raspbian.sh +530 -0
  145. package/server.json +8 -0
  146. package/src/config/config.ts +357 -0
  147. package/src/config/types.ts +89 -0
  148. package/src/index.ts +114 -0
  149. package/src/providers/duck-provider-enhanced.ts +294 -0
  150. package/src/providers/enhanced-manager.ts +290 -0
  151. package/src/providers/manager.ts +257 -0
  152. package/src/providers/provider.ts +207 -0
  153. package/src/providers/types.ts +78 -0
  154. package/src/server.ts +603 -0
  155. package/src/services/approval.ts +225 -0
  156. package/src/services/cache.ts +79 -0
  157. package/src/services/conversation.ts +146 -0
  158. package/src/services/function-bridge.ts +329 -0
  159. package/src/services/health.ts +107 -0
  160. package/src/services/mcp-client-manager.ts +362 -0
  161. package/src/tools/approve-mcp-request.ts +126 -0
  162. package/src/tools/ask-duck.ts +74 -0
  163. package/src/tools/chat-duck.ts +82 -0
  164. package/src/tools/clear-conversations.ts +24 -0
  165. package/src/tools/compare-ducks.ts +67 -0
  166. package/src/tools/duck-council.ts +88 -0
  167. package/src/tools/get-pending-approvals.ts +90 -0
  168. package/src/tools/list-ducks.ts +65 -0
  169. package/src/tools/list-models.ts +101 -0
  170. package/src/tools/mcp-status.ts +117 -0
  171. package/src/utils/ascii-art.ts +85 -0
  172. package/src/utils/logger.ts +116 -0
  173. package/src/utils/safe-logger.ts +165 -0
  174. package/systemd/mcp-rubber-duck-with-ollama.service +55 -0
  175. package/systemd/mcp-rubber-duck.service +58 -0
  176. package/test-functionality.js +147 -0
  177. package/test-mcp-interface.js +221 -0
  178. package/tests/ascii-art.test.ts +36 -0
  179. package/tests/config.test.ts +239 -0
  180. package/tests/conversation.test.ts +308 -0
  181. package/tests/mcp-bridge.test.ts +291 -0
  182. package/tests/providers.test.ts +269 -0
  183. package/tests/tools/clear-conversations.test.ts +163 -0
  184. 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