@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.
- package/package.json +4 -4
- package/packages/agent/src/api/accounts-routes.d.ts +11 -3
- package/packages/agent/src/api/accounts-routes.d.ts.map +1 -1
- package/packages/agent/src/api/accounts-routes.js +70 -123
- package/packages/agent/src/api/subscription-routes.js +27 -23
- package/packages/agent/src/runtime/plugin-role-gating.js +4 -3
- package/packages/app-core/src/api/client-agent.d.ts +63 -1
- package/packages/app-core/src/api/client-agent.d.ts.map +1 -1
- package/packages/app-core/src/api/client-agent.js +51 -0
- package/packages/app-core/src/components/accounts/AccountCard.d.ts +30 -0
- package/packages/app-core/src/components/accounts/AccountCard.d.ts.map +1 -0
- package/packages/app-core/src/components/accounts/AccountCard.js +183 -0
- package/packages/app-core/src/components/accounts/AccountList.d.ts +15 -0
- package/packages/app-core/src/components/accounts/AccountList.d.ts.map +1 -0
- package/packages/app-core/src/components/accounts/AccountList.js +80 -0
- package/packages/app-core/src/components/accounts/AddAccountDialog.d.ts +33 -0
- package/packages/app-core/src/components/accounts/AddAccountDialog.d.ts.map +1 -0
- package/packages/app-core/src/components/accounts/AddAccountDialog.js +277 -0
- package/packages/app-core/src/components/accounts/RotationStrategyPicker.d.ts +16 -0
- package/packages/app-core/src/components/accounts/RotationStrategyPicker.d.ts.map +1 -0
- package/packages/app-core/src/components/accounts/RotationStrategyPicker.js +50 -0
- package/packages/app-core/src/components/pages/PluginsView.d.ts.map +1 -1
- package/packages/app-core/src/components/pages/PluginsView.js +3 -34
- package/packages/app-core/src/components/settings/ProviderSwitcher.d.ts.map +1 -1
- package/packages/app-core/src/components/settings/ProviderSwitcher.js +2 -1
- package/packages/app-core/src/hooks/useAccounts.d.ts +41 -0
- package/packages/app-core/src/hooks/useAccounts.d.ts.map +1 -0
- package/packages/app-core/src/hooks/useAccounts.js +250 -0
- package/packages/app-core/src/i18n/locales/en.json +31 -31
- package/packages/app-core/src/i18n/locales/es.json +31 -31
- package/packages/app-core/src/i18n/locales/ko.json +31 -31
- package/packages/app-core/src/i18n/locales/pt.json +31 -31
- package/packages/app-core/src/i18n/locales/tl.json +31 -31
- package/packages/app-core/src/i18n/locales/vi.json +31 -31
- package/packages/app-core/src/i18n/locales/zh-CN.json +31 -31
- package/packages/typescript/src/features/basic-capabilities/index.d.ts +1 -0
- package/packages/typescript/src/features/basic-capabilities/index.d.ts.map +1 -1
- package/packages/typescript/src/features/basic-capabilities/index.js +4 -0
- package/packages/typescript/src/features/index.d.ts.map +1 -1
- package/packages/typescript/src/features/index.js +2 -7
- package/packages/typescript/src/features/plugin-manager/actions/plugin-handlers/core-status.d.ts +14 -0
- package/packages/typescript/src/features/plugin-manager/actions/plugin-handlers/core-status.d.ts.map +1 -0
- package/packages/typescript/src/features/plugin-manager/actions/plugin-handlers/core-status.js +48 -0
- package/packages/typescript/src/features/plugin-manager/actions/plugin-handlers/create.d.ts +32 -0
- package/packages/typescript/src/features/plugin-manager/actions/plugin-handlers/create.d.ts.map +1 -0
- package/packages/typescript/src/features/plugin-manager/actions/plugin-handlers/create.js +502 -0
- package/packages/typescript/src/features/plugin-manager/actions/plugin-handlers/eject.d.ts +16 -0
- package/packages/typescript/src/features/plugin-manager/actions/plugin-handlers/eject.d.ts.map +1 -0
- package/packages/typescript/src/features/plugin-manager/actions/plugin-handlers/eject.js +47 -0
- package/packages/typescript/src/features/plugin-manager/actions/plugin-handlers/install.d.ts +18 -0
- package/packages/typescript/src/features/plugin-manager/actions/plugin-handlers/install.d.ts.map +1 -0
- package/packages/typescript/src/features/plugin-manager/actions/plugin-handlers/install.js +59 -0
- package/packages/typescript/src/features/plugin-manager/actions/plugin-handlers/list-ejected.d.ts +14 -0
- package/packages/typescript/src/features/plugin-manager/actions/plugin-handlers/list-ejected.d.ts.map +1 -0
- package/packages/typescript/src/features/plugin-manager/actions/plugin-handlers/list-ejected.js +31 -0
- package/packages/typescript/src/features/plugin-manager/actions/plugin-handlers/list.d.ts +14 -0
- package/packages/typescript/src/features/plugin-manager/actions/plugin-handlers/list.d.ts.map +1 -0
- package/packages/typescript/src/features/plugin-manager/actions/plugin-handlers/list.js +51 -0
- package/packages/typescript/src/features/plugin-manager/actions/plugin-handlers/reinject.d.ts +15 -0
- package/packages/typescript/src/features/plugin-manager/actions/plugin-handlers/reinject.d.ts.map +1 -0
- package/packages/typescript/src/features/plugin-manager/actions/plugin-handlers/reinject.js +43 -0
- package/packages/typescript/src/features/plugin-manager/actions/plugin-handlers/search.d.ts +15 -0
- package/packages/typescript/src/features/plugin-manager/actions/plugin-handlers/search.d.ts.map +1 -0
- package/packages/typescript/src/features/plugin-manager/actions/plugin-handlers/search.js +52 -0
- package/packages/typescript/src/features/plugin-manager/actions/plugin-handlers/sync.d.ts +15 -0
- package/packages/typescript/src/features/plugin-manager/actions/plugin-handlers/sync.d.ts.map +1 -0
- package/packages/typescript/src/features/plugin-manager/actions/plugin-handlers/sync.js +48 -0
- package/packages/typescript/src/features/plugin-manager/actions/plugin.d.ts +25 -0
- package/packages/typescript/src/features/plugin-manager/actions/plugin.d.ts.map +1 -0
- package/packages/typescript/src/features/plugin-manager/actions/plugin.js +439 -0
- package/packages/typescript/src/features/plugin-manager/index.d.ts +2 -0
- package/packages/typescript/src/features/plugin-manager/index.d.ts.map +1 -1
- package/packages/typescript/src/features/plugin-manager/index.js +7 -10
- package/packages/typescript/src/features/plugin-manager/security.d.ts +33 -0
- package/packages/typescript/src/features/plugin-manager/security.d.ts.map +1 -0
- package/packages/typescript/src/features/plugin-manager/security.js +80 -0
- package/packages/app-core/src/runtime/plugin-manager-guard.d.ts +0 -12
- package/packages/app-core/src/runtime/plugin-manager-guard.d.ts.map +0 -1
- package/packages/app-core/src/runtime/plugin-manager-guard.js +0 -82
|
@@ -1316,3 +1316,54 @@ ElizaClient.prototype.saveStreamSettings = async function (settings) {
|
|
|
1316
1316
|
body: JSON.stringify({ settings }),
|
|
1317
1317
|
});
|
|
1318
1318
|
};
|
|
1319
|
+
// ---------------------------------------------------------------------------
|
|
1320
|
+
// Multi-account routes (WS3)
|
|
1321
|
+
// ---------------------------------------------------------------------------
|
|
1322
|
+
ElizaClient.prototype.listAccounts = async function () {
|
|
1323
|
+
return this.fetch("/api/accounts");
|
|
1324
|
+
};
|
|
1325
|
+
ElizaClient.prototype.createApiKeyAccount = async function (providerId, body) {
|
|
1326
|
+
return this.fetch(`/api/accounts/${encodeURIComponent(providerId)}`, {
|
|
1327
|
+
method: "POST",
|
|
1328
|
+
body: JSON.stringify({ source: "api-key", ...body }),
|
|
1329
|
+
});
|
|
1330
|
+
};
|
|
1331
|
+
ElizaClient.prototype.patchAccount = async function (providerId, accountId, body) {
|
|
1332
|
+
return this.fetch(`/api/accounts/${encodeURIComponent(providerId)}/${encodeURIComponent(accountId)}`, {
|
|
1333
|
+
method: "PATCH",
|
|
1334
|
+
body: JSON.stringify(body),
|
|
1335
|
+
});
|
|
1336
|
+
};
|
|
1337
|
+
ElizaClient.prototype.deleteAccount = async function (providerId, accountId) {
|
|
1338
|
+
return this.fetch(`/api/accounts/${encodeURIComponent(providerId)}/${encodeURIComponent(accountId)}`, { method: "DELETE" });
|
|
1339
|
+
};
|
|
1340
|
+
ElizaClient.prototype.testAccount = async function (providerId, accountId) {
|
|
1341
|
+
return this.fetch(`/api/accounts/${encodeURIComponent(providerId)}/${encodeURIComponent(accountId)}/test`, { method: "POST" });
|
|
1342
|
+
};
|
|
1343
|
+
ElizaClient.prototype.refreshAccountUsage = async function (providerId, accountId) {
|
|
1344
|
+
return this.fetch(`/api/accounts/${encodeURIComponent(providerId)}/${encodeURIComponent(accountId)}/refresh-usage`, { method: "POST" });
|
|
1345
|
+
};
|
|
1346
|
+
ElizaClient.prototype.startAccountOAuth = async function (providerId, body) {
|
|
1347
|
+
return this.fetch(`/api/accounts/${encodeURIComponent(providerId)}/oauth/start`, {
|
|
1348
|
+
method: "POST",
|
|
1349
|
+
body: JSON.stringify(body),
|
|
1350
|
+
});
|
|
1351
|
+
};
|
|
1352
|
+
ElizaClient.prototype.submitAccountOAuthCode = async function (providerId, body) {
|
|
1353
|
+
return this.fetch(`/api/accounts/${encodeURIComponent(providerId)}/oauth/submit-code`, {
|
|
1354
|
+
method: "POST",
|
|
1355
|
+
body: JSON.stringify(body),
|
|
1356
|
+
});
|
|
1357
|
+
};
|
|
1358
|
+
ElizaClient.prototype.cancelAccountOAuth = async function (providerId, body) {
|
|
1359
|
+
return this.fetch(`/api/accounts/${encodeURIComponent(providerId)}/oauth/cancel`, {
|
|
1360
|
+
method: "POST",
|
|
1361
|
+
body: JSON.stringify(body),
|
|
1362
|
+
});
|
|
1363
|
+
};
|
|
1364
|
+
ElizaClient.prototype.patchProviderStrategy = async function (providerId, body) {
|
|
1365
|
+
return this.fetch(`/api/providers/${encodeURIComponent(providerId)}/strategy`, {
|
|
1366
|
+
method: "PATCH",
|
|
1367
|
+
body: JSON.stringify(body),
|
|
1368
|
+
});
|
|
1369
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AccountCard — single account row inside an AccountList.
|
|
3
|
+
*
|
|
4
|
+
* Renders the credential's health glyph, label (inline-editable), source
|
|
5
|
+
* badge, priority controls (up/down arrows — no drag-drop dependency),
|
|
6
|
+
* usage bars (Anthropic shows session + weekly, Codex shows session
|
|
7
|
+
* only), enabled toggle, Test/Refresh/Delete actions, and a confirm
|
|
8
|
+
* dialog for delete.
|
|
9
|
+
*/
|
|
10
|
+
import type { AccountWithCredentialFlag } from "../../api/client-agent";
|
|
11
|
+
export interface AccountCardProps {
|
|
12
|
+
account: AccountWithCredentialFlag;
|
|
13
|
+
isFirst: boolean;
|
|
14
|
+
isLast: boolean;
|
|
15
|
+
saving: boolean;
|
|
16
|
+
onPatch: (body: Partial<{
|
|
17
|
+
label: string;
|
|
18
|
+
enabled: boolean;
|
|
19
|
+
priority: number;
|
|
20
|
+
}>) => Promise<void>;
|
|
21
|
+
onMoveUp: () => Promise<void>;
|
|
22
|
+
onMoveDown: () => Promise<void>;
|
|
23
|
+
onTest: () => Promise<void>;
|
|
24
|
+
onRefreshUsage: () => Promise<void>;
|
|
25
|
+
onDelete: () => Promise<void>;
|
|
26
|
+
testBusy?: boolean;
|
|
27
|
+
refreshBusy?: boolean;
|
|
28
|
+
}
|
|
29
|
+
export declare function AccountCard({ account, isFirst, isLast, saving, onPatch, onMoveUp, onMoveDown, onTest, onRefreshUsage, onDelete, testBusy, refreshBusy, }: AccountCardProps): import("react/jsx-runtime").JSX.Element;
|
|
30
|
+
//# sourceMappingURL=AccountCard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AccountCard.d.ts","sourceRoot":"","sources":["../../../../../../../app-core/src/components/accounts/AccountCard.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AA0BH,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,wBAAwB,CAAC;AAExE,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,yBAAyB,CAAC;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,CACP,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,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,UAAU,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAChC,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,cAAc,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAqHD,wBAAgB,WAAW,CAAC,EAC1B,OAAO,EACP,OAAO,EACP,MAAM,EACN,MAAM,EACN,OAAO,EACP,QAAQ,EACR,UAAU,EACV,MAAM,EACN,cAAc,EACd,QAAQ,EACR,QAAgB,EAChB,WAAmB,GACpB,EAAE,gBAAgB,2CAoSlB"}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* AccountCard — single account row inside an AccountList.
|
|
4
|
+
*
|
|
5
|
+
* Renders the credential's health glyph, label (inline-editable), source
|
|
6
|
+
* badge, priority controls (up/down arrows — no drag-drop dependency),
|
|
7
|
+
* usage bars (Anthropic shows session + weekly, Codex shows session
|
|
8
|
+
* only), enabled toggle, Test/Refresh/Delete actions, and a confirm
|
|
9
|
+
* dialog for delete.
|
|
10
|
+
*/
|
|
11
|
+
import { Badge, Button, Checkbox, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, Input, Spinner, StatusBadge, cn, } from "@elizaos/ui";
|
|
12
|
+
import { ChevronDown, ChevronUp, Pencil, Trash2 } from "lucide-react";
|
|
13
|
+
import { useCallback, useEffect, useState, } from "react";
|
|
14
|
+
import { useApp } from "../../state";
|
|
15
|
+
function formatRelativeTime(epochMs) {
|
|
16
|
+
if (!epochMs)
|
|
17
|
+
return "—";
|
|
18
|
+
const diff = Date.now() - epochMs;
|
|
19
|
+
if (diff < 60_000)
|
|
20
|
+
return "just now";
|
|
21
|
+
if (diff < 3_600_000)
|
|
22
|
+
return `${Math.floor(diff / 60_000)}m ago`;
|
|
23
|
+
if (diff < 86_400_000)
|
|
24
|
+
return `${Math.floor(diff / 3_600_000)}h ago`;
|
|
25
|
+
return `${Math.floor(diff / 86_400_000)}d ago`;
|
|
26
|
+
}
|
|
27
|
+
function formatResetIn(epochMs) {
|
|
28
|
+
if (!epochMs)
|
|
29
|
+
return null;
|
|
30
|
+
const diff = epochMs - Date.now();
|
|
31
|
+
if (diff <= 0)
|
|
32
|
+
return null;
|
|
33
|
+
if (diff < 3_600_000)
|
|
34
|
+
return `${Math.max(1, Math.floor(diff / 60_000))}m`;
|
|
35
|
+
if (diff < 86_400_000)
|
|
36
|
+
return `${Math.floor(diff / 3_600_000)}h`;
|
|
37
|
+
return `${Math.floor(diff / 86_400_000)}d`;
|
|
38
|
+
}
|
|
39
|
+
function clampPct(value) {
|
|
40
|
+
if (value == null || Number.isNaN(value))
|
|
41
|
+
return undefined;
|
|
42
|
+
return Math.max(0, Math.min(100, value));
|
|
43
|
+
}
|
|
44
|
+
function UsageBar({ label, pct, resetsAt }) {
|
|
45
|
+
const clamped = clampPct(pct);
|
|
46
|
+
const resetIn = formatResetIn(resetsAt);
|
|
47
|
+
const tone = clamped == null
|
|
48
|
+
? "bg-muted/30"
|
|
49
|
+
: clamped >= 85
|
|
50
|
+
? "bg-destructive"
|
|
51
|
+
: clamped >= 60
|
|
52
|
+
? "bg-warn"
|
|
53
|
+
: "bg-ok";
|
|
54
|
+
const titleParts = [
|
|
55
|
+
`${label}: ${clamped == null ? "—" : `${Math.round(clamped)}%`}`,
|
|
56
|
+
];
|
|
57
|
+
if (resetIn)
|
|
58
|
+
titleParts.push(`resets in ${resetIn}`);
|
|
59
|
+
return (_jsxs("div", { className: "flex min-w-0 items-center gap-1.5", title: titleParts.join(" · "), children: [_jsx("span", { className: "w-9 shrink-0 text-[10px] font-medium uppercase tracking-wider text-muted", children: label }), _jsx("div", { className: "relative h-1.5 min-w-[48px] flex-1 overflow-hidden rounded-full bg-bg-accent", children: _jsx("div", { className: cn("h-full transition-all", tone), style: { width: `${clamped ?? 0}%` } }) }), _jsx("span", { className: "w-8 shrink-0 text-right text-[10px] tabular-nums text-muted", children: clamped == null ? "—" : `${Math.round(clamped)}%` })] }));
|
|
60
|
+
}
|
|
61
|
+
function deriveHealthLabel(account, t) {
|
|
62
|
+
switch (account.health) {
|
|
63
|
+
case "ok":
|
|
64
|
+
return {
|
|
65
|
+
label: t("accounts.health.ok", { defaultValue: "Healthy" }),
|
|
66
|
+
tone: "success",
|
|
67
|
+
};
|
|
68
|
+
case "rate-limited": {
|
|
69
|
+
const resetIn = formatResetIn(account.healthDetail?.until);
|
|
70
|
+
return {
|
|
71
|
+
label: resetIn
|
|
72
|
+
? t("accounts.health.rateLimitedWithReset", {
|
|
73
|
+
defaultValue: `Rate-limited (resets in ${resetIn})`,
|
|
74
|
+
resetIn,
|
|
75
|
+
})
|
|
76
|
+
: t("accounts.health.rateLimited", {
|
|
77
|
+
defaultValue: "Rate-limited",
|
|
78
|
+
}),
|
|
79
|
+
tone: "warning",
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
case "needs-reauth":
|
|
83
|
+
return {
|
|
84
|
+
label: t("accounts.health.needsReauth", {
|
|
85
|
+
defaultValue: "Needs reauth",
|
|
86
|
+
}),
|
|
87
|
+
tone: "danger",
|
|
88
|
+
};
|
|
89
|
+
case "invalid":
|
|
90
|
+
return {
|
|
91
|
+
label: t("accounts.health.invalid", {
|
|
92
|
+
defaultValue: "Invalid credential",
|
|
93
|
+
}),
|
|
94
|
+
tone: "danger",
|
|
95
|
+
};
|
|
96
|
+
default:
|
|
97
|
+
return {
|
|
98
|
+
label: t("accounts.health.unknown", { defaultValue: "Unknown" }),
|
|
99
|
+
tone: "muted",
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
export function AccountCard({ account, isFirst, isLast, saving, onPatch, onMoveUp, onMoveDown, onTest, onRefreshUsage, onDelete, testBusy = false, refreshBusy = false, }) {
|
|
104
|
+
const { t } = useApp();
|
|
105
|
+
const [labelEditing, setLabelEditing] = useState(false);
|
|
106
|
+
const [labelDraft, setLabelDraft] = useState(account.label);
|
|
107
|
+
const [confirmingDelete, setConfirmingDelete] = useState(false);
|
|
108
|
+
const [deleteBusy, setDeleteBusy] = useState(false);
|
|
109
|
+
useEffect(() => {
|
|
110
|
+
if (!labelEditing)
|
|
111
|
+
setLabelDraft(account.label);
|
|
112
|
+
}, [account.label, labelEditing]);
|
|
113
|
+
const submitLabel = useCallback(async (event) => {
|
|
114
|
+
event?.preventDefault();
|
|
115
|
+
const trimmed = labelDraft.trim();
|
|
116
|
+
setLabelEditing(false);
|
|
117
|
+
if (!trimmed || trimmed === account.label) {
|
|
118
|
+
setLabelDraft(account.label);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
try {
|
|
122
|
+
await onPatch({ label: trimmed });
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
setLabelDraft(account.label);
|
|
126
|
+
}
|
|
127
|
+
}, [account.label, labelDraft, onPatch]);
|
|
128
|
+
const handleLabelKey = (event) => {
|
|
129
|
+
if (event.key === "Escape") {
|
|
130
|
+
event.preventDefault();
|
|
131
|
+
setLabelDraft(account.label);
|
|
132
|
+
setLabelEditing(false);
|
|
133
|
+
}
|
|
134
|
+
else if (event.key === "Enter") {
|
|
135
|
+
event.preventDefault();
|
|
136
|
+
void submitLabel();
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
const handleConfirmDelete = useCallback(async () => {
|
|
140
|
+
setDeleteBusy(true);
|
|
141
|
+
try {
|
|
142
|
+
await onDelete();
|
|
143
|
+
setConfirmingDelete(false);
|
|
144
|
+
}
|
|
145
|
+
finally {
|
|
146
|
+
setDeleteBusy(false);
|
|
147
|
+
}
|
|
148
|
+
}, [onDelete]);
|
|
149
|
+
const health = deriveHealthLabel(account, t);
|
|
150
|
+
const isAnthropic = account.providerId === "anthropic-subscription";
|
|
151
|
+
const isCodex = account.providerId === "openai-codex";
|
|
152
|
+
const usage = account.usage;
|
|
153
|
+
const lastUsed = formatRelativeTime(account.lastUsedAt);
|
|
154
|
+
return (_jsxs("div", { className: cn("flex flex-col gap-2 rounded-lg border border-border/45 bg-card/35 px-3 py-2.5 transition-opacity", !account.enabled && "opacity-60"), children: [_jsxs("div", { className: "flex flex-wrap items-center gap-x-3 gap-y-2", children: [_jsxs("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: [_jsx(StatusBadge, { label: health.label, tone: health.tone, withDot: true }), labelEditing ? (_jsx("form", { onSubmit: submitLabel, className: "min-w-0 flex-1", children: _jsx(Input, { value: labelDraft, onChange: (e) => setLabelDraft(e.target.value), onBlur: () => void submitLabel(), onKeyDown: handleLabelKey, autoFocus: true, className: "h-7 max-w-[240px] text-sm", "aria-label": t("accounts.label.edit", {
|
|
155
|
+
defaultValue: "Account label",
|
|
156
|
+
}) }) })) : (_jsxs("button", { type: "button", onClick: () => setLabelEditing(true), title: t("accounts.label.editTooltip", {
|
|
157
|
+
defaultValue: "Click to rename",
|
|
158
|
+
}), className: "inline-flex min-w-0 items-center gap-1 truncate rounded text-sm font-medium text-txt hover:text-accent", children: [_jsx("span", { className: "truncate", children: account.label }), _jsx(Pencil, { className: "h-3 w-3 shrink-0 opacity-0 transition-opacity group-hover:opacity-100", "aria-hidden": true })] })), _jsx(Badge, { variant: "outline", className: "shrink-0 text-[10px] uppercase", children: account.source === "oauth"
|
|
159
|
+
? t("accounts.source.oauth", { defaultValue: "OAuth" })
|
|
160
|
+
: t("accounts.source.apiKey", { defaultValue: "API key" }) }), _jsxs("span", { className: "shrink-0 text-[10px] tabular-nums text-muted", title: t("accounts.priority.tooltip", {
|
|
161
|
+
defaultValue: "Lower priority value runs first",
|
|
162
|
+
}), children: ["#", account.priority] }), _jsx("span", { className: "shrink-0 text-[10px] text-muted", children: t("accounts.lastUsed", {
|
|
163
|
+
defaultValue: `Last used ${lastUsed}`,
|
|
164
|
+
lastUsed,
|
|
165
|
+
}) })] }), _jsxs("div", { className: "flex shrink-0 items-center gap-1.5", children: [_jsx(Button, { type: "button", variant: "ghost", size: "sm", disabled: isFirst || saving, onClick: () => void onMoveUp(), "aria-label": t("accounts.moveUp", { defaultValue: "Move up" }), title: t("accounts.moveUp", { defaultValue: "Move up" }), className: "h-7 w-7 p-0", children: _jsx(ChevronUp, { className: "h-3.5 w-3.5", "aria-hidden": true }) }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", disabled: isLast || saving, onClick: () => void onMoveDown(), "aria-label": t("accounts.moveDown", { defaultValue: "Move down" }), title: t("accounts.moveDown", { defaultValue: "Move down" }), className: "h-7 w-7 p-0", children: _jsx(ChevronDown, { className: "h-3.5 w-3.5", "aria-hidden": true }) }), _jsxs("label", { className: "ml-1 inline-flex items-center gap-1.5 text-xs text-muted", children: [_jsx(Checkbox, { checked: account.enabled, disabled: saving, onCheckedChange: (value) => {
|
|
166
|
+
void onPatch({ enabled: value === true });
|
|
167
|
+
}, "aria-label": t("accounts.enabledToggle", {
|
|
168
|
+
defaultValue: "Account enabled",
|
|
169
|
+
}) }), t("accounts.enabled", { defaultValue: "Enabled" })] }), _jsx(Button, { type: "button", variant: "outline", size: "sm", disabled: testBusy || saving, onClick: () => void onTest(), className: "h-7 px-2 text-xs", children: testBusy ? (_jsx(Spinner, { className: "h-3 w-3" })) : (t("accounts.test", { defaultValue: "Test" })) }), _jsx(Button, { type: "button", variant: "outline", size: "sm", disabled: refreshBusy || saving, onClick: () => void onRefreshUsage(), className: "h-7 px-2 text-xs", children: refreshBusy ? (_jsx(Spinner, { className: "h-3 w-3" })) : (t("accounts.refresh", { defaultValue: "Refresh" })) }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", disabled: saving, onClick: () => setConfirmingDelete(true), "aria-label": t("accounts.delete", { defaultValue: "Delete account" }), title: t("accounts.delete", { defaultValue: "Delete account" }), className: "h-7 w-7 p-0 text-destructive hover:bg-destructive/10", children: _jsx(Trash2, { className: "h-3.5 w-3.5", "aria-hidden": true }) })] })] }), _jsxs("div", { className: "flex flex-wrap items-center gap-x-4 gap-y-1.5", children: [isAnthropic ? (_jsxs(_Fragment, { children: [_jsx(UsageBar, { label: t("accounts.usage.session5h", { defaultValue: "5h" }), pct: usage?.sessionPct, resetsAt: usage?.resetsAt }), _jsx(UsageBar, { label: t("accounts.usage.weekly", { defaultValue: "7d" }), pct: usage?.weeklyPct, resetsAt: usage?.resetsAt })] })) : isCodex ? (_jsx(UsageBar, { label: t("accounts.usage.session", { defaultValue: "Session" }), pct: usage?.sessionPct, resetsAt: usage?.resetsAt })) : usage ? (_jsx(UsageBar, { label: t("accounts.usage.session", { defaultValue: "Session" }), pct: usage.sessionPct, resetsAt: usage.resetsAt })) : (_jsx("span", { className: "text-xs text-muted", children: t("accounts.usage.none", {
|
|
170
|
+
defaultValue: "No usage data yet — click Refresh to probe.",
|
|
171
|
+
}) })), !account.hasCredential ? (_jsx("span", { className: "text-[10px] text-warn", title: t("accounts.orphan.tooltip", {
|
|
172
|
+
defaultValue: "Pool metadata exists but no on-disk credential was found.",
|
|
173
|
+
}), children: t("accounts.orphan.label", {
|
|
174
|
+
defaultValue: "Orphan metadata",
|
|
175
|
+
}) })) : null] }), _jsx(Dialog, { open: confirmingDelete, onOpenChange: (open) => {
|
|
176
|
+
if (!deleteBusy)
|
|
177
|
+
setConfirmingDelete(open);
|
|
178
|
+
}, children: _jsxs(DialogContent, { children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: t("accounts.deleteConfirm.title", {
|
|
179
|
+
defaultValue: "Remove this account?",
|
|
180
|
+
}) }), _jsx(DialogDescription, { children: t("accounts.deleteConfirm.description", {
|
|
181
|
+
defaultValue: "Removing the account deletes its stored credential and pool metadata. This cannot be undone.",
|
|
182
|
+
}) })] }), _jsxs(DialogFooter, { className: "gap-2", children: [_jsx(Button, { type: "button", variant: "ghost", disabled: deleteBusy, onClick: () => setConfirmingDelete(false), children: t("accounts.cancel", { defaultValue: "Cancel" }) }), _jsx(Button, { type: "button", variant: "destructive", disabled: deleteBusy, onClick: () => void handleConfirmDelete(), children: deleteBusy ? (_jsx(Spinner, { className: "h-3 w-3" })) : (t("accounts.delete.confirm", { defaultValue: "Remove account" })) })] })] }) })] }));
|
|
183
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AccountList — provider-scoped multi-account UI.
|
|
3
|
+
*
|
|
4
|
+
* Renders the rotation strategy picker, "Add account" button, and a
|
|
5
|
+
* priority-ordered stack of `AccountCard`s for the given providerId.
|
|
6
|
+
* Up/down reordering swaps priorities with the neighbour via two
|
|
7
|
+
* sequential PATCH calls (no drag-drop dependency).
|
|
8
|
+
*/
|
|
9
|
+
import type { LinkedAccountProviderId } from "@elizaos/shared";
|
|
10
|
+
interface AccountListProps {
|
|
11
|
+
providerId: LinkedAccountProviderId;
|
|
12
|
+
}
|
|
13
|
+
export declare function AccountList({ providerId }: AccountListProps): import("react/jsx-runtime").JSX.Element;
|
|
14
|
+
export {};
|
|
15
|
+
//# sourceMappingURL=AccountList.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AccountList.d.ts","sourceRoot":"","sources":["../../../../../../../app-core/src/components/accounts/AccountList.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAU/D,UAAU,gBAAgB;IACxB,UAAU,EAAE,uBAAuB,CAAC;CACrC;AAOD,wBAAgB,WAAW,CAAC,EAAE,UAAU,EAAE,EAAE,gBAAgB,2CA8I3D"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* AccountList — provider-scoped multi-account UI.
|
|
4
|
+
*
|
|
5
|
+
* Renders the rotation strategy picker, "Add account" button, and a
|
|
6
|
+
* priority-ordered stack of `AccountCard`s for the given providerId.
|
|
7
|
+
* Up/down reordering swaps priorities with the neighbour via two
|
|
8
|
+
* sequential PATCH calls (no drag-drop dependency).
|
|
9
|
+
*/
|
|
10
|
+
import { Button, Spinner } from "@elizaos/ui";
|
|
11
|
+
import { Plus } from "lucide-react";
|
|
12
|
+
import { useCallback, useMemo, useState } from "react";
|
|
13
|
+
import { useApp } from "../../state";
|
|
14
|
+
import { useAccounts } from "../../hooks/useAccounts";
|
|
15
|
+
import { AccountCard } from "./AccountCard";
|
|
16
|
+
import { AddAccountDialog } from "./AddAccountDialog";
|
|
17
|
+
import { RotationStrategyPicker } from "./RotationStrategyPicker";
|
|
18
|
+
const SUBSCRIPTION_PROVIDERS = new Set([
|
|
19
|
+
"anthropic-subscription",
|
|
20
|
+
"openai-codex",
|
|
21
|
+
]);
|
|
22
|
+
export function AccountList({ providerId }) {
|
|
23
|
+
const { t } = useApp();
|
|
24
|
+
const accounts = useAccounts();
|
|
25
|
+
const [addDialogOpen, setAddDialogOpen] = useState(false);
|
|
26
|
+
const providerEntry = useMemo(() => accounts.data?.providers.find((p) => p.providerId === providerId), [accounts.data, providerId]);
|
|
27
|
+
const sorted = useMemo(() => providerEntry
|
|
28
|
+
? [...providerEntry.accounts].sort((a, b) => a.priority - b.priority)
|
|
29
|
+
: [], [providerEntry]);
|
|
30
|
+
const subscriptionProvider = SUBSCRIPTION_PROVIDERS.has(providerId);
|
|
31
|
+
// The accounts API currently 501s on POST for *-api providers (the
|
|
32
|
+
// credential-storage layer doesn't yet write API-key records for
|
|
33
|
+
// those). The pool / GET still work for any pre-existing entries, so
|
|
34
|
+
// render the list either way and gate the Add button.
|
|
35
|
+
const addDisabled = !subscriptionProvider;
|
|
36
|
+
const handleMove = useCallback(async (accountId, direction) => {
|
|
37
|
+
const index = sorted.findIndex((a) => a.id === accountId);
|
|
38
|
+
if (index < 0)
|
|
39
|
+
return;
|
|
40
|
+
const neighbourIndex = direction === "up" ? index - 1 : index + 1;
|
|
41
|
+
if (neighbourIndex < 0 || neighbourIndex >= sorted.length)
|
|
42
|
+
return;
|
|
43
|
+
const self = sorted[index];
|
|
44
|
+
const neighbour = sorted[neighbourIndex];
|
|
45
|
+
if (!self || !neighbour || self.priority === neighbour.priority)
|
|
46
|
+
return;
|
|
47
|
+
// Swap priorities. We patch the lower-priority one to a temp
|
|
48
|
+
// sentinel first to avoid the unique-priority assumption later
|
|
49
|
+
// consumers might add — for the current pool there's no uniqueness
|
|
50
|
+
// constraint, so two sequential patches are enough.
|
|
51
|
+
await accounts.patch(providerId, self.id, {
|
|
52
|
+
priority: neighbour.priority,
|
|
53
|
+
});
|
|
54
|
+
await accounts.patch(providerId, neighbour.id, {
|
|
55
|
+
priority: self.priority,
|
|
56
|
+
});
|
|
57
|
+
}, [accounts, providerId, sorted]);
|
|
58
|
+
if (accounts.loading && !accounts.data) {
|
|
59
|
+
return (_jsxs("div", { className: "mt-3 flex items-center gap-2 text-xs text-muted", children: [_jsx(Spinner, { className: "h-3 w-3" }), t("accounts.loading", { defaultValue: "Loading accounts…" })] }));
|
|
60
|
+
}
|
|
61
|
+
return (_jsxs("div", { className: "mt-3 flex flex-col gap-2 rounded-xl border border-border/40 bg-bg-accent/40 p-3", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [_jsx("div", { className: "flex items-center gap-2", children: _jsx("h3", { className: "text-xs font-semibold uppercase tracking-wider text-muted", children: t("accounts.heading", {
|
|
62
|
+
defaultValue: "Accounts ({{count}})",
|
|
63
|
+
count: sorted.length,
|
|
64
|
+
}) }) }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(RotationStrategyPicker, { providerId: providerId, value: providerEntry?.strategy, onChange: (strategy) => {
|
|
65
|
+
void accounts.setStrategy(providerId, strategy);
|
|
66
|
+
}, disabled: accounts.saving.has(`strategy:${providerId}`) }), _jsxs(Button, { type: "button", variant: "default", size: "sm", disabled: addDisabled, onClick: () => setAddDialogOpen(true), title: addDisabled
|
|
67
|
+
? t("accounts.add.disabledHint", {
|
|
68
|
+
defaultValue: "API-key accounts for this provider are not yet supported.",
|
|
69
|
+
})
|
|
70
|
+
: undefined, className: "h-8 gap-1 px-2.5 text-xs", children: [_jsx(Plus, { className: "h-3.5 w-3.5", "aria-hidden": true }), t("accounts.add.button", { defaultValue: "Add account" })] })] })] }), sorted.length === 0 ? (_jsx("div", { className: "rounded-lg border border-dashed border-border/50 px-3 py-6 text-center text-xs text-muted", children: t("accounts.empty", {
|
|
71
|
+
defaultValue: "No accounts yet — add one to start using this provider.",
|
|
72
|
+
}) })) : (_jsx("div", { className: "flex flex-col gap-2", children: sorted.map((account, index) => (_jsx(AccountCard, { account: account, isFirst: index === 0, isLast: index === sorted.length - 1, saving: accounts.saving.has(account.id), testBusy: accounts.saving.has(`test:${account.id}`), refreshBusy: accounts.saving.has(`usage:${account.id}`), onPatch: (body) => accounts.patch(providerId, account.id, body), onMoveUp: () => handleMove(account.id, "up"), onMoveDown: () => handleMove(account.id, "down"), onTest: async () => {
|
|
73
|
+
await accounts.test(providerId, account.id);
|
|
74
|
+
}, onRefreshUsage: () => accounts.refreshUsage(providerId, account.id), onDelete: () => accounts.remove(providerId, account.id) }, account.id))) })), _jsx(AddAccountDialog, { open: addDialogOpen, providerId: providerId, onClose: () => setAddDialogOpen(false), onCreated: () => {
|
|
75
|
+
// useAccounts already injects the new entry on success, so
|
|
76
|
+
// there's nothing to do here. Refresh anyway in case the
|
|
77
|
+
// optimistic insert missed a server-side default.
|
|
78
|
+
void accounts.refresh();
|
|
79
|
+
} })] }));
|
|
80
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AddAccountDialog — modal that walks the user through adding a new
|
|
3
|
+
* credential to a provider's account pool.
|
|
4
|
+
*
|
|
5
|
+
* Two paths:
|
|
6
|
+
* - **OAuth** (subscription providers): start the server-side OAuth
|
|
7
|
+
* flow, open the auth URL in a real browser window via
|
|
8
|
+
* `preOpenWindow` + `navigatePreOpenedWindow` (preserves the user
|
|
9
|
+
* gesture so popup blockers don't fire), then subscribe to the
|
|
10
|
+
* SSE stream at `/api/accounts/:provider/oauth/status` for terminal
|
|
11
|
+
* state. On `success`, hand the new `LinkedAccountConfig` to the
|
|
12
|
+
* parent. On error / timeout / cancel, surface the message inline
|
|
13
|
+
* and let the user retry. If the dialog closes mid-flow we cancel
|
|
14
|
+
* the server-side listener so it doesn't leak.
|
|
15
|
+
* - **API key**: simple label + key form, immediate POST.
|
|
16
|
+
*
|
|
17
|
+
* The dialog is provider-aware: OAuth-only providers
|
|
18
|
+
* (`anthropic-subscription`, `openai-codex`) skip the chooser screen.
|
|
19
|
+
* Direct API providers (`anthropic-api`, `openai-api`) show only the
|
|
20
|
+
* API-key form. The server still 501s on POST for the api-key path on
|
|
21
|
+
* subscription providers, so the chooser is mostly forward-looking
|
|
22
|
+
* defence against future shapes.
|
|
23
|
+
*/
|
|
24
|
+
import type { LinkedAccountConfig, LinkedAccountProviderId } from "@elizaos/shared";
|
|
25
|
+
interface AddAccountDialogProps {
|
|
26
|
+
open: boolean;
|
|
27
|
+
providerId: LinkedAccountProviderId;
|
|
28
|
+
onClose: () => void;
|
|
29
|
+
onCreated: (account: LinkedAccountConfig) => void;
|
|
30
|
+
}
|
|
31
|
+
export declare function AddAccountDialog({ open, providerId, onClose, onCreated, }: AddAccountDialogProps): import("react/jsx-runtime").JSX.Element;
|
|
32
|
+
export {};
|
|
33
|
+
//# sourceMappingURL=AddAccountDialog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AddAccountDialog.d.ts","sourceRoot":"","sources":["../../../../../../../app-core/src/components/accounts/AddAccountDialog.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAeH,OAAO,KAAK,EACV,mBAAmB,EACnB,uBAAuB,EACxB,MAAM,iBAAiB,CAAC;AAYzB,UAAU,qBAAqB;IAC7B,IAAI,EAAE,OAAO,CAAC;IACd,UAAU,EAAE,uBAAuB,CAAC;IACpC,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,SAAS,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,IAAI,CAAC;CACnD;AAoDD,wBAAgB,gBAAgB,CAAC,EAC/B,IAAI,EACJ,UAAU,EACV,OAAO,EACP,SAAS,GACV,EAAE,qBAAqB,2CAkZvB"}
|