@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.
- package/bin/aiva.js +26 -14
- package/lib/bluebubbles.js +145 -0
- package/lib/config-gen.js +253 -0
- package/lib/constants.js +72 -0
- package/lib/launch-agent.js +112 -0
- package/lib/prerequisites.js +236 -0
- package/lib/process.js +59 -145
- package/lib/setup.js +224 -194
- package/lib/validate.js +194 -0
- package/package.json +9 -34
- package/auto-deploy.js +0 -190
- package/cli-sync.js +0 -126
- package/d2a-prompt-template.txt +0 -106
- package/diagnostics-api.js +0 -304
- package/docs/ara-dedup-fix-scope.md +0 -112
- package/docs/ara-fix-round2-scope.md +0 -61
- package/docs/ara-greeting-fix-scope.md +0 -70
- package/docs/calendar-date-fix-scope.md +0 -28
- package/docs/getting-started.md +0 -115
- package/docs/network-architecture-rollout-scope.md +0 -43
- package/docs/scope-google-oauth-integration.md +0 -351
- package/docs/settings-page-scope.md +0 -50
- package/docs/xai-imagine-scope.md +0 -116
- package/docs/xai-voice-integration-scope.md +0 -115
- package/docs/xai-voice-tools-scope.md +0 -165
- package/email-router.js +0 -512
- package/follow-up-handler.js +0 -606
- package/gateway-monitor.js +0 -158
- package/google-email.js +0 -379
- package/google-oauth.js +0 -310
- package/grok-imagine.js +0 -97
- package/health-reporter.js +0 -287
- package/invisible-prefix-base.txt +0 -206
- package/invisible-prefix-owner.txt +0 -26
- package/invisible-prefix-slim.txt +0 -10
- package/invisible-prefix.txt +0 -43
- package/knowledge-base.js +0 -472
- package/lib/cli.js +0 -19
- package/lib/server.js +0 -42
- package/meta-capi.js +0 -206
- package/meta-leads.js +0 -411
- package/notion-oauth.js +0 -323
- package/public/agent-config.html +0 -241
- package/public/aiva-avatar-anime.png +0 -0
- package/public/css/docs.css.bak +0 -688
- package/public/css/onboarding.css +0 -543
- package/public/diagrams/claude-subscription-pool.html +0 -329
- package/public/diagrams/claude-subscription-pool.png +0 -0
- package/public/docs-icon.png +0 -0
- package/public/escalation.html +0 -237
- package/public/group-config.html +0 -300
- package/public/icon-192.png +0 -0
- package/public/icon-512.png +0 -0
- package/public/icons/agents.svg +0 -1
- package/public/icons/attach.svg +0 -1
- package/public/icons/characters.svg +0 -1
- package/public/icons/chat.svg +0 -1
- package/public/icons/docs.svg +0 -1
- package/public/icons/heartbeat.svg +0 -1
- package/public/icons/messages.svg +0 -1
- package/public/icons/mic.svg +0 -1
- package/public/icons/notes.svg +0 -1
- package/public/icons/settings.svg +0 -1
- package/public/icons/tasks.svg +0 -1
- 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 +0 -11594
- package/public/js/onboarding.js +0 -699
- package/public/manifest.json +0 -24
- package/public/messages-v2.html +0 -2824
- package/public/permission-approve.html.bak +0 -107
- package/public/permissions.html +0 -150
- package/public/styles/design-system.css +0 -68
- package/router-db.js +0 -604
- package/router-utils.js +0 -28
- package/router-v2/adapters/imessage.js +0 -191
- package/router-v2/adapters/quo.js +0 -82
- package/router-v2/adapters/whatsapp.js +0 -192
- package/router-v2/contact-manager.js +0 -234
- package/router-v2/conversation-engine.js +0 -498
- package/router-v2/data/knowledge-base.json +0 -176
- 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 +0 -457
- package/router-v2/escalation-bridge.js +0 -540
- package/router-v2/follow-up-engine.js +0 -347
- package/router-v2/index.js +0 -441
- package/router-v2/ingestion.js +0 -213
- package/router-v2/knowledge-base.js +0 -231
- package/router-v2/lead-qualifier.js +0 -152
- package/router-v2/learning-loop.js +0 -202
- package/router-v2/outbound-sender.js +0 -160
- package/router-v2/package.json +0 -13
- package/router-v2/permission-gate.js +0 -86
- package/router-v2/playbook.js +0 -177
- package/router-v2/prompts/base.js +0 -52
- package/router-v2/prompts/first-contact.js +0 -38
- package/router-v2/prompts/lead-qualification.js +0 -37
- package/router-v2/prompts/scheduling.js +0 -72
- package/router-v2/prompts/style-overrides.js +0 -22
- package/router-v2/scheduler.js +0 -301
- package/router-v2/scripts/migrate-v1-to-v2.js +0 -215
- package/router-v2/scripts/seed-faq.js +0 -67
- package/router-v2/seed-knowledge-base.js +0 -39
- package/router-v2/utils/ai.js +0 -129
- package/router-v2/utils/phone.js +0 -52
- package/router-v2/utils/response-validator.js +0 -98
- package/router-v2/utils/sanitize.js +0 -222
- package/router.js +0 -5005
- package/routes/google-calendar.js +0 -186
- package/scripts/deploy.sh +0 -62
- package/scripts/macos-calendar.sh +0 -232
- package/scripts/onboard-device.sh +0 -466
- package/server.js +0 -5131
- package/start.sh +0 -24
- package/templates/AGENTS.md +0 -548
- package/templates/IDENTITY.md +0 -15
- package/templates/docs-agents.html +0 -132
- package/templates/docs-app.html +0 -130
- package/templates/docs-home.html +0 -83
- package/templates/docs-imessage.html +0 -121
- package/templates/docs-tasks.html +0 -123
- package/templates/docs-tips.html +0 -175
- package/templates/getting-started.html +0 -809
- package/templates/invisible-prefix-base.txt +0 -171
- package/templates/invisible-prefix-owner.txt +0 -282
- package/templates/invisible-prefix.txt +0 -338
- package/templates/manifest.json +0 -61
- package/templates/memory-org/clients.md +0 -7
- package/templates/memory-org/credentials.md +0 -9
- package/templates/memory-org/devices.md +0 -7
- package/templates/updates.html +0 -464
- package/tts-proxy.js +0 -96
- package/voice-call-local.js +0 -731
- package/voice-call.js +0 -732
- 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 {
|
|
9
|
-
const {
|
|
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('
|
|
26
|
-
console.log(pc.cyan(pc.bold('
|
|
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
|
-
//
|
|
86
|
-
|
|
87
|
-
|
|
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
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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
|
-
|
|
119
|
+
// ══════════════════════════════════════════════
|
|
120
|
+
// STEP 4: Clone AIVA App
|
|
121
|
+
// ══════════════════════════════════════════════
|
|
122
|
+
const appDir = await cloneApp();
|
|
110
123
|
|
|
111
|
-
//
|
|
112
|
-
|
|
113
|
-
|
|
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
|
|
175
|
-
|
|
176
|
-
console.log();
|
|
188
|
+
], { onCancel });
|
|
177
189
|
|
|
178
|
-
//
|
|
179
|
-
|
|
180
|
-
|
|
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
|
|
226
|
+
], { onCancel });
|
|
214
227
|
|
|
215
|
-
//
|
|
216
|
-
|
|
217
|
-
|
|
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
|
|
244
|
+
], { onCancel });
|
|
231
245
|
|
|
232
|
-
|
|
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
|
-
//
|
|
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:
|
|
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
|
-
//
|
|
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
|
-
//
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
//
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
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
|
-
//
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
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.
|
|
355
|
-
console.log(
|
|
356
|
-
console.log(pc.
|
|
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(
|
|
359
|
-
console.log(
|
|
360
|
-
console.log(
|
|
361
|
-
console.log(
|
|
362
|
-
console.log(
|
|
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 };
|