@elizaos/agent 2.0.0-alpha.421 → 2.0.0-alpha.425

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.
Files changed (79) hide show
  1. package/package.json +4 -4
  2. package/packages/agent/src/api/accounts-routes.d.ts +11 -3
  3. package/packages/agent/src/api/accounts-routes.d.ts.map +1 -1
  4. package/packages/agent/src/api/accounts-routes.js +70 -123
  5. package/packages/agent/src/api/subscription-routes.js +27 -23
  6. package/packages/agent/src/runtime/plugin-role-gating.js +4 -3
  7. package/packages/app-core/src/api/client-agent.d.ts +63 -1
  8. package/packages/app-core/src/api/client-agent.d.ts.map +1 -1
  9. package/packages/app-core/src/api/client-agent.js +51 -0
  10. package/packages/app-core/src/components/accounts/AccountCard.d.ts +30 -0
  11. package/packages/app-core/src/components/accounts/AccountCard.d.ts.map +1 -0
  12. package/packages/app-core/src/components/accounts/AccountCard.js +183 -0
  13. package/packages/app-core/src/components/accounts/AccountList.d.ts +15 -0
  14. package/packages/app-core/src/components/accounts/AccountList.d.ts.map +1 -0
  15. package/packages/app-core/src/components/accounts/AccountList.js +80 -0
  16. package/packages/app-core/src/components/accounts/AddAccountDialog.d.ts +33 -0
  17. package/packages/app-core/src/components/accounts/AddAccountDialog.d.ts.map +1 -0
  18. package/packages/app-core/src/components/accounts/AddAccountDialog.js +277 -0
  19. package/packages/app-core/src/components/accounts/RotationStrategyPicker.d.ts +16 -0
  20. package/packages/app-core/src/components/accounts/RotationStrategyPicker.d.ts.map +1 -0
  21. package/packages/app-core/src/components/accounts/RotationStrategyPicker.js +50 -0
  22. package/packages/app-core/src/components/pages/PluginsView.d.ts.map +1 -1
  23. package/packages/app-core/src/components/pages/PluginsView.js +3 -34
  24. package/packages/app-core/src/components/settings/ProviderSwitcher.d.ts.map +1 -1
  25. package/packages/app-core/src/components/settings/ProviderSwitcher.js +2 -1
  26. package/packages/app-core/src/hooks/useAccounts.d.ts +41 -0
  27. package/packages/app-core/src/hooks/useAccounts.d.ts.map +1 -0
  28. package/packages/app-core/src/hooks/useAccounts.js +250 -0
  29. package/packages/app-core/src/i18n/locales/en.json +31 -31
  30. package/packages/app-core/src/i18n/locales/es.json +31 -31
  31. package/packages/app-core/src/i18n/locales/ko.json +31 -31
  32. package/packages/app-core/src/i18n/locales/pt.json +31 -31
  33. package/packages/app-core/src/i18n/locales/tl.json +31 -31
  34. package/packages/app-core/src/i18n/locales/vi.json +31 -31
  35. package/packages/app-core/src/i18n/locales/zh-CN.json +31 -31
  36. package/packages/typescript/src/features/basic-capabilities/index.d.ts +1 -0
  37. package/packages/typescript/src/features/basic-capabilities/index.d.ts.map +1 -1
  38. package/packages/typescript/src/features/basic-capabilities/index.js +4 -0
  39. package/packages/typescript/src/features/index.d.ts.map +1 -1
  40. package/packages/typescript/src/features/index.js +2 -7
  41. package/packages/typescript/src/features/plugin-manager/actions/plugin-handlers/core-status.d.ts +14 -0
  42. package/packages/typescript/src/features/plugin-manager/actions/plugin-handlers/core-status.d.ts.map +1 -0
  43. package/packages/typescript/src/features/plugin-manager/actions/plugin-handlers/core-status.js +48 -0
  44. package/packages/typescript/src/features/plugin-manager/actions/plugin-handlers/create.d.ts +32 -0
  45. package/packages/typescript/src/features/plugin-manager/actions/plugin-handlers/create.d.ts.map +1 -0
  46. package/packages/typescript/src/features/plugin-manager/actions/plugin-handlers/create.js +502 -0
  47. package/packages/typescript/src/features/plugin-manager/actions/plugin-handlers/eject.d.ts +16 -0
  48. package/packages/typescript/src/features/plugin-manager/actions/plugin-handlers/eject.d.ts.map +1 -0
  49. package/packages/typescript/src/features/plugin-manager/actions/plugin-handlers/eject.js +47 -0
  50. package/packages/typescript/src/features/plugin-manager/actions/plugin-handlers/install.d.ts +18 -0
  51. package/packages/typescript/src/features/plugin-manager/actions/plugin-handlers/install.d.ts.map +1 -0
  52. package/packages/typescript/src/features/plugin-manager/actions/plugin-handlers/install.js +59 -0
  53. package/packages/typescript/src/features/plugin-manager/actions/plugin-handlers/list-ejected.d.ts +14 -0
  54. package/packages/typescript/src/features/plugin-manager/actions/plugin-handlers/list-ejected.d.ts.map +1 -0
  55. package/packages/typescript/src/features/plugin-manager/actions/plugin-handlers/list-ejected.js +31 -0
  56. package/packages/typescript/src/features/plugin-manager/actions/plugin-handlers/list.d.ts +14 -0
  57. package/packages/typescript/src/features/plugin-manager/actions/plugin-handlers/list.d.ts.map +1 -0
  58. package/packages/typescript/src/features/plugin-manager/actions/plugin-handlers/list.js +51 -0
  59. package/packages/typescript/src/features/plugin-manager/actions/plugin-handlers/reinject.d.ts +15 -0
  60. package/packages/typescript/src/features/plugin-manager/actions/plugin-handlers/reinject.d.ts.map +1 -0
  61. package/packages/typescript/src/features/plugin-manager/actions/plugin-handlers/reinject.js +43 -0
  62. package/packages/typescript/src/features/plugin-manager/actions/plugin-handlers/search.d.ts +15 -0
  63. package/packages/typescript/src/features/plugin-manager/actions/plugin-handlers/search.d.ts.map +1 -0
  64. package/packages/typescript/src/features/plugin-manager/actions/plugin-handlers/search.js +52 -0
  65. package/packages/typescript/src/features/plugin-manager/actions/plugin-handlers/sync.d.ts +15 -0
  66. package/packages/typescript/src/features/plugin-manager/actions/plugin-handlers/sync.d.ts.map +1 -0
  67. package/packages/typescript/src/features/plugin-manager/actions/plugin-handlers/sync.js +48 -0
  68. package/packages/typescript/src/features/plugin-manager/actions/plugin.d.ts +25 -0
  69. package/packages/typescript/src/features/plugin-manager/actions/plugin.d.ts.map +1 -0
  70. package/packages/typescript/src/features/plugin-manager/actions/plugin.js +439 -0
  71. package/packages/typescript/src/features/plugin-manager/index.d.ts +2 -0
  72. package/packages/typescript/src/features/plugin-manager/index.d.ts.map +1 -1
  73. package/packages/typescript/src/features/plugin-manager/index.js +7 -10
  74. package/packages/typescript/src/features/plugin-manager/security.d.ts +33 -0
  75. package/packages/typescript/src/features/plugin-manager/security.d.ts.map +1 -0
  76. package/packages/typescript/src/features/plugin-manager/security.js +80 -0
  77. package/packages/app-core/src/runtime/plugin-manager-guard.d.ts +0 -12
  78. package/packages/app-core/src/runtime/plugin-manager-guard.d.ts.map +0 -1
  79. package/packages/app-core/src/runtime/plugin-manager-guard.js +0 -82
