aidevops 2.79.2 → 2.80.1

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 (4) hide show
  1. package/VERSION +1 -1
  2. package/aidevops.sh +1 -1
  3. package/package.json +1 -1
  4. package/setup.sh +222 -29
package/VERSION CHANGED
@@ -1 +1 @@
1
- 2.79.2
1
+ 2.80.1
package/aidevops.sh CHANGED
@@ -3,7 +3,7 @@
3
3
  # AI DevOps Framework CLI
4
4
  # Usage: aidevops <command> [options]
5
5
  #
6
- # Version: 2.79.2
6
+ # Version: 2.80.1
7
7
 
8
8
  set -euo pipefail
9
9
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aidevops",
3
- "version": "2.79.2",
3
+ "version": "2.80.1",
4
4
  "description": "AI DevOps Framework - AI-assisted development workflows, code quality, and deployment automation",
5
5
  "type": "module",
6
6
  "main": "index.js",
package/setup.sh CHANGED
@@ -3,7 +3,7 @@
3
3
  # AI Assistant Server Access Framework Setup Script
4
4
  # Helps developers set up the framework for their infrastructure
5
5
  #
6
- # Version: 2.79.2
6
+ # Version: 2.80.1
7
7
  #
8
8
  # Quick Install (one-liner):
9
9
  # bash <(curl -fsSL https://aidevops.dev/install)
