@agenticmail/enterprise 0.5.71 → 0.5.73
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-G7BU3BUW.js +898 -0
- package/dist/chunk-KQCDQUAK.js +2167 -0
- package/dist/chunk-QDXUZP7Y.js +48 -0
- package/dist/chunk-WBHMHTF3.js +13099 -0
- package/dist/cli-recover-JYCMVLVL.js +97 -0
- package/dist/cli-verify-F7R5RO65.js +98 -0
- package/dist/cli.js +3 -3
- package/dist/dashboard/pages/agent-detail.js +52 -14
- package/dist/dashboard/pages/settings.js +100 -6
- package/dist/factory-GT6SUZSQ.js +9 -0
- package/dist/index.js +4 -4
- package/dist/postgres-HAHOD7HX.js +607 -0
- package/dist/routes-BWJXOYXC.js +6276 -0
- package/dist/runtime-LID6KTDP.js +47 -0
- package/dist/server-LSDU47XE.js +12 -0
- package/dist/setup-TPYVQULV.js +20 -0
- package/package.json +1 -1
- package/src/admin/routes.ts +43 -0
- package/src/dashboard/pages/agent-detail.js +52 -14
- package/src/dashboard/pages/settings.js +100 -6
- package/src/db/adapter.ts +12 -0
- package/src/db/postgres.ts +9 -0
- package/src/engine/agent-routes.ts +39 -3
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AgentRuntime,
|
|
3
|
+
EmailChannel,
|
|
4
|
+
FollowUpScheduler,
|
|
5
|
+
SessionManager,
|
|
6
|
+
SubAgentManager,
|
|
7
|
+
ToolRegistry,
|
|
8
|
+
callLLM,
|
|
9
|
+
createAgentRuntime,
|
|
10
|
+
createNoopHooks,
|
|
11
|
+
createRuntimeHooks,
|
|
12
|
+
estimateMessageTokens,
|
|
13
|
+
estimateTokens,
|
|
14
|
+
executeTool,
|
|
15
|
+
runAgentLoop,
|
|
16
|
+
toolsToDefinitions
|
|
17
|
+
} from "./chunk-WBHMHTF3.js";
|
|
18
|
+
import "./chunk-TYW5XTOW.js";
|
|
19
|
+
import "./chunk-JLSQOQ5L.js";
|
|
20
|
+
import {
|
|
21
|
+
PROVIDER_REGISTRY,
|
|
22
|
+
listAllProviders,
|
|
23
|
+
resolveApiKeyForProvider,
|
|
24
|
+
resolveProvider
|
|
25
|
+
} from "./chunk-67KZYSLU.js";
|
|
26
|
+
import "./chunk-KFQGP6VL.js";
|
|
27
|
+
export {
|
|
28
|
+
AgentRuntime,
|
|
29
|
+
EmailChannel,
|
|
30
|
+
FollowUpScheduler,
|
|
31
|
+
PROVIDER_REGISTRY,
|
|
32
|
+
SessionManager,
|
|
33
|
+
SubAgentManager,
|
|
34
|
+
ToolRegistry,
|
|
35
|
+
callLLM,
|
|
36
|
+
createAgentRuntime,
|
|
37
|
+
createNoopHooks,
|
|
38
|
+
createRuntimeHooks,
|
|
39
|
+
estimateMessageTokens,
|
|
40
|
+
estimateTokens,
|
|
41
|
+
executeTool,
|
|
42
|
+
listAllProviders,
|
|
43
|
+
resolveApiKeyForProvider,
|
|
44
|
+
resolveProvider,
|
|
45
|
+
runAgentLoop,
|
|
46
|
+
toolsToDefinitions
|
|
47
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createServer
|
|
3
|
+
} from "./chunk-KQCDQUAK.js";
|
|
4
|
+
import "./chunk-3SMTCIR4.js";
|
|
5
|
+
import "./chunk-JLSQOQ5L.js";
|
|
6
|
+
import "./chunk-RO537U6H.js";
|
|
7
|
+
import "./chunk-DRXMYYKN.js";
|
|
8
|
+
import "./chunk-67KZYSLU.js";
|
|
9
|
+
import "./chunk-KFQGP6VL.js";
|
|
10
|
+
export {
|
|
11
|
+
createServer
|
|
12
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import {
|
|
2
|
+
promptCompanyInfo,
|
|
3
|
+
promptDatabase,
|
|
4
|
+
promptDeployment,
|
|
5
|
+
promptDomain,
|
|
6
|
+
promptRegistration,
|
|
7
|
+
provision,
|
|
8
|
+
runSetupWizard
|
|
9
|
+
} from "./chunk-G7BU3BUW.js";
|
|
10
|
+
import "./chunk-QDXUZP7Y.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
|
@@ -602,6 +602,49 @@ export function createAdminRoutes(db: DatabaseAdapter) {
|
|
|
602
602
|
}
|
|
603
603
|
});
|
|
604
604
|
|
|
605
|
+
// ─── Organization Email Config ─────────────────────
|
|
606
|
+
|
|
607
|
+
api.get('/settings/org-email', requireRole('admin'), async (c) => {
|
|
608
|
+
const settings = await db.getSettings();
|
|
609
|
+
const cfg = settings?.orgEmailConfig;
|
|
610
|
+
if (!cfg) return c.json({ configured: false });
|
|
611
|
+
return c.json({
|
|
612
|
+
configured: cfg.configured || false,
|
|
613
|
+
provider: cfg.provider,
|
|
614
|
+
label: cfg.label,
|
|
615
|
+
oauthClientId: cfg.oauthClientId,
|
|
616
|
+
oauthTenantId: cfg.oauthTenantId,
|
|
617
|
+
});
|
|
618
|
+
});
|
|
619
|
+
|
|
620
|
+
api.put('/settings/org-email', requireRole('admin'), async (c) => {
|
|
621
|
+
const body = await c.req.json();
|
|
622
|
+
const { provider, oauthClientId, oauthClientSecret, oauthTenantId } = body;
|
|
623
|
+
if (!provider || !['google', 'microsoft'].includes(provider)) {
|
|
624
|
+
return c.json({ error: 'provider must be "google" or "microsoft"' }, 400);
|
|
625
|
+
}
|
|
626
|
+
if (!oauthClientId || !oauthClientSecret) {
|
|
627
|
+
return c.json({ error: 'oauthClientId and oauthClientSecret are required' }, 400);
|
|
628
|
+
}
|
|
629
|
+
const label = provider === 'google' ? 'Google Workspace' : 'Microsoft 365';
|
|
630
|
+
const orgEmailConfig = {
|
|
631
|
+
provider,
|
|
632
|
+
oauthClientId,
|
|
633
|
+
oauthClientSecret,
|
|
634
|
+
oauthTenantId: provider === 'microsoft' ? (oauthTenantId || 'common') : undefined,
|
|
635
|
+
oauthRedirectUri: '', // Will be set per-agent at OAuth time
|
|
636
|
+
configured: true,
|
|
637
|
+
label,
|
|
638
|
+
};
|
|
639
|
+
await db.updateSettings({ orgEmailConfig } as any);
|
|
640
|
+
return c.json({ success: true, orgEmailConfig: { configured: true, provider, label, oauthClientId, oauthTenantId: orgEmailConfig.oauthTenantId } });
|
|
641
|
+
});
|
|
642
|
+
|
|
643
|
+
api.delete('/settings/org-email', requireRole('admin'), async (c) => {
|
|
644
|
+
await db.updateSettings({ orgEmailConfig: null } as any);
|
|
645
|
+
return c.json({ success: true });
|
|
646
|
+
});
|
|
647
|
+
|
|
605
648
|
// ─── Tool Security Config ─────────────────────────
|
|
606
649
|
|
|
607
650
|
api.get('/settings/tool-security', requireRole('admin'), async (c) => {
|
|
@@ -3875,6 +3875,23 @@ function EmailSection(props) {
|
|
|
3875
3875
|
|
|
3876
3876
|
useEffect(function() { loadConfig(); }, [agentId]);
|
|
3877
3877
|
|
|
3878
|
+
// Listen for OAuth popup completion
|
|
3879
|
+
useEffect(function() {
|
|
3880
|
+
function onMessage(e) {
|
|
3881
|
+
if (e.data && e.data.type === 'oauth-result') {
|
|
3882
|
+
if (e.data.status === 'success') {
|
|
3883
|
+
toast('Email connected successfully', 'success');
|
|
3884
|
+
} else {
|
|
3885
|
+
toast('OAuth failed: ' + (e.data.message || 'Unknown error'), 'error');
|
|
3886
|
+
}
|
|
3887
|
+
loadConfig();
|
|
3888
|
+
if (reload) reload();
|
|
3889
|
+
}
|
|
3890
|
+
}
|
|
3891
|
+
window.addEventListener('message', onMessage);
|
|
3892
|
+
return function() { window.removeEventListener('message', onMessage); };
|
|
3893
|
+
}, []);
|
|
3894
|
+
|
|
3878
3895
|
// Preset changed → auto-fill hosts
|
|
3879
3896
|
var PRESETS = {
|
|
3880
3897
|
microsoft365: { label: 'Microsoft 365 / Outlook', imapHost: 'outlook.office365.com', imapPort: 993, smtpHost: 'smtp.office365.com', smtpPort: 587 },
|
|
@@ -3908,18 +3925,22 @@ function EmailSection(props) {
|
|
|
3908
3925
|
});
|
|
3909
3926
|
} else if (form.provider === 'microsoft') {
|
|
3910
3927
|
var baseUrl = window.location.origin;
|
|
3928
|
+
var hasOrgMs = emailConfig && emailConfig.orgEmailConfig && emailConfig.orgEmailConfig.provider === 'microsoft';
|
|
3911
3929
|
Object.assign(body, {
|
|
3912
|
-
oauthClientId: form.oauthClientId,
|
|
3913
|
-
oauthClientSecret: form.oauthClientSecret,
|
|
3930
|
+
oauthClientId: form.oauthClientId || undefined,
|
|
3931
|
+
oauthClientSecret: form.oauthClientSecret || undefined,
|
|
3914
3932
|
oauthTenantId: form.oauthTenantId,
|
|
3915
3933
|
oauthRedirectUri: baseUrl + '/api/engine/oauth/callback',
|
|
3934
|
+
useOrgConfig: hasOrgMs && !form.oauthClientId ? true : undefined,
|
|
3916
3935
|
});
|
|
3917
3936
|
} else if (form.provider === 'google') {
|
|
3918
3937
|
var gBaseUrl = window.location.origin;
|
|
3938
|
+
var hasOrgG = emailConfig && emailConfig.orgEmailConfig && emailConfig.orgEmailConfig.provider === 'google';
|
|
3919
3939
|
Object.assign(body, {
|
|
3920
|
-
oauthClientId: form.oauthClientId,
|
|
3921
|
-
oauthClientSecret: form.oauthClientSecret,
|
|
3940
|
+
oauthClientId: form.oauthClientId || undefined,
|
|
3941
|
+
oauthClientSecret: form.oauthClientSecret || undefined,
|
|
3922
3942
|
oauthRedirectUri: gBaseUrl + '/api/engine/oauth/callback',
|
|
3943
|
+
useOrgConfig: hasOrgG && !form.oauthClientId ? true : undefined,
|
|
3923
3944
|
});
|
|
3924
3945
|
}
|
|
3925
3946
|
|
|
@@ -4006,6 +4027,15 @@ function EmailSection(props) {
|
|
|
4006
4027
|
),
|
|
4007
4028
|
h('div', { className: 'card-body' },
|
|
4008
4029
|
|
|
4030
|
+
// ─── Org Email Config Banner ──────────────────────
|
|
4031
|
+
emailConfig && emailConfig.orgEmailConfig && h('div', { style: { padding: '12px 16px', background: 'var(--success-soft)', borderRadius: 'var(--radius)', marginBottom: 16, display: 'flex', alignItems: 'center', gap: 10 } },
|
|
4032
|
+
h('span', { style: { fontSize: 18 } }, '\u2705'),
|
|
4033
|
+
h('div', null,
|
|
4034
|
+
h('div', { style: { fontSize: 13, fontWeight: 600 } }, 'Your organization has configured ', emailConfig.orgEmailConfig.label || emailConfig.orgEmailConfig.provider),
|
|
4035
|
+
h('div', { style: { fontSize: 12, color: 'var(--text-muted)' } }, 'Select ', emailConfig.orgEmailConfig.provider === 'google' ? 'Google OAuth' : 'Microsoft OAuth', ' below — Client ID and Secret will be inherited automatically.')
|
|
4036
|
+
)
|
|
4037
|
+
),
|
|
4038
|
+
|
|
4009
4039
|
// ─── Provider Selection ─────────────────────────
|
|
4010
4040
|
h('div', { style: { marginBottom: 20 } },
|
|
4011
4041
|
h('label', { style: labelStyle }, 'Connection Method'),
|
|
@@ -4128,16 +4158,24 @@ function EmailSection(props) {
|
|
|
4128
4158
|
),
|
|
4129
4159
|
|
|
4130
4160
|
// ─── Google OAuth Config ────────────────────────
|
|
4131
|
-
form.provider === 'google' &&
|
|
4132
|
-
|
|
4133
|
-
|
|
4134
|
-
|
|
4135
|
-
|
|
4136
|
-
|
|
4137
|
-
|
|
4138
|
-
|
|
4161
|
+
form.provider === 'google' && (function() {
|
|
4162
|
+
var hasOrg = emailConfig && emailConfig.orgEmailConfig && emailConfig.orgEmailConfig.provider === 'google';
|
|
4163
|
+
return h(Fragment, null,
|
|
4164
|
+
hasOrg
|
|
4165
|
+
? h('div', { style: { padding: '12px 16px', background: 'var(--success-soft)', borderRadius: 'var(--radius)', fontSize: 12, marginBottom: 16 } },
|
|
4166
|
+
h('strong', null, '\u2705 Using organization Google Workspace credentials'), h('br'),
|
|
4167
|
+
'Client ID: ', h('code', { style: { fontSize: 11 } }, emailConfig.orgEmailConfig.oauthClientId), h('br'),
|
|
4168
|
+
h('span', { style: { color: 'var(--text-muted)' } }, 'Just click "Save Configuration" then authorize with the agent\'s Google account.')
|
|
4169
|
+
)
|
|
4170
|
+
: h('div', { style: { padding: '12px 16px', background: 'var(--info-soft)', borderRadius: 'var(--radius)', fontSize: 12, color: 'var(--info)', marginBottom: 16 } },
|
|
4171
|
+
h('strong', null, 'Setup Instructions:'), h('br'),
|
|
4172
|
+
'1. Go to ', h('a', { href: 'https://console.cloud.google.com/apis/credentials', target: '_blank', style: { color: 'var(--accent)' } }, 'Google Cloud Console \u2192 Credentials'), h('br'),
|
|
4173
|
+
'2. Create an OAuth 2.0 Client ID (Web application) \u2192 add redirect URI: ', h('code', { style: { background: 'var(--bg-tertiary)', padding: '1px 4px', borderRadius: 3 } }, window.location.origin + '/api/engine/oauth/callback'), h('br'),
|
|
4174
|
+
'3. Enable the Gmail API in your project', h('br'),
|
|
4175
|
+
'4. Copy the Client ID and Client Secret below'
|
|
4176
|
+
),
|
|
4139
4177
|
|
|
4140
|
-
h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12, marginBottom: 16 } },
|
|
4178
|
+
!hasOrg && h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12, marginBottom: 16 } },
|
|
4141
4179
|
h('div', null,
|
|
4142
4180
|
h('label', { style: labelStyle }, 'OAuth Client ID *'),
|
|
4143
4181
|
h('input', { style: inputStyle, value: form.oauthClientId, placeholder: 'xxxx.apps.googleusercontent.com', onChange: function(e) { set('oauthClientId', e.target.value); } })
|
|
@@ -4153,7 +4191,7 @@ function EmailSection(props) {
|
|
|
4153
4191
|
h('p', { style: { fontSize: 12, margin: '0 0 8px', color: 'var(--text-secondary)' } }, 'Click the button below to sign in with the agent\'s Google account and grant Gmail permissions.'),
|
|
4154
4192
|
h('button', { className: 'btn btn-primary btn-sm', onClick: openOAuth }, 'Authorize with Google')
|
|
4155
4193
|
)
|
|
4156
|
-
),
|
|
4194
|
+
); })(),
|
|
4157
4195
|
|
|
4158
4196
|
// ─── Test Result ─────────────────────────────────
|
|
4159
4197
|
testResult && h('div', { style: { padding: '12px 16px', borderRadius: 'var(--radius)', marginBottom: 16, background: testResult.success ? 'var(--success-soft)' : 'var(--danger-soft)' } },
|
|
@@ -53,6 +53,12 @@ export function SettingsPage() {
|
|
|
53
53
|
var _apiKeyInput = useState('');
|
|
54
54
|
var apiKeyInput = _apiKeyInput[0]; var setApiKeyInput = _apiKeyInput[1];
|
|
55
55
|
|
|
56
|
+
// Org Email Config
|
|
57
|
+
var _orgEmail = useState({ configured: false, provider: '', oauthClientId: '', oauthClientSecret: '', oauthTenantId: 'common', label: '' });
|
|
58
|
+
var orgEmail = _orgEmail[0]; var setOrgEmail = _orgEmail[1];
|
|
59
|
+
var _orgEmailSaving = useState(false);
|
|
60
|
+
var orgEmailSaving = _orgEmailSaving[0]; var setOrgEmailSaving = _orgEmailSaving[1];
|
|
61
|
+
|
|
56
62
|
useEffect(() => {
|
|
57
63
|
apiCall('/settings').then(d => { const s = d.settings || d || {}; setSettings(s); if (s.primaryColor) applyBrandColor(s.primaryColor); if (s.orgId) setOrgId(s.orgId); }).catch(() => {});
|
|
58
64
|
apiCall('/api-keys').then(d => setApiKeys(d.keys || [])).catch(() => {});
|
|
@@ -66,6 +72,9 @@ export function SettingsPage() {
|
|
|
66
72
|
}));
|
|
67
73
|
}).catch(() => {});
|
|
68
74
|
engineCall('/deploy-credentials?orgId=' + getOrgId()).then(d => setDeployCreds(d.credentials || [])).catch(() => {});
|
|
75
|
+
apiCall('/settings/org-email').then(d => {
|
|
76
|
+
if (d.configured) setOrgEmail({ configured: true, provider: d.provider, oauthClientId: d.oauthClientId || '', oauthClientSecret: '', oauthTenantId: d.oauthTenantId || 'common', label: d.label || '' });
|
|
77
|
+
}).catch(() => {});
|
|
69
78
|
apiCall('/settings/tool-security').then(d => {
|
|
70
79
|
var cfg = d.toolSecurityConfig || {};
|
|
71
80
|
setToolSec({
|
|
@@ -108,6 +117,29 @@ export function SettingsPage() {
|
|
|
108
117
|
try { await apiCall('/settings', { method: 'PATCH', body: JSON.stringify({ [key]: value }) }); toast('Settings saved', 'success'); } catch (e) { toast(e.message, 'error'); }
|
|
109
118
|
};
|
|
110
119
|
|
|
120
|
+
const saveOrgEmail = async () => {
|
|
121
|
+
if (!orgEmail.provider) { toast('Select a provider', 'error'); return; }
|
|
122
|
+
if (!orgEmail.oauthClientId) { toast('Enter Client ID', 'error'); return; }
|
|
123
|
+
if (!orgEmail.oauthClientSecret) { toast('Enter Client Secret', 'error'); return; }
|
|
124
|
+
setOrgEmailSaving(true);
|
|
125
|
+
try {
|
|
126
|
+
var result = await apiCall('/settings/org-email', { method: 'PUT', body: JSON.stringify({ provider: orgEmail.provider, oauthClientId: orgEmail.oauthClientId, oauthClientSecret: orgEmail.oauthClientSecret, oauthTenantId: orgEmail.oauthTenantId }) });
|
|
127
|
+
setOrgEmail(function(prev) { return Object.assign({}, prev, { configured: true, label: result.orgEmailConfig?.label || prev.label, oauthClientSecret: '' }); });
|
|
128
|
+
toast('Organization email configuration saved', 'success');
|
|
129
|
+
} catch (e) { toast(e.message, 'error'); }
|
|
130
|
+
setOrgEmailSaving(false);
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const removeOrgEmail = async () => {
|
|
134
|
+
var ok = await showConfirm({ title: 'Remove Organization Email', message: 'Agents using this org-level config will need to be individually configured.', danger: true, confirmText: 'Remove' });
|
|
135
|
+
if (!ok) return;
|
|
136
|
+
try {
|
|
137
|
+
await apiCall('/settings/org-email', { method: 'DELETE' });
|
|
138
|
+
setOrgEmail({ configured: false, provider: '', oauthClientId: '', oauthClientSecret: '', oauthTenantId: 'common', label: '' });
|
|
139
|
+
toast('Organization email configuration removed', 'success');
|
|
140
|
+
} catch (e) { toast(e.message, 'error'); }
|
|
141
|
+
};
|
|
142
|
+
|
|
111
143
|
const saveSaml = async () => {
|
|
112
144
|
try {
|
|
113
145
|
await apiCall('/settings/sso/saml', { method: 'PUT', body: JSON.stringify({ entityId: settings.samlEntityId, ssoUrl: settings.samlSsoUrl, certificate: settings.samlCertificate }) });
|
|
@@ -225,14 +257,76 @@ export function SettingsPage() {
|
|
|
225
257
|
)
|
|
226
258
|
),
|
|
227
259
|
h('div', { className: 'card' },
|
|
228
|
-
h('div', { className: 'card-header' },
|
|
260
|
+
h('div', { className: 'card-header' },
|
|
261
|
+
h('h3', null, 'Organization Email'),
|
|
262
|
+
orgEmail.configured && h('span', { className: 'badge badge-success', style: { marginLeft: 8 } }, orgEmail.label || 'Configured')
|
|
263
|
+
),
|
|
229
264
|
h('div', { className: 'card-body' },
|
|
230
|
-
h('
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
265
|
+
h('p', { style: { fontSize: 13, color: 'var(--text-muted)', marginBottom: 16 } }, 'Set up a shared OAuth application for all agents. Each agent will still authorize individually with their own account, but they\'ll use the same Client ID and Secret.'),
|
|
266
|
+
|
|
267
|
+
// Provider selector
|
|
268
|
+
h('div', { style: { display: 'flex', gap: 12, marginBottom: 16 } },
|
|
269
|
+
h('div', {
|
|
270
|
+
onClick: function() { setOrgEmail(function(p) { return Object.assign({}, p, { provider: 'google' }); }); },
|
|
271
|
+
style: { flex: 1, padding: '16px 12px', border: '2px solid ' + (orgEmail.provider === 'google' ? 'var(--accent)' : 'var(--border)'), borderRadius: 'var(--radius)', cursor: 'pointer', textAlign: 'center', background: orgEmail.provider === 'google' ? 'var(--accent-soft)' : 'var(--bg-primary)' }
|
|
272
|
+
},
|
|
273
|
+
h('div', { style: { fontSize: 20, marginBottom: 4 } }, '\uD83D\uDD35'),
|
|
274
|
+
h('div', { style: { fontSize: 13, fontWeight: 600 } }, 'Google Workspace'),
|
|
275
|
+
h('div', { style: { fontSize: 11, color: 'var(--text-muted)' } }, 'Gmail API OAuth')
|
|
276
|
+
),
|
|
277
|
+
h('div', {
|
|
278
|
+
onClick: function() { setOrgEmail(function(p) { return Object.assign({}, p, { provider: 'microsoft' }); }); },
|
|
279
|
+
style: { flex: 1, padding: '16px 12px', border: '2px solid ' + (orgEmail.provider === 'microsoft' ? 'var(--accent)' : 'var(--border)'), borderRadius: 'var(--radius)', cursor: 'pointer', textAlign: 'center', background: orgEmail.provider === 'microsoft' ? 'var(--accent-soft)' : 'var(--bg-primary)' }
|
|
280
|
+
},
|
|
281
|
+
h('div', { style: { fontSize: 20, marginBottom: 4 } }, '\uD83C\uDFE2'),
|
|
282
|
+
h('div', { style: { fontSize: 13, fontWeight: 600 } }, 'Microsoft 365'),
|
|
283
|
+
h('div', { style: { fontSize: 11, color: 'var(--text-muted)' } }, 'Azure AD / Entra ID')
|
|
284
|
+
)
|
|
285
|
+
),
|
|
286
|
+
|
|
287
|
+
// Setup instructions
|
|
288
|
+
orgEmail.provider && h('div', { style: { padding: '12px 16px', background: 'var(--info-soft)', borderRadius: 'var(--radius)', marginBottom: 16, fontSize: 12, color: 'var(--text-secondary)' } },
|
|
289
|
+
h('strong', { style: { color: 'var(--accent)' } }, 'Setup Instructions:'), h('br'),
|
|
290
|
+
orgEmail.provider === 'google'
|
|
291
|
+
? h(Fragment, null,
|
|
292
|
+
'1. Go to ', h('a', { href: 'https://console.cloud.google.com/apis/credentials', target: '_blank', style: { color: 'var(--accent)' } }, 'Google Cloud Console \u2192 Credentials'), h('br'),
|
|
293
|
+
'2. Create an OAuth 2.0 Client ID (Web application) \u2192 add redirect URI: ', h('code', { style: { background: 'var(--bg-tertiary)', padding: '1px 4px', borderRadius: 3, fontSize: 11 } }, window.location.origin + '/api/engine/oauth/callback'), h('br'),
|
|
294
|
+
'3. Enable the Gmail API in your project', h('br'),
|
|
295
|
+
'4. Copy the Client ID and Client Secret below'
|
|
296
|
+
)
|
|
297
|
+
: h(Fragment, null,
|
|
298
|
+
'1. Go to ', h('a', { href: 'https://portal.azure.com/#blade/Microsoft_AAD_RegisteredApps/ApplicationsListBlade', target: '_blank', style: { color: 'var(--accent)' } }, 'Azure Portal \u2192 App Registrations'), h('br'),
|
|
299
|
+
'2. Click "New Registration" \u2192 set redirect URI to: ', h('code', { style: { background: 'var(--bg-tertiary)', padding: '1px 4px', borderRadius: 3, fontSize: 11 } }, window.location.origin + '/api/engine/oauth/callback'), h('br'),
|
|
300
|
+
'3. Copy the Client ID and create a Client Secret below'
|
|
301
|
+
)
|
|
302
|
+
),
|
|
303
|
+
|
|
304
|
+
// Credentials form
|
|
305
|
+
orgEmail.provider && h(Fragment, null,
|
|
306
|
+
h('div', { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 12, marginBottom: 12 } },
|
|
307
|
+
h('div', null,
|
|
308
|
+
h('label', { className: 'form-label' }, 'OAuth Client ID *'),
|
|
309
|
+
h('input', { className: 'input', value: orgEmail.oauthClientId, placeholder: orgEmail.provider === 'google' ? 'xxxx.apps.googleusercontent.com' : 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx', onChange: function(e) { setOrgEmail(function(p) { return Object.assign({}, p, { oauthClientId: e.target.value }); }); } })
|
|
310
|
+
),
|
|
311
|
+
h('div', null,
|
|
312
|
+
h('label', { className: 'form-label' }, 'Client Secret *'),
|
|
313
|
+
h('input', { className: 'input', type: 'password', value: orgEmail.oauthClientSecret, placeholder: orgEmail.configured ? '\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022 (saved)' : 'Enter client secret', onChange: function(e) { setOrgEmail(function(p) { return Object.assign({}, p, { oauthClientSecret: e.target.value }); }); } })
|
|
314
|
+
)
|
|
315
|
+
),
|
|
316
|
+
orgEmail.provider === 'microsoft' && h('div', { style: { marginBottom: 12 } },
|
|
317
|
+
h('label', { className: 'form-label' }, 'Tenant ID'),
|
|
318
|
+
h('input', { className: 'input', style: { maxWidth: 400 }, value: orgEmail.oauthTenantId, placeholder: 'common', onChange: function(e) { setOrgEmail(function(p) { return Object.assign({}, p, { oauthTenantId: e.target.value }); }); } }),
|
|
319
|
+
h('p', { className: 'form-help' }, 'Use "common" for multi-tenant or your specific tenant ID')
|
|
320
|
+
),
|
|
321
|
+
h('div', { style: { display: 'flex', gap: 8 } },
|
|
322
|
+
h('button', { className: 'btn btn-primary', disabled: orgEmailSaving, onClick: saveOrgEmail }, orgEmailSaving ? 'Saving...' : (orgEmail.configured ? 'Update Configuration' : 'Save Configuration')),
|
|
323
|
+
orgEmail.configured && h('button', { className: 'btn btn-danger', onClick: removeOrgEmail }, 'Remove')
|
|
235
324
|
)
|
|
325
|
+
),
|
|
326
|
+
|
|
327
|
+
// Info about per-agent auth
|
|
328
|
+
orgEmail.configured && h('div', { style: { marginTop: 16, padding: '12px 16px', background: 'var(--bg-tertiary)', borderRadius: 'var(--radius)', fontSize: 12, color: 'var(--text-muted)' } },
|
|
329
|
+
'\u2139\uFE0F Each agent still needs to individually authorize via their Email tab. This org config provides the shared OAuth app credentials so agents don\'t need to enter Client ID/Secret individually.'
|
|
236
330
|
)
|
|
237
331
|
)
|
|
238
332
|
),
|
package/src/db/adapter.ts
CHANGED
|
@@ -200,10 +200,22 @@ export interface CompanySettings {
|
|
|
200
200
|
toolSecurityConfig?: Record<string, any>;
|
|
201
201
|
firewallConfig?: FirewallConfig;
|
|
202
202
|
modelPricingConfig?: ModelPricingConfig;
|
|
203
|
+
orgEmailConfig?: OrgEmailConfig;
|
|
203
204
|
createdAt: Date;
|
|
204
205
|
updatedAt: Date;
|
|
205
206
|
}
|
|
206
207
|
|
|
208
|
+
export interface OrgEmailConfig {
|
|
209
|
+
provider: 'google' | 'microsoft';
|
|
210
|
+
oauthClientId: string;
|
|
211
|
+
oauthClientSecret: string;
|
|
212
|
+
oauthTenantId?: string; // Microsoft only
|
|
213
|
+
oauthRedirectUri?: string;
|
|
214
|
+
oauthScopes?: string[];
|
|
215
|
+
configured: boolean;
|
|
216
|
+
label?: string; // e.g. "Google Workspace" or "Microsoft 365"
|
|
217
|
+
}
|
|
218
|
+
|
|
207
219
|
export interface FirewallConfig {
|
|
208
220
|
ipAccess?: {
|
|
209
221
|
enabled?: boolean;
|
package/src/db/postgres.ts
CHANGED
|
@@ -106,6 +106,9 @@ export class PostgresAdapter extends DatabaseAdapter {
|
|
|
106
106
|
ALTER TABLE company_settings ADD COLUMN IF NOT EXISTS cf_api_token TEXT;
|
|
107
107
|
ALTER TABLE company_settings ADD COLUMN IF NOT EXISTS cf_account_id TEXT;
|
|
108
108
|
`).catch(() => {});
|
|
109
|
+
await client.query(`
|
|
110
|
+
ALTER TABLE company_settings ADD COLUMN IF NOT EXISTS org_email_config JSONB;
|
|
111
|
+
`).catch(() => {});
|
|
109
112
|
await client.query('COMMIT');
|
|
110
113
|
} catch (err) {
|
|
111
114
|
await client.query('ROLLBACK');
|
|
@@ -175,6 +178,11 @@ export class PostgresAdapter extends DatabaseAdapter {
|
|
|
175
178
|
values.push(JSON.stringify(updates.modelPricingConfig));
|
|
176
179
|
i++;
|
|
177
180
|
}
|
|
181
|
+
if (updates.orgEmailConfig !== undefined) {
|
|
182
|
+
fields.push(`org_email_config = $${i}`);
|
|
183
|
+
values.push(JSON.stringify(updates.orgEmailConfig));
|
|
184
|
+
i++;
|
|
185
|
+
}
|
|
178
186
|
fields.push(`updated_at = NOW()`);
|
|
179
187
|
values.push('default');
|
|
180
188
|
const { rows } = await this.pool.query(
|
|
@@ -553,6 +561,7 @@ export class PostgresAdapter extends DatabaseAdapter {
|
|
|
553
561
|
domainStatus: r.domain_status || 'unregistered',
|
|
554
562
|
cfApiToken: r.cf_api_token || undefined,
|
|
555
563
|
cfAccountId: r.cf_account_id || undefined,
|
|
564
|
+
orgEmailConfig: r.org_email_config ? (typeof r.org_email_config === 'string' ? JSON.parse(r.org_email_config) : r.org_email_config) : undefined,
|
|
556
565
|
};
|
|
557
566
|
}
|
|
558
567
|
}
|
|
@@ -410,13 +410,30 @@ export function createAgentRoutes(opts: {
|
|
|
410
410
|
/**
|
|
411
411
|
* GET /bridge/agents/:id/email-config — Get agent's email configuration (without password).
|
|
412
412
|
*/
|
|
413
|
-
router.get('/bridge/agents/:id/email-config', (c) => {
|
|
413
|
+
router.get('/bridge/agents/:id/email-config', async (c) => {
|
|
414
414
|
const agentId = c.req.param('id');
|
|
415
415
|
const managed = lifecycle.getAgent(agentId);
|
|
416
416
|
if (!managed) return c.json({ error: 'Agent not found' }, 404);
|
|
417
417
|
|
|
418
|
+
// Fetch org-level email config
|
|
419
|
+
let orgEmailConfig: any = null;
|
|
420
|
+
try {
|
|
421
|
+
const adminDb = getAdminDb();
|
|
422
|
+
if (adminDb) {
|
|
423
|
+
const settings = await adminDb.getSettings();
|
|
424
|
+
if (settings?.orgEmailConfig?.configured) {
|
|
425
|
+
orgEmailConfig = {
|
|
426
|
+
provider: settings.orgEmailConfig.provider,
|
|
427
|
+
label: settings.orgEmailConfig.label,
|
|
428
|
+
oauthClientId: settings.orgEmailConfig.oauthClientId,
|
|
429
|
+
oauthTenantId: settings.orgEmailConfig.oauthTenantId,
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
} catch {}
|
|
434
|
+
|
|
418
435
|
const emailConfig = managed.config?.emailConfig || null;
|
|
419
|
-
if (!emailConfig) return c.json({ configured: false });
|
|
436
|
+
if (!emailConfig) return c.json({ configured: false, orgEmailConfig });
|
|
420
437
|
|
|
421
438
|
// Return config without sensitive data
|
|
422
439
|
return c.json({
|
|
@@ -436,6 +453,7 @@ export function createAgentRoutes(opts: {
|
|
|
436
453
|
oauthAuthUrl: emailConfig.oauthAuthUrl || undefined,
|
|
437
454
|
lastConnected: emailConfig.lastConnected,
|
|
438
455
|
lastError: emailConfig.lastError,
|
|
456
|
+
orgEmailConfig,
|
|
439
457
|
});
|
|
440
458
|
});
|
|
441
459
|
|
|
@@ -455,10 +473,28 @@ export function createAgentRoutes(opts: {
|
|
|
455
473
|
if (!managed) return c.json({ error: 'Agent not found' }, 404);
|
|
456
474
|
|
|
457
475
|
const body = await c.req.json();
|
|
458
|
-
|
|
476
|
+
let { provider, email, password, imapHost, imapPort, smtpHost, smtpPort, preset,
|
|
459
477
|
oauthClientId, oauthClientSecret, oauthTenantId, oauthRedirectUri } = body;
|
|
478
|
+
const useOrgConfig = body.useOrgConfig === true;
|
|
460
479
|
|
|
461
480
|
if (!provider) return c.json({ error: 'provider is required (imap, microsoft, or google)' }, 400);
|
|
481
|
+
|
|
482
|
+
// If using org-level OAuth config, inherit client credentials
|
|
483
|
+
if (useOrgConfig && (provider === 'google' || provider === 'microsoft')) {
|
|
484
|
+
try {
|
|
485
|
+
const adminDb = getAdminDb();
|
|
486
|
+
if (adminDb) {
|
|
487
|
+
const settings = await adminDb.getSettings();
|
|
488
|
+
if (settings?.orgEmailConfig?.configured && settings.orgEmailConfig.provider === provider) {
|
|
489
|
+
oauthClientId = oauthClientId || settings.orgEmailConfig.oauthClientId;
|
|
490
|
+
oauthClientSecret = oauthClientSecret || settings.orgEmailConfig.oauthClientSecret;
|
|
491
|
+
if (provider === 'microsoft') oauthTenantId = oauthTenantId || settings.orgEmailConfig.oauthTenantId;
|
|
492
|
+
} else {
|
|
493
|
+
return c.json({ error: 'Organization email config not found or provider mismatch' }, 400);
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
} catch {}
|
|
497
|
+
}
|
|
462
498
|
if (!email && provider === 'imap') return c.json({ error: 'email is required' }, 400);
|
|
463
499
|
|
|
464
500
|
const emailConfig: any = {
|