@@ -0,0 +1,277 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * AddAccountDialog — modal that walks the user through adding a new
4
+ * credential to a provider's account pool.
5
+ *
6
+ * Two paths:
7
+ * - **OAuth** (subscription providers): start the server-side OAuth
8
+ * flow, open the auth URL in a real browser window via
9
+ * `preOpenWindow` + `navigatePreOpenedWindow` (preserves the user
10
+ * gesture so popup blockers don't fire), then subscribe to the
11
+ * SSE stream at `/api/accounts/:provider/oauth/status` for terminal
12
+ * state. On `success`, hand the new `LinkedAccountConfig` to the
13
+ * parent. On error / timeout / cancel, surface the message inline
14
+ * and let the user retry. If the dialog closes mid-flow we cancel
15
+ * the server-side listener so it doesn't leak.
16
+ * - **API key**: simple label + key form, immediate POST.
17
+ *
18
+ * The dialog is provider-aware: OAuth-only providers
19
+ * (`anthropic-subscription`, `openai-codex`) skip the chooser screen.
20
+ * Direct API providers (`anthropic-api`, `openai-api`) show only the
21
+ * API-key form. The server still 501s on POST for the api-key path on
22
+ * subscription providers, so the chooser is mostly forward-looking
23
+ * defence against future shapes.
24
+ */
25
+ import { Button, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, Input, Label, Spinner, cn, } from "@elizaos/ui";
26
+ import { useCallback, useEffect, useRef, useState, } from "react";
27
+ import { client } from "../../api";
28
+ import { useApp } from "../../state";
29
+ import { navigatePreOpenedWindow, preOpenWindow } from "../../utils";
30
+ const SUBSCRIPTION_PROVIDERS = new Set([
31
+ "anthropic-subscription",
32
+ "openai-codex",
33
+ ]);
34
+ function isSubscriptionProvider(providerId) {
35
+ return SUBSCRIPTION_PROVIDERS.has(providerId);
36
+ }
37
+ function providerDisplayName(providerId, t) {
38
+ switch (providerId) {
39
+ case "anthropic-subscription":
40
+ return t("accounts.provider.anthropicSubscription", {
41
+ defaultValue: "Anthropic Claude subscription",
42
+ });
43
+ case "openai-codex":
44
+ return t("accounts.provider.openaiCodex", {
45
+ defaultValue: "OpenAI Codex subscription",
46
+ });
47
+ case "anthropic-api":
48
+ return t("accounts.provider.anthropicApi", {
49
+ defaultValue: "Anthropic API",
50
+ });
51
+ case "openai-api":
52
+ return t("accounts.provider.openaiApi", {
53
+ defaultValue: "OpenAI API",
54
+ });
55
+ default:
56
+ return providerId;
57
+ }
58
+ }
59
+ export function AddAccountDialog({ open, providerId, onClose, onCreated, }) {
60
+ const { t } = useApp();
61
+ const subscriptionProvider = isSubscriptionProvider(providerId);
62
+ const [step, setStep] = useState(subscriptionProvider ? "choose" : "apikey");
63
+ const [label, setLabel] = useState("");
64
+ const [apiKey, setApiKey] = useState("");
65
+ const [oauthCode, setOauthCode] = useState("");
66
+ const [errorMessage, setErrorMessage] = useState(null);
67
+ const [sessionId, setSessionId] = useState(null);
68
+ const eventSourceRef = useRef(null);
69
+ const sessionIdRef = useRef(null);
70
+ const closeEventSource = useCallback(() => {
71
+ if (eventSourceRef.current) {
72
+ eventSourceRef.current.close();
73
+ eventSourceRef.current = null;
74
+ }
75
+ }, []);
76
+ const cancelInflightFlow = useCallback(async () => {
77
+ closeEventSource();
78
+ const id = sessionIdRef.current;
79
+ if (id) {
80
+ sessionIdRef.current = null;
81
+ try {
82
+ await client.cancelAccountOAuth(providerId, { sessionId: id });
83
+ }
84
+ catch {
85
+ // Best-effort cleanup — server times out flows on its own.
86
+ }
87
+ }
88
+ }, [closeEventSource, providerId]);
89
+ const reset = useCallback(() => {
90
+ closeEventSource();
91
+ sessionIdRef.current = null;
92
+ setStep(subscriptionProvider ? "choose" : "apikey");
93
+ setLabel("");
94
+ setApiKey("");
95
+ setOauthCode("");
96
+ setErrorMessage(null);
97
+ setSessionId(null);
98
+ }, [closeEventSource, subscriptionProvider]);
99
+ // Dialog open/close side-effects: when closed, cancel any in-flight
100
+ // OAuth flow so the server can release the loopback listener.
101
+ useEffect(() => {
102
+ if (!open) {
103
+ void cancelInflightFlow();
104
+ reset();
105
+ }
106
+ }, [open, cancelInflightFlow, reset]);
107
+ useEffect(() => {
108
+ return () => {
109
+ closeEventSource();
110
+ };
111
+ }, [closeEventSource]);
112
+ const subscribeToFlow = useCallback((newSessionId) => {
113
+ closeEventSource();
114
+ const url = `/api/accounts/${providerId}/oauth/status?sessionId=${encodeURIComponent(newSessionId)}`;
115
+ const source = new EventSource(url);
116
+ eventSourceRef.current = source;
117
+ source.onmessage = (event) => {
118
+ try {
119
+ const data = JSON.parse(event.data);
120
+ if (data.status === "success" && data.account) {
121
+ closeEventSource();
122
+ sessionIdRef.current = null;
123
+ onCreated(data.account);
124
+ onClose();
125
+ }
126
+ else if (data.status === "error" ||
127
+ data.status === "cancelled" ||
128
+ data.status === "timeout") {
129
+ closeEventSource();
130
+ sessionIdRef.current = null;
131
+ setErrorMessage(data.error ??
132
+ t(`accounts.add.oauth.${data.status}`, {
133
+ defaultValue: data.status === "timeout"
134
+ ? "Login timed out. Try again."
135
+ : data.status === "cancelled"
136
+ ? "Login cancelled."
137
+ : "Login failed.",
138
+ }));
139
+ setStep("error");
140
+ }
141
+ }
142
+ catch {
143
+ // Malformed SSE event — ignore; the next valid one will progress.
144
+ }
145
+ };
146
+ source.onerror = () => {
147
+ // Auto-reconnect noise; ignore unless we hit a terminal status.
148
+ };
149
+ }, [closeEventSource, onClose, onCreated, providerId, t]);
150
+ const startOAuth = useCallback(async () => {
151
+ setErrorMessage(null);
152
+ setStep("oauth-starting");
153
+ // Open the popup BEFORE the await so the browser sees a synchronous
154
+ // user-gesture-triggered window.open. Once we have the URL, navigate
155
+ // it. preOpenWindow returns null on desktop (Electrobun handles
156
+ // routing via the IPC call inside openExternalUrl).
157
+ const win = preOpenWindow();
158
+ try {
159
+ const flow = await client.startAccountOAuth(providerId, {
160
+ label: label.trim(),
161
+ });
162
+ navigatePreOpenedWindow(win, flow.authUrl);
163
+ sessionIdRef.current = flow.sessionId;
164
+ setSessionId(flow.sessionId);
165
+ if (flow.needsCodeSubmission) {
166
+ setStep("oauth-need-code");
167
+ }
168
+ else {
169
+ setStep("oauth-waiting");
170
+ }
171
+ subscribeToFlow(flow.sessionId);
172
+ }
173
+ catch (err) {
174
+ setErrorMessage(err instanceof Error && err.message
175
+ ? err.message
176
+ : t("accounts.add.oauth.startFailed", {
177
+ defaultValue: "Failed to start login flow.",
178
+ }));
179
+ setStep("error");
180
+ try {
181
+ win?.close();
182
+ }
183
+ catch {
184
+ // Cross-origin — ignore.
185
+ }
186
+ }
187
+ }, [label, providerId, subscribeToFlow, t]);
188
+ const submitOAuthCode = useCallback(async (event) => {
189
+ event.preventDefault();
190
+ const code = oauthCode.trim();
191
+ const id = sessionIdRef.current;
192
+ if (!code || !id)
193
+ return;
194
+ try {
195
+ await client.submitAccountOAuthCode(providerId, {
196
+ sessionId: id,
197
+ code,
198
+ });
199
+ setOauthCode("");
200
+ setStep("oauth-waiting");
201
+ }
202
+ catch (err) {
203
+ setErrorMessage(err instanceof Error && err.message
204
+ ? err.message
205
+ : t("accounts.add.oauth.codeFailed", {
206
+ defaultValue: "Failed to submit code.",
207
+ }));
208
+ setStep("error");
209
+ }
210
+ }, [oauthCode, providerId, t]);
211
+ const submitApiKey = useCallback(async (event) => {
212
+ event.preventDefault();
213
+ const trimmedLabel = label.trim();
214
+ const trimmedKey = apiKey.trim();
215
+ if (!trimmedLabel || !trimmedKey)
216
+ return;
217
+ setErrorMessage(null);
218
+ setStep("apikey-submitting");
219
+ try {
220
+ const account = await client.createApiKeyAccount(providerId, {
221
+ label: trimmedLabel,
222
+ apiKey: trimmedKey,
223
+ });
224
+ onCreated(account);
225
+ onClose();
226
+ }
227
+ catch (err) {
228
+ setErrorMessage(err instanceof Error && err.message
229
+ ? err.message
230
+ : t("accounts.add.apikey.failed", {
231
+ defaultValue: "Failed to add account.",
232
+ }));
233
+ setStep("error");
234
+ }
235
+ }, [apiKey, label, onClose, onCreated, providerId, t]);
236
+ const handleClose = useCallback(() => {
237
+ void cancelInflightFlow();
238
+ onClose();
239
+ }, [cancelInflightFlow, onClose]);
240
+ const labelInput = (_jsxs("div", { className: "grid gap-1.5", children: [_jsx(Label, { htmlFor: "add-account-label", children: t("accounts.add.label", { defaultValue: "Account name" }) }), _jsx(Input, { id: "add-account-label", value: label, onChange: (e) => setLabel(e.target.value), placeholder: t("accounts.add.labelPlaceholder", {
241
+ defaultValue: "e.g. Personal, Work",
242
+ }), maxLength: 120, autoFocus: true })] }));
243
+ return (_jsx(Dialog, { open: open, onOpenChange: (next) => {
244
+ if (!next)
245
+ handleClose();
246
+ }, children: _jsxs(DialogContent, { className: "max-w-md", children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: t("accounts.add.title", {
247
+ defaultValue: `Add ${providerDisplayName(providerId, t)} account`,
248
+ provider: providerDisplayName(providerId, t),
249
+ }) }), _jsx(DialogDescription, { children: subscriptionProvider
250
+ ? t("accounts.add.subscriptionDescription", {
251
+ defaultValue: "Sign in with your provider to add another account to the rotation pool.",
252
+ })
253
+ : t("accounts.add.apiDescription", {
254
+ defaultValue: "Paste your API key. The key is stored locally with mode 0600.",
255
+ }) })] }), step === "choose" ? (_jsxs("div", { className: "grid gap-3 py-2", children: [labelInput, _jsx(Button, { type: "button", variant: "default", disabled: !label.trim(), onClick: () => void startOAuth(), className: "h-10", children: t("accounts.add.signIn", {
256
+ defaultValue: `Sign in with ${providerDisplayName(providerId, t)}`,
257
+ provider: providerDisplayName(providerId, t),
258
+ }) })] })) : null, step === "oauth-starting" ? (_jsxs("div", { className: "flex items-center gap-3 py-6 text-sm text-muted", children: [_jsx(Spinner, { className: "h-4 w-4" }), t("accounts.add.oauth.starting", {
259
+ defaultValue: "Starting login flow…",
260
+ })] })) : null, step === "oauth-waiting" ? (_jsxs("div", { className: "grid gap-3 py-3 text-sm text-muted", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Spinner, { className: "h-4 w-4" }), _jsx("span", { children: t("accounts.add.oauth.waiting", {
261
+ defaultValue: "Waiting for browser… Complete the sign-in there.",
262
+ }) })] }), sessionId ? (_jsx("p", { className: "text-xs text-muted", children: t("accounts.add.oauth.sessionHint", {
263
+ defaultValue: "Session: {{sessionId}}",
264
+ sessionId: `${sessionId.slice(0, 8)}…`,
265
+ }) })) : null] })) : null, step === "oauth-need-code" ? (_jsxs("form", { onSubmit: submitOAuthCode, className: "grid gap-3 py-2", children: [_jsx("p", { className: "text-xs text-muted", children: t("accounts.add.oauth.codeHint", {
266
+ defaultValue: "Auto-redirect didn't reach us. Paste the code (or full redirect URL) from the browser.",
267
+ }) }), _jsx(Input, { value: oauthCode, onChange: (e) => setOauthCode(e.target.value), placeholder: t("accounts.add.oauth.codePlaceholder", {
268
+ defaultValue: "Paste the code or redirect URL",
269
+ }), autoFocus: true }), _jsx(Button, { type: "submit", variant: "default", disabled: !oauthCode.trim(), className: "h-9", children: t("accounts.add.oauth.submitCode", {
270
+ defaultValue: "Submit code",
271
+ }) })] })) : null, step === "apikey" || step === "apikey-submitting" ? (_jsxs("form", { onSubmit: submitApiKey, className: "grid gap-3 py-2", children: [labelInput, _jsxs("div", { className: "grid gap-1.5", children: [_jsx(Label, { htmlFor: "add-account-apikey", children: t("accounts.add.apiKey", { defaultValue: "API key" }) }), _jsx(Input, { id: "add-account-apikey", type: "password", value: apiKey, onChange: (e) => setApiKey(e.target.value), placeholder: "sk-\u2026", autoComplete: "off", spellCheck: false })] }), _jsx(Button, { type: "submit", variant: "default", disabled: step === "apikey-submitting" ||
272
+ !label.trim() ||
273
+ !apiKey.trim(), className: "h-9", children: step === "apikey-submitting" ? (_jsx(Spinner, { className: "h-3 w-3" })) : (t("accounts.add.save", { defaultValue: "Add account" })) })] })) : null, step === "error" && errorMessage ? (_jsx("div", { className: cn("rounded-md border border-destructive/40 bg-destructive/5 px-3 py-2 text-sm text-destructive"), role: "alert", children: errorMessage })) : null, _jsxs(DialogFooter, { className: "gap-2", children: [step === "error" ? (_jsx(Button, { type: "button", variant: "ghost", onClick: () => {
274
+ setErrorMessage(null);
275
+ setStep(subscriptionProvider ? "choose" : "apikey");
276
+ }, children: t("accounts.add.tryAgain", { defaultValue: "Try again" }) })) : null, _jsx(Button, { type: "button", variant: "ghost", onClick: handleClose, children: t("accounts.cancel", { defaultValue: "Cancel" }) })] })] }) }));
277
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * RotationStrategyPicker — compact `Select` exposing the four account
3
+ * rotation strategies. Calls `onChange` with the chosen strategy; the
4
+ * caller is responsible for routing that through `client.patchProviderStrategy`.
5
+ */
6
+ import type { LinkedAccountProviderId } from "@elizaos/shared";
7
+ import type { AccountStrategy } from "../../api/client-agent";
8
+ interface RotationStrategyPickerProps {
9
+ providerId: LinkedAccountProviderId;
10
+ value: AccountStrategy | undefined;
11
+ onChange: (strategy: AccountStrategy) => void;
12
+ disabled?: boolean;
13
+ }
14
+ export declare function RotationStrategyPicker({ providerId, value, onChange, disabled, }: RotationStrategyPickerProps): import("react/jsx-runtime").JSX.Element;
15
+ export {};
16
+ //# sourceMappingURL=RotationStrategyPicker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RotationStrategyPicker.d.ts","sourceRoot":"","sources":["../../../../../../../app-core/src/components/accounts/RotationStrategyPicker.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AASH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAE/D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAE9D,UAAU,2BAA2B;IACnC,UAAU,EAAE,uBAAuB,CAAC;IACpC,KAAK,EAAE,eAAe,GAAG,SAAS,CAAC;IACnC,QAAQ,EAAE,CAAC,QAAQ,EAAE,eAAe,KAAK,IAAI,CAAC;IAC9C,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AA2CD,wBAAgB,sBAAsB,CAAC,EACrC,UAAU,EACV,KAAK,EACL,QAAQ,EACR,QAAQ,GACT,EAAE,2BAA2B,2CA6C7B"}
@@ -0,0 +1,50 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * RotationStrategyPicker — compact `Select` exposing the four account
4
+ * rotation strategies. Calls `onChange` with the chosen strategy; the
5
+ * caller is responsible for routing that through `client.patchProviderStrategy`.
6
+ */
7
+ import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@elizaos/ui";
8
+ import { useApp } from "../../state";
9
+ const STRATEGY_OPTIONS = [
10
+ {
11
+ id: "priority",
12
+ labelKey: "accounts.strategy.priority.label",
13
+ labelFallback: "Priority",
14
+ descriptionKey: "accounts.strategy.priority.description",
15
+ descriptionFallback: "Always prefer the top healthy account.",
16
+ },
17
+ {
18
+ id: "round-robin",
19
+ labelKey: "accounts.strategy.roundRobin.label",
20
+ labelFallback: "Round-robin",
21
+ descriptionKey: "accounts.strategy.roundRobin.description",
22
+ descriptionFallback: "Alternate across enabled accounts.",
23
+ },
24
+ {
25
+ id: "least-used",
26
+ labelKey: "accounts.strategy.leastUsed.label",
27
+ labelFallback: "Least used",
28
+ descriptionKey: "accounts.strategy.leastUsed.description",
29
+ descriptionFallback: "Prefer the account with the lowest current usage.",
30
+ },
31
+ {
32
+ id: "quota-aware",
33
+ labelKey: "accounts.strategy.quotaAware.label",
34
+ labelFallback: "Quota-aware",
35
+ descriptionKey: "accounts.strategy.quotaAware.description",
36
+ descriptionFallback: "Skip accounts above 85% utilization.",
37
+ },
38
+ ];
39
+ export function RotationStrategyPicker({ providerId, value, onChange, disabled, }) {
40
+ const { t } = useApp();
41
+ const resolved = value ?? "priority";
42
+ return (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx("span", { className: "text-[10px] font-medium uppercase tracking-wider text-muted", children: t("accounts.strategy.label", { defaultValue: "Strategy" }) }), _jsxs(Select, { value: resolved, onValueChange: (next) => {
43
+ if (next !== resolved)
44
+ onChange(next);
45
+ }, disabled: disabled, children: [_jsx(SelectTrigger, { id: `rotation-strategy-${providerId}`, className: "h-8 w-[160px] rounded-lg border border-border bg-card text-xs", children: _jsx(SelectValue, { placeholder: t("accounts.strategy.choose", {
46
+ defaultValue: "Choose strategy",
47
+ }) }) }), _jsx(SelectContent, { children: STRATEGY_OPTIONS.map((option) => (_jsx(SelectItem, { value: option.id, children: _jsxs("div", { className: "flex flex-col gap-0.5 py-0.5", children: [_jsx("span", { className: "text-sm font-medium text-txt", children: t(option.labelKey, { defaultValue: option.labelFallback }) }), _jsx("span", { className: "text-xs text-muted", children: t(option.descriptionKey, {
48
+ defaultValue: option.descriptionFallback,
49
+ }) })] }) }, option.id))) })] })] }));
50
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"PluginsView.d.ts","sourceRoot":"","sources":["../../../../../../../app-core/src/components/pages/PluginsView.tsx"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,SAAS,EAMf,MAAM,OAAO,CAAC;AAWf,OAAO,EAIL,KAAK,eAAe,EAIrB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAm5CrD,+CAA+C;AAC/C,wBAAgB,WAAW,CAAC,EAC1B,aAAa,EACb,IAAY,EACZ,OAAO,EACP,yBAAkC,GACnC,EAAE;IACD,aAAa,CAAC,EAAE,SAAS,CAAC;IAC1B,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,yBAAyB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CAC9C,2CAoBA"}
1
+ {"version":3,"file":"PluginsView.d.ts","sourceRoot":"","sources":["../../../../../../../app-core/src/components/pages/PluginsView.tsx"],"names":[],"mappings":"AACA,OAAO,EACL,KAAK,SAAS,EAMf,MAAM,OAAO,CAAC;AAMf,OAAO,EAIL,KAAK,eAAe,EAIrB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAg3CrD,+CAA+C;AAC/C,wBAAgB,WAAW,CAAC,EAC1B,aAAa,EACb,IAAY,EACZ,OAAO,EACP,yBAAkC,GACnC,EAAE;IACD,aAAa,CAAC,EAAE,SAAS,CAAC;IAC1B,IAAI,CAAC,EAAE,eAAe,CAAC;IACvB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,yBAAyB,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;CAC9C,2CAoBA"}
@@ -2,7 +2,6 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Package } from "lucide-react";
3
3
  import { useCallback, useEffect, useMemo, useRef, useState, } from "react";
