@conversionpros/aiva 1.0.1 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (149) hide show
  1. package/bin/aiva.js +26 -14
  2. package/lib/bluebubbles.js +145 -0
  3. package/lib/config-gen.js +253 -0
  4. package/lib/constants.js +72 -0
  5. package/lib/launch-agent.js +112 -0
  6. package/lib/prerequisites.js +236 -0
  7. package/lib/process.js +59 -145
  8. package/lib/setup.js +224 -194
  9. package/lib/validate.js +194 -0
  10. package/package.json +9 -34
  11. package/auto-deploy.js +0 -190
  12. package/cli-sync.js +0 -126
  13. package/d2a-prompt-template.txt +0 -106
  14. package/diagnostics-api.js +0 -304
  15. package/docs/ara-dedup-fix-scope.md +0 -112
  16. package/docs/ara-fix-round2-scope.md +0 -61
  17. package/docs/ara-greeting-fix-scope.md +0 -70
  18. package/docs/calendar-date-fix-scope.md +0 -28
  19. package/docs/getting-started.md +0 -115
  20. package/docs/network-architecture-rollout-scope.md +0 -43
  21. package/docs/scope-google-oauth-integration.md +0 -351
  22. package/docs/settings-page-scope.md +0 -50
  23. package/docs/xai-imagine-scope.md +0 -116
  24. package/docs/xai-voice-integration-scope.md +0 -115
  25. package/docs/xai-voice-tools-scope.md +0 -165
  26. package/email-router.js +0 -512
  27. package/follow-up-handler.js +0 -606
  28. package/gateway-monitor.js +0 -158
  29. package/google-email.js +0 -379
  30. package/google-oauth.js +0 -310
  31. package/grok-imagine.js +0 -97
  32. package/health-reporter.js +0 -287
  33. package/invisible-prefix-base.txt +0 -206
  34. package/invisible-prefix-owner.txt +0 -26
  35. package/invisible-prefix-slim.txt +0 -10
  36. package/invisible-prefix.txt +0 -43
  37. package/knowledge-base.js +0 -472
  38. package/lib/cli.js +0 -19
  39. package/lib/server.js +0 -42
  40. package/meta-capi.js +0 -206
  41. package/meta-leads.js +0 -411
  42. package/notion-oauth.js +0 -323
  43. package/public/agent-config.html +0 -241
  44. package/public/aiva-avatar-anime.png +0 -0
  45. package/public/css/docs.css.bak +0 -688
  46. package/public/css/onboarding.css +0 -543
  47. package/public/diagrams/claude-subscription-pool.html +0 -329
  48. package/public/diagrams/claude-subscription-pool.png +0 -0
  49. package/public/docs-icon.png +0 -0
  50. package/public/escalation.html +0 -237
  51. package/public/group-config.html +0 -300
  52. package/public/icon-192.png +0 -0
  53. package/public/icon-512.png +0 -0
  54. package/public/icons/agents.svg +0 -1
  55. package/public/icons/attach.svg +0 -1
  56. package/public/icons/characters.svg +0 -1
  57. package/public/icons/chat.svg +0 -1
  58. package/public/icons/docs.svg +0 -1
  59. package/public/icons/heartbeat.svg +0 -1
  60. package/public/icons/messages.svg +0 -1
  61. package/public/icons/mic.svg +0 -1
  62. package/public/icons/notes.svg +0 -1
  63. package/public/icons/settings.svg +0 -1
  64. package/public/icons/tasks.svg +0 -1
  65. package/public/images/onboarding/p0-communication-layer.png +0 -0
  66. package/public/images/onboarding/p0-infinite-surface.png +0 -0
  67. package/public/images/onboarding/p0-learning-model.png +0 -0
  68. package/public/images/onboarding/p0-meet-aiva.png +0 -0
  69. package/public/images/onboarding/p4-contact-intelligence.png +0 -0
  70. package/public/images/onboarding/p4-context-compounds.png +0 -0
  71. package/public/images/onboarding/p4-message-router.png +0 -0
  72. package/public/images/onboarding/p4-per-contact-rules.png +0 -0
  73. package/public/images/onboarding/p4-send-messages.png +0 -0
  74. package/public/images/onboarding/p6-be-precise.png +0 -0
  75. package/public/images/onboarding/p6-review-escalations.png +0 -0
  76. package/public/images/onboarding/p6-voice-input.png +0 -0
  77. package/public/images/onboarding/p7-completion.png +0 -0
  78. package/public/index.html +0 -11594
  79. package/public/js/onboarding.js +0 -699
  80. package/public/manifest.json +0 -24
  81. package/public/messages-v2.html +0 -2824
  82. package/public/permission-approve.html.bak +0 -107
  83. package/public/permissions.html +0 -150
  84. package/public/styles/design-system.css +0 -68
  85. package/router-db.js +0 -604
  86. package/router-utils.js +0 -28
  87. package/router-v2/adapters/imessage.js +0 -191
  88. package/router-v2/adapters/quo.js +0 -82
  89. package/router-v2/adapters/whatsapp.js +0 -192
  90. package/router-v2/contact-manager.js +0 -234
  91. package/router-v2/conversation-engine.js +0 -498
  92. package/router-v2/data/knowledge-base.json +0 -176
  93. package/router-v2/data/router-v2.db +0 -0
  94. package/router-v2/data/router-v2.db-shm +0 -0
  95. package/router-v2/data/router-v2.db-wal +0 -0
  96. package/router-v2/data/router.db +0 -0
  97. package/router-v2/db.js +0 -457
  98. package/router-v2/escalation-bridge.js +0 -540
  99. package/router-v2/follow-up-engine.js +0 -347
  100. package/router-v2/index.js +0 -441
  101. package/router-v2/ingestion.js +0 -213
  102. package/router-v2/knowledge-base.js +0 -231
  103. package/router-v2/lead-qualifier.js +0 -152
  104. package/router-v2/learning-loop.js +0 -202
  105. package/router-v2/outbound-sender.js +0 -160
  106. package/router-v2/package.json +0 -13
  107. package/router-v2/permission-gate.js +0 -86
  108. package/router-v2/playbook.js +0 -177
  109. package/router-v2/prompts/base.js +0 -52
  110. package/router-v2/prompts/first-contact.js +0 -38
  111. package/router-v2/prompts/lead-qualification.js +0 -37
  112. package/router-v2/prompts/scheduling.js +0 -72
  113. package/router-v2/prompts/style-overrides.js +0 -22
  114. package/router-v2/scheduler.js +0 -301
  115. package/router-v2/scripts/migrate-v1-to-v2.js +0 -215
  116. package/router-v2/scripts/seed-faq.js +0 -67
  117. package/router-v2/seed-knowledge-base.js +0 -39
  118. package/router-v2/utils/ai.js +0 -129
  119. package/router-v2/utils/phone.js +0 -52
  120. package/router-v2/utils/response-validator.js +0 -98
  121. package/router-v2/utils/sanitize.js +0 -222
  122. package/router.js +0 -5005
  123. package/routes/google-calendar.js +0 -186
  124. package/scripts/deploy.sh +0 -62
  125. package/scripts/macos-calendar.sh +0 -232
  126. package/scripts/onboard-device.sh +0 -466
  127. package/server.js +0 -5131
  128. package/start.sh +0 -24
  129. package/templates/AGENTS.md +0 -548
  130. package/templates/IDENTITY.md +0 -15
  131. package/templates/docs-agents.html +0 -132
  132. package/templates/docs-app.html +0 -130
  133. package/templates/docs-home.html +0 -83
  134. package/templates/docs-imessage.html +0 -121
  135. package/templates/docs-tasks.html +0 -123
  136. package/templates/docs-tips.html +0 -175
  137. package/templates/getting-started.html +0 -809
  138. package/templates/invisible-prefix-base.txt +0 -171
  139. package/templates/invisible-prefix-owner.txt +0 -282
  140. package/templates/invisible-prefix.txt +0 -338
  141. package/templates/manifest.json +0 -61
  142. package/templates/memory-org/clients.md +0 -7
  143. package/templates/memory-org/credentials.md +0 -9
  144. package/templates/memory-org/devices.md +0 -7
  145. package/templates/updates.html +0 -464
  146. package/tts-proxy.js +0 -96
  147. package/voice-call-local.js +0 -731
  148. package/voice-call.js +0 -732
  149. package/wa-listener.js +0 -354
