@nicotinetool/o7-cli 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.
@@ -0,0 +1,297 @@
1
+ #!/usr/bin/env bash
2
+ # ============================================================
3
+ # O7 OpenClaw macOS Installer
4
+ # One-click setup for the Optimum7 team
5
+ # Usage: bash install.sh
6
+ # ============================================================
7
+
8
+ set -euo pipefail
9
+ INSTALLER_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
10
+
11
+ # ── Profile isolation ────────────────────────────────────────
12
+ PROFILE="default"
13
+ while [[ $# -gt 0 ]]; do
14
+ case "$1" in
15
+ --profile) PROFILE="$2"; shift 2 ;;
16
+ *) shift ;;
17
+ esac
18
+ done
19
+ if [[ "$PROFILE" != "default" ]]; then
20
+ export OPENCLAW_HOME="${HOME}/.openclaw-${PROFILE}"
21
+ mkdir -p "$OPENCLAW_HOME"
22
+ fi
23
+
24
+ source "${INSTALLER_DIR}/lib/ui.sh"
25
+ source "${INSTALLER_DIR}/lib/checks.sh"
26
+ source "${INSTALLER_DIR}/lib/validate.sh"
27
+ source "${INSTALLER_DIR}/lib/auth.sh"
28
+ source "${INSTALLER_DIR}/lib/antigravity.sh"
29
+
30
+ # State file for resuming partial installs
31
+ STATE_FILE="${HOME}/.openclaw/.install-state"
32
+ mkdir -p "$(dirname "$STATE_FILE")"
33
+
34
+ state_done() { echo "$1" >> "$STATE_FILE"; }
35
+ state_check() { grep -q "^$1$" "$STATE_FILE" 2>/dev/null; }
36
+
37
+ # ── PHASE 1: Welcome ─────────────────────────────────────────
38
+ banner
39
+
40
+ echo -e " This installer will set up ${BOLD}OpenClaw + Mission Control${RESET} on your Mac."
41
+ echo -e " It handles everything: dependencies, AI model login, services, and health checks."
42
+ echo -e " Takes about ${BOLD}5-10 minutes${RESET} on a good connection."
43
+ echo
44
+
45
+ if [[ -f "$STATE_FILE" ]]; then
46
+ warn "Found a previous install attempt. Resuming from where it left off."
47
+ info "Delete ${STATE_FILE} to start fresh."
48
+ echo
49
+ fi
50
+
51
+ confirm "Ready to start?" || { echo "Aborted."; exit 0; }
52
+ log "=== INSTALL STARTED $(date) ==="
53
+
54
+ # ── PHASE 2: System Checks ───────────────────────────────────
55
+ if ! state_check "checks"; then
56
+ run_all_checks || exit 1
57
+ state_done "checks"
58
+ else
59
+ ok "System checks already passed — skipping"
60
+ fi
61
+
62
+ # ── PHASE 3: Install OpenClaw ────────────────────────────────
63
+ section 2 "Install OpenClaw"
64
+
65
+ if ! state_check "openclaw"; then
66
+ if command -v openclaw &>/dev/null; then
67
+ local_ver=$(openclaw --version 2>/dev/null | awk '{print $1}')
68
+ ok "OpenClaw already installed (${local_ver})"
69
+ info "Checking for updates..."
70
+ npm install -g openclaw@latest &>/dev/null &
71
+ spin $! "Updating OpenClaw..."
72
+ wait $!
73
+ else
74
+ info "Installing OpenClaw..."
75
+ npm install -g openclaw@latest &
76
+ spin $! "Installing OpenClaw (this may take a minute)..."
77
+ wait $!
78
+ if ! command -v openclaw &>/dev/null; then
79
+ fail "OpenClaw install failed."
80
+ heal_install_error "npm install -g openclaw@latest failed"
81
+ exit 1
82
+ fi
83
+ fi
84
+ ok "OpenClaw $(openclaw --version 2>/dev/null)"
85
+ log "OK: OpenClaw installed"
86
+ state_done "openclaw"
87
+ else
88
+ ok "OpenClaw already installed — skipping"
89
+ fi
90
+
91
+ # ── PHASE 4: AI Model Auth ───────────────────────────────────
92
+ if ! state_check "auth"; then
93
+ run_auth_setup || exit 1
94
+ state_done "auth"
95
+ else
96
+ ok "Auth already configured — skipping"
97
+ fi
98
+
99
+ # ── PHASE 5: Role Selection ──────────────────────────────────
100
+ section 4 "Your Role"
101
+ echo
102
+ echo -e " ${BOLD}What's your role at Optimum7?${RESET}"
103
+ echo -e " ${DIM}This pre-configures the right tools for you.${RESET}"
104
+ echo
105
+
106
+ ROLE_IDX=$(menu " Select your role:" \
107
+ "📧 Marketing / Email (Klaviyo, campaigns, flows)" \
108
+ "📈 PPC / Ads (Google Ads, Meta, reporting)" \
109
+ "💻 Developer (coding agents, GitHub, CI/CD)" \
110
+ "👑 Admin (full access, everything)" \
111
+ "🎛️ Custom (pick and choose later)")
112
+
113
+ ROLES=("marketing" "ppc" "dev" "admin" "custom")
114
+ SELECTED_ROLE="${ROLES[$ROLE_IDX]}"
115
+ ok "Role set: ${SELECTED_ROLE}"
116
+ log "ROLE: ${SELECTED_ROLE}"
117
+
118
+ # ── PHASE 6: OpenClaw Gateway Daemon ─────────────────────────
119
+ section 5 "OpenClaw Gateway"
120
+
121
+ if ! state_check "gateway"; then
122
+ info "Setting up OpenClaw gateway service (auto-starts on boot)..."
123
+
124
+ GATEWAY_TOKEN=$(openssl rand -base64 32 | tr -d /=+ | head -c 40)
125
+ GATEWAY_PORT=18789
126
+
127
+ openclaw onboard \
128
+ --non-interactive \
129
+ --accept-risk \
130
+ --install-daemon \
131
+ --gateway-auth token \
132
+ --gateway-token "$GATEWAY_TOKEN" \
133
+ --gateway-port "$GATEWAY_PORT" \
134
+ --gateway-bind loopback \
135
+ --flow quickstart \
136
+ 2>&1 | while read -r line; do
137
+ echo -e " ${DIM}${line}${RESET}"
138
+ done
139
+
140
+ sleep 3
141
+
142
+ if curl -s "http://localhost:${GATEWAY_PORT}/health" &>/dev/null; then
143
+ ok "OpenClaw gateway running on port ${GATEWAY_PORT}"
144
+ log "OK: Gateway started on ${GATEWAY_PORT}"
145
+ state_done "gateway"
146
+ else
147
+ warn "Gateway may still be starting up..."
148
+ heal_install_error "openclaw gateway not responding on port ${GATEWAY_PORT}"
149
+ fi
150
+ else
151
+ ok "Gateway already configured — skipping"
152
+ fi
153
+
154
+ # ── PHASE 7: Mission Control ──────────────────────────────────
155
+ section 6 "Mission Control Dashboard"
156
+
157
+ MC_DIR="${HOME}/projects/unified-mc"
158
+ MC_REPO="https://github.com/erenes1667/unified-mc.git"
159
+
160
+ if ! state_check "mc"; then
161
+ if [[ -d "$MC_DIR/.git" ]]; then
162
+ info "Mission Control already cloned. Pulling latest..."
163
+ git -C "$MC_DIR" pull --quiet &
164
+ spin $! "Updating Mission Control..."
165
+ wait $!
166
+ else
167
+ info "Cloning Mission Control..."
168
+ mkdir -p "$(dirname "$MC_DIR")"
169
+ git clone --quiet "$MC_REPO" "$MC_DIR" &
170
+ spin $! "Cloning Mission Control..."
171
+ wait $!
172
+ fi
173
+
174
+ if [[ ! -d "$MC_DIR" ]]; then
175
+ fail "Could not get Mission Control from ${MC_REPO}"
176
+ info "Ask Enes to share access to the repo."
177
+ log "FAIL: MC clone failed"
178
+ else
179
+ info "Installing dependencies..."
180
+ cd "$MC_DIR"
181
+ pnpm install --silent &
182
+ spin $! "Installing dependencies..."
183
+ wait $!
184
+
185
+ ok "Mission Control ready"
186
+ log "OK: Mission Control installed at ${MC_DIR}"
187
+ state_done "mc"
188
+ fi
189
+ else
190
+ ok "Mission Control already set up — skipping"
191
+ fi
192
+
193
+ # ── PHASE 8: Mission Control launchd Service ──────────────────
194
+ section 7 "Mission Control Service"
195
+
196
+ if ! state_check "mc-daemon" && [[ -d "$MC_DIR" ]]; then
197
+ MC_PLIST="${HOME}/Library/LaunchAgents/com.o7.mission-control.plist"
198
+ NODE_BIN=$(which node)
199
+ NODE_DIR=$(dirname "$NODE_BIN")
200
+
201
+ cat > "$MC_PLIST" << PLIST
202
+ <?xml version="1.0" encoding="UTF-8"?>
203
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
204
+ <plist version="1.0">
205
+ <dict>
206
+ <key>Label</key>
207
+ <string>com.o7.mission-control</string>
208
+ <key>WorkingDirectory</key>
209
+ <string>${MC_DIR}</string>
210
+ <key>ProgramArguments</key>
211
+ <array>
212
+ <string>${NODE_BIN}</string>
213
+ <string>node_modules/next/dist/bin/next</string>
214
+ <string>dev</string>
215
+ <string>--hostname</string>
216
+ <string>127.0.0.1</string>
217
+ </array>
218
+ <key>EnvironmentVariables</key>
219
+ <dict>
220
+ <key>PATH</key>
221
+ <string>${NODE_DIR}:/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
222
+ <key>NODE_ENV</key>
223
+ <string>development</string>
224
+ </dict>
225
+ <key>RunAtLoad</key>
226
+ <true/>
227
+ <key>KeepAlive</key>
228
+ <true/>
229
+ <key>StandardOutPath</key>
230
+ <string>/tmp/mission-control.log</string>
231
+ <key>StandardErrorPath</key>
232
+ <string>/tmp/mission-control.err</string>
233
+ </dict>
234
+ </plist>
235
+ PLIST
236
+
237
+ launchctl unload "$MC_PLIST" 2>/dev/null || true
238
+ launchctl load "$MC_PLIST"
239
+
240
+ sleep 5
241
+ if curl -s -o /dev/null -w "%{http_code}" http://localhost:3000 --max-time 10 | grep -qE "200|307"; then
242
+ ok "Mission Control running at http://localhost:3000"
243
+ log "OK: Mission Control daemon installed"
244
+ state_done "mc-daemon"
245
+ open "http://localhost:3000" 2>/dev/null || true
246
+ else
247
+ warn "Mission Control is starting (can take 30s on first boot)"
248
+ info "It will be available at http://localhost:3000 shortly"
249
+ log "WARN: MC not yet responding, but daemon installed"
250
+ state_done "mc-daemon"
251
+ fi
252
+ else
253
+ ok "Mission Control service already configured — skipping"
254
+ fi
255
+
256
+ # ── PHASE 9: Antigravity ──────────────────────────────────────
257
+ setup_antigravity
258
+
259
+ # ── PHASE 10: Summary ─────────────────────────────────────────
260
+ section 10 "Setup Complete"
261
+ echo
262
+ echo -e "${BOLD}${GREEN} 🎉 OpenClaw is installed and running!${RESET}"
263
+ echo
264
+ echo -e "${BOLD} What's set up:${RESET}"
265
+
266
+ # Auth results
267
+ for result in "${AUTH_RESULTS[@]}"; do
268
+ provider=$(echo "$result" | cut -d: -f1)
269
+ status=$(echo "$result" | cut -d: -f2)
270
+ case "$status" in
271
+ ok) summary_row " ✅ ${provider^}" "working" ;;
272
+ skipped) summary_row " ⏭️ ${provider^}" "skipped (set up later)" ;;
273
+ failed) summary_row " ❌ ${provider^}" "failed (needs attention)" ;;
274
+ esac
275
+ done
276
+
277
+ echo
278
+ echo -e "${BOLD} Services:${RESET}"
279
+ summary_row " 🦞 OpenClaw Gateway" "localhost:18789 (auto-start)"
280
+ summary_row " 📊 Mission Control" "http://localhost:3000 (auto-start)"
281
+ summary_row " 🛡️ Antigravity" "every 30 min (auto-heal)"
282
+ echo
283
+ echo -e "${BOLD} Quick links:${RESET}"
284
+ echo -e " ${CYAN}→ Mission Control:${RESET} http://localhost:3000"
285
+ echo -e " ${CYAN}→ Docs:${RESET} https://docs.openclaw.ai"
286
+ echo -e " ${CYAN}→ Discord:${RESET} https://discord.gg/clawd"
287
+ echo
288
+ echo -e "${BOLD} Useful commands:${RESET}"
289
+ echo -e " ${DIM}openclaw gateway status${RESET} — check gateway"
290
+ echo -e " ${DIM}openclaw configure${RESET} — add/fix auth profiles"
291
+ echo -e " ${DIM}openclaw agent --message 'hi'${RESET} — test your AI"
292
+ echo
293
+ echo -e "${DIM} Install log: ${HOME}/.openclaw/install.log${RESET}"
294
+ echo
295
+
296
+ rm -f "$STATE_FILE"
297
+ log "=== INSTALL COMPLETE $(date) role=${SELECTED_ROLE} ==="
@@ -0,0 +1,301 @@
1
+ #!/usr/bin/env bash
2
+ # Antigravity Standalone Health Checker
3
+ # Bundled with the installer — works on a fresh machine with just Node + OpenClaw.
4
+ # No external dependencies. No workspace assumptions.
5
+ # Checks the basics and fixes what it can. Calls Gemini for complex issues.
6
+
7
+ OPENCLAW_DIR="${HOME}/.openclaw"
8
+ ANTIGRAVITY_DIR="${OPENCLAW_DIR}/antigravity"
9
+ HEAL_LOG="${ANTIGRAVITY_DIR}/heal-history.jsonl"
10
+ HEALTH_LOG="${ANTIGRAVITY_DIR}/health.log"
11
+
12
+ mkdir -p "$ANTIGRAVITY_DIR"
13
+
14
+ # ─── Colors ────────────────────────────────────────────────────────────────────
15
+ if [[ -t 1 ]]; then
16
+ GREEN='\033[0;32m' RED='\033[0;31m' YELLOW='\033[1;33m'
17
+ CYAN='\033[0;36m' DIM='\033[2m' BOLD='\033[1m' RESET='\033[0m'
18
+ else
19
+ GREEN='' RED='' YELLOW='' CYAN='' DIM='' BOLD='' RESET=''
20
+ fi
21
+
22
+ ok() { echo -e "${GREEN}✅ $*${RESET}"; }
23
+ warn() { echo -e "${YELLOW}⚠️ $*${RESET}"; }
24
+ fail() { echo -e "${RED}❌ $*${RESET}"; }
25
+ info() { echo -e "${CYAN}ℹ️ $*${RESET}"; }
26
+
27
+ heal_log() {
28
+ echo "{\"ts\":\"$(date -u +%Y-%m-%dT%H:%M:%SZ)\",\"check\":\"$1\",\"status\":\"$2\",\"detail\":\"$3\"}" >> "$HEAL_LOG"
29
+ }
30
+
31
+ # ─── Health Checks (all self-contained) ───────────────────────────────────────
32
+
33
+ check_openclaw_installed() {
34
+ if command -v openclaw &>/dev/null; then
35
+ ok "OpenClaw installed: $(openclaw --version 2>/dev/null)"
36
+ heal_log "openclaw_installed" "ok" "$(openclaw --version 2>/dev/null)"
37
+ return 0
38
+ fi
39
+ fail "OpenClaw not found in PATH"
40
+ heal_log "openclaw_installed" "fail" "not in PATH"
41
+
42
+ # Auto-fix: try installing
43
+ if command -v npm &>/dev/null; then
44
+ warn "Attempting auto-fix: npm install -g openclaw@latest"
45
+ npm install -g openclaw@latest &>/dev/null
46
+ if command -v openclaw &>/dev/null; then
47
+ ok "Auto-fixed: OpenClaw installed"
48
+ heal_log "openclaw_installed" "healed" "npm install succeeded"
49
+ return 0
50
+ fi
51
+ fi
52
+ return 1
53
+ }
54
+
55
+ check_gateway_running() {
56
+ local port=${OPENCLAW_GATEWAY_PORT:-18789}
57
+ if curl -s --max-time 3 "http://localhost:${port}/health" &>/dev/null; then
58
+ ok "Gateway responding on port ${port}"
59
+ heal_log "gateway_running" "ok" "port ${port}"
60
+ return 0
61
+ fi
62
+
63
+ fail "Gateway not responding on port ${port}"
64
+ heal_log "gateway_running" "fail" "port ${port} no response"
65
+
66
+ # Auto-fix: try starting
67
+ warn "Attempting auto-fix: openclaw gateway start"
68
+ openclaw gateway start &>/dev/null &
69
+ sleep 5
70
+ if curl -s --max-time 3 "http://localhost:${port}/health" &>/dev/null; then
71
+ ok "Auto-fixed: Gateway started"
72
+ heal_log "gateway_running" "healed" "gateway start succeeded"
73
+ return 0
74
+ fi
75
+
76
+ # Check if launchd service exists
77
+ if launchctl list 2>/dev/null | grep -q "openclaw"; then
78
+ warn "Attempting: launchctl kickstart"
79
+ launchctl kickstart -k "gui/$(id -u)/com.openclaw.gateway" 2>/dev/null
80
+ sleep 5
81
+ if curl -s --max-time 3 "http://localhost:${port}/health" &>/dev/null; then
82
+ ok "Auto-fixed: Gateway kickstarted via launchd"
83
+ heal_log "gateway_running" "healed" "launchctl kickstart"
84
+ return 0
85
+ fi
86
+ fi
87
+ return 1
88
+ }
89
+
90
+ check_config_valid() {
91
+ local config="${OPENCLAW_DIR}/openclaw.json"
92
+ if [[ ! -f "$config" ]]; then
93
+ fail "No openclaw.json found at ${config}"
94
+ heal_log "config_valid" "fail" "file missing"
95
+ return 1
96
+ fi
97
+
98
+ # Check valid JSON
99
+ if node -e "JSON.parse(require('fs').readFileSync('${config}','utf8'))" 2>/dev/null; then
100
+ ok "openclaw.json is valid JSON"
101
+ heal_log "config_valid" "ok" ""
102
+ return 0
103
+ fi
104
+
105
+ fail "openclaw.json is invalid JSON"
106
+ heal_log "config_valid" "fail" "parse error"
107
+
108
+ # Auto-fix: try to find backup
109
+ if [[ -f "${config}.bak" ]]; then
110
+ warn "Restoring from backup: openclaw.json.bak"
111
+ cp "${config}.bak" "$config"
112
+ if node -e "JSON.parse(require('fs').readFileSync('${config}','utf8'))" 2>/dev/null; then
113
+ ok "Auto-fixed: restored from backup"
114
+ heal_log "config_valid" "healed" "restored .bak"
115
+ return 0
116
+ fi
117
+ fi
118
+ return 1
119
+ }
120
+
121
+ check_disk_space() {
122
+ local free_gb
123
+ if [[ "$(uname)" == "Darwin" ]]; then
124
+ free_gb=$(df -g / | tail -1 | awk '{print $4}')
125
+ else
126
+ free_gb=$(df -BG / | tail -1 | awk '{print $4}' | tr -d 'G')
127
+ fi
128
+
129
+ if (( free_gb < 1 )); then
130
+ fail "Critically low disk space: ${free_gb}GB free"
131
+ heal_log "disk_space" "fail" "${free_gb}GB"
132
+ return 1
133
+ elif (( free_gb < 3 )); then
134
+ warn "Low disk space: ${free_gb}GB free (recommend 5GB+)"
135
+ heal_log "disk_space" "warn" "${free_gb}GB"
136
+ return 0
137
+ fi
138
+ ok "Disk space: ${free_gb}GB free"
139
+ heal_log "disk_space" "ok" "${free_gb}GB"
140
+ }
141
+
142
+ check_node_version() {
143
+ if ! command -v node &>/dev/null; then
144
+ fail "Node.js not installed"
145
+ heal_log "node_version" "fail" "not installed"
146
+ return 1
147
+ fi
148
+ local ver major
149
+ ver=$(node -v | sed 's/v//')
150
+ major=$(echo "$ver" | cut -d. -f1)
151
+ if (( major < 22 )); then
152
+ warn "Node.js v${ver} (v22+ recommended)"
153
+ heal_log "node_version" "warn" "v${ver}"
154
+ return 0
155
+ fi
156
+ ok "Node.js v${ver}"
157
+ heal_log "node_version" "ok" "v${ver}"
158
+ }
159
+
160
+ check_auth_profiles() {
161
+ local config="${OPENCLAW_DIR}/openclaw.json"
162
+ [[ ! -f "$config" ]] && return 1
163
+
164
+ local profile_count
165
+ profile_count=$(node -e "
166
+ try {
167
+ const c = JSON.parse(require('fs').readFileSync('${config}','utf8'));
168
+ const profiles = c.auth?.profiles || {};
169
+ console.log(Object.keys(profiles).length);
170
+ } catch(e) { console.log(0); }
171
+ " 2>/dev/null)
172
+
173
+ if (( profile_count == 0 )); then
174
+ warn "No auth profiles configured. Run: openclaw configure"
175
+ heal_log "auth_profiles" "warn" "0 profiles"
176
+ return 0
177
+ fi
178
+ ok "${profile_count} auth profile(s) configured"
179
+ heal_log "auth_profiles" "ok" "${profile_count} profiles"
180
+ }
181
+
182
+ check_mission_control() {
183
+ if curl -s -o /dev/null --max-time 3 http://localhost:3000; then
184
+ ok "Mission Control responding on port 3000"
185
+ heal_log "mission_control" "ok" "port 3000"
186
+ return 0
187
+ fi
188
+
189
+ # Not critical, just warn
190
+ info "Mission Control not running (optional)"
191
+ heal_log "mission_control" "info" "not running"
192
+ return 0
193
+ }
194
+
195
+ # ─── Gemini-powered diagnosis (for complex issues) ────────────────────────────
196
+
197
+ diagnose_with_gemini() {
198
+ local error_desc="$1"
199
+ # Try loading from .env file
200
+ local gemini_key="${GEMINI_API_KEY:-}"
201
+ if [[ -z "$gemini_key" && -f "${ANTIGRAVITY_DIR}/.env" ]]; then
202
+ gemini_key=$(grep "^GEMINI_API_KEY=" "${ANTIGRAVITY_DIR}/.env" 2>/dev/null | cut -d= -f2-)
203
+ fi
204
+
205
+ if [[ -z "$gemini_key" ]]; then
206
+ info "Gemini API key not set. Skipping AI diagnosis."
207
+ info "Set GEMINI_API_KEY for smart auto-healing."
208
+ return 1
209
+ fi
210
+
211
+ info "Asking Gemini to diagnose: ${error_desc}"
212
+
213
+ local system_info
214
+ system_info=$(cat <<EOF
215
+ macOS: $(sw_vers -productVersion 2>/dev/null || echo "unknown")
216
+ Node: $(node -v 2>/dev/null || echo "not installed")
217
+ OpenClaw: $(openclaw --version 2>/dev/null || echo "not installed")
218
+ Disk free: $(df -g / 2>/dev/null | tail -1 | awk '{print $4}')GB
219
+ Gateway port: ${OPENCLAW_GATEWAY_PORT:-18789}
220
+ Config exists: $(test -f ~/.openclaw/openclaw.json && echo "yes" || echo "no")
221
+ EOF
222
+ )
223
+
224
+ local payload
225
+ payload=$(node -e "
226
+ console.log(JSON.stringify({
227
+ contents: [{
228
+ parts: [{
229
+ text: 'You are a system admin for OpenClaw (AI assistant platform). Diagnose this issue and suggest a specific shell command to fix it. Be concise. One command if possible.\n\nSystem info:\n${system_info}\n\nError: ${error_desc}\n\nRespond with just the fix command, nothing else. If no command can fix it, say MANUAL: and explain in one sentence.'
230
+ }]
231
+ }]
232
+ }));
233
+ " 2>/dev/null)
234
+
235
+ local response
236
+ response=$(curl -s --max-time 15 \
237
+ "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-preview-05-20:generateContent?key=${gemini_key}" \
238
+ -H "Content-Type: application/json" \
239
+ -d "$payload" 2>/dev/null)
240
+
241
+ local fix
242
+ fix=$(node -e "
243
+ try {
244
+ const r = JSON.parse('$(echo "$response" | sed "s/'/\\\\'/g")');
245
+ console.log(r.candidates?.[0]?.content?.parts?.[0]?.text || 'MANUAL: Could not parse response');
246
+ } catch(e) { console.log('MANUAL: API error'); }
247
+ " 2>/dev/null)
248
+
249
+ if [[ "$fix" == MANUAL:* ]]; then
250
+ info "${fix#MANUAL: }"
251
+ heal_log "gemini_diagnosis" "manual" "$fix"
252
+ return 1
253
+ fi
254
+
255
+ warn "Gemini suggests: ${fix}"
256
+ heal_log "gemini_diagnosis" "suggestion" "$fix"
257
+ echo "$fix"
258
+ }
259
+
260
+ # ─── Main Entry Points ────────────────────────────────────────────────────────
261
+
262
+ run_health_check() {
263
+ echo -e "\n${BOLD}🛡️ Antigravity Health Check${RESET}"
264
+ echo -e "${DIM}$(date)${RESET}\n"
265
+
266
+ local issues=0
267
+ check_node_version || ((issues++))
268
+ check_openclaw_installed || ((issues++))
269
+ check_config_valid || ((issues++))
270
+ check_gateway_running || ((issues++))
271
+ check_auth_profiles
272
+ check_disk_space || ((issues++))
273
+ check_mission_control
274
+
275
+ echo
276
+ if (( issues == 0 )); then
277
+ ok "All health checks passed"
278
+ echo "$(date -u +%Y-%m-%dT%H:%M:%SZ) HEALTH_OK" >> "$HEALTH_LOG"
279
+ else
280
+ warn "${issues} issue(s) found (auto-fix attempted where possible)"
281
+ echo "$(date -u +%Y-%m-%dT%H:%M:%SZ) HEALTH_ISSUES=${issues}" >> "$HEALTH_LOG"
282
+ fi
283
+ return $issues
284
+ }
285
+
286
+ run_heal() {
287
+ local error_desc="$1"
288
+ if [[ -z "$error_desc" ]]; then
289
+ echo "Usage: antigravity-standalone.sh heal 'description of the problem'"
290
+ return 1
291
+ fi
292
+ diagnose_with_gemini "$error_desc"
293
+ }
294
+
295
+ # ─── CLI ───────────────────────────────────────────────────────────────────────
296
+
297
+ case "${1:-health}" in
298
+ health) run_health_check ;;
299
+ heal) run_heal "$2" ;;
300
+ *) echo "Usage: $0 {health|heal 'error description'}" ;;
301
+ esac