4
4
  import { client } from "../../api";
5
- import { ensurePluginManagerAllowed, getPluginManagerBlockReason, PLUGIN_MANAGER_UNAVAILABLE_ERROR, } from "../../runtime/plugin-manager-guard";
6
5
  import { useApp } from "../../state";
7
6
  import { openExternalUrl } from "../../utils";
8
7
  import { buildPluginListState, getPluginResourceLinks, iconImageSource, resolveIcon, SUBGROUP_NAV_ICONS, } from "./plugin-list-utils";
@@ -294,39 +293,9 @@ function PluginListView({ label, contentHeader, mode = "all", inModal, }) {
294
293
  return next;
295
294
  });
296
295
  }, []);
297
- const runWithPluginManager = useCallback(async (_pluginName, notices, task) => {
298
- const restartForPluginManager = async (message) => {
299
- const pluginManagerGuard = await ensurePluginManagerAllowed();
300
- const pluginManagerBlockReason = getPluginManagerBlockReason(pluginManagerGuard);
301
- if (pluginManagerBlockReason) {
302
- throw new Error(pluginManagerBlockReason);
303
- }
304
- setActionNotice(message, "success");
305
- await client.restartAndWait(120_000);
306
- };
307
- let restartedForPluginManager = false;
308
- const pluginManagerGuard = await ensurePluginManagerAllowed();
309
- const pluginManagerBlockReason = getPluginManagerBlockReason(pluginManagerGuard);
310
- if (pluginManagerBlockReason) {
311
- throw new Error(pluginManagerBlockReason);
312
- }
313
- if (pluginManagerGuard === "enabled") {
314
- await restartForPluginManager(notices.prepare);
315
- restartedForPluginManager = true;
316
- }
317
- try {
318
- return await task();
319
- }
320
- catch (err) {
321
- if (err instanceof Error &&
322
- err.message.includes(PLUGIN_MANAGER_UNAVAILABLE_ERROR) &&
323
- !restartedForPluginManager) {
324
- await restartForPluginManager(notices.recover);
325
- return await task();
326
- }
327
- throw err;
328
- }
329
- }, [setActionNotice]);
296
+ // The plugin manager capability ships built-in with @elizaos/core, so no
297
+ // preflight enable / restart is required before invoking lifecycle tasks.
298
+ const runWithPluginManager = useCallback(async (_pluginName, _notices, task) => task(), []);
330
299
  const completePluginLifecycleRestart = useCallback(async (messages) => {
331
300
  setActionNotice(messages.waiting, "info", 120_000, false, true);
332
301
  const status = await client.restartAndWait(120_000);
@@ -1 +1 @@
1
- {"version":3,"file":"ProviderSwitcher.d.ts","sourceRoot":"","sources":["../../../../../../../app-core/src/components/settings/ProviderSwitcher.tsx"],"names":[],"mappings":"AAkCA,OAAO,EAAkC,KAAK,cAAc,EAAE,MAAM,WAAW,CAAC;AAahF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAoBhD,UAAU,UAAU;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,cAAc,EAAE,CAAC;IAC7B,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;CAC9C;AASD,UAAU,qBAAqB;IAC7B,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,OAAO,CAAC,EAAE,UAAU,EAAE,CAAC;IACvB,YAAY,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC3B,iBAAiB,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAChC,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,sBAAsB,CAAC,EAAE,CACvB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC5B,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B;AAwJD,wBAAgB,gBAAgB,CAAC,KAAK,GAAE,qBAA0B,2CAo7BjE"}
1
+ {"version":3,"file":"ProviderSwitcher.d.ts","sourceRoot":"","sources":["../../../../../../../app-core/src/components/settings/ProviderSwitcher.tsx"],"names":[],"mappings":"AAkCA,OAAO,EAAkC,KAAK,cAAc,EAAE,MAAM,WAAW,CAAC;AAahF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAqBhD,UAAU,UAAU;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,EAAE,OAAO,CAAC;IACpB,UAAU,EAAE,cAAc,EAAE,CAAC;IAC7B,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;CAC9C;AASD,UAAU,qBAAqB;IAC7B,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,OAAO,CAAC,EAAE,UAAU,EAAE,CAAC;IACvB,YAAY,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC3B,iBAAiB,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAChC,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,sBAAsB,CAAC,EAAE,CACvB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC5B,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B;AAwJD,wBAAgB,gBAAgB,CAAC,KAAK,GAAE,qBAA0B,2CA27BjE"}
@@ -10,6 +10,7 @@ import { getOnboardingProviderOption, isSubscriptionProviderSelectionId, SUBSCRI
10
10
  import { useApp } from "../../state";
11
11
  import { LocalInferencePanel } from "../local-inference/LocalInferencePanel";
12
12
  import { CloudDashboard } from "../pages/ElizaCloudDashboard";
13
+ import { AccountList } from "../accounts/AccountList";
13
14
  import { ApiKeyConfig } from "./ApiKeyConfig";
14
15
  import { buildCloudModelSchema, DEFAULT_ACTION_PLANNER_MODEL, DEFAULT_RESPONSE_HANDLER_MODEL, } from "./cloud-model-schema";
15
16
  import { SubscriptionStatus } from "./SubscriptionStatus";
@@ -567,6 +568,6 @@ export function ProviderSwitcher(props = {}) {
567
568
  (largeModelOptions.length > 0 || cloudModelSchema) ? (_jsxs("div", { className: "border-border/40 border-t px-4 py-4 sm:px-5", children: [largeModelOptions.length > 0 ? (_jsxs("div", { children: [_jsx("label", { htmlFor: "provider-switcher-primary-model", className: "mb-1.5 block text-muted text-xs font-medium uppercase tracking-wider", children: t("providerswitcher.model", { defaultValue: "Model" }) }), _jsxs(Select, { value: currentLargeModel || "", onValueChange: (v) => handleModelFieldChange("large", v), children: [_jsx(SelectTrigger, { id: "provider-switcher-primary-model", className: "h-9 w-full max-w-sm rounded-lg border border-border bg-card text-sm", children: _jsx(SelectValue, { placeholder: t("providerswitcher.chooseModel", {
568
569
  defaultValue: "Choose a model",
569
570
  }) }) }), _jsx(SelectContent, { className: "max-h-64", children: largeModelOptions.map((model) => (_jsx(SelectItem, { value: model.id, children: model.name }, model.id))) })] })] })) : null, cloudModelSchema ? (_jsx(AdvancedSettingsDisclosure, { title: "Model overrides", className: "mt-4", children: _jsx(ConfigRenderer, { schema: cloudModelSchema.schema, hints: cloudModelSchema.hints, values: modelValues.values, setKeys: modelValues.setKeys, registry: defaultRegistry, onChange: handleModelFieldChange }) })) : null, _jsxs("div", { className: "mt-2 flex items-center justify-between gap-2", children: [_jsx("p", { className: "text-muted text-xs-tight", children: t("providerswitcher.restartRequiredHint", appNameInterpolationVars(branding)) }), _jsxs("div", { className: "flex items-center gap-2", children: [modelSaving && (_jsx("span", { className: "inline-flex items-center text-muted", title: t("providerswitcher.savingRestarting"), role: "status", "aria-label": t("providerswitcher.savingRestarting"), children: _jsx(Loader2, { className: "h-3.5 w-3.5 animate-spin" }) })), modelSaveSuccess && (_jsx("span", { className: "inline-flex items-center text-ok", title: t("providerswitcher.savedRestartingAgent"), role: "status", "aria-label": t("providerswitcher.savedRestartingAgent"), children: _jsx(CheckCircle2, { className: "h-3.5 w-3.5" }) }))] })] })] })) : null] })) : null, isSubscriptionProviderSelectionId(visibleProviderPanelId) ? (_jsxs("div", { className: "min-w-0", children: [_jsx(ProviderPanelHeader, { icon: KeyRound, title: getSubscriptionProviderLabel(SUBSCRIPTION_PROVIDER_SELECTIONS.find((provider) => provider.id === visibleProviderPanelId) ?? SUBSCRIPTION_PROVIDER_SELECTIONS[0], t), description: "Connect subscription-backed access for models and task agents.", children: cloudCallsDisabled ||
570
- resolvedSelectedId !== visibleProviderPanelId ? (_jsx(Button, { type: "button", variant: "outline", className: "h-8 rounded-lg px-2.5 text-xs", onClick: () => void handleSelectSubscription(visibleProviderPanelId), children: "Use subscription" })) : null }), _jsxs("div", { className: "px-3 py-3 sm:px-4", children: [cloudCallsDisabled ? (_jsx("div", { className: "mb-3 rounded-lg border border-warn/30 bg-warn/5 px-3 py-2 text-warn text-xs-tight", children: "Local-only active. Remote subscription routing is paused." })) : null, _jsx(SubscriptionStatus, { resolvedSelectedId: visibleProviderPanelId, subscriptionStatus: subscriptionStatus, anthropicConnected: anthropicConnected, setAnthropicConnected: setAnthropicConnected, anthropicCliDetected: anthropicCliDetected, openaiConnected: openaiConnected, setOpenaiConnected: setOpenaiConnected, handleSelectSubscription: handleSelectSubscription, loadSubscriptionStatus: loadSubscriptionStatus })] })] })) : null, selectedPanelProvider ? (_jsxs("div", { className: "min-w-0", children: [_jsx(ProviderPanelHeader, { icon: KeyRound, title: apiProviderChoices.find((choice) => choice.id === visibleProviderPanelId)?.label ?? selectedPanelProvider.name, description: "Use your own provider API key and model routing.", children: cloudCallsDisabled ||
571
+ resolvedSelectedId !== visibleProviderPanelId ? (_jsx(Button, { type: "button", variant: "outline", className: "h-8 rounded-lg px-2.5 text-xs", onClick: () => void handleSelectSubscription(visibleProviderPanelId), children: "Use subscription" })) : null }), _jsxs("div", { className: "px-3 py-3 sm:px-4", children: [cloudCallsDisabled ? (_jsx("div", { className: "mb-3 rounded-lg border border-warn/30 bg-warn/5 px-3 py-2 text-warn text-xs-tight", children: "Local-only active. Remote subscription routing is paused." })) : null, _jsx(SubscriptionStatus, { resolvedSelectedId: visibleProviderPanelId, subscriptionStatus: subscriptionStatus, anthropicConnected: anthropicConnected, setAnthropicConnected: setAnthropicConnected, anthropicCliDetected: anthropicCliDetected, openaiConnected: openaiConnected, setOpenaiConnected: setOpenaiConnected, handleSelectSubscription: handleSelectSubscription, loadSubscriptionStatus: loadSubscriptionStatus }), _jsx(AccountList, { providerId: SUBSCRIPTION_PROVIDER_SELECTIONS.find((p) => p.id === visibleProviderPanelId)?.storedProvider ?? "anthropic-subscription" })] })] })) : null, selectedPanelProvider ? (_jsxs("div", { className: "min-w-0", children: [_jsx(ProviderPanelHeader, { icon: KeyRound, title: apiProviderChoices.find((choice) => choice.id === visibleProviderPanelId)?.label ?? selectedPanelProvider.name, description: "Use your own provider API key and model routing.", children: cloudCallsDisabled ||
571
572
  resolvedSelectedId !== visibleProviderPanelId ? (_jsx(Button, { type: "button", variant: "outline", className: "h-8 rounded-lg px-2.5 text-xs", onClick: () => void handleSwitchProvider(visibleProviderPanelId), children: "Use provider" })) : null }), _jsxs("div", { className: "px-3 py-3 sm:px-4", children: [cloudCallsDisabled ? (_jsx("div", { className: "mb-3 rounded-lg border border-warn/30 bg-warn/5 px-3 py-2 text-warn text-xs-tight", children: "Local-only active. Remote API routing is paused." })) : null, _jsx(ApiKeyConfig, { selectedProvider: selectedPanelProvider, pluginSaving: pluginSaving, pluginSaveSuccess: pluginSaveSuccess, handlePluginConfigSave: handlePluginConfigSave, loadPlugins: loadPlugins })] })] })) : null] })] }));