@@ -228,7 +228,7 @@ cleanup_deprecated_mcps() {
228
228
  jq 'del(.agent.SEO.tools["dataforseo_*"]) | del(.agent.SEO.tools["serper_*"]) | del(.agent.SEO.tools["ahrefs_*"])' "$tmp_config" > "${tmp_config}.new" && mv "${tmp_config}.new" "$tmp_config"
229
229
  fi
230
230
 
231
- # Migrate npx/pipx commands to bare binary names (faster startup)
231
+ # Migrate npx/pipx commands to full binary paths (faster startup, PATH-independent)
232
232
  # Maps: package-name -> binary-name
233
233
  local -A mcp_migrations=(
234
234
  ["chrome-devtools-mcp"]="chrome-devtools-mcp"
@@ -242,35 +242,38 @@ cleanup_deprecated_mcps() {
242
242
 
243
243
  for pkg in "${!mcp_migrations[@]}"; do
244
244
  local bin_name="${mcp_migrations[$pkg]}"
245
- # Check if any MCP entry uses npx/bunx with this package
246
- if jq -e ".mcp | to_entries[] | select(.value.command != null) | select(.value.command | join(\" \") | test(\"npx.*${pkg}|bunx.*${pkg}|pipx.*run.*${pkg}\"))" "$tmp_config" > /dev/null 2>&1; then
247
- if command -v "$bin_name" &> /dev/null; then
248
- # Find the MCP key and update its command to bare binary
249
- local mcp_key
250
- mcp_key=$(jq -r ".mcp | to_entries[] | select(.value.command != null) | select(.value.command | join(\" \") | test(\"npx.*${pkg}|bunx.*${pkg}|pipx.*run.*${pkg}\")) | .key" "$tmp_config" 2>/dev/null | head -1)
251
- if [[ -n "$mcp_key" ]]; then
252
- # Preserve --mcp flag for repomix
253
- if [[ "$bin_name" == "repomix" ]]; then
254
- jq ".mcp[\"$mcp_key\"].command = [\"$bin_name\", \"--mcp\"]" "$tmp_config" > "${tmp_config}.new" && mv "${tmp_config}.new" "$tmp_config"
255
- else
256
- jq ".mcp[\"$mcp_key\"].command = [\"$bin_name\"]" "$tmp_config" > "${tmp_config}.new" && mv "${tmp_config}.new" "$tmp_config"
257
- fi
258
- ((cleaned++))
245
+ # Find MCP key using npx/bunx/pipx for this package (single query)
246
+ local mcp_key
247
+ mcp_key=$(jq -r ".mcp | to_entries[] | select(.value.command != null) | select(.value.command | join(\" \") | test(\"npx.*${pkg}|bunx.*${pkg}|pipx.*run.*${pkg}\")) | .key" "$tmp_config" 2>/dev/null | head -1)
248
+
249
+ if [[ -n "$mcp_key" ]]; then
250
+ # Resolve full path for the binary
251
+ local full_path
252
+ full_path=$(resolve_mcp_binary_path "$bin_name")
253
+ if [[ -n "$full_path" ]]; then
254
+ # Preserve --mcp flag for repomix
255
+ if [[ "$bin_name" == "repomix" ]]; then
256
+ jq --arg k "$mcp_key" --arg p "$full_path" '.mcp[$k].command = [$p, "--mcp"]' "$tmp_config" > "${tmp_config}.new" && mv "${tmp_config}.new" "$tmp_config"
257
+ else
258
+ jq --arg k "$mcp_key" --arg p "$full_path" '.mcp[$k].command = [$p]' "$tmp_config" > "${tmp_config}.new" && mv "${tmp_config}.new" "$tmp_config"
259
259
  fi
260
+ ((cleaned++))
260
261
  fi
261
262
  fi
262
263
  done
263
264
 
264
- # Migrate outscraper from bash -c wrapper to bare binary
265
+ # Migrate outscraper from bash -c wrapper to full binary path
265
266
  if jq -e '.mcp.outscraper.command | join(" ") | test("bash.*outscraper")' "$tmp_config" > /dev/null 2>&1; then
266
- if command -v outscraper-mcp-server &> /dev/null; then
267
+ local outscraper_path
268
+ outscraper_path=$(resolve_mcp_binary_path "outscraper-mcp-server")
269
+ if [[ -n "$outscraper_path" ]]; then
267
270
  # Source the API key and set it in environment
268
271
  local outscraper_key=""
269
272
  if [[ -f "$HOME/.config/aidevops/mcp-env.sh" ]]; then
270
273
  # shellcheck source=/dev/null
271
274
  outscraper_key=$(source "$HOME/.config/aidevops/mcp-env.sh" && echo "${OUTSCRAPER_API_KEY:-}")
272
275
  fi
273
- jq --arg key "$outscraper_key" '.mcp.outscraper.command = ["outscraper-mcp-server"] | .mcp.outscraper.environment = {"OUTSCRAPER_API_KEY": $key}' "$tmp_config" > "${tmp_config}.new" && mv "${tmp_config}.new" "$tmp_config"
276
+ jq --arg p "$outscraper_path" --arg key "$outscraper_key" '.mcp.outscraper.command = [$p] | .mcp.outscraper.environment = {"OUTSCRAPER_API_KEY": $key}' "$tmp_config" > "${tmp_config}.new" && mv "${tmp_config}.new" "$tmp_config"
274
277
  ((cleaned++))
275
278
  fi
276
279
  fi
@@ -278,10 +281,13 @@ cleanup_deprecated_mcps() {
278
281
  if [[ $cleaned -gt 0 ]]; then
279
282
  create_backup_with_rotation "$opencode_config" "opencode"
280
283
  mv "$tmp_config" "$opencode_config"
281
- print_info "Updated $cleaned MCP entry/entries in opencode.json (removed npx overhead, using global binaries)"
284
+ print_info "Updated $cleaned MCP entry/entries in opencode.json (using full binary paths)"
282
285
  else
283
286
  rm -f "$tmp_config"
284
287
  fi
288
+
289
+ # Always resolve bare binary names to full paths (fixes PATH-dependent startup)
290
+ update_mcp_paths_in_opencode
285
291
 
286
292
  return 0
287
293
  }
@@ -2136,14 +2142,14 @@ setup_nodejs_env() {
2136
2142
  install_mcp_packages() {
2137
2143
  print_info "Installing MCP server packages globally (eliminates npx startup delay)..."
2138
2144
 
2139
- # Node.js MCP packages (prefer bun, fallback to npm)
2140
- local node_mcps=(
2141
- "chrome-devtools-mcp"
2142
- "mcp-server-gsc"
2143
- "repomix"
2144
- "playwriter"
2145
- "@steipete/macos-automator-mcp"
2146
- "@steipete/claude-code-mcp"
2145
+ # Node.js MCP packages: package-name -> binary-name
2146
+ local -A node_mcps=(
2147
+ ["chrome-devtools-mcp"]="chrome-devtools-mcp"
2148
+ ["mcp-server-gsc"]="mcp-server-gsc"
2149
+ ["repomix"]="repomix"
2150
+ ["playwriter"]="playwriter"
2151
+ ["@steipete/macos-automator-mcp"]="macos-automator-mcp"
2152
+ ["@steipete/claude-code-mcp"]="claude-code-mcp"
2147
2153
  )
2148
2154
 
2149
2155
  local installer=""
@@ -2166,7 +2172,7 @@ install_mcp_packages() {
2166
2172
  # Always install latest (bun install -g is fast and idempotent)
2167
2173
  local updated=0
2168
2174
  local failed=0
2169
- for pkg in "${node_mcps[@]}"; do
2175
+ for pkg in "${!node_mcps[@]}"; do
2170
2176
  if $install_cmd "${pkg}@latest" > /dev/null 2>&1; then
2171
2177
  ((updated++))
2172
2178
  else
@@ -2201,10 +2207,132 @@ install_mcp_packages() {
2201
2207
  fi
2202
2208
  fi
2203
2209
 
2210
+ # Update opencode.json with resolved full paths for all MCP binaries
2211
+ update_mcp_paths_in_opencode
2212
+
2204
2213
  print_info "MCP servers will start instantly (no registry lookups on each launch)"
2205
2214
  return 0
2206
2215
  }
2207
2216
 
2217
+ # Resolve full path for an MCP binary, checking common install locations
2218
+ # Usage: resolve_mcp_binary_path "binary-name"
2219
+ # Returns: full path on stdout, or empty string if not found
2220
+ resolve_mcp_binary_path() {
2221
+ local bin_name="$1"
2222
+ local resolved=""
2223
+
2224
+ # Check common locations in priority order
2225
+ local search_paths=(
2226
+ "$HOME/.bun/bin/$bin_name"
2227
+ "/opt/homebrew/bin/$bin_name"
2228
+ "/usr/local/bin/$bin_name"
2229
+ "$HOME/.local/bin/$bin_name"
2230
+ "$HOME/.npm-global/bin/$bin_name"
2231
+ )
2232
+
2233
+ for path in "${search_paths[@]}"; do
2234
+ if [[ -x "$path" ]]; then
2235
+ resolved="$path"
2236
+ break
2237
+ fi
2238
+ done
2239
+
2240
+ # Fallback: use command -v if in PATH (portable, POSIX-compliant)
2241
+ if [[ -z "$resolved" ]]; then
2242
+ resolved=$(command -v "$bin_name" 2>/dev/null || true)
2243
+ fi
2244
+
2245
+ echo "$resolved"
2246
+ return 0
2247
+ }
2248
+
2249
+ # Update opencode.json MCP commands to use full binary paths
2250
+ # This ensures MCPs start regardless of PATH configuration
2251
+ update_mcp_paths_in_opencode() {
2252
+ local opencode_config="$HOME/.config/opencode/opencode.json"
2253
+
2254
+ if [[ ! -f "$opencode_config" ]]; then
2255
+ return 0
2256
+ fi
2257
+
2258
+ if ! command -v jq &> /dev/null; then
2259
+ return 0
2260
+ fi
2261
+
2262
+ local tmp_config
2263
+ tmp_config=$(mktemp)
2264
+ cp "$opencode_config" "$tmp_config"
2265
+
2266
+ local updated=0
2267
+
2268
+ # Get all MCP entries with local commands
2269
+ local mcp_keys
2270
+ mcp_keys=$(jq -r '.mcp | to_entries[] | select(.value.type == "local") | select(.value.command != null) | .key' "$tmp_config" 2>/dev/null)
2271
+
2272
+ while IFS= read -r mcp_key; do
2273
+ [[ -z "$mcp_key" ]] && continue
2274
+
2275
+ # Get the first element of the command array (the binary)
2276
+ local current_cmd
2277
+ current_cmd=$(jq -r --arg k "$mcp_key" '.mcp[$k].command[0]' "$tmp_config" 2>/dev/null)
2278
+
2279
+ # Skip if already a full path
2280
+ if [[ "$current_cmd" == /* ]]; then
2281
+ # Verify the path still exists
2282
+ if [[ ! -x "$current_cmd" ]]; then
2283
+ # Path is stale, try to resolve
2284
+ local bin_name
2285
+ bin_name=$(basename "$current_cmd")
2286
+ local new_path
2287
+ new_path=$(resolve_mcp_binary_path "$bin_name")
2288
+ if [[ -n "$new_path" && "$new_path" != "$current_cmd" ]]; then
2289
+ jq --arg k "$mcp_key" --arg p "$new_path" '.mcp[$k].command[0] = $p' "$tmp_config" > "${tmp_config}.new" && mv "${tmp_config}.new" "$tmp_config"
2290
+ ((updated++))
2291
+ fi
2292
+ fi
2293
+ continue
2294
+ fi
2295
+
2296
+ # Skip docker (container runtime) and node (resolved separately below)
2297
+ case "$current_cmd" in
2298
+ docker|node) continue ;;
2299
+ esac
2300
+
2301
+ # Resolve the full path
2302
+ local full_path
2303
+ full_path=$(resolve_mcp_binary_path "$current_cmd")
2304
+
2305
+ if [[ -n "$full_path" && "$full_path" != "$current_cmd" ]]; then
2306
+ jq --arg k "$mcp_key" --arg p "$full_path" '.mcp[$k].command[0] = $p' "$tmp_config" > "${tmp_config}.new" && mv "${tmp_config}.new" "$tmp_config"
2307
+ ((updated++))
2308
+ fi
2309
+ done <<< "$mcp_keys"
2310
+
2311
+ # Also resolve 'node' commands (e.g., quickfile, amazon-order-history)
2312
+ # These use ["node", "/path/to/index.js"] - node itself should be resolved
2313
+ local node_path
2314
+ node_path=$(resolve_mcp_binary_path "node")
2315
+ if [[ -n "$node_path" ]]; then
2316
+ local node_mcp_keys
2317
+ node_mcp_keys=$(jq -r '.mcp | to_entries[] | select(.value.type == "local") | select(.value.command != null) | select(.value.command[0] == "node") | .key' "$tmp_config" 2>/dev/null)
2318
+ while IFS= read -r mcp_key; do
2319
+ [[ -z "$mcp_key" ]] && continue
2320
+ jq --arg k "$mcp_key" --arg p "$node_path" '.mcp[$k].command[0] = $p' "$tmp_config" > "${tmp_config}.new" && mv "${tmp_config}.new" "$tmp_config"
2321
+ ((updated++))
2322
+ done <<< "$node_mcp_keys"
2323
+ fi
2324
+
2325
+ if [[ $updated -gt 0 ]]; then
2326
+ create_backup_with_rotation "$opencode_config" "opencode"
2327
+ mv "$tmp_config" "$opencode_config"
2328
+ print_success "Updated $updated MCP commands to use full binary paths in opencode.json"
2329
+ else
2330
+ rm -f "$tmp_config"
2331
+ fi
2332
+
2333
+ return 0
2334
+ }
2335
+
2208
2336
  # Setup LocalWP MCP server for AI database access
2209
2337
  setup_localwp_mcp() {
2210
2338
  print_info "Setting up LocalWP MCP server..."
@@ -2994,6 +3122,70 @@ setup_google_analytics_mcp() {
2994
3122
  return 0
2995
3123
  }
2996
3124
 
3125
+ # Setup multi-tenant credential storage
3126
+ setup_multi_tenant_credentials() {
3127
+ print_info "Multi-tenant credential storage..."
3128
+
3129
+ local credential_helper="$HOME/.aidevops/agents/scripts/credential-helper.sh"
3130
+
3131
+ if [[ ! -f "$credential_helper" ]]; then
3132
+ # Try local script if deployed version not available yet
3133
+ credential_helper=".agent/scripts/credential-helper.sh"
3134
+ fi
3135
+
3136
+ if [[ ! -f "$credential_helper" ]]; then
3137
+ print_warning "credential-helper.sh not found - skipping"
3138
+ return 0
3139
+ fi
3140
+
3141
+ # Check if already initialized
3142
+ if [[ -d "$HOME/.config/aidevops/tenants" ]]; then
3143
+ local tenant_count
3144
+ tenant_count=$(find "$HOME/.config/aidevops/tenants" -maxdepth 1 -type d | wc -l)
3145
+ # Subtract 1 for the tenants/ dir itself
3146
+ tenant_count=$((tenant_count - 1))
3147
+ print_success "Multi-tenant already initialized ($tenant_count tenant(s))"
3148
+ bash "$credential_helper" status
3149
+ return 0
3150
+ fi
3151
+
3152
+ # Check if there are existing credentials to migrate
3153
+ if [[ -f "$HOME/.config/aidevops/mcp-env.sh" ]]; then
3154
+ local key_count
3155
+ key_count=$(grep -c "^export " "$HOME/.config/aidevops/mcp-env.sh" 2>/dev/null || echo "0")
3156
+ print_info "Found $key_count existing API keys in mcp-env.sh"
3157
+ print_info "Multi-tenant enables managing separate credential sets for:"
3158
+ echo " - Multiple clients (agency/freelance work)"
3159
+ echo " - Multiple environments (production, staging)"
3160
+ echo " - Multiple accounts (personal, work)"
3161
+ echo ""
3162
+ print_info "Your existing keys will be migrated to a 'default' tenant."
3163
+ print_info "Everything continues to work as before - this is non-breaking."
3164
+ echo ""
3165
+
3166
+ read -r -p "Enable multi-tenant credential storage? (y/n): " enable_mt
3167
+ enable_mt=$(echo "$enable_mt" | tr '[:upper:]' '[:lower:]')
3168
+
3169
+ if [[ "$enable_mt" == "y" || "$enable_mt" == "yes" ]]; then
3170
+ bash "$credential_helper" init
3171
+ print_success "Multi-tenant credential storage enabled"
3172
+ echo ""
3173
+ print_info "Quick start:"
3174
+ echo " credential-helper.sh create client-name # Create a tenant"
3175
+ echo " credential-helper.sh switch client-name # Switch active tenant"
3176
+ echo " credential-helper.sh set KEY val --tenant X # Add key to tenant"
3177
+ echo " credential-helper.sh status # Show current state"
3178
+ else
3179
+ print_info "Skipped. Enable later: credential-helper.sh init"
3180
+ fi
3181
+ else
3182
+ print_info "No existing credentials found. Multi-tenant available when needed."
3183
+ print_info "Enable later: credential-helper.sh init"
3184
+ fi
3185
+
3186
+ return 0
3187
+ }
3188
+
2997
3189
  # Check for tool updates after setup
2998
3190
  check_tool_updates() {
2999
3191
  print_info "Checking for tool updates..."
@@ -3139,6 +3331,7 @@ main() {
3139
3331
  confirm_step "Extract OpenCode prompts" && extract_opencode_prompts
3140
3332
  confirm_step "Check OpenCode prompt drift" && check_opencode_prompt_drift
3141
3333
  confirm_step "Deploy aidevops agents to ~/.aidevops/agents/" && deploy_aidevops_agents
3334
+ confirm_step "Setup multi-tenant credential storage" && setup_multi_tenant_credentials
3142
3335
  confirm_step "Generate agent skills (SKILL.md files)" && generate_agent_skills
3143
3336
  confirm_step "Create symlinks for imported skills" && create_skill_symlinks
3144
3337
  confirm_step "Check for skill updates from upstream" && check_skill_updates