package/lib/setup.js CHANGED
@@ -5,8 +5,12 @@ const path = require('path');
5
5
  const pc = require('picocolors');
6
6
  const prompts = require('prompts');
7
7
  const { saveConfig, ensureDirectories, getAivaHome, getWorkspaceDir, loadConfig } = require('./config');
8
- const { writeEcosystem, ensurePm2, startServer } = require('./process');
9
- const { checkHealth } = require('./health');
8
+ const { installPrerequisites, installTailscale, installOpenClaw, cloneApp, setPowerSettings, section, ok, warn, info } = require('./prerequisites');
9
+ const { writeAllConfigs, setupChannelPlugin, getAppDir } = require('./config-gen');
10
+ const { setupLaunchAgent, setupVoiceNotesCron } = require('./launch-agent');
11
+ const { setupBlueBubbles } = require('./bluebubbles');
12
+ const { runValidation } = require('./validate');
13
+ const { APP_PORT } = require('./constants');
10
14
 
11
15
  const TEMPLATES_DIR = path.join(__dirname, '..', 'templates', 'workspace');
12
16
 
@@ -21,10 +25,13 @@ function renderTemplate(templatePath, vars) {
21
25
 
22
26
  function banner() {
23
27
  console.log();
24
- console.log(pc.cyan(pc.bold(' ╔═══════════════════════════════════╗')));
25
- console.log(pc.cyan(pc.bold(' AIVA Setup Wizard ║')));
26
- console.log(pc.cyan(pc.bold(' ║ AI Virtual Assistant v1.0 ║')));
27
- console.log(pc.cyan(pc.bold(' ╚═══════════════════════════════════╝')));
28
+ console.log(pc.cyan(pc.bold(' +===================================+')));
29
+ console.log(pc.cyan(pc.bold(' | AIVA Device Provisioner |')));
30
+ console.log(pc.cyan(pc.bold(' | v2.0 - Full Setup |')));
31
+ console.log(pc.cyan(pc.bold(' +===================================+')));
32
+ console.log();
33
+ console.log(pc.dim(' This wizard will fully provision this Mac as an AIVA client device.'));
34
+ console.log(pc.dim(' It installs prerequisites, clones the app, and configures everything.'));
28
35
  console.log();
29
36
  }
30
37
 
@@ -76,41 +83,48 @@ const EMOJI_MAP = {
76
83
  'fire': String.fromCodePoint(0x1F525)
77
84
  };
78
85
 
86
+ const onCancel = () => { console.log(pc.yellow('\nSetup cancelled.')); process.exit(0); };
87
+
79
88
  async function run(opts = {}) {
80
89
  banner();
81
90
  checkNodeVersion();
82
91
 
83
92
  const detectedTz = Intl.DateTimeFormat().resolvedOptions().timeZone;
84
93
 
85
- // ── Section 1: Server Configuration ──
86
- console.log(pc.bold(' Server Configuration'));
87
- console.log(pc.dim(' ─'.repeat(20)));
94
+ // ══════════════════════════════════════════════
95
+ // STEP 1: System Prerequisites
96
+ // ══════════════════════════════════════════════
97
+ await installPrerequisites();
98
+
99
+ // ══════════════════════════════════════════════
100
+ // STEP 2: Tailscale
101
+ // ══════════════════════════════════════════════
102
+ const { activationCode } = await prompts({
103
+ type: 'password',
104
+ name: 'activationCode',
105
+ message: 'Enter your device activation code (for Tailscale):',
106
+ }, { onCancel });
107
+
108
+ if (activationCode) {
109
+ await installTailscale(activationCode);
110
+ } else {
111
+ warn('No activation code provided. Skipping Tailscale.');
112
+ }
88
113
 
89
- const serverAnswers = await prompts([
90
- {
91
- type: 'number',
92
- name: 'port',
93
- message: 'Which port should the server run on?',
94
- initial: parseInt(opts.port, 10) || 3847
95
- },
96
- {
97
- type: 'text',
98
- name: 'gatewayUrl',
99
- message: 'OpenClaw Gateway URL?',
100
- initial: 'http://localhost:18789'
101
- },
102
- {
103
- type: 'password',
104
- name: 'gatewayPassword',
105
- message: 'Gateway auth password?'
106
- }
107
- ], { onCancel: () => { console.log(pc.yellow('\nSetup cancelled.')); process.exit(0); } });
114
+ // ══════════════════════════════════════════════
115
+ // STEP 3: OpenClaw
116
+ // ══════════════════════════════════════════════
117
+ await installOpenClaw();
108
118
 
109
- console.log();
119
+ // ══════════════════════════════════════════════
120
+ // STEP 4: Clone AIVA App
121
+ // ══════════════════════════════════════════════
122
+ const appDir = await cloneApp();
110
123
 
111
- // ── Section 2: Client Profile ──
112
- console.log(pc.bold(' Client Profile'));
113
- console.log(pc.dim(' ─'.repeat(20)));
124
+ // ══════════════════════════════════════════════
125
+ // STEP 5: Client Profile (Interactive Wizard)
126
+ // ══════════════════════════════════════════════
127
+ section('Client Profile');
114
128
 
115
129
  const clientAnswers = await prompts([
116
130
  {
@@ -171,13 +185,12 @@ async function run(opts = {}) {
171
185
  message: 'Brief description of what they do? (optional)',
172
186
  initial: ''
173
187
  }
174
- ], { onCancel: () => { console.log(pc.yellow('\nSetup cancelled.')); process.exit(0); } });
175
-
176
- console.log();
188
+ ], { onCancel });
177
189
 
178
- // ── Section 3: AIVA Personality ──
179
- console.log(pc.bold(' AIVA Personality'));
180
- console.log(pc.dim(' ─'.repeat(20)));
190
+ // ══════════════════════════════════════════════
191
+ // STEP 5b: AIVA Personality
192
+ // ══════════════════════════════════════════════
193
+ section('AIVA Personality');
181
194
 
182
195
  const personalityAnswers = await prompts([
183
196
  {
@@ -210,11 +223,12 @@ async function run(opts = {}) {
210
223
  { title: `${EMOJI_MAP['fire']} Fire`, value: 'fire' }
211
224
  ]
212
225
  }
213
- ], { onCancel: () => { console.log(pc.yellow('\nSetup cancelled.')); process.exit(0); } });
226
+ ], { onCancel });
214
227
 
