@conversionpros/aiva 1.0.0 → 2.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.
Files changed (150) 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/config.js +1 -1
  5. package/lib/constants.js +72 -0
  6. package/lib/launch-agent.js +112 -0
  7. package/lib/prerequisites.js +236 -0
  8. package/lib/process.js +59 -145
  9. package/lib/setup.js +224 -194
  10. package/lib/validate.js +194 -0
  11. package/package.json +7 -32
  12. package/auto-deploy.js +0 -190
  13. package/cli-sync.js +0 -126
  14. package/d2a-prompt-template.txt +0 -106
  15. package/diagnostics-api.js +0 -304
  16. package/docs/ara-dedup-fix-scope.md +0 -112
  17. package/docs/ara-fix-round2-scope.md +0 -61
  18. package/docs/ara-greeting-fix-scope.md +0 -70
  19. package/docs/calendar-date-fix-scope.md +0 -28
  20. package/docs/getting-started.md +0 -115
  21. package/docs/network-architecture-rollout-scope.md +0 -43
  22. package/docs/scope-google-oauth-integration.md +0 -351
  23. package/docs/settings-page-scope.md +0 -50
  24. package/docs/xai-imagine-scope.md +0 -116
  25. package/docs/xai-voice-integration-scope.md +0 -115
  26. package/docs/xai-voice-tools-scope.md +0 -165
  27. package/email-router.js +0 -512
  28. package/follow-up-handler.js +0 -606
  29. package/gateway-monitor.js +0 -158
  30. package/google-email.js +0 -379
  31. package/google-oauth.js +0 -310
  32. package/grok-imagine.js +0 -97
  33. package/health-reporter.js +0 -287
  34. package/invisible-prefix-base.txt +0 -206
  35. package/invisible-prefix-owner.txt +0 -26
  36. package/invisible-prefix-slim.txt +0 -10
  37. package/invisible-prefix.txt +0 -43
  38. package/knowledge-base.js +0 -472
  39. package/lib/cli.js +0 -19
  40. package/lib/server.js +0 -42
  41. package/meta-capi.js +0 -206
  42. package/meta-leads.js +0 -411
  43. package/notion-oauth.js +0 -323
  44. package/public/agent-config.html +0 -241
  45. package/public/aiva-avatar-anime.png +0 -0
  46. package/public/css/docs.css.bak +0 -688
  47. package/public/css/onboarding.css +0 -543
  48. package/public/diagrams/claude-subscription-pool.html +0 -329
  49. package/public/diagrams/claude-subscription-pool.png +0 -0
  50. package/public/docs-icon.png +0 -0
  51. package/public/escalation.html +0 -237
  52. package/public/group-config.html +0 -300
  53. package/public/icon-192.png +0 -0
  54. package/public/icon-512.png +0 -0
  55. package/public/icons/agents.svg +0 -1
  56. package/public/icons/attach.svg +0 -1
  57. package/public/icons/characters.svg +0 -1
  58. package/public/icons/chat.svg +0 -1
  59. package/public/icons/docs.svg +0 -1
  60. package/public/icons/heartbeat.svg +0 -1
  61. package/public/icons/messages.svg +0 -1
  62. package/public/icons/mic.svg +0 -1
  63. package/public/icons/notes.svg +0 -1
  64. package/public/icons/settings.svg +0 -1
  65. package/public/icons/tasks.svg +0 -1
  66. package/public/images/onboarding/p0-communication-layer.png +0 -0
  67. package/public/images/onboarding/p0-infinite-surface.png +0 -0
  68. package/public/images/onboarding/p0-learning-model.png +0 -0
  69. package/public/images/onboarding/p0-meet-aiva.png +0 -0
  70. package/public/images/onboarding/p4-contact-intelligence.png +0 -0
  71. package/public/images/onboarding/p4-context-compounds.png +0 -0
  72. package/public/images/onboarding/p4-message-router.png +0 -0
  73. package/public/images/onboarding/p4-per-contact-rules.png +0 -0
  74. package/public/images/onboarding/p4-send-messages.png +0 -0
  75. package/public/images/onboarding/p6-be-precise.png +0 -0
  76. package/public/images/onboarding/p6-review-escalations.png +0 -0
  77. package/public/images/onboarding/p6-voice-input.png +0 -0
  78. package/public/images/onboarding/p7-completion.png +0 -0
  79. package/public/index.html +0 -11594
  80. package/public/js/onboarding.js +0 -699
  81. package/public/manifest.json +0 -24
  82. package/public/messages-v2.html +0 -2824
  83. package/public/permission-approve.html.bak +0 -107
  84. package/public/permissions.html +0 -150
  85. package/public/styles/design-system.css +0 -68
  86. package/router-db.js +0 -604
  87. package/router-utils.js +0 -28
  88. package/router-v2/adapters/imessage.js +0 -191
  89. package/router-v2/adapters/quo.js +0 -82
  90. package/router-v2/adapters/whatsapp.js +0 -192
  91. package/router-v2/contact-manager.js +0 -234
  92. package/router-v2/conversation-engine.js +0 -498
  93. package/router-v2/data/knowledge-base.json +0 -176
  94. package/router-v2/data/router-v2.db +0 -0
  95. package/router-v2/data/router-v2.db-shm +0 -0
  96. package/router-v2/data/router-v2.db-wal +0 -0
  97. package/router-v2/data/router.db +0 -0
  98. package/router-v2/db.js +0 -457
  99. package/router-v2/escalation-bridge.js +0 -540
  100. package/router-v2/follow-up-engine.js +0 -347
  101. package/router-v2/index.js +0 -441
  102. package/router-v2/ingestion.js +0 -213
  103. package/router-v2/knowledge-base.js +0 -231
  104. package/router-v2/lead-qualifier.js +0 -152
  105. package/router-v2/learning-loop.js +0 -202
  106. package/router-v2/outbound-sender.js +0 -160
  107. package/router-v2/package.json +0 -13
  108. package/router-v2/permission-gate.js +0 -86
  109. package/router-v2/playbook.js +0 -177
  110. package/router-v2/prompts/base.js +0 -52
  111. package/router-v2/prompts/first-contact.js +0 -38
  112. package/router-v2/prompts/lead-qualification.js +0 -37
  113. package/router-v2/prompts/scheduling.js +0 -72
  114. package/router-v2/prompts/style-overrides.js +0 -22
  115. package/router-v2/scheduler.js +0 -301
  116. package/router-v2/scripts/migrate-v1-to-v2.js +0 -215
  117. package/router-v2/scripts/seed-faq.js +0 -67
  118. package/router-v2/seed-knowledge-base.js +0 -39
  119. package/router-v2/utils/ai.js +0 -129
  120. package/router-v2/utils/phone.js +0 -52
  121. package/router-v2/utils/response-validator.js +0 -98
  122. package/router-v2/utils/sanitize.js +0 -222
  123. package/router.js +0 -5005
  124. package/routes/google-calendar.js +0 -186
  125. package/scripts/deploy.sh +0 -62
  126. package/scripts/macos-calendar.sh +0 -232
  127. package/scripts/onboard-device.sh +0 -466
  128. package/server.js +0 -5131
  129. package/start.sh +0 -24
  130. package/templates/AGENTS.md +0 -548
  131. package/templates/IDENTITY.md +0 -15
  132. package/templates/docs-agents.html +0 -132
  133. package/templates/docs-app.html +0 -130
  134. package/templates/docs-home.html +0 -83
  135. package/templates/docs-imessage.html +0 -121
  136. package/templates/docs-tasks.html +0 -123
  137. package/templates/docs-tips.html +0 -175
  138. package/templates/getting-started.html +0 -809
  139. package/templates/invisible-prefix-base.txt +0 -171
  140. package/templates/invisible-prefix-owner.txt +0 -282
  141. package/templates/invisible-prefix.txt +0 -338
  142. package/templates/manifest.json +0 -61
  143. package/templates/memory-org/clients.md +0 -7
  144. package/templates/memory-org/credentials.md +0 -9
  145. package/templates/memory-org/devices.md +0 -7
  146. package/templates/updates.html +0 -464
  147. package/tts-proxy.js +0 -96
  148. package/voice-call-local.js +0 -731
  149. package/voice-call.js +0 -732
  150. 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:3033'
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 };