@agenticmail/enterprise 0.5.13 ā 0.5.15
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/dist/chunk-LNWVYHWX.js +1987 -0
- package/dist/chunk-VBKWGYN3.js +889 -0
- package/dist/cli.js +1 -1
- package/dist/dashboard/pages/settings.js +74 -0
- package/dist/index.js +2 -2
- package/dist/server-JRXIBDTH.js +11 -0
- package/dist/setup-UH5AXHZX.js +20 -0
- package/package.json +1 -1
- package/src/admin/routes.ts +25 -0
- package/src/dashboard/pages/settings.js +74 -0
- package/src/server.ts +23 -0
package/dist/cli.js
CHANGED
|
@@ -48,6 +48,10 @@ export function SettingsPage() {
|
|
|
48
48
|
var newProvider = _newProvider[0]; var setNewProvider = _newProvider[1];
|
|
49
49
|
var _discoverResults = useState({});
|
|
50
50
|
var discoverResults = _discoverResults[0]; var setDiscoverResults = _discoverResults[1];
|
|
51
|
+
var _apiKeyModal = useState(null); // { providerId, providerName, isUpdate }
|
|
52
|
+
var apiKeyModal = _apiKeyModal[0]; var setApiKeyModal = _apiKeyModal[1];
|
|
53
|
+
var _apiKeyInput = useState('');
|
|
54
|
+
var apiKeyInput = _apiKeyInput[0]; var setApiKeyInput = _apiKeyInput[1];
|
|
51
55
|
|
|
52
56
|
useEffect(() => {
|
|
53
57
|
apiCall('/settings').then(d => { const s = d.settings || d || {}; setSettings(s); if (s.primaryColor) applyBrandColor(s.primaryColor); }).catch(() => {});
|
|
@@ -558,6 +562,10 @@ export function SettingsPage() {
|
|
|
558
562
|
setNewProvider: setNewProvider,
|
|
559
563
|
discoverResults: discoverResults,
|
|
560
564
|
setDiscoverResults: setDiscoverResults,
|
|
565
|
+
apiKeyModal: apiKeyModal,
|
|
566
|
+
setApiKeyModal: setApiKeyModal,
|
|
567
|
+
apiKeyInput: apiKeyInput,
|
|
568
|
+
setApiKeyInput: setApiKeyInput,
|
|
561
569
|
onSave: function() {
|
|
562
570
|
setPricingSaving(true);
|
|
563
571
|
apiCall('/settings/model-pricing', { method: 'PUT', body: JSON.stringify(pricing) }).then(function(d) {
|
|
@@ -1310,6 +1318,22 @@ function ProvidersSection(props) {
|
|
|
1310
1318
|
}, isConfigured ? 'Connected' : 'Not Configured')
|
|
1311
1319
|
),
|
|
1312
1320
|
isLocal && p.baseUrl && h('div', { style: { fontSize: 12, color: '#6b7280', marginBottom: 8, wordBreak: 'break-all' } }, p.baseUrl),
|
|
1321
|
+
!isLocal && !p.isCustom && p.requiresApiKey && h('div', { style: { marginTop: 8 } },
|
|
1322
|
+
isConfigured
|
|
1323
|
+
? h('div', { style: { display: 'flex', alignItems: 'center', gap: 6, fontSize: 12 } },
|
|
1324
|
+
h('span', { style: { color: 'var(--success, #16a34a)' } }, 'ā API key configured via environment'),
|
|
1325
|
+
h('button', { className: 'btn btn-sm btn-ghost', style: { padding: '2px 8px', fontSize: 11 }, onClick: function() {
|
|
1326
|
+
setApiKeyInput('');
|
|
1327
|
+
setApiKeyModal({ providerId: p.id, providerName: p.name, isUpdate: true });
|
|
1328
|
+
}}, 'Update Key')
|
|
1329
|
+
)
|
|
1330
|
+
: h('div', null,
|
|
1331
|
+
h('button', { className: 'btn btn-sm btn-primary', onClick: function() {
|
|
1332
|
+
setApiKeyInput('');
|
|
1333
|
+
setApiKeyModal({ providerId: p.id, providerName: p.name, isUpdate: false });
|
|
1334
|
+
}}, 'š Add API Key')
|
|
1335
|
+
)
|
|
1336
|
+
),
|
|
1313
1337
|
h('div', { style: { display: 'flex', gap: 6, marginTop: 8 } },
|
|
1314
1338
|
isLocal && h('button', {
|
|
1315
1339
|
className: 'btn btn-sm',
|
|
@@ -1377,6 +1401,52 @@ function ProvidersSection(props) {
|
|
|
1377
1401
|
h('button', { className: 'btn', onClick: function() { props.setShowAddProvider(false); } }, 'Cancel'),
|
|
1378
1402
|
h('button', { className: 'btn btn-primary', onClick: handleAddProvider }, 'Add Provider')
|
|
1379
1403
|
)
|
|
1404
|
+
),
|
|
1405
|
+
|
|
1406
|
+
// API Key Modal
|
|
1407
|
+
props.apiKeyModal && h(Modal, {
|
|
1408
|
+
title: (props.apiKeyModal.isUpdate ? 'Update' : 'Add') + ' API Key ā ' + props.apiKeyModal.providerName,
|
|
1409
|
+
onClose: function() { props.setApiKeyModal(null); props.setApiKeyInput(''); },
|
|
1410
|
+
},
|
|
1411
|
+
h('div', { style: { marginBottom: 16 } },
|
|
1412
|
+
h('p', { style: { color: 'var(--text-secondary)', fontSize: 13, margin: '0 0 16px 0', lineHeight: 1.5 } },
|
|
1413
|
+
props.apiKeyModal.isUpdate
|
|
1414
|
+
? 'Enter a new API key to replace the current one for ' + props.apiKeyModal.providerName + '.'
|
|
1415
|
+
: 'Enter your ' + props.apiKeyModal.providerName + ' API key to enable this provider.'
|
|
1416
|
+
),
|
|
1417
|
+
h('label', { className: 'form-label' }, 'API Key'),
|
|
1418
|
+
h('input', {
|
|
1419
|
+
className: 'input',
|
|
1420
|
+
type: 'password',
|
|
1421
|
+
placeholder: 'sk-...',
|
|
1422
|
+
value: props.apiKeyInput,
|
|
1423
|
+
autoFocus: true,
|
|
1424
|
+
onChange: function(e) { props.setApiKeyInput(e.target.value); },
|
|
1425
|
+
onKeyDown: function(e) {
|
|
1426
|
+
if (e.key === 'Enter' && props.apiKeyInput.trim()) {
|
|
1427
|
+
var m = props.apiKeyModal;
|
|
1428
|
+
apiCall('/providers/' + m.providerId + '/api-key', { method: 'POST', body: JSON.stringify({ apiKey: props.apiKeyInput.trim() }) })
|
|
1429
|
+
.then(function() { toast((m.isUpdate ? 'API key updated' : 'API key saved') + ' for ' + m.providerName + '!', 'success'); props.setApiKeyModal(null); props.setApiKeyInput(''); window.location.reload(); })
|
|
1430
|
+
.catch(function(e) { toast(e.message || 'Failed to save', 'error'); });
|
|
1431
|
+
}
|
|
1432
|
+
},
|
|
1433
|
+
style: { fontSize: 14, fontFamily: 'var(--font-mono, monospace)' },
|
|
1434
|
+
}),
|
|
1435
|
+
h('p', { className: 'form-help', style: { marginTop: 6 } }, 'Your key is stored encrypted and never exposed in the dashboard.')
|
|
1436
|
+
),
|
|
1437
|
+
h('div', { style: { display: 'flex', justifyContent: 'flex-end', gap: 8 } },
|
|
1438
|
+
h('button', { className: 'btn', onClick: function() { props.setApiKeyModal(null); props.setApiKeyInput(''); } }, 'Cancel'),
|
|
1439
|
+
h('button', {
|
|
1440
|
+
className: 'btn btn-primary',
|
|
1441
|
+
disabled: !props.apiKeyInput.trim(),
|
|
1442
|
+
onClick: function() {
|
|
1443
|
+
var m = props.apiKeyModal;
|
|
1444
|
+
apiCall('/providers/' + m.providerId + '/api-key', { method: 'POST', body: JSON.stringify({ apiKey: props.apiKeyInput.trim() }) })
|
|
1445
|
+
.then(function() { toast((m.isUpdate ? 'API key updated' : 'API key saved') + ' for ' + m.providerName + '!', 'success'); props.setApiKeyModal(null); props.setApiKeyInput(''); window.location.reload(); })
|
|
1446
|
+
.catch(function(e) { toast(e.message || 'Failed to save', 'error'); });
|
|
1447
|
+
}
|
|
1448
|
+
}, props.apiKeyModal.isUpdate ? 'Update Key' : 'Save Key')
|
|
1449
|
+
)
|
|
1380
1450
|
)
|
|
1381
1451
|
);
|
|
1382
1452
|
}
|
|
@@ -1410,6 +1480,10 @@ function ModelPricingTab(props) {
|
|
|
1410
1480
|
discoverResults: props.discoverResults,
|
|
1411
1481
|
setDiscoverResults: props.setDiscoverResults,
|
|
1412
1482
|
toast: props.toast,
|
|
1483
|
+
apiKeyModal: props.apiKeyModal,
|
|
1484
|
+
setApiKeyModal: props.setApiKeyModal,
|
|
1485
|
+
apiKeyInput: props.apiKeyInput,
|
|
1486
|
+
setApiKeyInput: props.setApiKeyInput,
|
|
1413
1487
|
}),
|
|
1414
1488
|
|
|
1415
1489
|
// Divider
|
package/dist/index.js
CHANGED
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
requireRole,
|
|
27
27
|
securityHeaders,
|
|
28
28
|
validate
|
|
29
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-LNWVYHWX.js";
|
|
30
30
|
import {
|
|
31
31
|
PROVIDER_REGISTRY,
|
|
32
32
|
listAllProviders,
|
|
@@ -36,7 +36,7 @@ import {
|
|
|
36
36
|
import {
|
|
37
37
|
provision,
|
|
38
38
|
runSetupWizard
|
|
39
|
-
} from "./chunk-
|
|
39
|
+
} from "./chunk-VBKWGYN3.js";
|
|
40
40
|
import {
|
|
41
41
|
ENGINE_TABLES,
|
|
42
42
|
ENGINE_TABLES_POSTGRES,
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import {
|
|
2
|
+
promptCompanyInfo,
|
|
3
|
+
promptDatabase,
|
|
4
|
+
promptDeployment,
|
|
5
|
+
promptDomain,
|
|
6
|
+
promptRegistration,
|
|
7
|
+
provision,
|
|
8
|
+
runSetupWizard
|
|
9
|
+
} from "./chunk-VBKWGYN3.js";
|
|
10
|
+
import "./chunk-6TDW6ZNI.js";
|
|
11
|
+
import "./chunk-KFQGP6VL.js";
|
|
12
|
+
export {
|
|
13
|
+
promptCompanyInfo,
|
|
14
|
+
promptDatabase,
|
|
15
|
+
promptDeployment,
|
|
16
|
+
promptDomain,
|
|
17
|
+
promptRegistration,
|
|
18
|
+
provision,
|
|
19
|
+
runSetupWizard
|
|
20
|
+
};
|
package/package.json
CHANGED
package/src/admin/routes.ts
CHANGED
|
@@ -629,6 +629,31 @@ export function createAdminRoutes(db: DatabaseAdapter) {
|
|
|
629
629
|
return c.json({ ok: true, provider: body });
|
|
630
630
|
});
|
|
631
631
|
|
|
632
|
+
// āāā Provider API Key Management āāāāāāāāāāāāāāāāāāāāāāāā
|
|
633
|
+
api.post('/providers/:id/api-key', requireRole('admin'), async (c) => {
|
|
634
|
+
var id = c.req.param('id');
|
|
635
|
+
var provider = PROVIDER_REGISTRY[id];
|
|
636
|
+
if (!provider) {
|
|
637
|
+
return c.json({ error: 'Unknown provider' }, 404);
|
|
638
|
+
}
|
|
639
|
+
var body = await c.req.json();
|
|
640
|
+
if (!body.apiKey || typeof body.apiKey !== 'string' || body.apiKey.trim().length < 5) {
|
|
641
|
+
return c.json({ error: 'Valid API key required' }, 400);
|
|
642
|
+
}
|
|
643
|
+
|
|
644
|
+
// Store API key in company settings (encrypted field)
|
|
645
|
+
var settings = await db.getSettings();
|
|
646
|
+
var config = (settings as any)?.modelPricingConfig || { models: [], currency: 'USD' };
|
|
647
|
+
config.providerApiKeys = config.providerApiKeys || {};
|
|
648
|
+
config.providerApiKeys[id] = body.apiKey.trim();
|
|
649
|
+
await db.updateSettings({ modelPricingConfig: config } as any);
|
|
650
|
+
|
|
651
|
+
// Also set in process.env so it takes effect immediately
|
|
652
|
+
process.env[provider.envKey] = body.apiKey.trim();
|
|
653
|
+
|
|
654
|
+
return c.json({ ok: true, message: 'API key saved for ' + provider.name });
|
|
655
|
+
});
|
|
656
|
+
|
|
632
657
|
api.put('/providers/:id', requireRole('admin'), async (c) => {
|
|
633
658
|
var id = c.req.param('id');
|
|
634
659
|
if (PROVIDER_REGISTRY[id]) {
|
|
@@ -48,6 +48,10 @@ export function SettingsPage() {
|
|
|
48
48
|
var newProvider = _newProvider[0]; var setNewProvider = _newProvider[1];
|
|
49
49
|
var _discoverResults = useState({});
|
|
50
50
|
var discoverResults = _discoverResults[0]; var setDiscoverResults = _discoverResults[1];
|
|
51
|
+
var _apiKeyModal = useState(null); // { providerId, providerName, isUpdate }
|
|
52
|
+
var apiKeyModal = _apiKeyModal[0]; var setApiKeyModal = _apiKeyModal[1];
|
|
53
|
+
var _apiKeyInput = useState('');
|
|
54
|
+
var apiKeyInput = _apiKeyInput[0]; var setApiKeyInput = _apiKeyInput[1];
|
|
51
55
|
|
|
52
56
|
useEffect(() => {
|
|
53
57
|
apiCall('/settings').then(d => { const s = d.settings || d || {}; setSettings(s); if (s.primaryColor) applyBrandColor(s.primaryColor); }).catch(() => {});
|
|
@@ -558,6 +562,10 @@ export function SettingsPage() {
|
|
|
558
562
|
setNewProvider: setNewProvider,
|
|
559
563
|
discoverResults: discoverResults,
|
|
560
564
|
setDiscoverResults: setDiscoverResults,
|
|
565
|
+
apiKeyModal: apiKeyModal,
|
|
566
|
+
setApiKeyModal: setApiKeyModal,
|
|
567
|
+
apiKeyInput: apiKeyInput,
|
|
568
|
+
setApiKeyInput: setApiKeyInput,
|
|
561
569
|
onSave: function() {
|
|
562
570
|
setPricingSaving(true);
|
|
563
571
|
apiCall('/settings/model-pricing', { method: 'PUT', body: JSON.stringify(pricing) }).then(function(d) {
|
|
@@ -1310,6 +1318,22 @@ function ProvidersSection(props) {
|
|
|
1310
1318
|
}, isConfigured ? 'Connected' : 'Not Configured')
|
|
1311
1319
|
),
|
|
1312
1320
|
isLocal && p.baseUrl && h('div', { style: { fontSize: 12, color: '#6b7280', marginBottom: 8, wordBreak: 'break-all' } }, p.baseUrl),
|
|
1321
|
+
!isLocal && !p.isCustom && p.requiresApiKey && h('div', { style: { marginTop: 8 } },
|
|
1322
|
+
isConfigured
|
|
1323
|
+
? h('div', { style: { display: 'flex', alignItems: 'center', gap: 6, fontSize: 12 } },
|
|
1324
|
+
h('span', { style: { color: 'var(--success, #16a34a)' } }, 'ā API key configured via environment'),
|
|
1325
|
+
h('button', { className: 'btn btn-sm btn-ghost', style: { padding: '2px 8px', fontSize: 11 }, onClick: function() {
|
|
1326
|
+
setApiKeyInput('');
|
|
1327
|
+
setApiKeyModal({ providerId: p.id, providerName: p.name, isUpdate: true });
|
|
1328
|
+
}}, 'Update Key')
|
|
1329
|
+
)
|
|
1330
|
+
: h('div', null,
|
|
1331
|
+
h('button', { className: 'btn btn-sm btn-primary', onClick: function() {
|
|
1332
|
+
setApiKeyInput('');
|
|
1333
|
+
setApiKeyModal({ providerId: p.id, providerName: p.name, isUpdate: false });
|
|
1334
|
+
}}, 'š Add API Key')
|
|
1335
|
+
)
|
|
1336
|
+
),
|
|
1313
1337
|
h('div', { style: { display: 'flex', gap: 6, marginTop: 8 } },
|
|
1314
1338
|
isLocal && h('button', {
|
|
1315
1339
|
className: 'btn btn-sm',
|
|
@@ -1377,6 +1401,52 @@ function ProvidersSection(props) {
|
|
|
1377
1401
|
h('button', { className: 'btn', onClick: function() { props.setShowAddProvider(false); } }, 'Cancel'),
|
|
1378
1402
|
h('button', { className: 'btn btn-primary', onClick: handleAddProvider }, 'Add Provider')
|
|
1379
1403
|
)
|
|
1404
|
+
),
|
|
1405
|
+
|
|
1406
|
+
// API Key Modal
|
|
1407
|
+
props.apiKeyModal && h(Modal, {
|
|
1408
|
+
title: (props.apiKeyModal.isUpdate ? 'Update' : 'Add') + ' API Key ā ' + props.apiKeyModal.providerName,
|
|
1409
|
+
onClose: function() { props.setApiKeyModal(null); props.setApiKeyInput(''); },
|
|
1410
|
+
},
|
|
1411
|
+
h('div', { style: { marginBottom: 16 } },
|
|
1412
|
+
h('p', { style: { color: 'var(--text-secondary)', fontSize: 13, margin: '0 0 16px 0', lineHeight: 1.5 } },
|
|
1413
|
+
props.apiKeyModal.isUpdate
|
|
1414
|
+
? 'Enter a new API key to replace the current one for ' + props.apiKeyModal.providerName + '.'
|
|
1415
|
+
: 'Enter your ' + props.apiKeyModal.providerName + ' API key to enable this provider.'
|
|
1416
|
+
),
|
|
1417
|
+
h('label', { className: 'form-label' }, 'API Key'),
|
|
1418
|
+
h('input', {
|
|
1419
|
+
className: 'input',
|
|
1420
|
+
type: 'password',
|
|
1421
|
+
placeholder: 'sk-...',
|
|
1422
|
+
value: props.apiKeyInput,
|
|
1423
|
+
autoFocus: true,
|
|
1424
|
+
onChange: function(e) { props.setApiKeyInput(e.target.value); },
|
|
1425
|
+
onKeyDown: function(e) {
|
|
1426
|
+
if (e.key === 'Enter' && props.apiKeyInput.trim()) {
|
|
1427
|
+
var m = props.apiKeyModal;
|
|
1428
|
+
apiCall('/providers/' + m.providerId + '/api-key', { method: 'POST', body: JSON.stringify({ apiKey: props.apiKeyInput.trim() }) })
|
|
1429
|
+
.then(function() { toast((m.isUpdate ? 'API key updated' : 'API key saved') + ' for ' + m.providerName + '!', 'success'); props.setApiKeyModal(null); props.setApiKeyInput(''); window.location.reload(); })
|
|
1430
|
+
.catch(function(e) { toast(e.message || 'Failed to save', 'error'); });
|
|
1431
|
+
}
|
|
1432
|
+
},
|
|
1433
|
+
style: { fontSize: 14, fontFamily: 'var(--font-mono, monospace)' },
|
|
1434
|
+
}),
|
|
1435
|
+
h('p', { className: 'form-help', style: { marginTop: 6 } }, 'Your key is stored encrypted and never exposed in the dashboard.')
|
|
1436
|
+
),
|
|
1437
|
+
h('div', { style: { display: 'flex', justifyContent: 'flex-end', gap: 8 } },
|
|
1438
|
+
h('button', { className: 'btn', onClick: function() { props.setApiKeyModal(null); props.setApiKeyInput(''); } }, 'Cancel'),
|
|
1439
|
+
h('button', {
|
|
1440
|
+
className: 'btn btn-primary',
|
|
1441
|
+
disabled: !props.apiKeyInput.trim(),
|
|
1442
|
+
onClick: function() {
|
|
1443
|
+
var m = props.apiKeyModal;
|
|
1444
|
+
apiCall('/providers/' + m.providerId + '/api-key', { method: 'POST', body: JSON.stringify({ apiKey: props.apiKeyInput.trim() }) })
|
|
1445
|
+
.then(function() { toast((m.isUpdate ? 'API key updated' : 'API key saved') + ' for ' + m.providerName + '!', 'success'); props.setApiKeyModal(null); props.setApiKeyInput(''); window.location.reload(); })
|
|
1446
|
+
.catch(function(e) { toast(e.message || 'Failed to save', 'error'); });
|
|
1447
|
+
}
|
|
1448
|
+
}, props.apiKeyModal.isUpdate ? 'Update Key' : 'Save Key')
|
|
1449
|
+
)
|
|
1380
1450
|
)
|
|
1381
1451
|
);
|
|
1382
1452
|
}
|
|
@@ -1410,6 +1480,10 @@ function ModelPricingTab(props) {
|
|
|
1410
1480
|
discoverResults: props.discoverResults,
|
|
1411
1481
|
setDiscoverResults: props.setDiscoverResults,
|
|
1412
1482
|
toast: props.toast,
|
|
1483
|
+
apiKeyModal: props.apiKeyModal,
|
|
1484
|
+
setApiKeyModal: props.setApiKeyModal,
|
|
1485
|
+
apiKeyInput: props.apiKeyInput,
|
|
1486
|
+
setApiKeyInput: props.setApiKeyInput,
|
|
1413
1487
|
}),
|
|
1414
1488
|
|
|
1415
1489
|
// Divider
|
package/src/server.ts
CHANGED
|
@@ -391,6 +391,29 @@ export function createServer(config: ServerConfig): ServerInstance {
|
|
|
391
391
|
// Start health monitoring
|
|
392
392
|
healthMonitor.start();
|
|
393
393
|
|
|
394
|
+
// Load saved provider API keys from DB into process.env
|
|
395
|
+
config.db.getSettings().then((settings: any) => {
|
|
396
|
+
const keys = settings?.modelPricingConfig?.providerApiKeys;
|
|
397
|
+
if (keys && typeof keys === 'object') {
|
|
398
|
+
// Map of provider IDs to their env var names
|
|
399
|
+
const envMap: Record<string, string> = {
|
|
400
|
+
anthropic: 'ANTHROPIC_API_KEY', openai: 'OPENAI_API_KEY',
|
|
401
|
+
google: 'GOOGLE_API_KEY', deepseek: 'DEEPSEEK_API_KEY',
|
|
402
|
+
xai: 'XAI_API_KEY', mistral: 'MISTRAL_API_KEY',
|
|
403
|
+
groq: 'GROQ_API_KEY', together: 'TOGETHER_API_KEY',
|
|
404
|
+
fireworks: 'FIREWORKS_API_KEY', perplexity: 'PERPLEXITY_API_KEY',
|
|
405
|
+
cohere: 'COHERE_API_KEY',
|
|
406
|
+
};
|
|
407
|
+
for (const [providerId, apiKey] of Object.entries(keys)) {
|
|
408
|
+
const envVar = envMap[providerId];
|
|
409
|
+
if (envVar && apiKey && !process.env[envVar]) {
|
|
410
|
+
process.env[envVar] = apiKey as string;
|
|
411
|
+
console.log(` š Loaded API key for ${providerId}`);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
}).catch(() => {});
|
|
416
|
+
|
|
394
417
|
// Graceful shutdown
|
|
395
418
|
const shutdown = () => {
|
|
396
419
|
console.log('\nā³ Shutting down gracefully...');
|