572
573
  }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * useAccounts — fetches and mutates the multi-account credential pool
3
+ * surfaced by `/api/accounts/*`.
4
+ *
5
+ * Polls `client.listAccounts()` on a configurable interval (default 30s)
6
+ * to keep usage / health rows fresh. Each mutation routes through the
7
+ * matching client method, applies an optimistic local update where safe,
8
+ * and reconciles after the server response. Failures bubble through
9
+ * `setActionNotice` so the parent settings panel can surface them.
10
+ */
11
+ import type { LinkedAccountProviderId } from "@elizaos/shared";
12
+ import type { AccountStrategy, AccountTestResult, AccountsListResponse } from "../api/client-agent";
13
+ type ActionTone = "info" | "success" | "error";
14
+ type ActionNoticeFn = (text: string, tone?: ActionTone, ttlMs?: number, once?: boolean, busy?: boolean) => void;
15
+ export interface UseAccountsOptions {
16
+ setActionNotice?: ActionNoticeFn;
17
+ /** How often to refetch the full list. Defaults to 30s. */
18
+ pollMs?: number;
19
+ }
20
+ export interface UseAccountsResult {
21
+ data: AccountsListResponse | null;
22
+ loading: boolean;
23
+ saving: Set<string>;
24
+ refresh: () => Promise<void>;
25
+ createApiKey: (providerId: LinkedAccountProviderId, body: {
26
+ label: string;
27
+ apiKey: string;
28
+ }) => Promise<void>;
29
+ patch: (providerId: LinkedAccountProviderId, accountId: string, body: Partial<{
30
+ label: string;
31
+ enabled: boolean;
32
+ priority: number;
33
+ }>) => Promise<void>;
34
+ remove: (providerId: LinkedAccountProviderId, accountId: string) => Promise<void>;
35
+ test: (providerId: LinkedAccountProviderId, accountId: string) => Promise<AccountTestResult>;
36
+ refreshUsage: (providerId: LinkedAccountProviderId, accountId: string) => Promise<void>;
37
+ setStrategy: (providerId: LinkedAccountProviderId, strategy: AccountStrategy) => Promise<void>;
38
+ }
39
+ export declare function useAccounts(opts?: UseAccountsOptions): UseAccountsResult;
40
+ export {};
41
+ //# sourceMappingURL=useAccounts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useAccounts.d.ts","sourceRoot":"","sources":["../../../../../../app-core/src/hooks/useAccounts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAuB,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAGpF,OAAO,KAAK,EAEV,eAAe,EACf,iBAAiB,EACjB,oBAAoB,EACrB,MAAM,qBAAqB,CAAC;AAE7B,KAAK,UAAU,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC;AAE/C,KAAK,cAAc,GAAG,CACpB,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,UAAU,EACjB,KAAK,CAAC,EAAE,MAAM,EACd,IAAI,CAAC,EAAE,OAAO,EACd,IAAI,CAAC,EAAE,OAAO,KACX,IAAI,CAAC;AAEV,MAAM,WAAW,kBAAkB;IACjC,eAAe,CAAC,EAAE,cAAc,CAAC;IACjC,2DAA2D;IAC3D,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,oBAAoB,GAAG,IAAI,CAAC;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACpB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,YAAY,EAAE,CACZ,UAAU,EAAE,uBAAuB,EACnC,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,KACpC,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,KAAK,EAAE,CACL,UAAU,EAAE,uBAAuB,EACnC,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,KACjE,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,MAAM,EAAE,CACN,UAAU,EAAE,uBAAuB,EACnC,SAAS,EAAE,MAAM,KACd,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,IAAI,EAAE,CACJ,UAAU,EAAE,uBAAuB,EACnC,SAAS,EAAE,MAAM,KACd,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAChC,YAAY,EAAE,CACZ,UAAU,EAAE,uBAAuB,EACnC,SAAS,EAAE,MAAM,KACd,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,WAAW,EAAE,CACX,UAAU,EAAE,uBAAuB,EACnC,QAAQ,EAAE,eAAe,KACtB,OAAO,CAAC,IAAI,CAAC,CAAC;CACpB;AAmCD,wBAAgB,WAAW,CACzB,IAAI,GAAE,kBAAuB,GAC5B,iBAAiB,CAuOnB"}