@agent-native/dispatch 0.13.13 → 0.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/actions/delete-destination.d.ts +12 -12
- package/dist/actions/navigate.js +1 -1
- package/dist/actions/navigate.js.map +1 -1
- package/dist/actions/provider-api-audit.d.ts +7 -0
- package/dist/actions/provider-api-audit.d.ts.map +1 -0
- package/dist/actions/provider-api-audit.js +74 -0
- package/dist/actions/provider-api-audit.js.map +1 -0
- package/dist/actions/provider-api-catalog.d.ts +1 -1
- package/dist/actions/provider-api-request.js +10 -0
- package/dist/actions/provider-api-request.js.map +1 -1
- package/dist/actions/send-platform-message.d.ts +1 -0
- package/dist/actions/send-platform-message.js +43 -22
- package/dist/actions/send-platform-message.js.map +1 -1
- package/dist/actions/view-screen.js +9 -0
- package/dist/actions/view-screen.js.map +1 -1
- package/dist/components/layout/Layout.d.ts.map +1 -1
- package/dist/components/layout/Layout.js +75 -70
- package/dist/components/layout/Layout.js.map +1 -1
- package/dist/components/messaging-setup-panel.d.ts.map +1 -1
- package/dist/components/messaging-setup-panel.js +203 -127
- package/dist/components/messaging-setup-panel.js.map +1 -1
- package/dist/routes/index.d.ts.map +1 -1
- package/dist/routes/index.js +1 -0
- package/dist/routes/index.js.map +1 -1
- package/dist/routes/pages/messaging.js +1 -1
- package/dist/routes/pages/messaging.js.map +1 -1
- package/dist/routes/pages/operations.d.ts +5 -0
- package/dist/routes/pages/operations.d.ts.map +1 -0
- package/dist/routes/pages/operations.js +51 -0
- package/dist/routes/pages/operations.js.map +1 -0
- package/dist/routes/pages/overview.d.ts +2 -2
- package/dist/server/lib/dispatch-integrations.d.ts +7 -1
- package/dist/server/lib/dispatch-integrations.d.ts.map +1 -1
- package/dist/server/lib/dispatch-integrations.js +178 -8
- package/dist/server/lib/dispatch-integrations.js.map +1 -1
- package/dist/server/lib/dispatch-routing.d.ts +6 -0
- package/dist/server/lib/dispatch-routing.d.ts.map +1 -0
- package/dist/server/lib/dispatch-routing.js +30 -0
- package/dist/server/lib/dispatch-routing.js.map +1 -0
- package/dist/server/lib/env-config.d.ts.map +1 -1
- package/dist/server/lib/env-config.js +50 -0
- package/dist/server/lib/env-config.js.map +1 -1
- package/dist/server/lib/onboarding-steps.d.ts.map +1 -1
- package/dist/server/lib/onboarding-steps.js +25 -0
- package/dist/server/lib/onboarding-steps.js.map +1 -1
- package/dist/server/lib/provider-api.d.ts +2 -2
- package/dist/server/lib/provider-api.d.ts.map +1 -1
- package/dist/server/plugins/integrations.d.ts.map +1 -1
- package/dist/server/plugins/integrations.js +5 -2
- package/dist/server/plugins/integrations.js.map +1 -1
- package/package.json +2 -2
- package/src/actions/navigate.ts +1 -1
- package/src/actions/provider-api-audit.spec.ts +52 -0
- package/src/actions/provider-api-audit.ts +88 -0
- package/src/actions/provider-api-request.ts +10 -0
- package/src/actions/send-platform-message.spec.ts +108 -0
- package/src/actions/send-platform-message.ts +73 -25
- package/src/actions/view-screen.ts +11 -0
- package/src/components/layout/Layout.spec.tsx +154 -0
- package/src/components/layout/Layout.tsx +196 -189
- package/src/components/messaging-setup-panel.spec.tsx +191 -0
- package/src/components/messaging-setup-panel.tsx +472 -193
- package/src/routes/index.spec.ts +23 -0
- package/src/routes/index.ts +1 -0
- package/src/routes/pages/messaging.tsx +1 -1
- package/src/routes/pages/operations.tsx +134 -0
- package/src/server/lib/dispatch-integrations.spec.ts +113 -0
- package/src/server/lib/dispatch-integrations.ts +239 -6
- package/src/server/lib/dispatch-routing.spec.ts +44 -0
- package/src/server/lib/dispatch-routing.ts +42 -0
- package/src/server/lib/env-config.ts +50 -0
- package/src/server/lib/onboarding-steps.ts +32 -0
- package/src/server/plugins/integrations.ts +5 -2
- package/src/styles/dispatch-css.spec.ts +11 -0
|
@@ -1,82 +1,38 @@
|
|
|
1
1
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { disconnectManagedIntegrationInstallation, listManagedIntegrationBudgets, listManagedIntegrationInstallations, listManagedIntegrationScopes, listIntegrationEnvStatuses, listIntegrationStatuses, managedIntegrationOAuthUrl, managedSlackAgentManifestUrl, saveIntegrationEnvVars, saveManagedIntegrationBudget, saveManagedIntegrationScope, setIntegrationEnabled, setupIntegration, testManagedIntegrationInstallation, useFormatters, useT, } from "@agent-native/core/client";
|
|
3
|
+
import { listBuiltInChannelIntegrations, } from "@agent-native/core/integrations";
|
|
4
|
+
import { IconBrandDiscord, IconBrandSlack, IconBrandTelegram, IconBrandTeams, IconBrandWhatsapp, IconCheck, IconChevronRight, IconCopy, IconExternalLink, IconFileDescription, IconInfoCircle, IconLoader2, IconMail, IconPlug, } from "@tabler/icons-react";
|
|
4
5
|
import { useEffect, useMemo, useState } from "react";
|
|
5
6
|
import { toast } from "sonner";
|
|
6
7
|
import { Button } from "./ui/button.js";
|
|
7
8
|
import { Collapsible, CollapsibleContent, CollapsibleTrigger, } from "./ui/collapsible.js";
|
|
8
9
|
import { Input } from "./ui/input.js";
|
|
10
|
+
import { Switch } from "./ui/switch.js";
|
|
9
11
|
import { Tooltip, TooltipContent, TooltipTrigger } from "./ui/tooltip.js";
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
externalUrl: "https://t.me/BotFather",
|
|
35
|
-
externalLabel: "Open BotFather",
|
|
36
|
-
envKeys: ["TELEGRAM_BOT_TOKEN"],
|
|
37
|
-
setupSteps: [
|
|
38
|
-
"Open @BotFather in Telegram and send /newbot.",
|
|
39
|
-
"Save the bot token here, then click Set up webhook below.",
|
|
40
|
-
"DM the bot in Telegram to test.",
|
|
41
|
-
],
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
id: "email",
|
|
45
|
-
label: "Email",
|
|
46
|
-
icon: IconMail,
|
|
47
|
-
description: "Give your agent an email address. People can email it directly or CC it on threads.",
|
|
48
|
-
docsUrl: "/docs/messaging#email",
|
|
49
|
-
externalUrl: "https://resend.com/webhooks",
|
|
50
|
-
externalLabel: "Open Resend webhooks",
|
|
51
|
-
envKeys: ["EMAIL_AGENT_ADDRESS"],
|
|
52
|
-
setupSteps: [
|
|
53
|
-
"Save your Resend or SendGrid API key (Vault or onboarding).",
|
|
54
|
-
"Pick an email address — the easiest is a free <slug>.resend.app address.",
|
|
55
|
-
"If using your own domain, add MX records pointing to your provider.",
|
|
56
|
-
"Save the address here, then register the webhook URL below in Resend (event: email.received).",
|
|
57
|
-
],
|
|
58
|
-
},
|
|
59
|
-
{
|
|
60
|
-
id: "whatsapp",
|
|
61
|
-
label: "WhatsApp",
|
|
62
|
-
icon: IconBrandWhatsapp,
|
|
63
|
-
description: "Receive WhatsApp messages and reply through a Meta-managed phone number.",
|
|
64
|
-
docsUrl: "/docs/messaging#whatsapp",
|
|
65
|
-
externalUrl: "https://developers.facebook.com/apps",
|
|
66
|
-
externalLabel: "Open Meta developer console",
|
|
67
|
-
envKeys: [
|
|
68
|
-
"WHATSAPP_ACCESS_TOKEN",
|
|
69
|
-
"WHATSAPP_VERIFY_TOKEN",
|
|
70
|
-
"WHATSAPP_PHONE_NUMBER_ID",
|
|
71
|
-
],
|
|
72
|
-
setupSteps: [
|
|
73
|
-
"Create a Meta app and add the WhatsApp product.",
|
|
74
|
-
"Save the access token, verify token, and phone number ID below.",
|
|
75
|
-
"In Meta's WhatsApp configuration, paste the webhook URL and your verify token.",
|
|
76
|
-
"Subscribe to the messages field, then enable here.",
|
|
77
|
-
],
|
|
78
|
-
},
|
|
79
|
-
];
|
|
12
|
+
const CHANNELS = listBuiltInChannelIntegrations();
|
|
13
|
+
const PLATFORM_ICONS = {
|
|
14
|
+
slack: IconBrandSlack,
|
|
15
|
+
"microsoft-teams": IconBrandTeams,
|
|
16
|
+
discord: IconBrandDiscord,
|
|
17
|
+
telegram: IconBrandTelegram,
|
|
18
|
+
whatsapp: IconBrandWhatsapp,
|
|
19
|
+
email: IconMail,
|
|
20
|
+
};
|
|
21
|
+
function hasMissingRequiredCredentials(credentials, envStatusByKey) {
|
|
22
|
+
const alternatives = new Map();
|
|
23
|
+
for (const credential of credentials) {
|
|
24
|
+
if (!credential.required)
|
|
25
|
+
continue;
|
|
26
|
+
if (!credential.alternativeGroup) {
|
|
27
|
+
if (!envStatusByKey.get(credential.key)?.configured)
|
|
28
|
+
return true;
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
const group = alternatives.get(credential.alternativeGroup) ?? [];
|
|
32
|
+
alternatives.set(credential.alternativeGroup, [...group, credential]);
|
|
33
|
+
}
|
|
34
|
+
return [...alternatives.values()].some((group) => group.every((credential) => !envStatusByKey.get(credential.key)?.configured));
|
|
35
|
+
}
|
|
80
36
|
function HelpTooltip({ content }) {
|
|
81
37
|
return (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", className: "text-muted-foreground/60 hover:text-foreground cursor-pointer", children: _jsx(IconInfoCircle, { className: "h-3.5 w-3.5" }) }) }), _jsx(TooltipContent, { side: "top", className: "max-w-64 text-xs leading-relaxed", children: content })] }));
|
|
82
38
|
}
|
|
@@ -107,6 +63,8 @@ function ConnectionStatus({ configured, enabled, }) {
|
|
|
107
63
|
return _jsx(StatusPill, { tone: "neutral", label: "Not configured" });
|
|
108
64
|
}
|
|
109
65
|
export function MessagingSetupPanel() {
|
|
66
|
+
const t = useT();
|
|
67
|
+
const { formatDate } = useFormatters();
|
|
110
68
|
const [statuses, setStatuses] = useState([]);
|
|
111
69
|
const [loading, setLoading] = useState(true);
|
|
112
70
|
const [envStatuses, setEnvStatuses] = useState([]);
|
|
@@ -116,12 +74,16 @@ export function MessagingSetupPanel() {
|
|
|
116
74
|
const [togglingPlatform, setTogglingPlatform] = useState(null);
|
|
117
75
|
const [setupPlatform, setSetupPlatform] = useState(null);
|
|
118
76
|
const [copiedWebhook, setCopiedWebhook] = useState(null);
|
|
77
|
+
const [installations, setInstallations] = useState([]);
|
|
78
|
+
const [installationAction, setInstallationAction] = useState(null);
|
|
79
|
+
const [scopes, setScopes] = useState([]);
|
|
80
|
+
const [budgets, setBudgets] = useState([]);
|
|
81
|
+
const [scopeBudget, setScopeBudget] = useState({});
|
|
82
|
+
const [savingScope, setSavingScope] = useState(null);
|
|
119
83
|
const refreshStatuses = async () => {
|
|
120
84
|
setLoading(true);
|
|
121
85
|
try {
|
|
122
|
-
|
|
123
|
-
const rows = res.ok ? await res.json() : [];
|
|
124
|
-
setStatuses(Array.isArray(rows) ? rows : []);
|
|
86
|
+
setStatuses(await listIntegrationStatuses());
|
|
125
87
|
}
|
|
126
88
|
finally {
|
|
127
89
|
setLoading(false);
|
|
@@ -129,11 +91,10 @@ export function MessagingSetupPanel() {
|
|
|
129
91
|
};
|
|
130
92
|
useEffect(() => {
|
|
131
93
|
let active = true;
|
|
132
|
-
|
|
133
|
-
.then((res) => (res.ok ? res.json() : []))
|
|
94
|
+
listIntegrationStatuses()
|
|
134
95
|
.then((rows) => {
|
|
135
96
|
if (active) {
|
|
136
|
-
setStatuses(
|
|
97
|
+
setStatuses(rows);
|
|
137
98
|
setLoading(false);
|
|
138
99
|
}
|
|
139
100
|
})
|
|
@@ -147,11 +108,34 @@ export function MessagingSetupPanel() {
|
|
|
147
108
|
}, []);
|
|
148
109
|
useEffect(() => {
|
|
149
110
|
let active = true;
|
|
150
|
-
|
|
151
|
-
.then((
|
|
111
|
+
listManagedIntegrationInstallations("slack")
|
|
112
|
+
.then((rows) => {
|
|
113
|
+
if (active)
|
|
114
|
+
setInstallations(rows);
|
|
115
|
+
})
|
|
116
|
+
.catch(() => { });
|
|
117
|
+
listManagedIntegrationScopes("slack")
|
|
118
|
+
.then((rows) => {
|
|
119
|
+
if (active)
|
|
120
|
+
setScopes(rows);
|
|
121
|
+
})
|
|
122
|
+
.catch(() => { });
|
|
123
|
+
listManagedIntegrationBudgets()
|
|
124
|
+
.then((rows) => {
|
|
125
|
+
if (active)
|
|
126
|
+
setBudgets(rows);
|
|
127
|
+
})
|
|
128
|
+
.catch(() => { });
|
|
129
|
+
return () => {
|
|
130
|
+
active = false;
|
|
131
|
+
};
|
|
132
|
+
}, []);
|
|
133
|
+
useEffect(() => {
|
|
134
|
+
let active = true;
|
|
135
|
+
listIntegrationEnvStatuses()
|
|
152
136
|
.then((rows) => {
|
|
153
137
|
if (active) {
|
|
154
|
-
setEnvStatuses(
|
|
138
|
+
setEnvStatuses(rows);
|
|
155
139
|
setEnvLoading(false);
|
|
156
140
|
}
|
|
157
141
|
})
|
|
@@ -168,9 +152,7 @@ export function MessagingSetupPanel() {
|
|
|
168
152
|
const refreshEnvStatus = async () => {
|
|
169
153
|
setEnvLoading(true);
|
|
170
154
|
try {
|
|
171
|
-
|
|
172
|
-
const rows = res.ok ? await res.json() : [];
|
|
173
|
-
setEnvStatuses(Array.isArray(rows) ? rows : []);
|
|
155
|
+
setEnvStatuses(await listIntegrationEnvStatuses());
|
|
174
156
|
}
|
|
175
157
|
finally {
|
|
176
158
|
setEnvLoading(false);
|
|
@@ -186,16 +168,8 @@ export function MessagingSetupPanel() {
|
|
|
186
168
|
}
|
|
187
169
|
setSavingKeysFor(platform.id);
|
|
188
170
|
try {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
headers: { "Content-Type": "application/json" },
|
|
192
|
-
body: JSON.stringify({ vars }),
|
|
193
|
-
});
|
|
194
|
-
if (!res.ok) {
|
|
195
|
-
const payload = await res.json().catch(() => ({}));
|
|
196
|
-
throw new Error(payload.error || "Failed to save credentials");
|
|
197
|
-
}
|
|
198
|
-
toast.success(`${platform.label} credentials saved`);
|
|
171
|
+
await saveIntegrationEnvVars(vars);
|
|
172
|
+
toast.success(`${platform.name} credentials saved`);
|
|
199
173
|
setEnvValues((current) => {
|
|
200
174
|
const next = { ...current };
|
|
201
175
|
for (const key of keys)
|
|
@@ -215,17 +189,10 @@ export function MessagingSetupPanel() {
|
|
|
215
189
|
const togglePlatform = async (platform, enabled) => {
|
|
216
190
|
setTogglingPlatform(platform.id);
|
|
217
191
|
try {
|
|
218
|
-
|
|
219
|
-
const res = await fetch(agentNativePath(`/_agent-native/integrations/${platform.id}/${action}`), {
|
|
220
|
-
method: "POST",
|
|
221
|
-
});
|
|
222
|
-
if (!res.ok) {
|
|
223
|
-
const payload = await res.json().catch(() => ({}));
|
|
224
|
-
throw new Error(payload.error || `Failed to ${action} ${platform.label}`);
|
|
225
|
-
}
|
|
192
|
+
await setIntegrationEnabled(platform.id, !enabled);
|
|
226
193
|
toast.success(enabled
|
|
227
|
-
? `${platform.
|
|
228
|
-
: `${platform.
|
|
194
|
+
? `${platform.name} disconnected`
|
|
195
|
+
: `${platform.name} connected`);
|
|
229
196
|
await refreshStatuses();
|
|
230
197
|
}
|
|
231
198
|
catch (error) {
|
|
@@ -238,22 +205,16 @@ export function MessagingSetupPanel() {
|
|
|
238
205
|
const runSetup = async (platform) => {
|
|
239
206
|
setSetupPlatform(platform.id);
|
|
240
207
|
try {
|
|
241
|
-
|
|
242
|
-
method: "POST",
|
|
243
|
-
});
|
|
244
|
-
if (!res.ok) {
|
|
245
|
-
const payload = await res.json().catch(() => ({}));
|
|
246
|
-
throw new Error(payload.error || `Failed to set up ${platform.label}`);
|
|
247
|
-
}
|
|
208
|
+
await setupIntegration(platform.id);
|
|
248
209
|
toast.success(platform.id === "telegram"
|
|
249
210
|
? "Telegram webhook registered"
|
|
250
|
-
: `${platform.
|
|
211
|
+
: `${platform.name} setup complete`);
|
|
251
212
|
await refreshStatuses();
|
|
252
213
|
}
|
|
253
214
|
catch (error) {
|
|
254
215
|
toast.error(error instanceof Error
|
|
255
216
|
? error.message
|
|
256
|
-
: `Failed to set up ${platform.
|
|
217
|
+
: `Failed to set up ${platform.name}`);
|
|
257
218
|
}
|
|
258
219
|
finally {
|
|
259
220
|
setSetupPlatform(null);
|
|
@@ -265,21 +226,136 @@ export function MessagingSetupPanel() {
|
|
|
265
226
|
toast.success("Webhook URL copied");
|
|
266
227
|
setTimeout(() => setCopiedWebhook(null), 1500);
|
|
267
228
|
};
|
|
268
|
-
|
|
229
|
+
const runInstallationAction = async (installation, action) => {
|
|
230
|
+
setInstallationAction(`${action}:${installation.id}`);
|
|
231
|
+
try {
|
|
232
|
+
await (action === "test"
|
|
233
|
+
? testManagedIntegrationInstallation(installation.id)
|
|
234
|
+
: disconnectManagedIntegrationInstallation(installation.id));
|
|
235
|
+
setInstallations(await listManagedIntegrationInstallations("slack"));
|
|
236
|
+
toast.success(action === "test"
|
|
237
|
+
? t("messaging.managed.connectionChecked")
|
|
238
|
+
: t("messaging.managed.workspaceDisconnected"));
|
|
239
|
+
}
|
|
240
|
+
catch (error) {
|
|
241
|
+
toast.error(error instanceof Error
|
|
242
|
+
? error.message
|
|
243
|
+
: t("messaging.managed.actionFailed"));
|
|
244
|
+
}
|
|
245
|
+
finally {
|
|
246
|
+
setInstallationAction(null);
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
const updateScopePolicy = async (scope, policy) => {
|
|
250
|
+
setSavingScope(scope.id);
|
|
251
|
+
try {
|
|
252
|
+
await saveManagedIntegrationScope({
|
|
253
|
+
platform: scope.platform,
|
|
254
|
+
tenantId: scope.tenantId,
|
|
255
|
+
conversationId: scope.conversationId,
|
|
256
|
+
conversationType: scope.conversationType,
|
|
257
|
+
trust: scope.trust,
|
|
258
|
+
orgId: scope.orgId,
|
|
259
|
+
serviceOwnerEmail: scope.serviceOwnerEmail,
|
|
260
|
+
defaultModel: scope.defaultModel,
|
|
261
|
+
policy: { ...scope.policy, ...policy },
|
|
262
|
+
});
|
|
263
|
+
setScopes(await listManagedIntegrationScopes("slack"));
|
|
264
|
+
toast.success(t("messaging.managed.channelPolicyUpdated"));
|
|
265
|
+
}
|
|
266
|
+
catch (error) {
|
|
267
|
+
toast.error(error instanceof Error
|
|
268
|
+
? error.message
|
|
269
|
+
: t("messaging.managed.policyUpdateFailed"));
|
|
270
|
+
}
|
|
271
|
+
finally {
|
|
272
|
+
setSavingScope(null);
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
const saveScopeBudget = async (scope) => {
|
|
276
|
+
const dollars = Number(scopeBudget[scope.id]);
|
|
277
|
+
if (!Number.isFinite(dollars) || dollars <= 0) {
|
|
278
|
+
toast.error(t("messaging.managed.positiveMonthlyBudget"));
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
281
|
+
setSavingScope(`budget:${scope.id}`);
|
|
282
|
+
try {
|
|
283
|
+
await saveManagedIntegrationBudget({
|
|
284
|
+
subject: {
|
|
285
|
+
type: "scope",
|
|
286
|
+
scope: {
|
|
287
|
+
platform: scope.platform,
|
|
288
|
+
tenantId: scope.tenantId,
|
|
289
|
+
conversationId: scope.conversationId,
|
|
290
|
+
},
|
|
291
|
+
},
|
|
292
|
+
period: "month",
|
|
293
|
+
limitMicros: Math.round(dollars * 1_000_000),
|
|
294
|
+
});
|
|
295
|
+
setBudgets(await listManagedIntegrationBudgets());
|
|
296
|
+
toast.success(t("messaging.managed.channelBudgetSaved"));
|
|
297
|
+
}
|
|
298
|
+
catch (error) {
|
|
299
|
+
toast.error(error instanceof Error
|
|
300
|
+
? error.message
|
|
301
|
+
: t("messaging.managed.budgetSaveFailed"));
|
|
302
|
+
}
|
|
303
|
+
finally {
|
|
304
|
+
setSavingScope(null);
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
return (_jsxs("div", { className: "space-y-4", children: [_jsx("div", { className: "grid gap-4 xl:grid-cols-2", children: CHANNELS.map((platform) => {
|
|
269
308
|
const status = statusByPlatform.get(platform.id);
|
|
270
|
-
const configured = !!status?.configured
|
|
309
|
+
const configured = !!status?.configured ||
|
|
310
|
+
(platform.id === "slack" &&
|
|
311
|
+
installations.some((installation) => installation.status === "connected"));
|
|
271
312
|
const enabled = !!status?.enabled;
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
const
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
313
|
+
const envKeys = platform.credentialRequirements;
|
|
314
|
+
const missingRequiredCredentials = hasMissingRequiredCredentials(envKeys, envStatusByKey);
|
|
315
|
+
const Icon = PLATFORM_ICONS[platform.iconKey] ?? IconPlug;
|
|
316
|
+
return (_jsxs("section", { className: "rounded-2xl border bg-card p-5", children: [_jsxs("div", { className: "flex items-start justify-between gap-4", children: [_jsxs("div", { className: "flex items-start gap-3", children: [_jsx("div", { className: "flex h-10 w-10 items-center justify-center rounded-xl border bg-muted/30 text-foreground", children: _jsx(Icon, { size: 18 }) }), _jsxs("div", { children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("h3", { className: "text-base font-semibold text-foreground", children: platform.name }), _jsx(ConnectionStatus, { configured: configured, enabled: enabled })] }), _jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: platform.description })] })] }), _jsxs("div", { className: "flex shrink-0 items-center gap-1", children: [_jsx(Button, { asChild: true, variant: "ghost", size: "sm", className: "h-7 px-2 text-xs text-muted-foreground", children: _jsxs("a", { href: platform.documentation.href, target: "_blank", rel: "noreferrer", children: ["Docs", _jsx(IconExternalLink, { className: "ml-1 h-3 w-3" })] }) }), platform.documentation.externalHref ? (_jsx(Button, { asChild: true, variant: "ghost", size: "sm", className: "h-7 px-2 text-xs text-muted-foreground", children: _jsxs("a", { href: platform.documentation.externalHref, target: "_blank", rel: "noreferrer", children: [platform.documentation.externalLabel ?? "Open", _jsx(IconExternalLink, { className: "ml-1 h-3 w-3" })] }) })) : null] })] }), platform.id === "slack" ? (_jsxs("div", { className: "mt-5 space-y-3 rounded-xl border bg-muted/20 p-4", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3", children: [_jsxs("div", { children: [_jsx("div", { className: "text-sm font-medium text-foreground", children: t("messaging.managed.title") }), _jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: t("messaging.managed.description") }), _jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: t("messaging.managed.agentManifestDescription") })] }), _jsxs("div", { className: "space-y-2", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx(Button, { asChild: true, variant: "outline", size: "sm", children: _jsxs("a", { href: managedSlackAgentManifestUrl(), children: [_jsx(IconFileDescription, { className: "mr-2 h-4 w-4" }), t("messaging.managed.agentManifest")] }) }), missingRequiredCredentials ? (_jsxs(Button, { size: "sm", disabled: true, children: [_jsx(IconBrandSlack, { className: "mr-2 h-4 w-4" }), t("messaging.managed.addToSlack")] })) : (_jsx(Button, { asChild: true, size: "sm", children: _jsxs("a", { href: managedIntegrationOAuthUrl("slack"), children: [_jsx(IconBrandSlack, { className: "mr-2 h-4 w-4" }), t("messaging.managed.addToSlack")] }) }))] }), missingRequiredCredentials ? (_jsx("p", { className: "max-w-72 text-xs text-amber-700 dark:text-amber-300", children: t("messaging.managed.requiredCredentials") })) : null] })] }), installations.length ? (_jsx("div", { className: "space-y-2", children: installations.map((installation) => (_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-3 rounded-lg border bg-background/60 px-3 py-2", children: [_jsxs("div", { children: [_jsx("div", { className: "text-sm font-medium text-foreground", children: installation.teamName ||
|
|
317
|
+
installation.enterpriseName ||
|
|
318
|
+
installation.teamId ||
|
|
319
|
+
t("messaging.managed.workspaceFallback") }), _jsx("div", { className: "mt-0.5 text-xs text-muted-foreground", children: t("messaging.managed.scopesUpdated", {
|
|
320
|
+
count: installation.scopes.length,
|
|
321
|
+
date: formatDate(installation.updatedAt, {
|
|
322
|
+
dateStyle: "medium",
|
|
323
|
+
}),
|
|
324
|
+
}) })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(StatusPill, { tone: installation.health === "healthy"
|
|
325
|
+
? "success"
|
|
326
|
+
: installation.health === "degraded" ||
|
|
327
|
+
installation.health === "revoked"
|
|
328
|
+
? "warning"
|
|
329
|
+
: "neutral", label: t(`messaging.managed.health.${installation.health}`) }), _jsx(Button, { variant: "outline", size: "sm", onClick: () => runInstallationAction(installation, "test"), disabled: installationAction === `test:${installation.id}`, children: t("messaging.managed.test") }), _jsx(Button, { variant: "ghost", size: "sm", onClick: () => runInstallationAction(installation, "disconnect"), disabled: installationAction ===
|
|
330
|
+
`disconnect:${installation.id}`, children: t("messaging.managed.disconnect") })] })] }, installation.id))) })) : (_jsx("p", { className: "text-xs text-muted-foreground", children: t("messaging.managed.empty") }))] })) : null, platform.id === "slack" && scopes.length ? (_jsxs("div", { className: "mt-4 space-y-3 rounded-xl border bg-muted/20 p-4", children: [_jsxs("div", { children: [_jsx("div", { className: "text-sm font-medium text-foreground", children: t("messaging.managed.channelAccessTitle") }), _jsx("p", { className: "mt-1 text-xs text-muted-foreground", children: t("messaging.managed.channelAccessDescription") })] }), scopes.map((scope) => {
|
|
331
|
+
const subjectId = JSON.stringify([
|
|
332
|
+
scope.platform,
|
|
333
|
+
scope.tenantId,
|
|
334
|
+
scope.conversationId,
|
|
335
|
+
]);
|
|
336
|
+
const budget = budgets.find((item) => item.subjectType === "scope" &&
|
|
337
|
+
item.subjectId === subjectId &&
|
|
338
|
+
item.period === "month");
|
|
339
|
+
return (_jsxs("div", { className: "space-y-3 rounded-lg border bg-background/60 p-3", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [_jsxs("div", { children: [_jsx("div", { className: "font-mono text-xs text-foreground", children: scope.conversationId }), _jsx("div", { className: "mt-0.5 text-xs text-muted-foreground", children: t("messaging.managed.isolatedIdentity", {
|
|
340
|
+
trust: t(`messaging.managed.trust.${scope.trust}`),
|
|
341
|
+
}) })] }), _jsx(StatusPill, { tone: scope.trust === "trusted" ? "success" : "warning", label: t(`messaging.managed.trust.${scope.trust}`) })] }), _jsx("div", { className: "grid gap-2 sm:grid-cols-3", children: [
|
|
342
|
+
[
|
|
343
|
+
"requireMention",
|
|
344
|
+
t("messaging.managed.requireMention"),
|
|
345
|
+
],
|
|
346
|
+
["allowGuests", t("messaging.managed.allowGuests")],
|
|
347
|
+
[
|
|
348
|
+
"allowExternalShared",
|
|
349
|
+
t("messaging.managed.allowSlackConnect"),
|
|
350
|
+
],
|
|
351
|
+
].map(([key, label]) => (_jsxs("label", { className: "flex items-center justify-between gap-2 rounded-md border px-2.5 py-2 text-xs", children: [label, _jsx(Switch, { checked: scope.policy[key], disabled: savingScope === scope.id, onCheckedChange: (checked) => updateScopePolicy(scope, { [key]: checked }) })] }, key))) }), _jsxs("div", { className: "flex flex-wrap items-end gap-2", children: [_jsxs("div", { className: "min-w-40 flex-1 space-y-1", children: [_jsx("label", { className: "text-xs font-medium text-foreground", children: t("messaging.managed.monthlyBudgetUsd") }), _jsx(Input, { inputMode: "decimal", value: scopeBudget[scope.id] ??
|
|
352
|
+
(budget
|
|
353
|
+
? String(budget.limitMicros / 1_000_000)
|
|
354
|
+
: ""), onChange: (event) => setScopeBudget((current) => ({
|
|
355
|
+
...current,
|
|
356
|
+
[scope.id]: event.target.value,
|
|
357
|
+
})), placeholder: "25" })] }), _jsx(Button, { variant: "outline", onClick: () => saveScopeBudget(scope), disabled: savingScope === `budget:${scope.id}`, children: t("messaging.managed.saveBudget") })] })] }, scope.id));
|
|
358
|
+
})] })) : null, _jsxs(Collapsible, { className: "mt-5", children: [_jsxs(CollapsibleTrigger, { className: "group flex w-full cursor-pointer items-center gap-1.5 text-xs font-medium text-muted-foreground hover:text-foreground", children: [_jsx(IconChevronRight, { className: "h-3.5 w-3.5 transition-transform group-data-[state=open]:rotate-90" }), _jsx("span", { children: "Setup steps" })] }), _jsx(CollapsibleContent, { children: _jsx("div", { className: "mt-2 rounded-xl border bg-muted/20 p-4", children: _jsx("ol", { className: "space-y-2 text-sm text-muted-foreground", children: platform.setup.steps.map((step, index) => (_jsxs("li", { className: "flex gap-2", children: [_jsxs("span", { className: "text-muted-foreground/60", children: [index + 1, "."] }), _jsx("span", { children: step })] }, step))) }) }) })] }), _jsxs("div", { className: "mt-4 space-y-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("div", { className: "text-sm font-medium text-foreground", children: "Credentials" }), envLoading ? (_jsx("span", { className: "text-xs text-muted-foreground", children: "Checking..." })) : null] }), _jsx("div", { className: "space-y-3", children: envKeys.map((envKey) => {
|
|
283
359
|
const envStatus = envStatusByKey.get(envKey.key);
|
|
284
360
|
const isConfigured = !!envStatus?.configured;
|
|
285
361
|
const helpText = envKey.helpText ?? envStatus?.helpText;
|
|
@@ -293,7 +369,7 @@ export function MessagingSetupPanel() {
|
|
|
293
369
|
})), placeholder: isPublicValue
|
|
294
370
|
? "agent@yourcompany.com"
|
|
295
371
|
: `Enter ${label}`, autoComplete: "off" })) : null] }, envKey.key));
|
|
296
|
-
}) }),
|
|
372
|
+
}) }), missingRequiredCredentials ? (_jsx(Button, { variant: "outline", onClick: () => saveEnvKeys(platform, envKeys.map((k) => k.key)), disabled: savingKeysFor === platform.id, children: savingKeysFor === platform.id ? (_jsxs(_Fragment, { children: [_jsx(IconLoader2, { className: "mr-2 h-4 w-4 animate-spin" }), "Saving..."] })) : ("Save credentials") })) : null] }), status?.webhookUrl ? (_jsxs("div", { className: "mt-4 space-y-2", children: [_jsx("div", { className: "text-sm font-medium text-foreground", children: "Webhook URL" }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx("code", { className: "flex-1 truncate rounded-md border bg-muted/30 px-3 py-2 text-xs text-foreground", children: status.webhookUrl }), _jsx(Button, { variant: "outline", size: "icon", onClick: () => copyWebhook(status.webhookUrl), "aria-label": `Copy ${platform.name} webhook URL`, children: copiedWebhook === status.webhookUrl ? (_jsx(IconCheck, { className: "h-4 w-4" })) : (_jsx(IconCopy, { className: "h-4 w-4" })) })] })] })) : null, _jsxs("div", { className: "mt-5 flex flex-wrap items-center justify-end gap-2 border-t border-border pt-4", children: [platform.id === "telegram" && configured ? (_jsx(Button, { variant: "outline", onClick: () => runSetup(platform), disabled: setupPlatform === platform.id, children: setupPlatform === platform.id ? (_jsxs(_Fragment, { children: [_jsx(IconLoader2, { className: "mr-2 h-4 w-4 animate-spin" }), "Setting up..."] })) : ("Set up webhook") })) : null, !configured && !enabled ? (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("span", { tabIndex: 0, children: _jsx(Button, { disabled: true, children: "Enable" }) }) }), _jsx(TooltipContent, { children: "Save the required credentials first." })] })) : (_jsx(Button, { onClick: () => togglePlatform(platform, enabled), disabled: togglingPlatform === platform.id, children: togglingPlatform === platform.id ? (_jsxs(_Fragment, { children: [_jsx(IconLoader2, { className: "mr-2 h-4 w-4 animate-spin" }), "Saving..."] })) : enabled ? ("Disable") : ("Enable") }))] })] }, platform.id));
|
|
297
373
|
}) }), loading ? (_jsx("div", { className: "rounded-2xl border border-dashed px-4 py-6 text-sm text-muted-foreground", children: "Loading messaging status..." })) : null] }));
|
|
298
374
|
}
|
|
299
375
|
//# sourceMappingURL=messaging-setup-panel.js.map
|