@conversionpros/aiva 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +148 -0
- package/auto-deploy.js +190 -0
- package/bin/aiva.js +81 -0
- package/cli-sync.js +126 -0
- package/d2a-prompt-template.txt +106 -0
- package/diagnostics-api.js +304 -0
- package/docs/ara-dedup-fix-scope.md +112 -0
- package/docs/ara-fix-round2-scope.md +61 -0
- package/docs/ara-greeting-fix-scope.md +70 -0
- package/docs/calendar-date-fix-scope.md +28 -0
- package/docs/getting-started.md +115 -0
- package/docs/network-architecture-rollout-scope.md +43 -0
- package/docs/scope-google-oauth-integration.md +351 -0
- package/docs/settings-page-scope.md +50 -0
- package/docs/xai-imagine-scope.md +116 -0
- package/docs/xai-voice-integration-scope.md +115 -0
- package/docs/xai-voice-tools-scope.md +165 -0
- package/email-router.js +512 -0
- package/follow-up-handler.js +606 -0
- package/gateway-monitor.js +158 -0
- package/google-email.js +379 -0
- package/google-oauth.js +310 -0
- package/grok-imagine.js +97 -0
- package/health-reporter.js +287 -0
- package/invisible-prefix-base.txt +206 -0
- package/invisible-prefix-owner.txt +26 -0
- package/invisible-prefix-slim.txt +10 -0
- package/invisible-prefix.txt +43 -0
- package/knowledge-base.js +472 -0
- package/lib/cli.js +19 -0
- package/lib/config.js +124 -0
- package/lib/health.js +57 -0
- package/lib/process.js +207 -0
- package/lib/server.js +42 -0
- package/lib/setup.js +472 -0
- package/meta-capi.js +206 -0
- package/meta-leads.js +411 -0
- package/notion-oauth.js +323 -0
- package/package.json +61 -0
- package/public/agent-config.html +241 -0
- package/public/aiva-avatar-anime.png +0 -0
- package/public/css/docs.css.bak +688 -0
- package/public/css/onboarding.css +543 -0
- package/public/diagrams/claude-subscription-pool.html +329 -0
- package/public/diagrams/claude-subscription-pool.png +0 -0
- package/public/docs-icon.png +0 -0
- package/public/escalation.html +237 -0
- package/public/group-config.html +300 -0
- package/public/icon-192.png +0 -0
- package/public/icon-512.png +0 -0
- package/public/icons/agents.svg +1 -0
- package/public/icons/attach.svg +1 -0
- package/public/icons/characters.svg +1 -0
- package/public/icons/chat.svg +1 -0
- package/public/icons/docs.svg +1 -0
- package/public/icons/heartbeat.svg +1 -0
- package/public/icons/messages.svg +1 -0
- package/public/icons/mic.svg +1 -0
- package/public/icons/notes.svg +1 -0
- package/public/icons/settings.svg +1 -0
- package/public/icons/tasks.svg +1 -0
- package/public/images/onboarding/p0-communication-layer.png +0 -0
- package/public/images/onboarding/p0-infinite-surface.png +0 -0
- package/public/images/onboarding/p0-learning-model.png +0 -0
- package/public/images/onboarding/p0-meet-aiva.png +0 -0
- package/public/images/onboarding/p4-contact-intelligence.png +0 -0
- package/public/images/onboarding/p4-context-compounds.png +0 -0
- package/public/images/onboarding/p4-message-router.png +0 -0
- package/public/images/onboarding/p4-per-contact-rules.png +0 -0
- package/public/images/onboarding/p4-send-messages.png +0 -0
- package/public/images/onboarding/p6-be-precise.png +0 -0
- package/public/images/onboarding/p6-review-escalations.png +0 -0
- package/public/images/onboarding/p6-voice-input.png +0 -0
- package/public/images/onboarding/p7-completion.png +0 -0
- package/public/index.html +11594 -0
- package/public/js/onboarding.js +699 -0
- package/public/manifest.json +24 -0
- package/public/messages-v2.html +2824 -0
- package/public/permission-approve.html.bak +107 -0
- package/public/permissions.html +150 -0
- package/public/styles/design-system.css +68 -0
- package/router-db.js +604 -0
- package/router-utils.js +28 -0
- package/router-v2/adapters/imessage.js +191 -0
- package/router-v2/adapters/quo.js +82 -0
- package/router-v2/adapters/whatsapp.js +192 -0
- package/router-v2/contact-manager.js +234 -0
- package/router-v2/conversation-engine.js +498 -0
- package/router-v2/data/knowledge-base.json +176 -0
- package/router-v2/data/router-v2.db +0 -0
- package/router-v2/data/router-v2.db-shm +0 -0
- package/router-v2/data/router-v2.db-wal +0 -0
- package/router-v2/data/router.db +0 -0
- package/router-v2/db.js +457 -0
- package/router-v2/escalation-bridge.js +540 -0
- package/router-v2/follow-up-engine.js +347 -0
- package/router-v2/index.js +441 -0
- package/router-v2/ingestion.js +213 -0
- package/router-v2/knowledge-base.js +231 -0
- package/router-v2/lead-qualifier.js +152 -0
- package/router-v2/learning-loop.js +202 -0
- package/router-v2/outbound-sender.js +160 -0
- package/router-v2/package.json +13 -0
- package/router-v2/permission-gate.js +86 -0
- package/router-v2/playbook.js +177 -0
- package/router-v2/prompts/base.js +52 -0
- package/router-v2/prompts/first-contact.js +38 -0
- package/router-v2/prompts/lead-qualification.js +37 -0
- package/router-v2/prompts/scheduling.js +72 -0
- package/router-v2/prompts/style-overrides.js +22 -0
- package/router-v2/scheduler.js +301 -0
- package/router-v2/scripts/migrate-v1-to-v2.js +215 -0
- package/router-v2/scripts/seed-faq.js +67 -0
- package/router-v2/seed-knowledge-base.js +39 -0
- package/router-v2/utils/ai.js +129 -0
- package/router-v2/utils/phone.js +52 -0
- package/router-v2/utils/response-validator.js +98 -0
- package/router-v2/utils/sanitize.js +222 -0
- package/router.js +5005 -0
- package/routes/google-calendar.js +186 -0
- package/scripts/deploy.sh +62 -0
- package/scripts/macos-calendar.sh +232 -0
- package/scripts/onboard-device.sh +466 -0
- package/server.js +5131 -0
- package/start.sh +24 -0
- package/templates/AGENTS.md +548 -0
- package/templates/IDENTITY.md +15 -0
- package/templates/docs-agents.html +132 -0
- package/templates/docs-app.html +130 -0
- package/templates/docs-home.html +83 -0
- package/templates/docs-imessage.html +121 -0
- package/templates/docs-tasks.html +123 -0
- package/templates/docs-tips.html +175 -0
- package/templates/getting-started.html +809 -0
- package/templates/invisible-prefix-base.txt +171 -0
- package/templates/invisible-prefix-owner.txt +282 -0
- package/templates/invisible-prefix.txt +338 -0
- package/templates/manifest.json +61 -0
- package/templates/memory-org/clients.md +7 -0
- package/templates/memory-org/credentials.md +9 -0
- package/templates/memory-org/devices.md +7 -0
- package/templates/updates.html +464 -0
- package/templates/workspace/AGENTS.md.tmpl +161 -0
- package/templates/workspace/HEARTBEAT.md.tmpl +17 -0
- package/templates/workspace/IDENTITY.md.tmpl +15 -0
- package/templates/workspace/MEMORY.md.tmpl +16 -0
- package/templates/workspace/SOUL.md.tmpl +51 -0
- package/templates/workspace/USER.md.tmpl +25 -0
- package/tts-proxy.js +96 -0
- package/voice-call-local.js +731 -0
- package/voice-call.js +732 -0
- package/wa-listener.js +354 -0
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ============================================================================
|
|
3
|
+
# AIVA Device Onboarding Script
|
|
4
|
+
# Fully provisions a fresh Mac Mini as an AIVA client device.
|
|
5
|
+
# Idempotent — safe to run multiple times.
|
|
6
|
+
#
|
|
7
|
+
# Usage:
|
|
8
|
+
# ./onboard-device.sh \
|
|
9
|
+
# --client-name "Kurt Burgan" \
|
|
10
|
+
# --device-id "kurt-mac-mini" \
|
|
11
|
+
# --dashboard-url "http://100.x.x.x:3851" \
|
|
12
|
+
# --tailscale-key "tskey-auth-xxx"
|
|
13
|
+
#
|
|
14
|
+
# Optional:
|
|
15
|
+
# --webhook-secret "xxx" GitHub webhook secret for auto-deploy
|
|
16
|
+
# --skip-tailscale Skip Tailscale join (already on tailnet)
|
|
17
|
+
# --app-port 3847 AIVA app port (default: 3847)
|
|
18
|
+
# --deploy-port 3849 Auto-deploy port (default: 3849)
|
|
19
|
+
# ============================================================================
|
|
20
|
+
|
|
21
|
+
set -euo pipefail
|
|
22
|
+
|
|
23
|
+
# ── Colors & helpers ────────────────────────────────────────────────────────
|
|
24
|
+
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'
|
|
25
|
+
CYAN='\033[0;36m'; BOLD='\033[1m'; NC='\033[0m'
|
|
26
|
+
|
|
27
|
+
step_num=0
|
|
28
|
+
step_results=()
|
|
29
|
+
|
|
30
|
+
log() { echo -e "${BLUE}[INFO]${NC} $*"; }
|
|
31
|
+
ok() { echo -e "${GREEN}[ OK]${NC} $*"; step_results+=("✅ $*"); }
|
|
32
|
+
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; step_results+=("⚠️ $*"); }
|
|
33
|
+
fail() { echo -e "${RED}[FAIL]${NC} $*"; step_results+=("❌ $*"); }
|
|
34
|
+
die() { fail "$*"; print_summary; exit 1; }
|
|
35
|
+
|
|
36
|
+
step() {
|
|
37
|
+
step_num=$((step_num + 1))
|
|
38
|
+
echo ""
|
|
39
|
+
echo -e "${CYAN}${BOLD}━━━ Step ${step_num}: $* ━━━${NC}"
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
# ── Parse arguments ─────────────────────────────────────────────────────────
|
|
43
|
+
CLIENT_NAME=""
|
|
44
|
+
DEVICE_ID=""
|
|
45
|
+
DASHBOARD_URL=""
|
|
46
|
+
TAILSCALE_KEY=""
|
|
47
|
+
WEBHOOK_SECRET=""
|
|
48
|
+
SKIP_TAILSCALE=false
|
|
49
|
+
APP_PORT=3847
|
|
50
|
+
DEPLOY_PORT=3849
|
|
51
|
+
|
|
52
|
+
while [[ $# -gt 0 ]]; do
|
|
53
|
+
case "$1" in
|
|
54
|
+
--client-name) CLIENT_NAME="$2"; shift 2 ;;
|
|
55
|
+
--device-id) DEVICE_ID="$2"; shift 2 ;;
|
|
56
|
+
--dashboard-url) DASHBOARD_URL="$2"; shift 2 ;;
|
|
57
|
+
--tailscale-key) TAILSCALE_KEY="$2"; shift 2 ;;
|
|
58
|
+
--webhook-secret) WEBHOOK_SECRET="$2"; shift 2 ;;
|
|
59
|
+
--skip-tailscale) SKIP_TAILSCALE=true; shift ;;
|
|
60
|
+
--app-port) APP_PORT="$2"; shift 2 ;;
|
|
61
|
+
--deploy-port) DEPLOY_PORT="$2"; shift 2 ;;
|
|
62
|
+
-h|--help)
|
|
63
|
+
echo "Usage: $0 --client-name NAME --device-id ID --dashboard-url URL [--tailscale-key KEY] [--webhook-secret SECRET] [--skip-tailscale] [--app-port PORT] [--deploy-port PORT]"
|
|
64
|
+
exit 0 ;;
|
|
65
|
+
*) echo "Unknown option: $1"; exit 1 ;;
|
|
66
|
+
esac
|
|
67
|
+
done
|
|
68
|
+
|
|
69
|
+
# Validate required args
|
|
70
|
+
[[ -z "$CLIENT_NAME" ]] && die "Missing --client-name"
|
|
71
|
+
[[ -z "$DEVICE_ID" ]] && die "Missing --device-id"
|
|
72
|
+
[[ -z "$DASHBOARD_URL" ]] && die "Missing --dashboard-url"
|
|
73
|
+
|
|
74
|
+
REPO_URL="git@github.com:mistermakeithappen/aiva-app.git"
|
|
75
|
+
APP_DIR="$HOME/aiva-tasks"
|
|
76
|
+
CURRENT_USER=$(whoami)
|
|
77
|
+
|
|
78
|
+
echo ""
|
|
79
|
+
echo -e "${BOLD}${CYAN}╔══════════════════════════════════════════════╗${NC}"
|
|
80
|
+
echo -e "${BOLD}${CYAN}║ AIVA Device Onboarding ║${NC}"
|
|
81
|
+
echo -e "${BOLD}${CYAN}╚══════════════════════════════════════════════╝${NC}"
|
|
82
|
+
echo ""
|
|
83
|
+
echo -e " Client: ${BOLD}${CLIENT_NAME}${NC}"
|
|
84
|
+
echo -e " Device ID: ${BOLD}${DEVICE_ID}${NC}"
|
|
85
|
+
echo -e " Dashboard: ${BOLD}${DASHBOARD_URL}${NC}"
|
|
86
|
+
echo -e " User: ${BOLD}${CURRENT_USER}${NC}"
|
|
87
|
+
echo -e " App Dir: ${BOLD}${APP_DIR}${NC}"
|
|
88
|
+
echo ""
|
|
89
|
+
|
|
90
|
+
# ── Step 1: Validate prerequisites ──────────────────────────────────────────
|
|
91
|
+
step "Validate prerequisites"
|
|
92
|
+
|
|
93
|
+
# macOS check
|
|
94
|
+
if [[ "$(uname)" != "Darwin" ]]; then
|
|
95
|
+
die "This script only runs on macOS. Detected: $(uname)"
|
|
96
|
+
fi
|
|
97
|
+
MACOS_VERSION=$(sw_vers -productVersion)
|
|
98
|
+
log "macOS version: $MACOS_VERSION"
|
|
99
|
+
ok "macOS $MACOS_VERSION detected"
|
|
100
|
+
|
|
101
|
+
# Xcode CLI tools (needed for git)
|
|
102
|
+
if ! xcode-select -p &>/dev/null; then
|
|
103
|
+
log "Installing Xcode Command Line Tools..."
|
|
104
|
+
xcode-select --install 2>/dev/null || true
|
|
105
|
+
warn "Xcode CLI tools installing — you may need to accept the dialog and re-run this script"
|
|
106
|
+
fi
|
|
107
|
+
|
|
108
|
+
# Homebrew
|
|
109
|
+
if ! command -v brew &>/dev/null; then
|
|
110
|
+
log "Installing Homebrew..."
|
|
111
|
+
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
|
|
112
|
+
# Add to path for Apple Silicon
|
|
113
|
+
if [[ -f /opt/homebrew/bin/brew ]]; then
|
|
114
|
+
eval "$(/opt/homebrew/bin/brew shellenv)"
|
|
115
|
+
# Persist for future shells
|
|
116
|
+
echo 'eval "$(/opt/homebrew/bin/brew shellenv)"' >> "$HOME/.zprofile" 2>/dev/null || true
|
|
117
|
+
fi
|
|
118
|
+
ok "Homebrew installed"
|
|
119
|
+
else
|
|
120
|
+
ok "Homebrew present ($(brew --version | head -1))"
|
|
121
|
+
fi
|
|
122
|
+
|
|
123
|
+
# Git
|
|
124
|
+
if ! command -v git &>/dev/null; then
|
|
125
|
+
log "Installing git via Homebrew..."
|
|
126
|
+
brew install git
|
|
127
|
+
ok "Git installed"
|
|
128
|
+
else
|
|
129
|
+
ok "Git present ($(git --version))"
|
|
130
|
+
fi
|
|
131
|
+
|
|
132
|
+
# Node.js
|
|
133
|
+
if ! command -v node &>/dev/null; then
|
|
134
|
+
log "Installing Node.js via Homebrew..."
|
|
135
|
+
brew install node
|
|
136
|
+
ok "Node.js installed ($(node --version))"
|
|
137
|
+
else
|
|
138
|
+
NODE_VERSION=$(node --version)
|
|
139
|
+
ok "Node.js present ($NODE_VERSION)"
|
|
140
|
+
fi
|
|
141
|
+
|
|
142
|
+
# npm
|
|
143
|
+
if ! command -v npm &>/dev/null; then
|
|
144
|
+
die "npm not found even though Node.js is installed"
|
|
145
|
+
fi
|
|
146
|
+
ok "npm present ($(npm --version))"
|
|
147
|
+
|
|
148
|
+
# ── Step 2: Join Tailscale ───────────────────────────────────────────────────
|
|
149
|
+
step "Join Tailscale network"
|
|
150
|
+
|
|
151
|
+
if [[ "$SKIP_TAILSCALE" == "true" ]]; then
|
|
152
|
+
log "Skipping Tailscale (--skip-tailscale flag)"
|
|
153
|
+
ok "Tailscale skipped by request"
|
|
154
|
+
elif [[ -z "$TAILSCALE_KEY" ]]; then
|
|
155
|
+
warn "No --tailscale-key provided — skipping Tailscale join. Device must already be on tailnet."
|
|
156
|
+
else
|
|
157
|
+
if ! command -v tailscale &>/dev/null; then
|
|
158
|
+
log "Tailscale CLI not found. Install Tailscale from https://tailscale.com/download/mac"
|
|
159
|
+
warn "Tailscale not installed — install manually and re-run"
|
|
160
|
+
else
|
|
161
|
+
# Check if already connected
|
|
162
|
+
TS_STATUS=$(tailscale status --json 2>/dev/null | grep -o '"BackendState":"[^"]*"' | cut -d'"' -f4 || echo "unknown")
|
|
163
|
+
if [[ "$TS_STATUS" == "Running" ]]; then
|
|
164
|
+
ok "Tailscale already connected"
|
|
165
|
+
else
|
|
166
|
+
log "Joining Tailscale with auth key..."
|
|
167
|
+
sudo tailscale up --authkey="$TAILSCALE_KEY" --hostname="$DEVICE_ID"
|
|
168
|
+
ok "Tailscale joined as $DEVICE_ID"
|
|
169
|
+
fi
|
|
170
|
+
fi
|
|
171
|
+
fi
|
|
172
|
+
|
|
173
|
+
# Get Tailscale IP
|
|
174
|
+
TAILSCALE_IP=""
|
|
175
|
+
if command -v tailscale &>/dev/null; then
|
|
176
|
+
TAILSCALE_IP=$(tailscale ip -4 2>/dev/null || echo "")
|
|
177
|
+
fi
|
|
178
|
+
if [[ -z "$TAILSCALE_IP" ]]; then
|
|
179
|
+
TAILSCALE_IP=$(ifconfig | grep "inet " | grep -v 127.0.0.1 | head -1 | awk '{print $2}')
|
|
180
|
+
warn "Could not get Tailscale IP — using local IP: $TAILSCALE_IP"
|
|
181
|
+
else
|
|
182
|
+
ok "Tailscale IP: $TAILSCALE_IP"
|
|
183
|
+
fi
|
|
184
|
+
|
|
185
|
+
# ── Step 3: Create directory structure ───────────────────────────────────────
|
|
186
|
+
step "Create directory structure"
|
|
187
|
+
|
|
188
|
+
mkdir -p "$APP_DIR"
|
|
189
|
+
mkdir -p "$APP_DIR/data"
|
|
190
|
+
ok "Directory structure ready: $APP_DIR"
|
|
191
|
+
|
|
192
|
+
# ── Step 4: Clone or update repo ────────────────────────────────────────────
|
|
193
|
+
step "Clone/update AIVA app repository"
|
|
194
|
+
|
|
195
|
+
if [[ -d "$APP_DIR/.git" ]]; then
|
|
196
|
+
log "Repository already exists — pulling latest..."
|
|
197
|
+
cd "$APP_DIR"
|
|
198
|
+
git fetch origin
|
|
199
|
+
git reset --hard origin/main
|
|
200
|
+
ok "Repository updated (git pull)"
|
|
201
|
+
else
|
|
202
|
+
# Check if directory has files (avoid clobbering)
|
|
203
|
+
if [[ -n "$(ls -A "$APP_DIR" 2>/dev/null)" ]]; then
|
|
204
|
+
log "Directory not empty but no git repo — backing up and cloning fresh"
|
|
205
|
+
mv "$APP_DIR" "${APP_DIR}.bak.$(date +%s)"
|
|
206
|
+
mkdir -p "$APP_DIR"
|
|
207
|
+
fi
|
|
208
|
+
log "Cloning repository..."
|
|
209
|
+
git clone "$REPO_URL" "$APP_DIR"
|
|
210
|
+
ok "Repository cloned"
|
|
211
|
+
fi
|
|
212
|
+
|
|
213
|
+
cd "$APP_DIR"
|
|
214
|
+
|
|
215
|
+
# ── Step 5: Install dependencies ────────────────────────────────────────────
|
|
216
|
+
step "Install Node.js dependencies"
|
|
217
|
+
|
|
218
|
+
cd "$APP_DIR"
|
|
219
|
+
npm install --production 2>&1 | tail -3
|
|
220
|
+
ok "Dependencies installed"
|
|
221
|
+
|
|
222
|
+
# ── Step 6: Configure environment ────────────────────────────────────────────
|
|
223
|
+
step "Configure environment (.env)"
|
|
224
|
+
|
|
225
|
+
ENV_FILE="$APP_DIR/.env"
|
|
226
|
+
|
|
227
|
+
# Only create/update if needed — preserve existing keys
|
|
228
|
+
if [[ -f "$ENV_FILE" ]]; then
|
|
229
|
+
log "Existing .env found — merging configuration"
|
|
230
|
+
# Helper: set a key in .env (update if exists, append if not)
|
|
231
|
+
set_env() {
|
|
232
|
+
local key="$1" val="$2"
|
|
233
|
+
if grep -q "^${key}=" "$ENV_FILE" 2>/dev/null; then
|
|
234
|
+
sed -i '' "s|^${key}=.*|${key}=${val}|" "$ENV_FILE"
|
|
235
|
+
else
|
|
236
|
+
echo "${key}=${val}" >> "$ENV_FILE"
|
|
237
|
+
fi
|
|
238
|
+
}
|
|
239
|
+
else
|
|
240
|
+
log "Creating new .env file"
|
|
241
|
+
touch "$ENV_FILE"
|
|
242
|
+
set_env() {
|
|
243
|
+
echo "$1=$2" >> "$ENV_FILE"
|
|
244
|
+
}
|
|
245
|
+
fi
|
|
246
|
+
|
|
247
|
+
set_env "DEVICE_ID" "$DEVICE_ID"
|
|
248
|
+
set_env "CLIENT_NAME" "$CLIENT_NAME"
|
|
249
|
+
set_env "DASHBOARD_URL" "$DASHBOARD_URL"
|
|
250
|
+
set_env "APP_PORT" "$APP_PORT"
|
|
251
|
+
set_env "AUTO_DEPLOY_PORT" "$DEPLOY_PORT"
|
|
252
|
+
|
|
253
|
+
if [[ -n "$WEBHOOK_SECRET" ]]; then
|
|
254
|
+
set_env "GITHUB_WEBHOOK_SECRET" "$WEBHOOK_SECRET"
|
|
255
|
+
fi
|
|
256
|
+
|
|
257
|
+
# Ensure CODING_ENABLED defaults to false for client devices
|
|
258
|
+
if ! grep -q "^CODING_ENABLED=" "$ENV_FILE" 2>/dev/null; then
|
|
259
|
+
set_env "CODING_ENABLED" "false"
|
|
260
|
+
fi
|
|
261
|
+
|
|
262
|
+
ok "Environment configured"
|
|
263
|
+
|
|
264
|
+
# ── Step 7: Start AIVA app ──────────────────────────────────────────────────
|
|
265
|
+
step "Start AIVA app service"
|
|
266
|
+
|
|
267
|
+
PLIST_DIR="$HOME/Library/LaunchAgents"
|
|
268
|
+
AIVA_PLIST="$PLIST_DIR/com.aiva.app.plist"
|
|
269
|
+
DEPLOY_PLIST="$PLIST_DIR/com.aiva.auto-deploy.plist"
|
|
270
|
+
|
|
271
|
+
# Prefer PM2 if available, otherwise LaunchAgent
|
|
272
|
+
if command -v pm2 &>/dev/null; then
|
|
273
|
+
log "PM2 detected — using PM2 for process management"
|
|
274
|
+
|
|
275
|
+
# Stop existing if running
|
|
276
|
+
pm2 delete aiva-app 2>/dev/null || true
|
|
277
|
+
pm2 delete aiva-auto-deploy 2>/dev/null || true
|
|
278
|
+
|
|
279
|
+
cd "$APP_DIR"
|
|
280
|
+
pm2 start server.js --name aiva-app --cwd "$APP_DIR"
|
|
281
|
+
pm2 start auto-deploy.js --name aiva-auto-deploy --cwd "$APP_DIR"
|
|
282
|
+
pm2 save
|
|
283
|
+
|
|
284
|
+
# Ensure PM2 starts on boot
|
|
285
|
+
pm2 startup 2>/dev/null || true
|
|
286
|
+
|
|
287
|
+
ok "AIVA app started via PM2"
|
|
288
|
+
else
|
|
289
|
+
log "PM2 not found — using macOS LaunchAgent"
|
|
290
|
+
mkdir -p "$PLIST_DIR"
|
|
291
|
+
|
|
292
|
+
# AIVA App LaunchAgent
|
|
293
|
+
cat > "$AIVA_PLIST" << PLIST
|
|
294
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
295
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
296
|
+
<plist version="1.0">
|
|
297
|
+
<dict>
|
|
298
|
+
<key>Label</key>
|
|
299
|
+
<string>com.aiva.app</string>
|
|
300
|
+
<key>ProgramArguments</key>
|
|
301
|
+
<array>
|
|
302
|
+
<string>$(which node)</string>
|
|
303
|
+
<string>${APP_DIR}/server.js</string>
|
|
304
|
+
</array>
|
|
305
|
+
<key>WorkingDirectory</key>
|
|
306
|
+
<string>${APP_DIR}</string>
|
|
307
|
+
<key>EnvironmentVariables</key>
|
|
308
|
+
<dict>
|
|
309
|
+
<key>PATH</key>
|
|
310
|
+
<string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
|
|
311
|
+
<key>HOME</key>
|
|
312
|
+
<string>${HOME}</string>
|
|
313
|
+
</dict>
|
|
314
|
+
<key>RunAtLoad</key>
|
|
315
|
+
<true/>
|
|
316
|
+
<key>KeepAlive</key>
|
|
317
|
+
<true/>
|
|
318
|
+
<key>StandardOutPath</key>
|
|
319
|
+
<string>${APP_DIR}/data/aiva-app.log</string>
|
|
320
|
+
<key>StandardErrorPath</key>
|
|
321
|
+
<string>${APP_DIR}/data/aiva-app-error.log</string>
|
|
322
|
+
</dict>
|
|
323
|
+
</plist>
|
|
324
|
+
PLIST
|
|
325
|
+
|
|
326
|
+
# Auto-Deploy LaunchAgent
|
|
327
|
+
cat > "$DEPLOY_PLIST" << PLIST
|
|
328
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
329
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
330
|
+
<plist version="1.0">
|
|
331
|
+
<dict>
|
|
332
|
+
<key>Label</key>
|
|
333
|
+
<string>com.aiva.auto-deploy</string>
|
|
334
|
+
<key>ProgramArguments</key>
|
|
335
|
+
<array>
|
|
336
|
+
<string>$(which node)</string>
|
|
337
|
+
<string>${APP_DIR}/auto-deploy.js</string>
|
|
338
|
+
</array>
|
|
339
|
+
<key>WorkingDirectory</key>
|
|
340
|
+
<string>${APP_DIR}</string>
|
|
341
|
+
<key>EnvironmentVariables</key>
|
|
342
|
+
<dict>
|
|
343
|
+
<key>PATH</key>
|
|
344
|
+
<string>/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin</string>
|
|
345
|
+
<key>HOME</key>
|
|
346
|
+
<string>${HOME}</string>
|
|
347
|
+
</dict>
|
|
348
|
+
<key>RunAtLoad</key>
|
|
349
|
+
<true/>
|
|
350
|
+
<key>KeepAlive</key>
|
|
351
|
+
<true/>
|
|
352
|
+
<key>StandardOutPath</key>
|
|
353
|
+
<string>${APP_DIR}/data/auto-deploy.log</string>
|
|
354
|
+
<key>StandardErrorPath</key>
|
|
355
|
+
<string>${APP_DIR}/data/auto-deploy-error.log</string>
|
|
356
|
+
</dict>
|
|
357
|
+
</plist>
|
|
358
|
+
PLIST
|
|
359
|
+
|
|
360
|
+
# Unload then load (idempotent)
|
|
361
|
+
launchctl unload "$AIVA_PLIST" 2>/dev/null || true
|
|
362
|
+
launchctl load "$AIVA_PLIST"
|
|
363
|
+
launchctl unload "$DEPLOY_PLIST" 2>/dev/null || true
|
|
364
|
+
launchctl load "$DEPLOY_PLIST"
|
|
365
|
+
|
|
366
|
+
ok "AIVA app started via LaunchAgent"
|
|
367
|
+
fi
|
|
368
|
+
|
|
369
|
+
# ── Step 8: Disable sleep ───────────────────────────────────────────────────
|
|
370
|
+
step "Configure power management (prevent sleep)"
|
|
371
|
+
|
|
372
|
+
sudo pmset -a sleep 0 displaysleep 0 disksleep 0 2>/dev/null && \
|
|
373
|
+
ok "Sleep disabled (pmset)" || \
|
|
374
|
+
warn "Could not set pmset — may need sudo password"
|
|
375
|
+
|
|
376
|
+
# ── Step 9: Register with dashboard ─────────────────────────────────────────
|
|
377
|
+
step "Register with dashboard"
|
|
378
|
+
|
|
379
|
+
# Wait a moment for the app to start
|
|
380
|
+
sleep 3
|
|
381
|
+
|
|
382
|
+
# Get macOS version and Node version for registration
|
|
383
|
+
OS_VERSION=$(sw_vers -productVersion)
|
|
384
|
+
NODE_VER=$(node --version)
|
|
385
|
+
MAC_ADDR=$(ifconfig en0 2>/dev/null | grep ether | awk '{print $2}' || echo "unknown")
|
|
386
|
+
|
|
387
|
+
REGISTER_BODY=$(cat << EOF
|
|
388
|
+
{
|
|
389
|
+
"deviceId": "${DEVICE_ID}",
|
|
390
|
+
"clientName": "${CLIENT_NAME}",
|
|
391
|
+
"tailscaleIp": "${TAILSCALE_IP}",
|
|
392
|
+
"macAddress": "${MAC_ADDR}",
|
|
393
|
+
"osVersion": "${OS_VERSION}",
|
|
394
|
+
"nodeVersion": "${NODE_VER}",
|
|
395
|
+
"appPath": "${APP_DIR}",
|
|
396
|
+
"sshUser": "${CURRENT_USER}"
|
|
397
|
+
}
|
|
398
|
+
EOF
|
|
399
|
+
)
|
|
400
|
+
|
|
401
|
+
REGISTER_RESPONSE=$(curl -s -w "\n%{http_code}" -X POST \
|
|
402
|
+
"${DASHBOARD_URL}/api/devices/register" \
|
|
403
|
+
-H "Content-Type: application/json" \
|
|
404
|
+
-d "$REGISTER_BODY" 2>/dev/null || echo -e "\n000")
|
|
405
|
+
|
|
406
|
+
HTTP_CODE=$(echo "$REGISTER_RESPONSE" | tail -1)
|
|
407
|
+
RESPONSE_BODY=$(echo "$REGISTER_RESPONSE" | sed '$d')
|
|
408
|
+
|
|
409
|
+
if [[ "$HTTP_CODE" == "200" || "$HTTP_CODE" == "201" ]]; then
|
|
410
|
+
ok "Registered with dashboard (HTTP $HTTP_CODE)"
|
|
411
|
+
log "Response: $RESPONSE_BODY"
|
|
412
|
+
else
|
|
413
|
+
warn "Dashboard registration returned HTTP $HTTP_CODE — device may need manual registration"
|
|
414
|
+
[[ -n "$RESPONSE_BODY" ]] && log "Response: $RESPONSE_BODY"
|
|
415
|
+
fi
|
|
416
|
+
|
|
417
|
+
# ── Step 10: Self-test ───────────────────────────────────────────────────────
|
|
418
|
+
step "Run self-test"
|
|
419
|
+
|
|
420
|
+
sleep 2
|
|
421
|
+
|
|
422
|
+
# Test AIVA app
|
|
423
|
+
APP_CHECK=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:${APP_PORT}" 2>/dev/null || echo "000")
|
|
424
|
+
if [[ "$APP_CHECK" == "200" ]]; then
|
|
425
|
+
ok "AIVA app responding on port $APP_PORT"
|
|
426
|
+
else
|
|
427
|
+
warn "AIVA app returned HTTP $APP_CHECK on port $APP_PORT (may still be starting)"
|
|
428
|
+
fi
|
|
429
|
+
|
|
430
|
+
# Test auto-deploy
|
|
431
|
+
DEPLOY_CHECK=$(curl -s -o /dev/null -w "%{http_code}" "http://localhost:${DEPLOY_PORT}" 2>/dev/null || echo "000")
|
|
432
|
+
if [[ "$DEPLOY_CHECK" == "200" || "$DEPLOY_CHECK" == "404" ]]; then
|
|
433
|
+
ok "Auto-deploy service responding on port $DEPLOY_PORT"
|
|
434
|
+
else
|
|
435
|
+
warn "Auto-deploy returned HTTP $DEPLOY_CHECK on port $DEPLOY_PORT"
|
|
436
|
+
fi
|
|
437
|
+
|
|
438
|
+
# Test git status
|
|
439
|
+
cd "$APP_DIR"
|
|
440
|
+
GIT_BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "unknown")
|
|
441
|
+
GIT_HASH=$(git rev-parse --short HEAD 2>/dev/null || echo "unknown")
|
|
442
|
+
ok "Git: branch=$GIT_BRANCH commit=$GIT_HASH"
|
|
443
|
+
|
|
444
|
+
# ── Summary ──────────────────────────────────────────────────────────────────
|
|
445
|
+
print_summary() {
|
|
446
|
+
echo ""
|
|
447
|
+
echo -e "${BOLD}${CYAN}╔══════════════════════════════════════════════╗${NC}"
|
|
448
|
+
echo -e "${BOLD}${CYAN}║ Onboarding Summary ║${NC}"
|
|
449
|
+
echo -e "${BOLD}${CYAN}╚══════════════════════════════════════════════╝${NC}"
|
|
450
|
+
echo ""
|
|
451
|
+
for result in "${step_results[@]}"; do
|
|
452
|
+
echo -e " $result"
|
|
453
|
+
done
|
|
454
|
+
echo ""
|
|
455
|
+
echo -e " ${BOLD}Device ID:${NC} $DEVICE_ID"
|
|
456
|
+
echo -e " ${BOLD}Client:${NC} $CLIENT_NAME"
|
|
457
|
+
echo -e " ${BOLD}Tailscale IP:${NC} $TAILSCALE_IP"
|
|
458
|
+
echo -e " ${BOLD}App URL:${NC} http://${TAILSCALE_IP}:${APP_PORT}"
|
|
459
|
+
echo -e " ${BOLD}App Dir:${NC} $APP_DIR"
|
|
460
|
+
echo -e " ${BOLD}Git:${NC} $GIT_BRANCH @ $GIT_HASH"
|
|
461
|
+
echo ""
|
|
462
|
+
echo -e " ${GREEN}${BOLD}Onboarding complete!${NC}"
|
|
463
|
+
echo ""
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
print_summary
|