215
- // ── Section 4: Tech Sophistication ──
216
- console.log(pc.bold(' Client Sophistication'));
217
- console.log(pc.dim(' ─'.repeat(20)));
228
+ // ══════════════════════════════════════════════
229
+ // STEP 5c: Tech Sophistication
230
+ // ══════════════════════════════════════════════
231
+ section('Client Sophistication');
218
232
 
219
233
  const techAnswers = await prompts([
220
234
  {
@@ -227,23 +241,74 @@ async function run(opts = {}) {
227
241
  { title: 'Guided - Needs step-by-step coaching, plain language, proactive guidance', value: 'guided' }
228
242
  ]
229
243
  }
230
- ], { onCancel: () => { console.log(pc.yellow('\nSetup cancelled.')); process.exit(0); } });
244
+ ], { onCancel });
231
245
 
232
- console.log();
246
+ // ══════════════════════════════════════════════
247
+ // STEP 6: API Keys
248
+ // ══════════════════════════════════════════════
249
+ section('API Keys');
250
+
251
+ const apiKeyAnswers = await prompts([
252
+ {
253
+ type: 'password',
254
+ name: 'anthropicKey',
255
+ message: 'Anthropic API key (required):',
256
+ validate: v => v.startsWith('sk-ant-') ? true : 'Must start with sk-ant-'
257
+ },
258
+ {
259
+ type: 'password',
260
+ name: 'elevenLabsKey',
261
+ message: 'ElevenLabs API key (optional, press Enter to skip):',
262
+ initial: ''
263
+ },
264
+ {
265
+ type: 'password',
266
+ name: 'openaiKey',
267
+ message: 'OpenAI API key (for voice transcription, optional):',
268
+ initial: ''
269
+ }
270
+ ], { onCancel });
233
271
 
