@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,300 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<link rel="stylesheet" href="/styles/design-system.css">
|
|
7
|
+
<meta name="apple-mobile-web-app-capable" content="yes">
|
|
8
|
+
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
|
9
|
+
<title>AIVA - Group Chat Config</title>
|
|
10
|
+
<style>
|
|
11
|
+
body{font-family:var(--font-sans);background:var(--bg-secondary);color:var(--text);min-height:100vh;display:flex;flex-direction:column;align-items:center;padding:20px}
|
|
12
|
+
.container{max-width:520px;width:100%}
|
|
13
|
+
.header{text-align:center;margin-bottom:24px;padding:16px 0;border-bottom:1px solid var(--border-dim)}
|
|
14
|
+
.header h1{font-size:18px;color:var(--text-dim);font-weight:500;letter-spacing:1px}
|
|
15
|
+
.header .subtitle{font-size:14px;color:var(--blue);margin-top:4px}
|
|
16
|
+
.card{background:var(--surface-glass);border:1px solid var(--border-dim);border-radius:var(--radius-lg);padding:var(--space-lg);margin-bottom:var(--space-lg)}
|
|
17
|
+
.card-label{font-size:12px;color:var(--yellow-border);text-transform:uppercase;letter-spacing:1px;font-weight:600;margin-bottom:10px}
|
|
18
|
+
.group-name{font-size:20px;font-weight:600;color:var(--text)}
|
|
19
|
+
.group-guid{font-size:12px;color:var(--text-muted);margin-top:var(--space-xs);word-break:break-all}
|
|
20
|
+
.participant{display:flex;align-items:center;gap:10px;padding:8px 0;border-bottom:1px solid var(--border-dim)}
|
|
21
|
+
.participant:last-child{border-bottom:none}
|
|
22
|
+
.participant-avatar{width:32px;height:32px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:14px;flex-shrink:0;font-weight:600}
|
|
23
|
+
.participant-avatar.known{background:rgba(35,134,54,0.2);color:var(--green)}
|
|
24
|
+
.participant-avatar.unknown{background:var(--border);color:var(--text-dim)}
|
|
25
|
+
.participant-info{flex:1;min-width:0}
|
|
26
|
+
.participant-name{font-size:14px;color:var(--text);font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
27
|
+
.participant-phone{font-size:12px;color:var(--text-dim)}
|
|
28
|
+
.participant-badge{font-size:11px;padding:2px 8px;border-radius:var(--radius-sm);margin-left:auto;flex-shrink:0;font-weight:500}
|
|
29
|
+
.participant-badge.known-contact{color:var(--green);background:rgba(35,134,54,0.15)}
|
|
30
|
+
.participant-badge.unknown-contact{color:var(--text-dim);background:rgba(139,148,158,0.1)}
|
|
31
|
+
.message-preview{padding:8px 0;border-bottom:1px solid var(--border-dim)}
|
|
32
|
+
.message-preview:last-child{border-bottom:none}
|
|
33
|
+
.msg-sender{font-size:12px;color:var(--blue);font-weight:600}
|
|
34
|
+
.msg-text{font-size:14px;color:var(--text-secondary);margin-top:2px;word-break:break-word}
|
|
35
|
+
.msg-time{font-size:11px;color:var(--text-muted);margin-top:2px}
|
|
36
|
+
.mode-option{width:100%;padding:14px 16px;border:2px solid var(--border);border-radius:var(--radius-lg);font-size:15px;cursor:pointer;margin-bottom:var(--space-sm);transition:all .2s;background:var(--surface-glass);color:var(--text-secondary);text-align:left;display:flex;align-items:center;gap:10px}
|
|
37
|
+
.mode-option:hover{border-color:var(--blue);background:#1c2333}
|
|
38
|
+
.mode-option.selected{border-color:var(--blue);background:#1c2333}
|
|
39
|
+
.mode-icon{font-size:20px;flex-shrink:0}
|
|
40
|
+
.mode-text{flex:1}
|
|
41
|
+
.mode-title{font-size:15px;font-weight:600}
|
|
42
|
+
.mode-desc{font-size:12px;color:var(--text-dim);margin-top:2px;font-weight:400;line-height:1.4}
|
|
43
|
+
.section-label{display:block;font-size:12px;color:var(--text-dim);margin-bottom:6px;text-transform:uppercase;letter-spacing:.5px;margin-top:var(--space-lg)}
|
|
44
|
+
input[type=text],textarea{width:100%;background:var(--surface-glass);border:1px solid var(--border);border-radius:var(--radius-lg);padding:12px 14px;font-size:15px;color:var(--text);font-family:inherit;transition:border-color .2s}
|
|
45
|
+
input[type=text]:focus,textarea:focus{outline:none;border-color:var(--blue)}
|
|
46
|
+
textarea{resize:none;min-height:70px}
|
|
47
|
+
.btn{width:100%;padding:14px;background:var(--green-border);color:#fff;border:none;border-radius:var(--radius-lg);font-size:16px;font-weight:600;cursor:pointer;margin-top:var(--space-lg);transition:background .2s}
|
|
48
|
+
.btn:hover{background:#2ea043}
|
|
49
|
+
.btn:disabled{background:var(--border-dim);color:var(--text-muted);cursor:not-allowed}
|
|
50
|
+
.status{text-align:center;padding:var(--space-lg);border-radius:var(--radius-lg);margin-top:var(--space-lg);font-size:15px}
|
|
51
|
+
.status.success{background:var(--green-bg);border:1px solid var(--green-border);color:var(--green)}
|
|
52
|
+
.status.error{background:var(--red-bg);border:1px solid var(--red-border);color:var(--red)}
|
|
53
|
+
.expired{text-align:center;padding:40px 20px}
|
|
54
|
+
.expired h2{color:var(--red);margin-bottom:var(--space-md)}
|
|
55
|
+
.expired p{color:var(--text-dim);line-height:1.5}
|
|
56
|
+
.loading{text-align:center;padding:60px;color:var(--text-dim)}
|
|
57
|
+
.loading .spinner{display:inline-block;width:24px;height:24px;border:3px solid var(--border);border-top-color:var(--blue);border-radius:50%;animation:spin .8s linear infinite;margin-bottom:12px}
|
|
58
|
+
@keyframes spin{to{transform:rotate(360deg)}}
|
|
59
|
+
.optional-section{margin-top:var(--space-lg)}
|
|
60
|
+
.toggle-btn{color:var(--blue);font-size:13px;cursor:pointer;border:none;background:none;font-family:inherit;padding:0;margin-bottom:var(--space-md)}
|
|
61
|
+
.toggle-btn:hover{text-decoration:underline}
|
|
62
|
+
.hidden{display:none}
|
|
63
|
+
.already-configured{text-align:center;padding:30px 20px}
|
|
64
|
+
.already-configured .mode-badge{display:inline-block;background:var(--blue-bg);color:var(--blue);padding:6px 16px;border-radius:var(--radius-md);font-size:15px;font-weight:600;margin-top:var(--space-sm)}
|
|
65
|
+
.already-configured p{color:var(--text-dim);margin-top:var(--space-md);line-height:1.5}
|
|
66
|
+
</style>
|
|
67
|
+
</head>
|
|
68
|
+
<body>
|
|
69
|
+
<div class="container" id="app">
|
|
70
|
+
<div class="loading" id="loading"><div class="spinner"></div><br>Loading group info...</div>
|
|
71
|
+
</div>
|
|
72
|
+
<script>
|
|
73
|
+
let selectedMode = null;
|
|
74
|
+
let configData = null;
|
|
75
|
+
|
|
76
|
+
function esc(s) { const d = document.createElement('div'); d.textContent = s || ''; return d.innerHTML; }
|
|
77
|
+
|
|
78
|
+
(async () => {
|
|
79
|
+
const app = document.getElementById('app');
|
|
80
|
+
const params = new URLSearchParams(window.location.search);
|
|
81
|
+
const code = params.get('code');
|
|
82
|
+
const token = params.get('token');
|
|
83
|
+
|
|
84
|
+
if (!code || !token) {
|
|
85
|
+
app.innerHTML = '<div class="expired"><h2>Invalid Link</h2><p>This link is missing required parameters.</p></div>';
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
try {
|
|
90
|
+
const res = await fetch(`/api/groups/config/${code}?token=${encodeURIComponent(token)}`);
|
|
91
|
+
if (res.status === 401) {
|
|
92
|
+
app.innerHTML = '<div class="expired"><h2>Link Expired</h2><p>This configuration link has expired or has already been used.</p></div>';
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
if (!res.ok) {
|
|
96
|
+
app.innerHTML = '<div class="expired"><h2>Error</h2><p>Could not load group information. Please try again later.</p></div>';
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
const d = await res.json();
|
|
100
|
+
configData = d;
|
|
101
|
+
|
|
102
|
+
if (d.escalation.status !== 'pending') {
|
|
103
|
+
const modeLabels = { ignore: '🚫 Ignore', monitor: '👁️ Monitor', 'mentioned-only': '💬 Mentioned Only', relevant: '🧠 Relevant', engage: '🗣️ Engage' };
|
|
104
|
+
app.innerHTML = `<div class="header"><h1>📱 GROUP CHAT CONFIG</h1></div>
|
|
105
|
+
<div class="already-configured">
|
|
106
|
+
<h2 style="color:#f0f6fc;margin-bottom:8px">${esc(d.group.name || d.group.nickname || 'Group Chat')}</h2>
|
|
107
|
+
<p>This group is already configured as:</p>
|
|
108
|
+
<div class="mode-badge">${modeLabels[d.group.response_mode] || d.group.response_mode}</div>
|
|
109
|
+
<p style="margin-top:16px;font-size:13px">To change settings, ask AIVA to send a new config link.</p>
|
|
110
|
+
</div>`;
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const groupName = d.group.name || d.group.nickname || 'Unnamed Group';
|
|
115
|
+
|
|
116
|
+
// Build participants HTML with contact status
|
|
117
|
+
let participantsHtml = '';
|
|
118
|
+
if (d.participants && d.participants.length > 0) {
|
|
119
|
+
participantsHtml = d.participants.map(p => {
|
|
120
|
+
const name = p.resolved_name || p.display_name || '';
|
|
121
|
+
const initial = (name || p.phone || '?')[0].toUpperCase();
|
|
122
|
+
const isKnown = p.category && p.category !== 'unknown';
|
|
123
|
+
return `<div class="participant">
|
|
124
|
+
<div class="participant-avatar ${isKnown ? 'known' : 'unknown'}">${esc(initial)}</div>
|
|
125
|
+
<div class="participant-info">
|
|
126
|
+
<div class="participant-name">${esc(name || p.phone)}</div>
|
|
127
|
+
${name && !name.includes('Unknown') ? `<div class="participant-phone">${esc(p.phone)}</div>` : ''}
|
|
128
|
+
</div>
|
|
129
|
+
<span class="participant-badge ${isKnown ? 'known-contact' : 'unknown-contact'}">${isKnown ? '✓ Known' : 'Unknown'}</span>
|
|
130
|
+
</div>`;
|
|
131
|
+
}).join('');
|
|
132
|
+
} else {
|
|
133
|
+
participantsHtml = '<div style="color:#8b949e;font-size:14px;padding:8px 0">No participants identified yet</div>';
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Build recent messages HTML
|
|
137
|
+
let messagesHtml = '';
|
|
138
|
+
if (d.recentMessages && d.recentMessages.length > 0) {
|
|
139
|
+
messagesHtml = d.recentMessages.slice(0, 8).map(m => {
|
|
140
|
+
const time = new Date(m.ts).toLocaleString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true });
|
|
141
|
+
return `<div class="message-preview">
|
|
142
|
+
<div class="msg-sender">${esc(m.sender)}</div>
|
|
143
|
+
<div class="msg-text">${esc(m.text)}</div>
|
|
144
|
+
<div class="msg-time">${time}</div>
|
|
145
|
+
</div>`;
|
|
146
|
+
}).join('');
|
|
147
|
+
} else {
|
|
148
|
+
messagesHtml = '<div style="color:#8b949e;font-size:14px;padding:8px 0">No recent messages available</div>';
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
app.innerHTML = `
|
|
152
|
+
<div class="header">
|
|
153
|
+
<h1>📱 GROUP CHAT CONFIG</h1>
|
|
154
|
+
<div class="subtitle">New group detected</div>
|
|
155
|
+
</div>
|
|
156
|
+
|
|
157
|
+
<div class="card">
|
|
158
|
+
<div class="group-name">${esc(groupName)}</div>
|
|
159
|
+
<div class="group-guid">${esc(d.group.chat_guid)}</div>
|
|
160
|
+
</div>
|
|
161
|
+
|
|
162
|
+
<div class="card">
|
|
163
|
+
<div class="card-label">Participants (${d.participants ? d.participants.length : 0})</div>
|
|
164
|
+
${participantsHtml}
|
|
165
|
+
</div>
|
|
166
|
+
|
|
167
|
+
<div class="card">
|
|
168
|
+
<div class="card-label">Recent Messages</div>
|
|
169
|
+
${messagesHtml}
|
|
170
|
+
</div>
|
|
171
|
+
|
|
172
|
+
<div class="card-label" style="margin-top:20px">How should AIVA handle this group?</div>
|
|
173
|
+
|
|
174
|
+
<button class="mode-option" onclick="selectMode('ignore',this)">
|
|
175
|
+
<span class="mode-icon">🚫</span>
|
|
176
|
+
<span class="mode-text">
|
|
177
|
+
<div class="mode-title">Ignore</div>
|
|
178
|
+
<div class="mode-desc">Completely ignore all messages in this group. No logging, no responses. Best for groups AIVA shouldn't be involved in at all.</div>
|
|
179
|
+
</span>
|
|
180
|
+
</button>
|
|
181
|
+
|
|
182
|
+
<button class="mode-option" onclick="selectMode('monitor',this)">
|
|
183
|
+
<span class="mode-icon">👁️</span>
|
|
184
|
+
<span class="mode-text">
|
|
185
|
+
<div class="mode-title">Monitor</div>
|
|
186
|
+
<div class="mode-desc">Silently watch the conversation. Never responds, but will alert you about important messages (action items, mentions of you, urgent requests).</div>
|
|
187
|
+
</span>
|
|
188
|
+
</button>
|
|
189
|
+
|
|
190
|
+
<button class="mode-option" onclick="selectMode('mentioned-only',this)">
|
|
191
|
+
<span class="mode-icon">💬</span>
|
|
192
|
+
<span class="mode-text">
|
|
193
|
+
<div class="mode-title">Mentioned Only</div>
|
|
194
|
+
<div class="mode-desc">Only responds when someone says "Aiva" or "@Aiva" in the chat. Stays quiet otherwise. Good for groups where people know AIVA exists.</div>
|
|
195
|
+
</span>
|
|
196
|
+
</button>
|
|
197
|
+
|
|
198
|
+
<button class="mode-option" onclick="selectMode('relevant',this)">
|
|
199
|
+
<span class="mode-icon">🧠</span>
|
|
200
|
+
<span class="mode-text">
|
|
201
|
+
<div class="mode-title">Respond When Relevant</div>
|
|
202
|
+
<div class="mode-desc">AI uses judgment to decide when a response would be helpful — questions it can answer, topics it has context on, etc. Won't spam the chat.</div>
|
|
203
|
+
</span>
|
|
204
|
+
</button>
|
|
205
|
+
|
|
206
|
+
<button class="mode-option" onclick="selectMode('engage',this)">
|
|
207
|
+
<span class="mode-icon">🗣️</span>
|
|
208
|
+
<span class="mode-text">
|
|
209
|
+
<div class="mode-title">Full Engagement</div>
|
|
210
|
+
<div class="mode-desc">Participates naturally in conversations like a group member. Still uses judgment about when to chime in. Best for close friend/family groups.</div>
|
|
211
|
+
</span>
|
|
212
|
+
</button>
|
|
213
|
+
|
|
214
|
+
<button class="toggle-btn" onclick="toggleOptional()">⚙️ Optional settings ▾</button>
|
|
215
|
+
<div class="optional-section hidden" id="optionalSection">
|
|
216
|
+
<label class="section-label">Group Nickname</label>
|
|
217
|
+
<input type="text" id="nickname" placeholder="e.g. Family Chat, Work Team" value="${esc(d.group.nickname || '')}">
|
|
218
|
+
|
|
219
|
+
<label class="section-label">Group Purpose / Context</label>
|
|
220
|
+
<textarea id="purpose" placeholder="What is this group about? Helps AI make better decisions about when to respond.">${esc(d.group.purpose || '')}</textarea>
|
|
221
|
+
|
|
222
|
+
<label class="section-label">Context Notes</label>
|
|
223
|
+
<textarea id="contextNotes" placeholder="Any context about this group (inside jokes, sensitive topics to avoid, etc.)">${esc(d.group.context_notes || '')}</textarea>
|
|
224
|
+
|
|
225
|
+
<label class="section-label">Custom Instructions</label>
|
|
226
|
+
<textarea id="customInstructions" placeholder="Special rules for AIVA in this group (e.g. 'never discuss work topics', 'always respond in Spanish')">${esc(d.group.custom_instructions || '')}</textarea>
|
|
227
|
+
</div>
|
|
228
|
+
|
|
229
|
+
<button class="btn" id="saveBtn" onclick="saveConfig()" disabled>Select a mode above</button>
|
|
230
|
+
<div id="status"></div>
|
|
231
|
+
`;
|
|
232
|
+
} catch (e) {
|
|
233
|
+
app.innerHTML = '<div class="expired"><h2>Connection Error</h2><p>Could not connect to the server. Check your connection and try again.</p></div>';
|
|
234
|
+
}
|
|
235
|
+
})();
|
|
236
|
+
|
|
237
|
+
function selectMode(mode, btn) {
|
|
238
|
+
selectedMode = mode;
|
|
239
|
+
document.querySelectorAll('.mode-option').forEach(b => b.classList.remove('selected'));
|
|
240
|
+
btn.classList.add('selected');
|
|
241
|
+
const saveBtn = document.getElementById('saveBtn');
|
|
242
|
+
saveBtn.disabled = false;
|
|
243
|
+
saveBtn.textContent = 'Save Configuration';
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function toggleOptional() {
|
|
247
|
+
document.getElementById('optionalSection').classList.toggle('hidden');
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
async function saveConfig() {
|
|
251
|
+
if (!selectedMode) return;
|
|
252
|
+
const btn = document.getElementById('saveBtn');
|
|
253
|
+
const statusEl = document.getElementById('status');
|
|
254
|
+
btn.disabled = true;
|
|
255
|
+
btn.textContent = 'Saving...';
|
|
256
|
+
statusEl.className = 'status';
|
|
257
|
+
statusEl.textContent = '';
|
|
258
|
+
|
|
259
|
+
const params = new URLSearchParams(window.location.search);
|
|
260
|
+
const code = params.get('code');
|
|
261
|
+
const token = params.get('token');
|
|
262
|
+
|
|
263
|
+
const body = {
|
|
264
|
+
token,
|
|
265
|
+
mode: selectedMode,
|
|
266
|
+
name: configData.group.name || '',
|
|
267
|
+
nickname: document.getElementById('nickname')?.value.trim() || '',
|
|
268
|
+
purpose: document.getElementById('purpose')?.value.trim() || '',
|
|
269
|
+
contextNotes: document.getElementById('contextNotes')?.value.trim() || '',
|
|
270
|
+
customInstructions: document.getElementById('customInstructions')?.value.trim() || ''
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
try {
|
|
274
|
+
const res = await fetch(`/api/groups/config/${code}`, {
|
|
275
|
+
method: 'POST',
|
|
276
|
+
headers: { 'Content-Type': 'application/json' },
|
|
277
|
+
body: JSON.stringify(body)
|
|
278
|
+
});
|
|
279
|
+
const d = await res.json();
|
|
280
|
+
if (res.ok && d.success) {
|
|
281
|
+
statusEl.className = 'status success';
|
|
282
|
+
statusEl.textContent = `✅ Group configured as "${selectedMode}"`;
|
|
283
|
+
btn.style.display = 'none';
|
|
284
|
+
document.querySelectorAll('.mode-option').forEach(b => { b.style.pointerEvents = 'none'; b.style.opacity = '0.5'; });
|
|
285
|
+
} else {
|
|
286
|
+
statusEl.className = 'status error';
|
|
287
|
+
statusEl.textContent = d.error || 'Failed to save configuration';
|
|
288
|
+
btn.disabled = false;
|
|
289
|
+
btn.textContent = 'Retry';
|
|
290
|
+
}
|
|
291
|
+
} catch (e) {
|
|
292
|
+
statusEl.className = 'status error';
|
|
293
|
+
statusEl.textContent = 'Network error — check your connection and try again';
|
|
294
|
+
btn.disabled = false;
|
|
295
|
+
btn.textContent = 'Retry';
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
</script>
|
|
299
|
+
</body>
|
|
300
|
+
</html>
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="4" y="4" width="16" height="16" rx="2"/><circle cx="9" cy="10" r="1.5"/><circle cx="15" cy="10" r="1.5"/><path d="M9 15h6"/><line x1="12" y1="2" x2="12" y2="4"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 21v-2a4 4 0 0 0-4-4H8a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/><line x1="8" y1="9" x2="16" y2="9"/><line x1="8" y1="13" x2="13" y2="13"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="8" y1="13" x2="16" y2="13"/><line x1="8" y1="17" x2="12" y2="17"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M22 12h-4l-3 9L9 3l-3 9H2"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><path d="M12 1a3 3 0 0 0-3 3v8a3 3 0 0 0 6 0V4a3 3 0 0 0-3-3z"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><line x1="12" y1="19" x2="12" y2="23"/><line x1="8" y1="23" x2="16" y2="23"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z"/><path d="M19 10v2a7 7 0 0 1-14 0v-2"/><line x1="12" y1="19" x2="12" y2="22"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 2.83-2.83l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M9 11l2 2 4-4"/><line x1="9" y1="17" x2="15" y2="17"/></svg>
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|