aidevops 3.13.95 → 3.14.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.
- package/README.md +0 -1
- package/VERSION +1 -1
- package/aidevops.sh +44 -26
- package/package.json +1 -1
- package/setup.sh +25 -21
- package/aidevops-init-lib.sh +0 -1411
- package/aidevops-repos-lib.sh +0 -700
- package/aidevops-skills-plugin-lib.sh +0 -697
- package/aidevops-status-lib.sh +0 -141
- package/aidevops-update-lib.sh +0 -512
- package/aidevops-upgrade-planning-lib.sh +0 -370
- package/setup-modules/agent-deploy.sh +0 -1035
- package/setup-modules/agent-runtime.sh +0 -287
- package/setup-modules/config.sh +0 -478
- package/setup-modules/core.sh +0 -736
- package/setup-modules/mcp-setup.sh +0 -947
- package/setup-modules/migrations.sh +0 -1688
- package/setup-modules/plugins.sh +0 -728
- package/setup-modules/post-setup.sh +0 -301
- package/setup-modules/schedulers-linux.sh +0 -386
- package/setup-modules/schedulers-platform.sh +0 -1072
- package/setup-modules/schedulers-pulse.sh +0 -978
- package/setup-modules/schedulers.sh +0 -565
- package/setup-modules/shell-env.sh +0 -1240
- package/setup-modules/tool-beads.sh +0 -324
- package/setup-modules/tool-install.sh +0 -2134
|
@@ -1,947 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env bash
|
|
2
|
-
# SPDX-License-Identifier: MIT
|
|
3
|
-
# SPDX-FileCopyrightText: 2025-2026 Marcus Quinn
|
|
4
|
-
# MCP setup functions: install_mcp_packages, resolve_mcp_binary, localwp, seo, analytics, quickfile, browser-tools, opencode-plugins
|
|
5
|
-
# Part of aidevops setup.sh modularization (t316.3)
|
|
6
|
-
|
|
7
|
-
# Shell safety baseline
|
|
8
|
-
set -Eeuo pipefail
|
|
9
|
-
IFS=$'\n\t'
|
|
10
|
-
# shellcheck disable=SC2154 # rc is assigned by $? in the trap string
|
|
11
|
-
trap 'rc=$?; echo "[ERROR] ${BASH_SOURCE[0]}:${LINENO} exit $rc" >&2' ERR
|
|
12
|
-
shopt -s inherit_errexit 2>/dev/null || true
|
|
13
|
-
|
|
14
|
-
_install_mcp_packages_node() {
|
|
15
|
-
# Install/update Node.js MCP packages globally.
|
|
16
|
-
# Security note: MCP servers run as persistent processes with access to conversation
|
|
17
|
-
# context, credentials, and network. The packages below are from known/vetted sources.
|
|
18
|
-
# Before adding new MCP packages to this list, verify the source repository and scan
|
|
19
|
-
# dependencies with: npx @socketsecurity/cli npm info <package>
|
|
20
|
-
# See: .agents/tools/mcp-toolkit/mcporter.md "Security Considerations"
|
|
21
|
-
local -a node_mcps=(
|
|
22
|
-
"chrome-devtools-mcp"
|
|
23
|
-
"mcp-server-gsc"
|
|
24
|
-
"playwriter"
|
|
25
|
-
"@steipete/macos-automator-mcp"
|
|
26
|
-
"@steipete/claude-code-mcp"
|
|
27
|
-
)
|
|
28
|
-
|
|
29
|
-
local installer="npm"
|
|
30
|
-
command -v bun &>/dev/null && installer="bun"
|
|
31
|
-
print_info "Using $installer to install/update Node.js MCP packages..."
|
|
32
|
-
|
|
33
|
-
# Always install latest (bun install -g is fast and idempotent)
|
|
34
|
-
local updated=0
|
|
35
|
-
local failed=0
|
|
36
|
-
local pkg
|
|
37
|
-
for pkg in "${node_mcps[@]}"; do
|
|
38
|
-
local short_name="${pkg##*/}" # Strip @scope/ prefix for display
|
|
39
|
-
if run_with_spinner "Installing $short_name" npm_global_install "${pkg}@latest"; then
|
|
40
|
-
((++updated))
|
|
41
|
-
else
|
|
42
|
-
((++failed))
|
|
43
|
-
print_warning "Failed to install/update $pkg"
|
|
44
|
-
fi
|
|
45
|
-
done
|
|
46
|
-
|
|
47
|
-
if [[ $updated -gt 0 ]]; then
|
|
48
|
-
print_success "$updated Node.js MCP packages installed/updated to latest via $installer"
|
|
49
|
-
fi
|
|
50
|
-
if [[ $failed -gt 0 ]]; then
|
|
51
|
-
print_warning "$failed packages failed (check network or package names)"
|
|
52
|
-
fi
|
|
53
|
-
return 0
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
_install_mcp_packages_python() {
|
|
57
|
-
# Install/update Python MCP packages via pipx and uv.
|
|
58
|
-
if command -v pipx &>/dev/null; then
|
|
59
|
-
print_info "Installing/updating analytics-mcp via pipx..."
|
|
60
|
-
if command -v analytics-mcp &>/dev/null; then
|
|
61
|
-
pipx upgrade analytics-mcp >/dev/null 2>&1 || true
|
|
62
|
-
else
|
|
63
|
-
pipx install analytics-mcp >/dev/null 2>&1 || print_warning "Failed to install analytics-mcp"
|
|
64
|
-
fi
|
|
65
|
-
fi
|
|
66
|
-
|
|
67
|
-
if command -v uv &>/dev/null && uv tool --help &>/dev/null; then
|
|
68
|
-
print_info "Installing/updating outscraper-mcp-server via uv..."
|
|
69
|
-
if command -v outscraper-mcp-server &>/dev/null; then
|
|
70
|
-
uv tool upgrade outscraper-mcp-server >/dev/null 2>&1 || true
|
|
71
|
-
else
|
|
72
|
-
uv tool install outscraper-mcp-server >/dev/null 2>&1 || print_warning "Failed to install outscraper-mcp-server"
|
|
73
|
-
fi
|
|
74
|
-
elif command -v uv &>/dev/null; then
|
|
75
|
-
print_warning "uv is installed but too old to support 'tool' subcommand — skipping outscraper-mcp-server"
|
|
76
|
-
print_info "Update uv with: curl -LsSf https://astral.sh/uv/install.sh | sh"
|
|
77
|
-
fi
|
|
78
|
-
return 0
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
install_mcp_packages() {
|
|
82
|
-
# Check prerequisites before announcing setup (GH#5240)
|
|
83
|
-
if ! command -v bun &>/dev/null && ! command -v npm &>/dev/null; then
|
|
84
|
-
print_skip "MCP packages" "neither bun nor npm found" "Install bun: brew install oven-sh/bun/bun (or npm via Node.js)"
|
|
85
|
-
setup_track_deferred "MCP packages" "Install bun or npm"
|
|
86
|
-
return 0
|
|
87
|
-
fi
|
|
88
|
-
|
|
89
|
-
print_info "Installing MCP server packages globally (eliminates npx startup delay)..."
|
|
90
|
-
|
|
91
|
-
_install_mcp_packages_node
|
|
92
|
-
_install_mcp_packages_python
|
|
93
|
-
|
|
94
|
-
# Update opencode.json with resolved full paths for all MCP binaries
|
|
95
|
-
update_mcp_paths_in_opencode
|
|
96
|
-
|
|
97
|
-
print_info "MCP servers will start instantly (no registry lookups on each launch)"
|
|
98
|
-
return 0
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
resolve_mcp_binary_path() {
|
|
102
|
-
local bin_name
|
|
103
|
-
bin_name="$1"
|
|
104
|
-
local resolved=""
|
|
105
|
-
|
|
106
|
-
# Check common locations in priority order
|
|
107
|
-
local search_paths=(
|
|
108
|
-
"$HOME/.bun/bin/$bin_name"
|
|
109
|
-
"/opt/homebrew/bin/$bin_name"
|
|
110
|
-
"/usr/local/bin/$bin_name"
|
|
111
|
-
"$HOME/.local/bin/$bin_name"
|
|
112
|
-
"$HOME/.npm-global/bin/$bin_name"
|
|
113
|
-
)
|
|
114
|
-
|
|
115
|
-
for path in "${search_paths[@]}"; do
|
|
116
|
-
if [[ -x "$path" ]]; then
|
|
117
|
-
resolved="$path"
|
|
118
|
-
break
|
|
119
|
-
fi
|
|
120
|
-
done
|
|
121
|
-
|
|
122
|
-
# Fallback: use command -v if in PATH (portable, POSIX-compliant)
|
|
123
|
-
if [[ -z "$resolved" ]]; then
|
|
124
|
-
resolved=$(command -v "$bin_name" 2>/dev/null || true)
|
|
125
|
-
fi
|
|
126
|
-
|
|
127
|
-
echo "$resolved"
|
|
128
|
-
return 0
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
_update_mcp_paths_resolve_local_cmds() {
|
|
132
|
-
# Resolve local MCP command binaries to full paths. Prints update count.
|
|
133
|
-
local tmp_config="$1"
|
|
134
|
-
local updated=0
|
|
135
|
-
|
|
136
|
-
local mcp_keys
|
|
137
|
-
mcp_keys=$(jq -r '.mcp | to_entries[] | select(.value.type == "local") | select(.value.command != null) | .key' "$tmp_config" 2>/dev/null)
|
|
138
|
-
|
|
139
|
-
while IFS= read -r mcp_key; do
|
|
140
|
-
[[ -z "$mcp_key" ]] && continue
|
|
141
|
-
|
|
142
|
-
local current_cmd
|
|
143
|
-
current_cmd=$(jq -r --arg k "$mcp_key" '.mcp[$k].command[0]' "$tmp_config" 2>/dev/null)
|
|
144
|
-
|
|
145
|
-
# Skip if already a full path
|
|
146
|
-
if [[ "$current_cmd" == /* ]]; then
|
|
147
|
-
# Verify the path still exists; resolve stale paths
|
|
148
|
-
if [[ ! -x "$current_cmd" ]]; then
|
|
149
|
-
local bin_name
|
|
150
|
-
bin_name=$(basename "$current_cmd")
|
|
151
|
-
local new_path
|
|
152
|
-
new_path=$(resolve_mcp_binary_path "$bin_name")
|
|
153
|
-
if [[ -n "$new_path" && "$new_path" != "$current_cmd" ]]; then
|
|
154
|
-
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"
|
|
155
|
-
((++updated))
|
|
156
|
-
fi
|
|
157
|
-
fi
|
|
158
|
-
continue
|
|
159
|
-
fi
|
|
160
|
-
|
|
161
|
-
# Skip docker (container runtime) and node (resolved separately)
|
|
162
|
-
case "$current_cmd" in
|
|
163
|
-
docker | node) continue ;;
|
|
164
|
-
esac
|
|
165
|
-
|
|
166
|
-
local full_path
|
|
167
|
-
full_path=$(resolve_mcp_binary_path "$current_cmd")
|
|
168
|
-
|
|
169
|
-
if [[ -n "$full_path" && "$full_path" != "$current_cmd" ]]; then
|
|
170
|
-
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"
|
|
171
|
-
((++updated))
|
|
172
|
-
fi
|
|
173
|
-
done <<<"$mcp_keys"
|
|
174
|
-
|
|
175
|
-
echo "$updated"
|
|
176
|
-
return 0
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
_update_mcp_paths_resolve_node_cmds() {
|
|
180
|
-
# Resolve 'node' commands to full path (e.g., quickfile, amazon-order-history).
|
|
181
|
-
# These use ["node", "/path/to/index.js"] — node itself should be resolved.
|
|
182
|
-
# Prints update count.
|
|
183
|
-
local tmp_config="$1"
|
|
184
|
-
local updated=0
|
|
185
|
-
|
|
186
|
-
local node_path
|
|
187
|
-
node_path=$(resolve_mcp_binary_path "node")
|
|
188
|
-
if [[ -n "$node_path" ]]; then
|
|
189
|
-
local node_mcp_keys
|
|
190
|
-
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)
|
|
191
|
-
local mcp_key
|
|
192
|
-
while IFS= read -r mcp_key; do
|
|
193
|
-
[[ -z "$mcp_key" ]] && continue
|
|
194
|
-
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"
|
|
195
|
-
((++updated))
|
|
196
|
-
done <<<"$node_mcp_keys"
|
|
197
|
-
fi
|
|
198
|
-
|
|
199
|
-
echo "$updated"
|
|
200
|
-
return 0
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
update_mcp_paths_in_opencode() {
|
|
204
|
-
local opencode_config
|
|
205
|
-
opencode_config=$(find_opencode_config) || return 0
|
|
206
|
-
|
|
207
|
-
if [[ ! -f "$opencode_config" ]]; then
|
|
208
|
-
return 0
|
|
209
|
-
fi
|
|
210
|
-
|
|
211
|
-
if ! command -v jq &>/dev/null; then
|
|
212
|
-
return 0
|
|
213
|
-
fi
|
|
214
|
-
|
|
215
|
-
local tmp_config
|
|
216
|
-
tmp_config=$(mktemp)
|
|
217
|
-
trap 'rm -f "${tmp_config:-}"' RETURN
|
|
218
|
-
cp "$opencode_config" "$tmp_config"
|
|
219
|
-
|
|
220
|
-
local updated=0
|
|
221
|
-
local local_count node_count
|
|
222
|
-
local_count=$(_update_mcp_paths_resolve_local_cmds "$tmp_config")
|
|
223
|
-
node_count=$(_update_mcp_paths_resolve_node_cmds "$tmp_config")
|
|
224
|
-
updated=$((local_count + node_count))
|
|
225
|
-
|
|
226
|
-
if [[ $updated -gt 0 ]]; then
|
|
227
|
-
create_backup_with_rotation "$opencode_config" "opencode"
|
|
228
|
-
mv "$tmp_config" "$opencode_config"
|
|
229
|
-
print_success "Updated $updated MCP commands to use full binary paths in opencode.json"
|
|
230
|
-
else
|
|
231
|
-
rm -f "$tmp_config"
|
|
232
|
-
fi
|
|
233
|
-
|
|
234
|
-
return 0
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
setup_localwp_mcp() {
|
|
238
|
-
# Check prerequisites before announcing setup (GH#5240)
|
|
239
|
-
local localwp_found=false
|
|
240
|
-
if [[ -d "/Applications/Local.app" ]] || [[ -d "$HOME/Applications/Local.app" ]]; then
|
|
241
|
-
localwp_found=true
|
|
242
|
-
fi
|
|
243
|
-
|
|
244
|
-
if [[ "$localwp_found" != "true" ]]; then
|
|
245
|
-
print_skip "LocalWP MCP" "LocalWP not installed" "Install from https://localwp.com/ then re-run setup"
|
|
246
|
-
setup_track_skipped "LocalWP MCP" "LocalWP not installed"
|
|
247
|
-
return 0
|
|
248
|
-
fi
|
|
249
|
-
|
|
250
|
-
if ! command -v npm &>/dev/null; then
|
|
251
|
-
print_skip "LocalWP MCP" "npm not found" "Install Node.js and npm first"
|
|
252
|
-
setup_track_deferred "LocalWP MCP" "Install Node.js/npm, then re-run setup"
|
|
253
|
-
return 0
|
|
254
|
-
fi
|
|
255
|
-
|
|
256
|
-
# Prerequisites met — proceed with setup
|
|
257
|
-
print_info "Setting up LocalWP MCP server..."
|
|
258
|
-
|
|
259
|
-
if command -v mcp-local-wp &>/dev/null; then
|
|
260
|
-
print_success "LocalWP MCP server already installed"
|
|
261
|
-
setup_track_configured "LocalWP MCP"
|
|
262
|
-
return 0
|
|
263
|
-
fi
|
|
264
|
-
|
|
265
|
-
print_info "LocalWP MCP server enables AI assistants to query WordPress databases"
|
|
266
|
-
setup_prompt install_mcp "Install LocalWP MCP server (@verygoodplugins/mcp-local-wp)? [Y/n]: " "Y"
|
|
267
|
-
|
|
268
|
-
if [[ "$install_mcp" =~ ^[Yy]?$ ]]; then
|
|
269
|
-
if run_with_spinner "Installing LocalWP MCP server" npm_global_install "@verygoodplugins/mcp-local-wp"; then
|
|
270
|
-
print_info "Start with: ~/.aidevops/agents/scripts/localhost-helper.sh start-mcp"
|
|
271
|
-
print_info "Or configure in OpenCode MCP settings for auto-start"
|
|
272
|
-
setup_track_configured "LocalWP MCP"
|
|
273
|
-
else
|
|
274
|
-
print_info "Try manually: sudo npm install -g @verygoodplugins/mcp-local-wp"
|
|
275
|
-
fi
|
|
276
|
-
else
|
|
277
|
-
print_info "Skipped LocalWP MCP server installation"
|
|
278
|
-
print_info "Install later: npm install -g @verygoodplugins/mcp-local-wp"
|
|
279
|
-
fi
|
|
280
|
-
|
|
281
|
-
return 0
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
_setup_browser_tools_install_bun() {
|
|
285
|
-
# Install Bun and add it to all shell rc files. Sets has_bun in caller scope.
|
|
286
|
-
print_info "Installing Bun (required for dev-browser)..."
|
|
287
|
-
if ! verified_install "Bun" "https://bun.sh/install"; then
|
|
288
|
-
print_warning "Bun installation failed - dev-browser will need manual setup"
|
|
289
|
-
return 0
|
|
290
|
-
fi
|
|
291
|
-
|
|
292
|
-
# Source the updated PATH
|
|
293
|
-
export BUN_INSTALL="$HOME/.bun"
|
|
294
|
-
export PATH="$BUN_INSTALL/bin:$PATH"
|
|
295
|
-
if ! command -v bun &>/dev/null; then
|
|
296
|
-
return 0
|
|
297
|
-
fi
|
|
298
|
-
|
|
299
|
-
print_success "Bun installed: $(bun --version)"
|
|
300
|
-
|
|
301
|
-
# Bun's installer may only write to the running shell's rc file.
|
|
302
|
-
# Ensure Bun PATH is in all shell rc files for cross-shell compat.
|
|
303
|
-
# shellcheck disable=SC2016 # written to rc files; must expand at shell startup, not now
|
|
304
|
-
local bun_path_line='export BUN_INSTALL="$HOME/.bun"'
|
|
305
|
-
# shellcheck disable=SC2016 # written to rc files; must expand at shell startup, not now
|
|
306
|
-
local bun_export_line='export PATH="$BUN_INSTALL/bin:$PATH"'
|
|
307
|
-
local bun_rc
|
|
308
|
-
while IFS= read -r bun_rc; do
|
|
309
|
-
[[ -z "$bun_rc" ]] && continue
|
|
310
|
-
[[ ! -f "$bun_rc" ]] && touch "$bun_rc"
|
|
311
|
-
if ! grep -q '\.bun' "$bun_rc" 2>/dev/null; then
|
|
312
|
-
{
|
|
313
|
-
echo ""
|
|
314
|
-
echo "# Bun (added by aidevops setup)"
|
|
315
|
-
echo "$bun_path_line"
|
|
316
|
-
echo "$bun_export_line"
|
|
317
|
-
} >>"$bun_rc"
|
|
318
|
-
print_info "Added Bun to PATH in $bun_rc"
|
|
319
|
-
fi
|
|
320
|
-
done < <(get_all_shell_rcs)
|
|
321
|
-
return 0
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
_setup_browser_tools_dev_browser() {
|
|
325
|
-
# Install dev-browser (stateful browser automation) using Bun.
|
|
326
|
-
local dev_browser_dir="$HOME/.aidevops/dev-browser"
|
|
327
|
-
|
|
328
|
-
if [[ -d "${dev_browser_dir}/skills/dev-browser" ]]; then
|
|
329
|
-
print_success "dev-browser already installed"
|
|
330
|
-
return 0
|
|
331
|
-
fi
|
|
332
|
-
|
|
333
|
-
print_info "Installing dev-browser (stateful browser automation)..."
|
|
334
|
-
local dev_browser_output
|
|
335
|
-
if dev_browser_output=$(bash "$HOME/.aidevops/agents/scripts/dev-browser-helper.sh" setup 2>&1); then
|
|
336
|
-
print_success "dev-browser installed"
|
|
337
|
-
print_info "Start server with: bash ~/.aidevops/agents/scripts/dev-browser-helper.sh start"
|
|
338
|
-
else
|
|
339
|
-
print_warning "dev-browser setup failed:"
|
|
340
|
-
# Show last few lines of error output for debugging
|
|
341
|
-
echo "$dev_browser_output" | tail -5 | sed 's/^/ /'
|
|
342
|
-
echo ""
|
|
343
|
-
print_info "Run manually to see full output:"
|
|
344
|
-
print_info " bash ~/.aidevops/agents/scripts/dev-browser-helper.sh setup"
|
|
345
|
-
fi
|
|
346
|
-
return 0
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
_setup_browser_tools_playwright() {
|
|
350
|
-
# Install Playwright MCP browsers (chromium, firefox, webkit).
|
|
351
|
-
print_info "Setting up Playwright MCP..."
|
|
352
|
-
|
|
353
|
-
# Check if Playwright browsers are installed (--no-install prevents auto-download)
|
|
354
|
-
if npx --no-install playwright --version &>/dev/null 2>&1; then
|
|
355
|
-
print_success "Playwright already installed"
|
|
356
|
-
print_info "Playwright MCP runs via: npx playwright-mcp@latest"
|
|
357
|
-
return 0
|
|
358
|
-
fi
|
|
359
|
-
|
|
360
|
-
local install_playwright
|
|
361
|
-
setup_prompt install_playwright "Install Playwright MCP with browsers (chromium, firefox, webkit)? [Y/n]: " "Y"
|
|
362
|
-
|
|
363
|
-
if [[ "$install_playwright" =~ ^[Yy]?$ ]]; then
|
|
364
|
-
print_info "Installing Playwright browsers..."
|
|
365
|
-
# Use -y to auto-confirm npx install, suppress the "install without dependencies" warning
|
|
366
|
-
# Use PIPESTATUS to check npx exit code, not grep's exit code
|
|
367
|
-
npx -y playwright@latest install 2>&1 | grep -v "WARNING: It looks like you are running"
|
|
368
|
-
if [[ ${PIPESTATUS[0]} -eq 0 ]]; then
|
|
369
|
-
print_success "Playwright browsers installed"
|
|
370
|
-
else
|
|
371
|
-
print_warning "Playwright browser installation failed"
|
|
372
|
-
print_info "Run manually: npx -y playwright@latest install"
|
|
373
|
-
fi
|
|
374
|
-
else
|
|
375
|
-
print_info "Skipped Playwright installation"
|
|
376
|
-
print_info "Install later with: npx playwright install"
|
|
377
|
-
fi
|
|
378
|
-
|
|
379
|
-
print_info "Playwright MCP runs via: npx playwright-mcp@latest"
|
|
380
|
-
return 0
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
setup_browser_tools() {
|
|
384
|
-
print_info "Setting up browser automation tools..."
|
|
385
|
-
|
|
386
|
-
local has_bun=false
|
|
387
|
-
local has_node=false
|
|
388
|
-
|
|
389
|
-
# Check Bun
|
|
390
|
-
if command -v bun &>/dev/null; then
|
|
391
|
-
has_bun=true
|
|
392
|
-
print_success "Bun $(bun --version) found"
|
|
393
|
-
fi
|
|
394
|
-
|
|
395
|
-
# Check Node.js (for Playwriter / Playwright)
|
|
396
|
-
if command -v node &>/dev/null; then
|
|
397
|
-
has_node=true
|
|
398
|
-
fi
|
|
399
|
-
|
|
400
|
-
# Install Bun if not present (required for dev-browser)
|
|
401
|
-
if [[ "$has_bun" == "false" ]]; then
|
|
402
|
-
_setup_browser_tools_install_bun
|
|
403
|
-
command -v bun &>/dev/null && has_bun=true
|
|
404
|
-
fi
|
|
405
|
-
|
|
406
|
-
# Setup dev-browser if Bun is available
|
|
407
|
-
[[ "$has_bun" == "true" ]] && _setup_browser_tools_dev_browser
|
|
408
|
-
|
|
409
|
-
# Playwriter MCP (Node.js based, runs via npx)
|
|
410
|
-
if [[ "$has_node" == "true" ]]; then
|
|
411
|
-
print_success "Playwriter MCP available (runs via npx playwriter@latest)"
|
|
412
|
-
print_info "Install Chrome extension: https://chromewebstore.google.com/detail/playwriter-mcp/jfeammnjpkecdekppnclgkkffahnhfhe"
|
|
413
|
-
else
|
|
414
|
-
print_warning "Node.js not found - Playwriter MCP unavailable"
|
|
415
|
-
fi
|
|
416
|
-
|
|
417
|
-
# Playwright MCP (cross-browser testing automation)
|
|
418
|
-
[[ "$has_node" == "true" ]] && _setup_browser_tools_playwright
|
|
419
|
-
|
|
420
|
-
if [[ "$has_node" == "true" ]]; then
|
|
421
|
-
print_info "Browser tools: dev-browser (stateful), Playwriter (extension), Playwright (testing), Stagehand (AI)"
|
|
422
|
-
else
|
|
423
|
-
print_info "Browser tools: dev-browser (stateful), Stagehand (AI)"
|
|
424
|
-
fi
|
|
425
|
-
return 0
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
_setup_opencode_plugins_remove_cursor_oauth() {
|
|
429
|
-
# Remove the broken opencode-cursor-oauth plugin if present.
|
|
430
|
-
# The opencode-cursor-oauth npm plugin crashes during startup and
|
|
431
|
-
# silently prevents ALL plugins from loading (including ours).
|
|
432
|
-
# Filed: https://github.com/ephraimduncan/opencode-cursor/issues/15
|
|
433
|
-
# Re-enable when the upstream fix is released.
|
|
434
|
-
local opencode_config="$1"
|
|
435
|
-
local cursor_plugin="opencode-cursor-oauth"
|
|
436
|
-
|
|
437
|
-
local cursor_present
|
|
438
|
-
cursor_present=$(jq --arg p "$cursor_plugin" \
|
|
439
|
-
'(.plugin // []) | map(select(. == $p)) | length' \
|
|
440
|
-
"$opencode_config" 2>/dev/null || echo "0")
|
|
441
|
-
if [[ "$cursor_present" -gt 0 ]]; then
|
|
442
|
-
local tmp_cursor="${opencode_config}.tmp.$$"
|
|
443
|
-
if jq --arg p "$cursor_plugin" \
|
|
444
|
-
'.plugin = [.plugin[] | select(. != $p)]' \
|
|
445
|
-
"$opencode_config" >"$tmp_cursor" 2>/dev/null; then
|
|
446
|
-
mv "$tmp_cursor" "$opencode_config"
|
|
447
|
-
print_warning "Removed opencode-cursor-oauth plugin (crashes all plugin loading)"
|
|
448
|
-
print_info " Filed: https://github.com/ephraimduncan/opencode-cursor/issues/15"
|
|
449
|
-
else
|
|
450
|
-
rm -f "$tmp_cursor"
|
|
451
|
-
print_warning "Failed to remove opencode-cursor-oauth plugin from opencode.json (file: $opencode_config)"
|
|
452
|
-
fi
|
|
453
|
-
fi
|
|
454
|
-
return 0
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
_setup_opencode_plugins_register_file_url() {
|
|
458
|
-
# Mechanism 1: register aidevops plugin via file:// URL in opencode.json.
|
|
459
|
-
# Also removes the broken opencode-cursor-oauth plugin if present.
|
|
460
|
-
# Prints "true" or "false" to indicate registration status.
|
|
461
|
-
local opencode_config="$1"
|
|
462
|
-
local aidevops_plugin_entrypoint="$2"
|
|
463
|
-
local plugin_url="file://${aidevops_plugin_entrypoint}"
|
|
464
|
-
|
|
465
|
-
if ! command -v jq &>/dev/null; then
|
|
466
|
-
print_info "jq not installed — cannot update opencode.json plugin array"
|
|
467
|
-
echo "false"
|
|
468
|
-
return 0
|
|
469
|
-
fi
|
|
470
|
-
|
|
471
|
-
# Check if the plugin URL is already in the array
|
|
472
|
-
local already_registered
|
|
473
|
-
already_registered=$(jq --arg url "$plugin_url" \
|
|
474
|
-
'(.plugin // []) | map(select(. == $url)) | length' \
|
|
475
|
-
"$opencode_config" || echo "0")
|
|
476
|
-
|
|
477
|
-
if [[ "$already_registered" -eq 0 ]]; then
|
|
478
|
-
local tmp_config="${opencode_config}.tmp.$$"
|
|
479
|
-
if jq --arg url "$plugin_url" \
|
|
480
|
-
'.plugin = ((.plugin // []) + [$url] | unique)' \
|
|
481
|
-
"$opencode_config" >"$tmp_config"; then
|
|
482
|
-
mv "$tmp_config" "$opencode_config"
|
|
483
|
-
print_success "aidevops plugin registered in opencode.json"
|
|
484
|
-
else
|
|
485
|
-
rm -f "$tmp_config"
|
|
486
|
-
print_warning "Failed to update opencode.json plugin array (file: $opencode_config)"
|
|
487
|
-
fi
|
|
488
|
-
else
|
|
489
|
-
print_success "aidevops plugin already registered in opencode.json"
|
|
490
|
-
fi
|
|
491
|
-
|
|
492
|
-
_setup_opencode_plugins_remove_cursor_oauth "$opencode_config"
|
|
493
|
-
|
|
494
|
-
echo "true"
|
|
495
|
-
return 0
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
_setup_opencode_plugins_register_symlink() {
|
|
499
|
-
# Mechanism 2: symlink in ~/.config/opencode/plugins/ (belt-and-suspenders).
|
|
500
|
-
local plugins_dir="$1"
|
|
501
|
-
local aidevops_plugin_src="$2"
|
|
502
|
-
local aidevops_plugin_dst="$3"
|
|
503
|
-
|
|
504
|
-
mkdir -p "$plugins_dir"
|
|
505
|
-
if [[ -L "$aidevops_plugin_dst" ]]; then
|
|
506
|
-
if [[ ! -e "$aidevops_plugin_dst" ]]; then
|
|
507
|
-
print_warning "Broken aidevops plugin symlink detected; recreating"
|
|
508
|
-
ln -sfn "$aidevops_plugin_src" "$aidevops_plugin_dst"
|
|
509
|
-
fi
|
|
510
|
-
elif [[ ! -d "$aidevops_plugin_dst" ]]; then
|
|
511
|
-
ln -sfn "$aidevops_plugin_src" "$aidevops_plugin_dst"
|
|
512
|
-
fi
|
|
513
|
-
return 0
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
_setup_opencode_plugins_print_pool_guidance() {
|
|
517
|
-
# Print OAuth pool authentication instructions for OpenCode v1.2.30+.
|
|
518
|
-
local pool_plugin_registered="$1"
|
|
519
|
-
|
|
520
|
-
if [[ "$pool_plugin_registered" == "true" ]]; then
|
|
521
|
-
print_info "Use the aidevops OAuth pool (provided by the aidevops plugin above):"
|
|
522
|
-
print_info " 1. Run: opencode auth login"
|
|
523
|
-
print_info " 2. Select: 'Anthropic Pool' (added by aidevops plugin)"
|
|
524
|
-
print_info " 3. Enter your Claude account email"
|
|
525
|
-
print_info " 4. Complete the OAuth flow in your browser"
|
|
526
|
-
print_info " 5. Repeat to add more accounts for automatic rotation"
|
|
527
|
-
print_info " 6. Switch to 'Anthropic' provider and select a model to start chatting"
|
|
528
|
-
print_info ""
|
|
529
|
-
print_info "For Cursor Pro accounts:"
|
|
530
|
-
print_info " Run: opencode auth login --provider cursor"
|
|
531
|
-
print_info ""
|
|
532
|
-
print_info " Health check: /models-pool-check"
|
|
533
|
-
print_info " Manage accounts: /model-accounts-pool list|status|remove"
|
|
534
|
-
else
|
|
535
|
-
print_warning "aidevops OpenCode plugin was not registered; 'Anthropic Pool' may be unavailable"
|
|
536
|
-
print_info "Re-run aidevops setup to register the plugin, then run: opencode auth login"
|
|
537
|
-
fi
|
|
538
|
-
return 0
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
_setup_opencode_plugins_auth_guidance() {
|
|
542
|
-
# Print version-appropriate authentication instructions.
|
|
543
|
-
# Note: opencode-anthropic-auth is built into OpenCode v1.1.36+
|
|
544
|
-
# Adding it as an external plugin causes TypeError due to double-loading.
|
|
545
|
-
# Removed in v2.90.0 - see PR #230.
|
|
546
|
-
local pool_plugin_registered="$1"
|
|
547
|
-
|
|
548
|
-
# Detect OpenCode version to give appropriate auth guidance (t1546, GH#5312)
|
|
549
|
-
# v1.2.30+ removes the built-in anthropic-auth plugin entirely.
|
|
550
|
-
local oc_raw_version
|
|
551
|
-
oc_raw_version=$(opencode --version 2>/dev/null | head -1 | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1 || echo "0.0.0")
|
|
552
|
-
|
|
553
|
-
local oc_major oc_minor oc_patch
|
|
554
|
-
IFS='.' read -r oc_major oc_minor oc_patch <<<"$oc_raw_version"
|
|
555
|
-
oc_major="${oc_major:-0}"
|
|
556
|
-
oc_minor="${oc_minor:-0}"
|
|
557
|
-
oc_patch="${oc_patch:-0}"
|
|
558
|
-
|
|
559
|
-
# Compare against 1.2.30 (where built-in anthropic-auth was removed)
|
|
560
|
-
local builtin_auth_removed="false"
|
|
561
|
-
if [[ "$oc_major" -gt 1 ]] ||
|
|
562
|
-
[[ "$oc_major" -eq 1 && "$oc_minor" -gt 2 ]] ||
|
|
563
|
-
[[ "$oc_major" -eq 1 && "$oc_minor" -eq 2 && "$oc_patch" -ge 30 ]]; then
|
|
564
|
-
builtin_auth_removed="true"
|
|
565
|
-
fi
|
|
566
|
-
|
|
567
|
-
if [[ "$builtin_auth_removed" == "true" ]]; then
|
|
568
|
-
print_info "OpenCode v${oc_raw_version}: built-in Anthropic OAuth removed in v1.2.30"
|
|
569
|
-
_setup_opencode_plugins_print_pool_guidance "$pool_plugin_registered"
|
|
570
|
-
else
|
|
571
|
-
print_info "After setup, authenticate with: opencode auth login"
|
|
572
|
-
print_info " - For Claude OAuth (v1.1.36-v1.2.29): Select 'Anthropic' -> 'Claude Pro/Max' (built-in)"
|
|
573
|
-
print_info " - Or use the aidevops OAuth pool: Select 'Anthropic Pool' for multi-account rotation"
|
|
574
|
-
fi
|
|
575
|
-
return 0
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
setup_opencode_plugins() {
|
|
579
|
-
# Check prerequisites before announcing setup (GH#5240)
|
|
580
|
-
if ! command -v opencode &>/dev/null; then
|
|
581
|
-
print_skip "OpenCode plugins" "OpenCode not installed" "Install from https://opencode.ai"
|
|
582
|
-
setup_track_skipped "OpenCode plugins" "OpenCode not installed"
|
|
583
|
-
return 0
|
|
584
|
-
fi
|
|
585
|
-
|
|
586
|
-
# Prerequisites met — proceed with setup
|
|
587
|
-
print_info "Setting up OpenCode plugins..."
|
|
588
|
-
|
|
589
|
-
# Register aidevops plugin using two complementary mechanisms:
|
|
590
|
-
# 1. file:// URL in opencode.json "plugin" array (works on all tested versions)
|
|
591
|
-
# 2. Symlink in ~/.config/opencode/plugins/ (newer OpenCode convention)
|
|
592
|
-
# Both are idempotent — the plugin's registerPoolProvider() checks before adding.
|
|
593
|
-
local plugins_dir="$HOME/.config/opencode/plugins"
|
|
594
|
-
local aidevops_plugin_src="$HOME/.aidevops/agents/plugins/opencode-aidevops"
|
|
595
|
-
local aidevops_plugin_dst="$plugins_dir/opencode-aidevops"
|
|
596
|
-
local aidevops_plugin_entrypoint="$aidevops_plugin_src/index.mjs"
|
|
597
|
-
|
|
598
|
-
if [[ ! -f "$aidevops_plugin_entrypoint" ]]; then
|
|
599
|
-
print_skip "OpenCode plugins" "aidevops plugin entry point not found: $aidevops_plugin_entrypoint"
|
|
600
|
-
setup_track_deferred "OpenCode plugins" "Install/restore aidevops plugin at $aidevops_plugin_entrypoint"
|
|
601
|
-
return 0
|
|
602
|
-
fi
|
|
603
|
-
|
|
604
|
-
# Mechanism 1: file:// URL in opencode.json
|
|
605
|
-
local pool_plugin_registered="false"
|
|
606
|
-
local opencode_config
|
|
607
|
-
if opencode_config=$(find_opencode_config); then
|
|
608
|
-
pool_plugin_registered=$(_setup_opencode_plugins_register_file_url "$opencode_config" "$aidevops_plugin_entrypoint")
|
|
609
|
-
else
|
|
610
|
-
print_info "opencode.json not found — run 'opencode' once to create it, then re-run setup"
|
|
611
|
-
fi
|
|
612
|
-
|
|
613
|
-
# Mechanism 2: symlink in plugins directory
|
|
614
|
-
_setup_opencode_plugins_register_symlink "$plugins_dir" "$aidevops_plugin_src" "$aidevops_plugin_dst"
|
|
615
|
-
|
|
616
|
-
setup_track_configured "OpenCode plugins"
|
|
617
|
-
|
|
618
|
-
# Version-appropriate auth guidance
|
|
619
|
-
_setup_opencode_plugins_auth_guidance "$pool_plugin_registered"
|
|
620
|
-
|
|
621
|
-
return 0
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
setup_seo_mcps() {
|
|
625
|
-
print_info "Setting up SEO integrations..."
|
|
626
|
-
|
|
627
|
-
# SEO services use curl-based subagents (no MCP needed)
|
|
628
|
-
# Subagents: serper.md, dataforseo.md, ahrefs.md, google-search-console.md
|
|
629
|
-
print_info "SEO uses curl-based subagents (zero context cost until invoked)"
|
|
630
|
-
|
|
631
|
-
# Check if credentials are configured
|
|
632
|
-
if [[ -f "$HOME/.config/aidevops/credentials.sh" ]]; then
|
|
633
|
-
# shellcheck source=/dev/null
|
|
634
|
-
source "$HOME/.config/aidevops/credentials.sh"
|
|
635
|
-
|
|
636
|
-
if [[ -n "${DATAFORSEO_USERNAME:-}" ]]; then
|
|
637
|
-
print_success "DataForSEO credentials configured"
|
|
638
|
-
else
|
|
639
|
-
print_info "DataForSEO: set DATAFORSEO_USERNAME and DATAFORSEO_PASSWORD in credentials.sh"
|
|
640
|
-
fi
|
|
641
|
-
|
|
642
|
-
if [[ -n "${SERPER_API_KEY:-}" ]]; then
|
|
643
|
-
print_success "Serper API key configured"
|
|
644
|
-
else
|
|
645
|
-
print_info "Serper: set SERPER_API_KEY in credentials.sh"
|
|
646
|
-
fi
|
|
647
|
-
|
|
648
|
-
if [[ -n "${AHREFS_API_KEY:-}" ]]; then
|
|
649
|
-
print_success "Ahrefs API key configured"
|
|
650
|
-
else
|
|
651
|
-
print_info "Ahrefs: set AHREFS_API_KEY in credentials.sh"
|
|
652
|
-
fi
|
|
653
|
-
else
|
|
654
|
-
print_info "Configure SEO API credentials in ~/.config/aidevops/credentials.sh"
|
|
655
|
-
fi
|
|
656
|
-
|
|
657
|
-
# GSC uses MCP (OAuth2 complexity warrants it)
|
|
658
|
-
local gsc_creds="$HOME/.config/aidevops/gsc-credentials.json"
|
|
659
|
-
if [[ -f "$gsc_creds" ]]; then
|
|
660
|
-
print_success "Google Search Console credentials configured"
|
|
661
|
-
else
|
|
662
|
-
print_info "GSC: Create service account JSON at $gsc_creds"
|
|
663
|
-
print_info " See: ~/.aidevops/agents/seo/google-search-console.md"
|
|
664
|
-
fi
|
|
665
|
-
|
|
666
|
-
print_info "SEO documentation: ~/.aidevops/agents/seo/"
|
|
667
|
-
return 0
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
_setup_google_analytics_mcp_detect_creds() {
|
|
671
|
-
# Detect GSC credentials and print three lines: creds_path, project_id, enable_mcp.
|
|
672
|
-
local gsc_creds="$1"
|
|
673
|
-
local creds_path=""
|
|
674
|
-
local project_id=""
|
|
675
|
-
local enable_mcp="false"
|
|
676
|
-
|
|
677
|
-
if [[ -f "$gsc_creds" ]]; then
|
|
678
|
-
creds_path="$gsc_creds"
|
|
679
|
-
project_id=$(jq -r '.project_id // empty' "$gsc_creds" 2>/dev/null)
|
|
680
|
-
if [[ -n "$project_id" ]]; then
|
|
681
|
-
enable_mcp="true"
|
|
682
|
-
print_success "Found GSC credentials - sharing with Google Analytics MCP"
|
|
683
|
-
print_info "Project: $project_id"
|
|
684
|
-
fi
|
|
685
|
-
fi
|
|
686
|
-
|
|
687
|
-
printf '%s\n%s\n%s\n' "$creds_path" "$project_id" "$enable_mcp"
|
|
688
|
-
return 0
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
_setup_google_analytics_mcp_update_existing() {
|
|
692
|
-
# Update an existing google-analytics-mcp entry in opencode.json.
|
|
693
|
-
local opencode_config="$1"
|
|
694
|
-
local creds_path="$2"
|
|
695
|
-
local project_id="$3"
|
|
696
|
-
local enable_mcp="$4"
|
|
697
|
-
|
|
698
|
-
if [[ "$enable_mcp" == "true" ]]; then
|
|
699
|
-
local tmp_config
|
|
700
|
-
tmp_config=$(mktemp)
|
|
701
|
-
trap 'rm -f "${tmp_config:-}"' RETURN
|
|
702
|
-
if jq --arg creds "$creds_path" --arg proj "$project_id" \
|
|
703
|
-
'.mcp["google-analytics-mcp"].environment.GOOGLE_APPLICATION_CREDENTIALS = $creds |
|
|
704
|
-
.mcp["google-analytics-mcp"].environment.GOOGLE_PROJECT_ID = $proj |
|
|
705
|
-
.mcp["google-analytics-mcp"].enabled = true' \
|
|
706
|
-
"$opencode_config" >"$tmp_config" 2>/dev/null; then
|
|
707
|
-
mv "$tmp_config" "$opencode_config"
|
|
708
|
-
print_success "Updated Google Analytics MCP with GSC credentials (enabled)"
|
|
709
|
-
else
|
|
710
|
-
rm -f "$tmp_config"
|
|
711
|
-
print_warning "Failed to update Google Analytics MCP config"
|
|
712
|
-
fi
|
|
713
|
-
else
|
|
714
|
-
print_info "Google Analytics MCP already configured in OpenCode"
|
|
715
|
-
fi
|
|
716
|
-
return 0
|
|
717
|
-
}
|
|
718
|
-
|
|
719
|
-
_setup_google_analytics_mcp_add_new() {
|
|
720
|
-
# Add a new google-analytics-mcp entry to opencode.json.
|
|
721
|
-
local opencode_config="$1"
|
|
722
|
-
local creds_path="$2"
|
|
723
|
-
local project_id="$3"
|
|
724
|
-
local enable_mcp="$4"
|
|
725
|
-
local gsc_creds="$5"
|
|
726
|
-
|
|
727
|
-
local tmp_config
|
|
728
|
-
tmp_config=$(mktemp)
|
|
729
|
-
trap 'rm -f "${tmp_config:-}"' RETURN
|
|
730
|
-
|
|
731
|
-
if jq --arg creds "$creds_path" --arg proj "$project_id" --argjson enabled "$enable_mcp" \
|
|
732
|
-
'.mcp["google-analytics-mcp"] = {
|
|
733
|
-
"type": "local",
|
|
734
|
-
"command": ["analytics-mcp"],
|
|
735
|
-
"environment": {
|
|
736
|
-
"GOOGLE_APPLICATION_CREDENTIALS": $creds,
|
|
737
|
-
"GOOGLE_PROJECT_ID": $proj
|
|
738
|
-
},
|
|
739
|
-
"enabled": $enabled
|
|
740
|
-
}' "$opencode_config" >"$tmp_config" 2>/dev/null; then
|
|
741
|
-
mv "$tmp_config" "$opencode_config"
|
|
742
|
-
if [[ "$enable_mcp" == "true" ]]; then
|
|
743
|
-
print_success "Added Google Analytics MCP to OpenCode (enabled with GSC credentials)"
|
|
744
|
-
else
|
|
745
|
-
print_success "Added Google Analytics MCP to OpenCode (disabled - no credentials found)"
|
|
746
|
-
print_info "To enable: Create service account JSON at $gsc_creds"
|
|
747
|
-
fi
|
|
748
|
-
print_info "Or use the google-analytics subagent which enables it automatically"
|
|
749
|
-
else
|
|
750
|
-
rm -f "$tmp_config"
|
|
751
|
-
print_warning "Failed to add Google Analytics MCP to config"
|
|
752
|
-
fi
|
|
753
|
-
return 0
|
|
754
|
-
}
|
|
755
|
-
|
|
756
|
-
_setup_google_analytics_mcp_write_config() {
|
|
757
|
-
# Update or add the google-analytics-mcp entry in opencode.json.
|
|
758
|
-
local opencode_config="$1"
|
|
759
|
-
local creds_path="$2"
|
|
760
|
-
local project_id="$3"
|
|
761
|
-
local enable_mcp="$4"
|
|
762
|
-
local gsc_creds="$5"
|
|
763
|
-
|
|
764
|
-
# Update existing entry if present
|
|
765
|
-
if jq -e '.mcp["google-analytics-mcp"]' "$opencode_config" >/dev/null 2>&1; then
|
|
766
|
-
_setup_google_analytics_mcp_update_existing "$opencode_config" "$creds_path" "$project_id" "$enable_mcp"
|
|
767
|
-
return 0
|
|
768
|
-
fi
|
|
769
|
-
|
|
770
|
-
# Add new entry
|
|
771
|
-
_setup_google_analytics_mcp_add_new "$opencode_config" "$creds_path" "$project_id" "$enable_mcp" "$gsc_creds"
|
|
772
|
-
return 0
|
|
773
|
-
}
|
|
774
|
-
|
|
775
|
-
setup_google_analytics_mcp() {
|
|
776
|
-
local gsc_creds="$HOME/.config/aidevops/gsc-credentials.json"
|
|
777
|
-
|
|
778
|
-
# Check prerequisites before announcing setup (GH#5240)
|
|
779
|
-
local opencode_config
|
|
780
|
-
if ! opencode_config=$(find_opencode_config); then
|
|
781
|
-
print_skip "Google Analytics MCP" "OpenCode config not found" "Run 'opencode' once to create config, then re-run setup"
|
|
782
|
-
setup_track_skipped "Google Analytics MCP" "OpenCode config not found"
|
|
783
|
-
return 0
|
|
784
|
-
fi
|
|
785
|
-
|
|
786
|
-
if ! command -v jq &>/dev/null; then
|
|
787
|
-
print_skip "Google Analytics MCP" "jq not installed" "Install jq: brew install jq (macOS) or apt install jq"
|
|
788
|
-
setup_track_deferred "Google Analytics MCP" "Install jq"
|
|
789
|
-
return 0
|
|
790
|
-
fi
|
|
791
|
-
|
|
792
|
-
if ! command -v pipx &>/dev/null; then
|
|
793
|
-
print_skip "Google Analytics MCP" "pipx not installed" "Install pipx: brew install pipx (macOS) or pip install pipx"
|
|
794
|
-
setup_track_deferred "Google Analytics MCP" "Install pipx"
|
|
795
|
-
return 0
|
|
796
|
-
fi
|
|
797
|
-
|
|
798
|
-
# Prerequisites met — proceed with setup
|
|
799
|
-
print_info "Setting up Google Analytics MCP..."
|
|
800
|
-
|
|
801
|
-
# Auto-detect credentials from shared GSC service account
|
|
802
|
-
local creds_output
|
|
803
|
-
creds_output=$(_setup_google_analytics_mcp_detect_creds "$gsc_creds")
|
|
804
|
-
local creds_path project_id enable_mcp
|
|
805
|
-
creds_path=$(printf '%s\n' "$creds_output" | sed -n '1p')
|
|
806
|
-
project_id=$(printf '%s\n' "$creds_output" | sed -n '2p')
|
|
807
|
-
enable_mcp=$(printf '%s\n' "$creds_output" | sed -n '3p')
|
|
808
|
-
|
|
809
|
-
# Update or add config entry
|
|
810
|
-
_setup_google_analytics_mcp_write_config "$opencode_config" "$creds_path" "$project_id" "$enable_mcp" "$gsc_creds"
|
|
811
|
-
|
|
812
|
-
# Show setup instructions
|
|
813
|
-
print_info "Google Analytics MCP setup:"
|
|
814
|
-
print_info " 1. Enable Google Analytics Admin & Data APIs in Google Cloud Console"
|
|
815
|
-
print_info " 2. Configure ADC: gcloud auth application-default login --scopes https://www.googleapis.com/auth/analytics.readonly,https://www.googleapis.com/auth/cloud-platform"
|
|
816
|
-
print_info " 3. Update GOOGLE_APPLICATION_CREDENTIALS path in opencode.json"
|
|
817
|
-
print_info " 4. Set GOOGLE_PROJECT_ID in opencode.json"
|
|
818
|
-
print_info "Documentation: ~/.aidevops/agents/services/analytics/google-analytics.md"
|
|
819
|
-
|
|
820
|
-
return 0
|
|
821
|
-
}
|
|
822
|
-
|
|
823
|
-
_setup_quickfile_mcp_clone_and_build() {
|
|
824
|
-
# Clone and build the QuickFile MCP server. Returns 1 if user skips or build fails.
|
|
825
|
-
local quickfile_dir="$1"
|
|
826
|
-
|
|
827
|
-
if [[ -f "$quickfile_dir/dist/index.js" ]]; then
|
|
828
|
-
print_success "QuickFile MCP already installed at $quickfile_dir"
|
|
829
|
-
return 0
|
|
830
|
-
fi
|
|
831
|
-
|
|
832
|
-
print_info "QuickFile MCP provides AI access to UK accounting (invoices, clients, reports)"
|
|
833
|
-
local install_qf
|
|
834
|
-
setup_prompt install_qf "Clone and build QuickFile MCP server? [Y/n]: " "Y"
|
|
835
|
-
|
|
836
|
-
if [[ ! "$install_qf" =~ ^[Yy]?$ ]]; then
|
|
837
|
-
print_info "Skipped QuickFile MCP installation"
|
|
838
|
-
print_info "Install later: git clone https://github.com/marcusquinn/quickfile-mcp.git ~/Git/quickfile-mcp"
|
|
839
|
-
return 1
|
|
840
|
-
fi
|
|
841
|
-
|
|
842
|
-
if [[ ! -d "$quickfile_dir" ]]; then
|
|
843
|
-
if ! run_with_spinner "Cloning quickfile-mcp" git clone https://github.com/marcusquinn/quickfile-mcp.git "$quickfile_dir"; then
|
|
844
|
-
print_warning "Failed to clone quickfile-mcp"
|
|
845
|
-
return 1
|
|
846
|
-
fi
|
|
847
|
-
print_success "Cloned quickfile-mcp"
|
|
848
|
-
fi
|
|
849
|
-
|
|
850
|
-
if ! run_with_spinner "Installing dependencies" npm install --prefix "$quickfile_dir"; then
|
|
851
|
-
print_warning "npm install failed - try manually: cd $quickfile_dir && npm install"
|
|
852
|
-
return 1
|
|
853
|
-
fi
|
|
854
|
-
|
|
855
|
-
if ! run_with_spinner "Building QuickFile MCP" npm run build --prefix "$quickfile_dir"; then
|
|
856
|
-
print_warning "Build failed - try manually: cd $quickfile_dir && npm run build"
|
|
857
|
-
return 1
|
|
858
|
-
fi
|
|
859
|
-
|
|
860
|
-
print_success "QuickFile MCP built successfully"
|
|
861
|
-
return 0
|
|
862
|
-
}
|
|
863
|
-
|
|
864
|
-
_setup_quickfile_mcp_check_credentials() {
|
|
865
|
-
# Check and display QuickFile credential status.
|
|
866
|
-
local credentials_dir="$1"
|
|
867
|
-
local credentials_file="$2"
|
|
868
|
-
|
|
869
|
-
if [[ -f "$credentials_file" ]]; then
|
|
870
|
-
print_success "QuickFile credentials configured at $credentials_file"
|
|
871
|
-
else
|
|
872
|
-
print_info "QuickFile credentials not found"
|
|
873
|
-
print_info "Create credentials:"
|
|
874
|
-
print_info " mkdir -p $credentials_dir && chmod 700 $credentials_dir"
|
|
875
|
-
print_info " Create $credentials_file with:"
|
|
876
|
-
print_info " accountNumber: from QuickFile dashboard (top-right)"
|
|
877
|
-
print_info " apiKey: Account Settings > 3rd Party Integrations > API Key"
|
|
878
|
-
print_info " applicationId: Account Settings > Create a QuickFile App"
|
|
879
|
-
fi
|
|
880
|
-
return 0
|
|
881
|
-
}
|
|
882
|
-
|
|
883
|
-
_setup_quickfile_mcp_update_opencode() {
|
|
884
|
-
# Add QuickFile MCP entry to OpenCode config if not already present.
|
|
885
|
-
local quickfile_dir="$1"
|
|
886
|
-
|
|
887
|
-
local opencode_config
|
|
888
|
-
if ! opencode_config=$(find_opencode_config); then
|
|
889
|
-
return 0
|
|
890
|
-
fi
|
|
891
|
-
|
|
892
|
-
local quickfile_entry
|
|
893
|
-
quickfile_entry=$(jq -r '.mcp.quickfile // empty' "$opencode_config" 2>/dev/null)
|
|
894
|
-
|
|
895
|
-
if [[ -n "$quickfile_entry" ]]; then
|
|
896
|
-
print_success "QuickFile MCP already in OpenCode config"
|
|
897
|
-
return 0
|
|
898
|
-
fi
|
|
899
|
-
|
|
900
|
-
print_info "Adding QuickFile MCP to OpenCode config..."
|
|
901
|
-
local node_path
|
|
902
|
-
node_path=$(resolve_mcp_binary_path "node")
|
|
903
|
-
[[ -z "$node_path" ]] && node_path="node"
|
|
904
|
-
|
|
905
|
-
local tmp_config
|
|
906
|
-
tmp_config=$(mktemp)
|
|
907
|
-
trap 'rm -f "${tmp_config:-}"' RETURN
|
|
908
|
-
|
|
909
|
-
if jq --arg np "$node_path" --arg dp "$quickfile_dir/dist/index.js" \
|
|
910
|
-
'.mcp.quickfile = {"type": "local", "command": [$np, $dp], "enabled": true}' \
|
|
911
|
-
"$opencode_config" >"$tmp_config" 2>/dev/null; then
|
|
912
|
-
create_backup_with_rotation "$opencode_config" "opencode"
|
|
913
|
-
mv "$tmp_config" "$opencode_config"
|
|
914
|
-
print_success "QuickFile MCP added to OpenCode config"
|
|
915
|
-
else
|
|
916
|
-
rm -f "$tmp_config"
|
|
917
|
-
print_warning "Failed to update OpenCode config - add manually"
|
|
918
|
-
fi
|
|
919
|
-
return 0
|
|
920
|
-
}
|
|
921
|
-
|
|
922
|
-
setup_quickfile_mcp() {
|
|
923
|
-
local quickfile_dir="$HOME/Git/quickfile-mcp"
|
|
924
|
-
local credentials_dir="$HOME/.config/.quickfile-mcp"
|
|
925
|
-
local credentials_file="$credentials_dir/credentials.json"
|
|
926
|
-
|
|
927
|
-
# Check prerequisites before announcing setup (GH#5240)
|
|
928
|
-
if ! command -v node &>/dev/null; then
|
|
929
|
-
print_skip "QuickFile MCP" "Node.js not installed" "Install Node.js 18+: brew install node (macOS) or nvm install 18"
|
|
930
|
-
setup_track_deferred "QuickFile MCP" "Install Node.js 18+"
|
|
931
|
-
return 0
|
|
932
|
-
fi
|
|
933
|
-
|
|
934
|
-
# Prerequisites met — proceed with setup
|
|
935
|
-
print_info "Setting up QuickFile MCP server..."
|
|
936
|
-
|
|
937
|
-
# Clone and build (returns 1 if skipped or failed)
|
|
938
|
-
if ! _setup_quickfile_mcp_clone_and_build "$quickfile_dir"; then
|
|
939
|
-
return 0
|
|
940
|
-
fi
|
|
941
|
-
|
|
942
|
-
_setup_quickfile_mcp_check_credentials "$credentials_dir" "$credentials_file"
|
|
943
|
-
_setup_quickfile_mcp_update_opencode "$quickfile_dir"
|
|
944
|
-
|
|
945
|
-
print_info "Documentation: ~/.aidevops/agents/services/accounting/quickfile.md"
|
|
946
|
-
return 0
|
|
947
|
-
}
|