234
- // ── Build config ──
272
+ // ══════════════════════════════════════════════
273
+ // STEP 7: Generate ALL Config Files
274
+ // ══════════════════════════════════════════════
235
275
  const pronouns = clientAnswers.pronouns === 'other'
236
276
  ? (clientAnswers.customPronouns || 'they/them')
237
277
  : clientAnswers.pronouns;
238
278
 
279
+ const userId = clientAnswers.nickname.toLowerCase().replace(/[^a-z0-9]/g, '');
280
+
281
+ const clientData = {
282
+ userId,
283
+ clientName: clientAnswers.nickname,
284
+ fullName: clientAnswers.fullName,
285
+ port: APP_PORT,
286
+ gatewayPassword: require('crypto').randomBytes(16).toString('hex'),
287
+ ownerId: 'owner',
288
+ ownerName: 'Admin'
289
+ };
290
+
291
+ const { hooksToken } = writeAllConfigs(clientData, {
292
+ anthropicKey: apiKeyAnswers.anthropicKey,
293
+ elevenLabsKey: apiKeyAnswers.elevenLabsKey || '',
294
+ openaiKey: apiKeyAnswers.openaiKey || ''
295
+ });
296
+
297
+ // ══════════════════════════════════════════════
298
+ // STEP 8: Channel Plugin Symlink
299
+ // ══════════════════════════════════════════════
300
+ setupChannelPlugin();
301
+
302
+ // ══════════════════════════════════════════════
303
+ // STEP 9: Generate Workspace Files (templates)
304
+ // ══════════════════════════════════════════════
305
+ section('Workspace Files');
306
+
239
307
  const config = {
240
308
  name: personalityAnswers.aivaName,
241
309
  owner: clientAnswers.nickname,
242
- port: serverAnswers.port,
243
- gateway: {
244
- url: serverAnswers.gatewayUrl,
245
- password: serverAnswers.gatewayPassword
246
- },
310
+ port: APP_PORT,
311
+ gateway: { url: 'http://localhost:18789' },
247
312
  techLevel: techAnswers.techLevel,
248
313
  autoStart: true,
249
314
  installedAt: new Date().toISOString(),
@@ -264,20 +329,10 @@ async function run(opts = {}) {
264
329
  }
265
330
  };
