@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,107 @@
|
|
|
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
|
+
<title>Permission Request - AIVA</title>
|
|
7
|
+
<style>
|
|
8
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
9
|
+
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; background: #0a0a0a; color: #e0e0e0; min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 20px; }
|
|
10
|
+
.card { background: #1a1a1a; border: 1px solid #333; border-radius: 16px; padding: 32px; max-width: 480px; width: 100%; }
|
|
11
|
+
.icon { font-size: 48px; margin-bottom: 16px; }
|
|
12
|
+
h1 { font-size: 20px; margin-bottom: 8px; color: #fff; }
|
|
13
|
+
.subtitle { color: #888; font-size: 14px; margin-bottom: 24px; }
|
|
14
|
+
.field { margin-bottom: 16px; }
|
|
15
|
+
.field label { display: block; font-size: 12px; color: #666; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 4px; }
|
|
16
|
+
.field .value { font-size: 15px; color: #ccc; background: #111; padding: 10px 12px; border-radius: 8px; border: 1px solid #222; }
|
|
17
|
+
.field .value.message { white-space: pre-wrap; font-style: italic; }
|
|
18
|
+
.actions { display: flex; gap: 12px; margin-top: 24px; }
|
|
19
|
+
.btn { flex: 1; padding: 14px; border: none; border-radius: 10px; font-size: 16px; font-weight: 600; cursor: pointer; transition: opacity 0.2s; }
|
|
20
|
+
.btn:hover { opacity: 0.85; }
|
|
21
|
+
.btn:disabled { opacity: 0.4; cursor: not-allowed; }
|
|
22
|
+
.btn-allow { background: #22c55e; color: #fff; }
|
|
23
|
+
.btn-deny { background: #333; color: #ccc; }
|
|
24
|
+
.result { text-align: center; padding: 20px 0; font-size: 18px; }
|
|
25
|
+
.result.approved { color: #22c55e; }
|
|
26
|
+
.result.denied { color: #ef4444; }
|
|
27
|
+
.loading { text-align: center; color: #666; padding: 40px 0; }
|
|
28
|
+
.error { text-align: center; color: #ef4444; padding: 40px 0; }
|
|
29
|
+
</style>
|
|
30
|
+
</head>
|
|
31
|
+
<body>
|
|
32
|
+
<div class="card" id="main">
|
|
33
|
+
<div class="loading" id="loading">Loading request...</div>
|
|
34
|
+
<div id="content" style="display:none">
|
|
35
|
+
<div class="icon">🔐</div>
|
|
36
|
+
<h1>Permission Request</h1>
|
|
37
|
+
<div class="subtitle">Someone is requesting AIVA to take action on their behalf.</div>
|
|
38
|
+
<div class="field"><label>Contact</label><div class="value" id="contact"></div></div>
|
|
39
|
+
<div class="field"><label>Request Type</label><div class="value" id="type"></div></div>
|
|
40
|
+
<div class="field"><label>What they're asking</label><div class="value" id="details"></div></div>
|
|
41
|
+
<div class="field"><label>Original Message</label><div class="value message" id="message"></div></div>
|
|
42
|
+
<div class="field"><label>Notes / Instructions for AIVA</label><textarea id="notes" placeholder="How should AIVA handle this? (e.g., 'always book for mornings', 'check with me first on cost')" style="width:100%;min-height:80px;background:#111;color:#ccc;border:1px solid #222;border-radius:8px;padding:10px 12px;font-size:14px;font-family:inherit;resize:vertical;"></textarea></div>
|
|
43
|
+
<div class="actions" style="flex-wrap: wrap;">
|
|
44
|
+
<button class="btn btn-allow" id="btnApproveOnce" onclick="resolve('approved', false)" style="background:#3b82f6;">Approve This Time</button>
|
|
45
|
+
<button class="btn btn-allow" id="btnAlwaysAllow" onclick="resolve('approved', true)">Always Allow</button>
|
|
46
|
+
<button class="btn btn-deny" id="btnDeny" onclick="resolve('denied', false)">Deny</button>
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
49
|
+
<div id="result" style="display:none"></div>
|
|
50
|
+
<div id="error" style="display:none" class="error"></div>
|
|
51
|
+
</div>
|
|
52
|
+
<script>
|
|
53
|
+
const token = window.location.pathname.split('/').pop();
|
|
54
|
+
const API = '/api/router/permission-requests/' + token;
|
|
55
|
+
|
|
56
|
+
fetch(API, { headers: { 'x-aiva-internal': 'true' } })
|
|
57
|
+
.then(r => { if (!r.ok) throw new Error('Not found'); return r.json(); })
|
|
58
|
+
.then(data => {
|
|
59
|
+
document.getElementById('loading').style.display = 'none';
|
|
60
|
+
if (data.status !== 'pending') {
|
|
61
|
+
document.getElementById('result').style.display = 'block';
|
|
62
|
+
document.getElementById('result').className = 'result ' + data.status;
|
|
63
|
+
document.getElementById('result').textContent = 'This request has already been ' + data.status + '.';
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
document.getElementById('content').style.display = 'block';
|
|
67
|
+
document.getElementById('contact').textContent = (data.contact_name || 'Unknown') + ' (' + data.phone + ')';
|
|
68
|
+
document.getElementById('type').textContent = data.permission_type;
|
|
69
|
+
document.getElementById('details').textContent = data.request_details || 'N/A';
|
|
70
|
+
document.getElementById('message').textContent = data.original_message || 'N/A';
|
|
71
|
+
})
|
|
72
|
+
.catch(() => {
|
|
73
|
+
document.getElementById('loading').style.display = 'none';
|
|
74
|
+
document.getElementById('error').style.display = 'block';
|
|
75
|
+
document.getElementById('error').textContent = 'Permission request not found or expired.';
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
function resolve(status, alwaysAllow) {
|
|
79
|
+
document.querySelectorAll('.btn').forEach(b => b.disabled = true);
|
|
80
|
+
const notes = (document.getElementById('notes')?.value || '').trim();
|
|
81
|
+
fetch(API + '/resolve', {
|
|
82
|
+
method: 'POST',
|
|
83
|
+
headers: { 'Content-Type': 'application/json', 'x-aiva-internal': 'true' },
|
|
84
|
+
body: JSON.stringify({ status, alwaysAllow: !!alwaysAllow, notes: notes || undefined })
|
|
85
|
+
})
|
|
86
|
+
.then(r => r.json())
|
|
87
|
+
.then(() => {
|
|
88
|
+
document.getElementById('content').style.display = 'none';
|
|
89
|
+
document.getElementById('result').style.display = 'block';
|
|
90
|
+
document.getElementById('result').className = 'result ' + status;
|
|
91
|
+
if (status === 'approved' && alwaysAllow) {
|
|
92
|
+
document.getElementById('result').textContent = 'Approved! This permission type is now always allowed for this contact.';
|
|
93
|
+
} else if (status === 'approved') {
|
|
94
|
+
document.getElementById('result').textContent = 'Approved this time! They\'ll need approval again next time.';
|
|
95
|
+
} else {
|
|
96
|
+
document.getElementById('result').textContent = 'Denied. They can ask again later.';
|
|
97
|
+
}
|
|
98
|
+
})
|
|
99
|
+
.catch(() => {
|
|
100
|
+
document.getElementById('error').style.display = 'block';
|
|
101
|
+
document.getElementById('error').textContent = 'Failed to process. Try again.';
|
|
102
|
+
document.querySelectorAll('.btn').forEach(b => b.disabled = false);
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
</script>
|
|
106
|
+
</body>
|
|
107
|
+
</html>
|
|
@@ -0,0 +1,150 @@
|
|
|
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
|
+
<title>AIVA - Permission Request</title>
|
|
8
|
+
<style>
|
|
9
|
+
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}
|
|
10
|
+
.container{max-width:480px;width:100%}
|
|
11
|
+
.header{text-align:center;margin-bottom:24px;padding:16px 0;border-bottom:1px solid var(--border-dim)}
|
|
12
|
+
.header h1{font-size:18px;color:var(--text-dim);font-weight:500;letter-spacing:1px}
|
|
13
|
+
.contact-info{background:var(--surface-glass);border:1px solid var(--border-dim);border-radius:var(--radius-lg);padding:var(--space-lg);margin-bottom:var(--space-lg)}
|
|
14
|
+
.contact-name{font-size:20px;font-weight:600;color:var(--text)}
|
|
15
|
+
.contact-phone{font-size:14px;color:var(--text-dim);margin-top:var(--space-xs)}
|
|
16
|
+
.bubble{background:#1c2333;border:1px solid var(--border-dim);border-radius:var(--radius-xl);border-top-left-radius:var(--radius-sm);padding:14px 18px;margin-bottom:var(--space-lg);font-size:16px;line-height:1.5;color:var(--text-secondary)}
|
|
17
|
+
.bubble-label{font-size:12px;color:var(--text-dim);margin-bottom:6px;text-transform:uppercase;letter-spacing:.5px}
|
|
18
|
+
.detail-box{background:var(--surface-glass);border:1px solid var(--border-dim);border-radius:var(--radius-lg);padding:var(--space-lg);margin-bottom:var(--space-lg)}
|
|
19
|
+
.detail-label{font-size:12px;color:var(--text-dim);text-transform:uppercase;letter-spacing:.5px;margin-bottom:var(--space-xs)}
|
|
20
|
+
.detail-value{font-size:15px;color:var(--text-secondary);line-height:1.4}
|
|
21
|
+
.permission-badge{display:inline-block;background:#1f2937;border:1px solid var(--border);border-radius:var(--radius-md);padding:6px 12px;font-size:14px;color:var(--blue);font-weight:600;margin-top:var(--space-xs)}
|
|
22
|
+
.decision-section{margin-top:var(--space-xl)}
|
|
23
|
+
.decision-label{font-size:12px;color:var(--text-dim);text-transform:uppercase;letter-spacing:.5px;margin-bottom:var(--space-md)}
|
|
24
|
+
.decision-btn{width:100%;padding:14px 16px;border:2px solid var(--border);border-radius:var(--radius-lg);font-size:15px;font-weight:600;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;font-family:var(--font-sans)}
|
|
25
|
+
.decision-btn:hover{border-color:var(--blue);background:#1c2333}
|
|
26
|
+
.decision-btn.selected{border-color:var(--blue);background:#1c2333}
|
|
27
|
+
.decision-btn.approve-once.selected{border-color:var(--green-border);background:var(--green-bg)}
|
|
28
|
+
.decision-btn.approve-always.selected{border-color:var(--yellow-border);background:var(--yellow-bg)}
|
|
29
|
+
.decision-btn.deny.selected{border-color:var(--red-border);background:var(--red-bg)}
|
|
30
|
+
.decision-icon{font-size:20px;flex-shrink:0}
|
|
31
|
+
.decision-text{flex:1}
|
|
32
|
+
.decision-title{font-size:15px;font-weight:600}
|
|
33
|
+
.decision-desc{font-size:12px;color:var(--text-dim);margin-top:2px;font-weight:400}
|
|
34
|
+
.btn{width:100%;padding:14px;color:#fff;border:none;border-radius:var(--radius-lg);font-size:16px;font-weight:600;cursor:pointer;transition:all .2s;margin-top:var(--space-md)}
|
|
35
|
+
.btn-approve{background:var(--green-border)}
|
|
36
|
+
.btn-approve:hover{background:#2ea043}
|
|
37
|
+
.btn-warn{background:var(--yellow-border)}
|
|
38
|
+
.btn-warn:hover{background:#d29922}
|
|
39
|
+
.btn-deny{background:var(--red-border)}
|
|
40
|
+
.btn-deny:hover{background:#f85149}
|
|
41
|
+
.btn:disabled{background:var(--border-dim);color:var(--text-muted);cursor:not-allowed}
|
|
42
|
+
.status{text-align:center;padding:16px;border-radius:var(--radius-lg);margin-top:var(--space-lg);font-size:15px}
|
|
43
|
+
.status.success{background:var(--green-bg);border:1px solid var(--green-border);color:var(--green)}
|
|
44
|
+
.status.error{background:var(--red-bg);border:1px solid var(--red-border);color:var(--red)}
|
|
45
|
+
.status.denied{background:var(--red-bg);border:1px solid var(--red-border);color:var(--red)}
|
|
46
|
+
.expired{text-align:center;padding:40px 20px}
|
|
47
|
+
.expired h2{color:var(--red);margin-bottom:var(--space-md)}
|
|
48
|
+
.expired p{color:var(--text-dim)}
|
|
49
|
+
.loading{text-align:center;padding:60px;color:var(--text-dim)}
|
|
50
|
+
</style>
|
|
51
|
+
</head>
|
|
52
|
+
<body>
|
|
53
|
+
<div class="container" id="app">
|
|
54
|
+
<div class="loading" id="loading">Loading permission request...</div>
|
|
55
|
+
</div>
|
|
56
|
+
<script>
|
|
57
|
+
(async()=>{
|
|
58
|
+
const app=document.getElementById('app');
|
|
59
|
+
const token=window.location.pathname.split('/').pop();
|
|
60
|
+
if(!token){app.innerHTML='<div class="expired"><h2>Invalid Link</h2><p>Missing token.</p></div>';return}
|
|
61
|
+
try{
|
|
62
|
+
const res=await fetch(`/api/router/permission-requests/${encodeURIComponent(token)}`,{headers:{'x-aiva-internal':'true'}});
|
|
63
|
+
if(!res.ok){app.innerHTML='<div class="expired"><h2>Not Found</h2><p>This permission request was not found or has expired.</p></div>';return}
|
|
64
|
+
const d=await res.json();
|
|
65
|
+
if(d.status!=='pending'){
|
|
66
|
+
const color=d.status==='approved'?'#3fb950':'#f85149';
|
|
67
|
+
app.innerHTML=`<div class="header"><h1>AIVA PERMISSION</h1></div><div class="status" style="background:${d.status==='approved'?'#0d1f0d':'#1f0d0d'};border:1px solid ${color};color:${color};padding:24px;font-size:18px">This request has already been ${esc(d.status)}.</div>`;
|
|
68
|
+
return}
|
|
69
|
+
app.innerHTML=`
|
|
70
|
+
<div class="header"><h1>AIVA PERMISSION REQUEST</h1></div>
|
|
71
|
+
<div class="contact-info">
|
|
72
|
+
<div class="contact-name">${esc(d.contact_name||'Unknown')}</div>
|
|
73
|
+
<div class="contact-phone">${esc(d.phone)}</div>
|
|
74
|
+
</div>
|
|
75
|
+
<div class="bubble-label">Their message</div>
|
|
76
|
+
<div class="bubble">${esc(d.original_message)}</div>
|
|
77
|
+
<div class="detail-box">
|
|
78
|
+
<div class="detail-label">What they're requesting</div>
|
|
79
|
+
<div class="detail-value">${esc(d.request_details)}</div>
|
|
80
|
+
</div>
|
|
81
|
+
<div class="detail-box">
|
|
82
|
+
<div class="detail-label">Permission needed</div>
|
|
83
|
+
<div class="permission-badge">${esc(d.permission_type)}</div>
|
|
84
|
+
</div>
|
|
85
|
+
<div class="decision-section">
|
|
86
|
+
<div class="decision-label">What do you want to do?</div>
|
|
87
|
+
<button class="decision-btn approve-once" id="approveOnceBtn" onclick="selectDecision('approved',this)">
|
|
88
|
+
<span class="decision-icon">✅</span>
|
|
89
|
+
<span class="decision-text">
|
|
90
|
+
<div class="decision-title">Allow This Time</div>
|
|
91
|
+
<div class="decision-desc">Handle this one request only</div>
|
|
92
|
+
</span>
|
|
93
|
+
</button>
|
|
94
|
+
<button class="decision-btn approve-always" id="approveAlwaysBtn" onclick="selectDecision('approved_always',this)">
|
|
95
|
+
<span class="decision-icon">🔓</span>
|
|
96
|
+
<span class="decision-text">
|
|
97
|
+
<div class="decision-title">Allow Always</div>
|
|
98
|
+
<div class="decision-desc">Auto-approve this type of request from ${esc(d.contact_name||'this contact')} going forward</div>
|
|
99
|
+
</span>
|
|
100
|
+
</button>
|
|
101
|
+
<button class="decision-btn deny" id="denyBtn" onclick="selectDecision('denied',this)">
|
|
102
|
+
<span class="decision-icon">🚫</span>
|
|
103
|
+
<span class="decision-text">
|
|
104
|
+
<div class="decision-title">Deny</div>
|
|
105
|
+
<div class="decision-desc">Reject this request</div>
|
|
106
|
+
</span>
|
|
107
|
+
</button>
|
|
108
|
+
</div>
|
|
109
|
+
<button class="btn" id="sendBtn" disabled>Select a decision above</button>
|
|
110
|
+
<div id="status"></div>`;
|
|
111
|
+
}catch(e){app.innerHTML='<div class="expired"><h2>Error</h2><p>Could not load request.</p></div>'}
|
|
112
|
+
function esc(s){const d=document.createElement('div');d.textContent=s||'';return d.innerHTML}
|
|
113
|
+
let currentDecision=null;
|
|
114
|
+
window.selectDecision=function(decision,btn){
|
|
115
|
+
currentDecision=decision;
|
|
116
|
+
document.querySelectorAll('.decision-btn').forEach(b=>b.classList.remove('selected'));
|
|
117
|
+
btn.classList.add('selected');
|
|
118
|
+
const sendBtn=document.getElementById('sendBtn');
|
|
119
|
+
sendBtn.disabled=false;
|
|
120
|
+
if(decision==='approved'){sendBtn.textContent='Allow This Time';sendBtn.className='btn btn-approve';sendBtn.onclick=resolve}
|
|
121
|
+
else if(decision==='approved_always'){sendBtn.textContent='Allow Always';sendBtn.className='btn btn-warn';sendBtn.onclick=resolve}
|
|
122
|
+
else{sendBtn.textContent='Deny Request';sendBtn.className='btn btn-deny';sendBtn.onclick=resolve}
|
|
123
|
+
};
|
|
124
|
+
window.resolve=async()=>{
|
|
125
|
+
if(!currentDecision)return;
|
|
126
|
+
const sendBtn=document.getElementById('sendBtn');
|
|
127
|
+
sendBtn.disabled=true;sendBtn.textContent='Saving...';
|
|
128
|
+
const status=currentDecision==='approved_always'?'approved':currentDecision;
|
|
129
|
+
const always=currentDecision==='approved_always';
|
|
130
|
+
try{
|
|
131
|
+
const r=await fetch(`/api/router/permission-requests/${encodeURIComponent(token)}/resolve`,{method:'POST',headers:{'Content-Type':'application/json','x-aiva-internal':'true'},body:JSON.stringify({status,always})});
|
|
132
|
+
const d=await r.json();
|
|
133
|
+
const el=document.getElementById('status');
|
|
134
|
+
if(r.ok){
|
|
135
|
+
el.className='status '+(status==='approved'?'success':'denied');
|
|
136
|
+
el.textContent=status==='approved'?(always?'Permission granted permanently. AIVA will auto-approve this going forward.':'Permission granted for this request. AIVA will handle it now.'):'Request denied.';
|
|
137
|
+
sendBtn.style.display='none';
|
|
138
|
+
document.querySelectorAll('.decision-btn').forEach(b=>{b.style.pointerEvents='none';b.style.opacity='0.5'});
|
|
139
|
+
}else{
|
|
140
|
+
el.className='status error';el.textContent=d.error||'Failed';
|
|
141
|
+
sendBtn.disabled=false;sendBtn.textContent='Retry';
|
|
142
|
+
}
|
|
143
|
+
}catch(e){
|
|
144
|
+
const el=document.getElementById('status');el.className='status error';el.textContent='Network error';
|
|
145
|
+
sendBtn.disabled=false;sendBtn.textContent='Retry';
|
|
146
|
+
}}
|
|
147
|
+
})();
|
|
148
|
+
</script>
|
|
149
|
+
</body>
|
|
150
|
+
</html>
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/* AIVA Design System — Shared across all pages */
|
|
2
|
+
|
|
3
|
+
/* Global reset */
|
|
4
|
+
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
|
|
5
|
+
|
|
6
|
+
:root {
|
|
7
|
+
/* Colors */
|
|
8
|
+
--bg: #0a0a0f;
|
|
9
|
+
--bg-secondary: #0f1117;
|
|
10
|
+
--surface: #14141f;
|
|
11
|
+
--surface2: #1e1e2e;
|
|
12
|
+
--surface-glass: #161b22;
|
|
13
|
+
--border: #2a2a3e;
|
|
14
|
+
--border-dim: #21262d;
|
|
15
|
+
|
|
16
|
+
/* Text */
|
|
17
|
+
--text: #e0e0f0;
|
|
18
|
+
--text-secondary: #c9d1d9;
|
|
19
|
+
--text-dim: #8888aa;
|
|
20
|
+
--text-muted: #484f58;
|
|
21
|
+
|
|
22
|
+
/* Accent */
|
|
23
|
+
--accent: #6c63ff;
|
|
24
|
+
--accent-hover: #7c74ff;
|
|
25
|
+
|
|
26
|
+
/* Semantic */
|
|
27
|
+
--green: #4ade80;
|
|
28
|
+
--green-bg: #0d1f0d;
|
|
29
|
+
--green-border: #238636;
|
|
30
|
+
--red: #f87171;
|
|
31
|
+
--red-bg: #1f0d0d;
|
|
32
|
+
--red-border: #da3633;
|
|
33
|
+
--yellow: #fbbf24;
|
|
34
|
+
--yellow-bg: #1f1a0d;
|
|
35
|
+
--yellow-border: #f0883e;
|
|
36
|
+
--blue: #60a5fa;
|
|
37
|
+
--blue-bg: rgba(96,165,250,0.1);
|
|
38
|
+
|
|
39
|
+
/* Spacing scale */
|
|
40
|
+
--space-xs: 4px;
|
|
41
|
+
--space-sm: 8px;
|
|
42
|
+
--space-md: 12px;
|
|
43
|
+
--space-lg: 16px;
|
|
44
|
+
--space-xl: 20px;
|
|
45
|
+
--space-2xl: 24px;
|
|
46
|
+
--space-3xl: 32px;
|
|
47
|
+
|
|
48
|
+
/* Border radius scale */
|
|
49
|
+
--radius-sm: 4px;
|
|
50
|
+
--radius-md: 8px;
|
|
51
|
+
--radius-lg: 12px;
|
|
52
|
+
--radius-xl: 16px;
|
|
53
|
+
|
|
54
|
+
/* Typography */
|
|
55
|
+
--font-sans: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
56
|
+
--font-mono: ui-monospace, 'Cascadia Code', 'Source Code Pro', Menlo, Consolas, monospace;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/* Focus styles — global accessibility */
|
|
60
|
+
*:focus-visible {
|
|
61
|
+
outline: 2px solid var(--accent);
|
|
62
|
+
outline-offset: 2px;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/* Remove outline for mouse users */
|
|
66
|
+
*:focus:not(:focus-visible) {
|
|
67
|
+
outline: none;
|
|
68
|
+
}
|