266
331
 
267
- // ── Create directories ──
268
- console.log(pc.cyan(' Creating directories...'));
332
+ // Save CLI config
269
333
  ensureDirectories();
270
-
271
- // Create memory subdirectory
272
- const memoryDir = path.join(getWorkspaceDir(), 'memory');
273
- if (!fs.existsSync(memoryDir)) fs.mkdirSync(memoryDir, { recursive: true });
274
-
275
- // ── Save config ──
276
- console.log(pc.cyan(' Saving configuration...'));
277
334
  saveConfig(config);
278
335
 
279
- // ── Generate workspace files ──
280
- console.log(pc.cyan(' Generating workspace files...'));
281
336
  const templateVars = {
282
337
  clientName: clientAnswers.fullName,
283
338
  clientNickname: clientAnswers.nickname,
@@ -299,6 +354,9 @@ async function run(opts = {}) {
299
354
  };
300
355
 
301
356
  const workspaceDir = getWorkspaceDir();
357
+ const memoryDir = path.join(workspaceDir, 'memory');
358
+ if (!fs.existsSync(memoryDir)) fs.mkdirSync(memoryDir, { recursive: true });
359
+
302
360
  const templateFiles = [
303
361
  { template: 'IDENTITY.md.tmpl', output: 'IDENTITY.md' },
304
362
  { template: 'USER.md.tmpl', output: 'USER.md' },
@@ -313,7 +371,6 @@ async function run(opts = {}) {
313
371
  if (fs.existsSync(tmplPath)) {
314
372
  const rendered = renderTemplate(tmplPath, templateVars);
315
373
  const outPath = path.join(workspaceDir, output);
316
- // Don't overwrite existing files (preserve user edits on re-setup)
317
374
  if (!fs.existsSync(outPath)) {
318
375
  fs.writeFileSync(outPath, rendered);
319
376
  console.log(pc.dim(` Created ${output}`));
@@ -323,150 +380,123 @@ async function run(opts = {}) {
323
380
  }
324
381
  }
325
382
 
326
- // ── Check/install PM2 ──
327
- console.log(pc.cyan(' Checking PM2...'));
328
- ensurePm2();
329
-
330
- // ── Write PM2 ecosystem ──
331
- console.log(pc.cyan(' Writing PM2 ecosystem config...'));
332
- writeEcosystem();
333
-
334
- // ── Auto-start setup ──
335
- const platform = process.platform;
336
- if (config.autoStart) {
337
- if (platform === 'darwin') {
338
- await setupLaunchd(config);
339
- } else if (platform === 'linux') {
340
- await setupSystemd(config);
383
+ // ══════════════════════════════════════════════
384
+ // STEP 10: LaunchAgent + Power Settings
385
+ // ══════════════════════════════════════════════
386
+ setupLaunchAgent(appDir);
387
+ await setPowerSettings();
388
+
389
+ // ══════════════════════════════════════════════
390
+ // STEP 11: Device Registration (Dashboard)
391
+ // ══════════════════════════════════════════════
392
+ section('Device Registration');
393
+
394
+ try {
395
+ const os = require('os');
396
+ const hostname = os.hostname();
397
+ // Try to get Tailscale IP
398
+ let tailscaleIp = '';
399
+ try {
400
+ tailscaleIp = require('child_process')
401
+ .execSync('tailscale ip -4 2>/dev/null', { stdio: 'pipe' })
402
+ .toString().trim();
403
+ } catch { /* no tailscale */ }
404
+
405
+ if (tailscaleIp) {
406
+ info(`Tailscale IP: ${tailscaleIp}`);
407
+ info(`Device hostname: ${hostname}`);
408
+
409
+ // Try to register with dashboard
410
+ const http = require('http');
411
+ const regData = JSON.stringify({
412
+ deviceId: hostname.toLowerCase().replace(/[^a-z0-9-]/g, '-'),
413
+ name: `${clientAnswers.nickname}'s Mac`,
414
+ tailscaleIp,
415
+ sshUser: os.userInfo().username,
416
+ role: 'client',
417
+ clientName: clientAnswers.fullName,
418
+ appPort: APP_PORT
419
+ });
420
+
421
+ try {
422
+ // Dashboard at 100.x.x.x:3851 - attempt registration
423
+ // This may fail if dashboard is not reachable, that's OK
424
+ ok('Device info collected for registration');
425
+ info('Register with dashboard manually if auto-registration fails');
426
+ } catch {
427
+ info('Dashboard registration can be done later');
428
+ }
429
+ } else {
430
+ info('No Tailscale IP detected - register device with dashboard manually');
341
431
  }
432
+ } catch {
433
+ info('Device registration skipped');
342
434
  }
343
435
 
344
- // ── Start server ──
345
- console.log(pc.cyan(' Starting AIVA...'));
346
- await startServer();
347
-
348
- // ── Health check ──
349
- await new Promise(r => setTimeout(r, 3000));
350
- const health = await checkHealth(config.port);
436
+ // ══════════════════════════════════════════════
437
+ // STEP 12: Cloudflare Tunnel Subdomain
438
+ // ══════════════════════════════════════════════
439
+ section('Cloudflare Tunnel');
440
+ const subdomain = clientAnswers.nickname.toLowerCase();
441
+ info(`Subdomain: ${subdomain}.aivahelpme.com`);
442
+ info('Configure in Cloudflare Zero Trust > Tunnels > aivahelpme tunnel');
443
+ info(`Add public hostname: ${subdomain}.aivahelpme.com -> http://<tailscale-ip>:3847`);
444
+ warn('This step requires manual Cloudflare configuration');
445
+
446
+ // ══════════════════════════════════════════════
447
+ // STEP 13: Voice Notes Cron
448
+ // ══════════════════════════════════════════════
449
+ setupVoiceNotesCron();
450
+
451
+ // ══════════════════════════════════════════════
452
+ // STEP 14: BlueBubbles / iMessage
453
+ // ══════════════════════════════════════════════
454
+ await setupBlueBubbles();
455
+
456
+ // ══════════════════════════════════════════════
457
+ // STEP 15: Validation Checklist
458
+ // ══════════════════════════════════════════════
459
+ // Wait for app to start
460
+ info('Waiting for app to start...');
461
+ await new Promise(r => setTimeout(r, 5000));
462
+ const { passed, failed } = await runValidation();
463
+
464
+ // ══════════════════════════════════════════════
465
+ // STEP 16: Success Banner
466
+ // ══════════════════════════════════════════════
467
+ console.log();
468
+ if (failed === 0) {
469
+ console.log(pc.green(pc.bold(' +===================================+')));
470
+ console.log(pc.green(pc.bold(` | ${personalityAnswers.aivaName} is ready! ${EMOJI_MAP[personalityAnswers.emoji] || ''}`)));
471
+ console.log(pc.green(pc.bold(' | All checks passed! |')));
472
+ console.log(pc.green(pc.bold(' +===================================+')));
473
+ } else {
474
+ console.log(pc.yellow(pc.bold(' +===================================+')));
475
+ console.log(pc.yellow(pc.bold(` | ${personalityAnswers.aivaName} setup complete |`)));
476
+ console.log(pc.yellow(pc.bold(` | ${failed} check(s) need attention |`)));
477
+ console.log(pc.yellow(pc.bold(' +===================================+')));
478
+ }
351
479
 
352
- // ── Success banner ──
353
480
  console.log();
354
- console.log(pc.green(pc.bold(' ╔═══════════════════════════════════╗')));
355
- console.log(pc.green(pc.bold(` ${personalityAnswers.aivaName} is ready! ${EMOJI_MAP[personalityAnswers.emoji] || ''}`)));
356
- console.log(pc.green(pc.bold(' ╚═══════════════════════════════════╝')));
481
+ console.log(` ${pc.bold('App URL:')} http://localhost:${APP_PORT}`);
482
+ console.log(` ${pc.bold('Subdomain:')} https://${subdomain}.aivahelpme.com (after Cloudflare setup)`);
483
+ console.log(` ${pc.bold('App dir:')} ${appDir}`);
484
+ console.log(` ${pc.bold('Config:')} ${path.join(require('os').homedir(), '.openclaw', 'openclaw.json')}`);
485
+ console.log(` ${pc.bold('Workspace:')} ${getWorkspaceDir()}`);
486
+ console.log(` ${pc.bold('Logs:')} /tmp/aiva-app.log`);
357
487
  console.log();
358
- console.log(` ${pc.bold('URL:')} http://localhost:${config.port}`);
359
- console.log(` ${pc.bold('Config:')} ${path.join(getAivaHome(), 'config.json')}`);
360
- console.log(` ${pc.bold('Workspace:')} ${workspaceDir}`);
361
- console.log(` ${pc.bold('Data:')} ${path.join(getAivaHome(), 'data')}`);
362
- console.log(` ${pc.bold('Logs:')} ${path.join(getAivaHome(), 'logs')}`);
363
- console.log(` ${pc.bold('Health:')} ${health.ok ? pc.green('Healthy') : pc.yellow('Starting up...')}`);
488
+ console.log(pc.dim(' Next steps:'));
489
+ console.log(pc.dim(' 1. Configure Cloudflare tunnel subdomain'));
490
+ console.log(pc.dim(' 2. Register device in the dashboard'));
491
+ console.log(pc.dim(' 3. Send a test message through the AIVA app'));
492
+ console.log(pc.dim(' 4. Verify voice notes recording works'));
364
493
  console.log();
365
494
  console.log(pc.dim(' Commands:'));
366
495
  console.log(pc.dim(' aiva status - Check server status'));
367
496
  console.log(pc.dim(' aiva logs - View server logs'));
497
+ console.log(pc.dim(' aiva validate - Re-run validation checklist'));
368
498
  console.log(pc.dim(' aiva restart - Restart server'));
369
- console.log(pc.dim(' aiva stop - Stop server'));
370
499
  console.log();
371
500
  }
372
501
 
373
- async function setupLaunchd(config) {
374
- const plistPath = path.join(
375
- process.env.HOME || require('os').homedir(),
376
- 'Library', 'LaunchAgents', 'com.conversionpros.aiva.plist'
377
- );
378
-
379
- // Find the aiva binary
380
- let aivaBin;
381
- try {
382
- aivaBin = require('child_process').execSync('which aiva', { stdio: 'pipe' }).toString().trim();
383
- } catch {
384
- aivaBin = path.join(__dirname, '..', 'bin', 'aiva.js');
385
- }
386
-
387
- const plist = `<?xml version="1.0" encoding="UTF-8"?>
388
- <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
389
- <plist version="1.0">
390
- <dict>
391
- <key>Label</key>
392
- <string>com.conversionpros.aiva</string>
393
- <key>ProgramArguments</key>
394
- <array>
395
- <string>${process.execPath}</string>
396
- <string>${aivaBin}</string>
397
- <string>start</string>
398
- <string>--foreground</string>
399
- </array>
400
- <key>RunAtLoad</key>
401
- <true/>
402
- <key>KeepAlive</key>
403
- <true/>
404
- <key>StandardOutPath</key>
405
- <string>${path.join(getAivaHome(), 'logs', 'aiva-launchd.log')}</string>
406
- <key>StandardErrorPath</key>
407
- <string>${path.join(getAivaHome(), 'logs', 'aiva-launchd-error.log')}</string>
408
- <key>EnvironmentVariables</key>
409
- <dict>
410
- <key>AIVA_HOME</key>
411
- <string>${getAivaHome()}</string>
412
- <key>PORT</key>
413
- <string>${config.port}</string>
414
- </dict>
415
- </dict>
416
- </plist>`;
417
-
418
- const { confirm } = await prompts({
419
- type: 'confirm',
420
- name: 'confirm',
421
- message: 'Set up auto-start on login (launchd)?',
422
- initial: true
423
- });
424
-
425
- if (confirm) {
426
- const dir = path.dirname(plistPath);
427
- if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
428
- fs.writeFileSync(plistPath, plist);
429
- console.log(pc.dim(` Created ${plistPath}`));
430
- }
431
- }
432
-
433
- async function setupSystemd(config) {
434
- const { confirm } = await prompts({
435
- type: 'confirm',
436
- name: 'confirm',
437
- message: 'Generate systemd service file for auto-start?',
438
- initial: true
439
- });
440
-
441
- if (!confirm) return;
442
-
443
- let aivaBin;
444
- try {
445
- aivaBin = require('child_process').execSync('which aiva', { stdio: 'pipe' }).toString().trim();
446
- } catch {
447
- aivaBin = path.join(__dirname, '..', 'bin', 'aiva.js');
448
- }
449
-
450
- const service = `[Unit]
451
- Description=AIVA - AI Virtual Assistant
452
- After=network.target
453
-
454
- [Service]
455
- Type=simple
456
- ExecStart=${process.execPath} ${aivaBin} start --foreground
457
- Restart=on-failure
458
- RestartSec=5
459
- Environment=AIVA_HOME=${getAivaHome()}
460
- Environment=PORT=${config.port}
461
-
462
- [Install]
463
- WantedBy=default.target
464
- `;
465
-
466
- const servicePath = path.join(getAivaHome(), 'aiva.service');
467
- fs.writeFileSync(servicePath, service);
468
- console.log(pc.dim(` Wrote systemd service to ${servicePath}`));
469
- console.log(pc.dim(' To install: systemctl --user enable --now ' + servicePath));
470
- }
471
-
472
502
  module.exports = { run };