@elizaos/plugin-vincent 2.0.3-beta.6 → 2.0.3-beta.7

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 (91) hide show
  1. package/dist/TradingProfileCard.d.ts +8 -0
  2. package/dist/TradingProfileCard.d.ts.map +1 -0
  3. package/dist/TradingProfileCard.js +70 -0
  4. package/dist/TradingProfileCard.js.map +1 -0
  5. package/dist/TradingStrategyPanel.d.ts +10 -0
  6. package/dist/TradingStrategyPanel.d.ts.map +1 -0
  7. package/dist/TradingStrategyPanel.js +133 -0
  8. package/dist/TradingStrategyPanel.js.map +1 -0
  9. package/dist/VincentAppView.d.ts +4 -0
  10. package/dist/VincentAppView.d.ts.map +1 -0
  11. package/dist/VincentAppView.helpers.d.ts +10 -0
  12. package/dist/VincentAppView.helpers.d.ts.map +1 -0
  13. package/dist/VincentAppView.helpers.js +21 -0
  14. package/dist/VincentAppView.helpers.js.map +1 -0
  15. package/dist/VincentAppView.interact.d.ts +2 -0
  16. package/dist/VincentAppView.interact.d.ts.map +1 -0
  17. package/dist/VincentAppView.interact.js +45 -0
  18. package/dist/VincentAppView.interact.js.map +1 -0
  19. package/dist/VincentAppView.js +145 -0
  20. package/dist/VincentAppView.js.map +1 -0
  21. package/dist/VincentConnectionCard.d.ts +9 -0
  22. package/dist/VincentConnectionCard.d.ts.map +1 -0
  23. package/dist/VincentConnectionCard.js +121 -0
  24. package/dist/VincentConnectionCard.js.map +1 -0
  25. package/dist/VincentView.d.ts +13 -0
  26. package/dist/VincentView.d.ts.map +1 -0
  27. package/dist/VincentView.js +86 -0
  28. package/dist/VincentView.js.map +1 -0
  29. package/dist/WalletStatusCard.d.ts +12 -0
  30. package/dist/WalletStatusCard.d.ts.map +1 -0
  31. package/dist/WalletStatusCard.js +203 -0
  32. package/dist/WalletStatusCard.js.map +1 -0
  33. package/dist/client.d.ts +21 -0
  34. package/dist/client.d.ts.map +1 -0
  35. package/dist/client.js +31 -0
  36. package/dist/client.js.map +1 -0
  37. package/dist/components/VincentSpatialView.d.ts +32 -0
  38. package/dist/components/VincentSpatialView.d.ts.map +1 -0
  39. package/dist/components/VincentSpatialView.js +191 -0
  40. package/dist/components/VincentSpatialView.js.map +1 -0
  41. package/dist/index.d.ts +15 -0
  42. package/dist/index.d.ts.map +1 -0
  43. package/dist/index.js +21 -0
  44. package/dist/index.js.map +1 -0
  45. package/dist/plugin.d.ts +14 -0
  46. package/dist/plugin.d.ts.map +1 -0
  47. package/dist/plugin.js +98 -0
  48. package/dist/plugin.js.map +1 -0
  49. package/dist/register-routes.d.ts +2 -0
  50. package/dist/register-routes.d.ts.map +1 -0
  51. package/dist/register-routes.js +6 -0
  52. package/dist/register-routes.js.map +1 -0
  53. package/dist/register-terminal-view.d.ts +15 -0
  54. package/dist/register-terminal-view.d.ts.map +1 -0
  55. package/dist/register-terminal-view.js +28 -0
  56. package/dist/register-terminal-view.js.map +1 -0
  57. package/dist/register.d.ts +2 -0
  58. package/dist/register.d.ts.map +1 -0
  59. package/dist/register.js +6 -0
  60. package/dist/register.js.map +1 -0
  61. package/dist/routes.d.ts +24 -0
  62. package/dist/routes.d.ts.map +1 -0
  63. package/dist/routes.js +429 -0
  64. package/dist/routes.js.map +1 -0
  65. package/dist/ui.d.ts +11 -0
  66. package/dist/ui.d.ts.map +1 -0
  67. package/dist/ui.js +16 -0
  68. package/dist/ui.js.map +1 -0
  69. package/dist/useVincentDashboard.d.ts +21 -0
  70. package/dist/useVincentDashboard.d.ts.map +1 -0
  71. package/dist/useVincentDashboard.js +105 -0
  72. package/dist/useVincentDashboard.js.map +1 -0
  73. package/dist/useVincentState.d.ts +15 -0
  74. package/dist/useVincentState.d.ts.map +1 -0
  75. package/dist/useVincentState.js +116 -0
  76. package/dist/useVincentState.js.map +1 -0
  77. package/dist/views/bundle.js +500 -0
  78. package/dist/views/bundle.js.map +1 -0
  79. package/dist/vincent-app.d.ts +10 -0
  80. package/dist/vincent-app.d.ts.map +1 -0
  81. package/dist/vincent-app.js +16 -0
  82. package/dist/vincent-app.js.map +1 -0
  83. package/dist/vincent-contracts.d.ts +52 -0
  84. package/dist/vincent-contracts.d.ts.map +1 -0
  85. package/dist/vincent-contracts.js +8 -0
  86. package/dist/vincent-contracts.js.map +1 -0
  87. package/dist/vincent-view-bundle.d.ts +3 -0
  88. package/dist/vincent-view-bundle.d.ts.map +1 -0
  89. package/dist/vincent-view-bundle.js +7 -0
  90. package/dist/vincent-view-bundle.js.map +1 -0
  91. package/package.json +7 -7
@@ -0,0 +1,121 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { Button, StatusDot } from "@elizaos/ui";
3
+ import { useAgentElement } from "@elizaos/ui/agent-surface";
4
+ import { KeyRound, LogIn, LogOut, RefreshCw, ShieldCheck } from "lucide-react";
5
+ import { memo } from "react";
6
+ import { useVincentState } from "./useVincentState.js";
7
+ function formatConnectedAt(ts) {
8
+ if (!ts) return "";
9
+ try {
10
+ return new Date(ts).toLocaleDateString("en-US", {
11
+ month: "short",
12
+ day: "numeric",
13
+ year: "numeric",
14
+ hour: "2-digit",
15
+ minute: "2-digit"
16
+ });
17
+ } catch {
18
+ return "";
19
+ }
20
+ }
21
+ const VincentConnectionCard = memo(function VincentConnectionCard2({
22
+ setActionNotice,
23
+ t
24
+ }) {
25
+ const {
26
+ vincentConnected,
27
+ vincentLoginBusy,
28
+ vincentLoginError,
29
+ vincentConnectedAt,
30
+ handleVincentLogin,
31
+ handleVincentDisconnect
32
+ } = useVincentState({ setActionNotice, t });
33
+ const disconnectLabel = t("vincent.disconnect", {
34
+ defaultValue: "Disconnect"
35
+ });
36
+ const connectLabel = t("vincent.connect", {
37
+ defaultValue: "Connect Vincent"
38
+ });
39
+ const disconnect = useAgentElement({
40
+ id: "action-disconnect",
41
+ role: "button",
42
+ label: disconnectLabel,
43
+ group: "vincent-connection",
44
+ description: "Disconnect the linked Vincent account"
45
+ });
46
+ const connect = useAgentElement({
47
+ id: "action-connect",
48
+ role: "button",
49
+ label: connectLabel,
50
+ group: "vincent-connection",
51
+ description: "Start the Vincent OAuth login to connect an account"
52
+ });
53
+ return /* @__PURE__ */ jsxs("div", { className: "px-1 py-3", children: [
54
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-4", children: [
55
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 flex-wrap items-center gap-x-5 gap-y-1.5 text-xs font-semibold", children: [
56
+ /* @__PURE__ */ jsxs(
57
+ "span",
58
+ {
59
+ className: `flex items-center gap-2 ${vincentConnected ? "text-ok" : "text-muted"}`,
60
+ children: [
61
+ /* @__PURE__ */ jsx(
62
+ StatusDot,
63
+ {
64
+ status: vincentConnected ? "connected" : "muted",
65
+ tone: vincentConnected ? "success" : "muted",
66
+ className: "shrink-0"
67
+ }
68
+ ),
69
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: vincentConnected ? t("vincent.connected", { defaultValue: "Connected" }) : t("vincent.disconnected", { defaultValue: "Offline" }) })
70
+ ]
71
+ }
72
+ ),
73
+ /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-2 text-muted", children: [
74
+ /* @__PURE__ */ jsx(KeyRound, { className: "h-3.5 w-3.5 shrink-0" }),
75
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: "OAuth" })
76
+ ] }),
77
+ /* @__PURE__ */ jsxs("span", { className: "hidden items-center gap-2 text-muted sm:flex", children: [
78
+ /* @__PURE__ */ jsx(ShieldCheck, { className: "h-3.5 w-3.5 shrink-0" }),
79
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: vincentConnectedAt ? formatConnectedAt(vincentConnectedAt) : "Ready" })
80
+ ] })
81
+ ] }),
82
+ /* @__PURE__ */ jsx("div", { className: "flex shrink-0 items-center gap-2", children: vincentConnected ? /* @__PURE__ */ jsxs(
83
+ Button,
84
+ {
85
+ ref: disconnect.ref,
86
+ ...disconnect.agentProps,
87
+ variant: "outline",
88
+ size: "sm",
89
+ className: "h-9 px-4 text-xs font-semibold text-status-danger border-status-danger/30 hover:bg-status-danger-bg hover:text-status-danger",
90
+ onClick: () => void handleVincentDisconnect(),
91
+ "aria-label": disconnectLabel,
92
+ children: [
93
+ /* @__PURE__ */ jsx(LogOut, { className: "h-3.5 w-3.5" }),
94
+ disconnectLabel
95
+ ]
96
+ }
97
+ ) : /* @__PURE__ */ jsxs(
98
+ Button,
99
+ {
100
+ ref: connect.ref,
101
+ ...connect.agentProps,
102
+ variant: "default",
103
+ size: "sm",
104
+ className: "h-9 px-4 text-xs font-semibold",
105
+ onClick: () => void handleVincentLogin(),
106
+ disabled: vincentLoginBusy,
107
+ "aria-label": connectLabel,
108
+ children: [
109
+ vincentLoginBusy ? /* @__PURE__ */ jsx(RefreshCw, { className: "h-3.5 w-3.5 animate-spin" }) : /* @__PURE__ */ jsx(LogIn, { className: "h-3.5 w-3.5" }),
110
+ vincentLoginBusy ? t("vincent.connecting", { defaultValue: "Connecting\u2026" }) : t("vincent.connect", { defaultValue: "Connect Vincent" })
111
+ ]
112
+ }
113
+ ) })
114
+ ] }),
115
+ vincentLoginError && /* @__PURE__ */ jsx("div", { className: "mt-3 px-1 py-2 text-xs text-status-danger", children: vincentLoginError })
116
+ ] });
117
+ });
118
+ export {
119
+ VincentConnectionCard
120
+ };
121
+ //# sourceMappingURL=VincentConnectionCard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/VincentConnectionCard.tsx"],"sourcesContent":["/** VincentConnectionCard — OAuth connect/disconnect UI for Vincent. */\n\nimport { Button, StatusDot } from \"@elizaos/ui\";\nimport { useAgentElement } from \"@elizaos/ui/agent-surface\";\nimport { KeyRound, LogIn, LogOut, RefreshCw, ShieldCheck } from \"lucide-react\";\nimport { memo } from \"react\";\nimport { useVincentState } from \"./useVincentState.js\";\n\ninterface VincentConnectionCardProps {\n onConnectedChange?: (connected: boolean) => void;\n setActionNotice: (\n text: string,\n tone?: \"info\" | \"success\" | \"error\",\n ttlMs?: number,\n ) => void;\n t: (key: string, opts?: Record<string, unknown>) => string;\n}\n\nfunction formatConnectedAt(ts: number | null): string {\n if (!ts) return \"\";\n try {\n return new Date(ts).toLocaleDateString(\"en-US\", {\n month: \"short\",\n day: \"numeric\",\n year: \"numeric\",\n hour: \"2-digit\",\n minute: \"2-digit\",\n });\n } catch {\n return \"\";\n }\n}\n\nexport const VincentConnectionCard = memo(function VincentConnectionCard({\n setActionNotice,\n t,\n}: VincentConnectionCardProps) {\n const {\n vincentConnected,\n vincentLoginBusy,\n vincentLoginError,\n vincentConnectedAt,\n handleVincentLogin,\n handleVincentDisconnect,\n } = useVincentState({ setActionNotice, t });\n\n const disconnectLabel = t(\"vincent.disconnect\", {\n defaultValue: \"Disconnect\",\n });\n const connectLabel = t(\"vincent.connect\", {\n defaultValue: \"Connect Vincent\",\n });\n const disconnect = useAgentElement<HTMLButtonElement>({\n id: \"action-disconnect\",\n role: \"button\",\n label: disconnectLabel,\n group: \"vincent-connection\",\n description: \"Disconnect the linked Vincent account\",\n });\n const connect = useAgentElement<HTMLButtonElement>({\n id: \"action-connect\",\n role: \"button\",\n label: connectLabel,\n group: \"vincent-connection\",\n description: \"Start the Vincent OAuth login to connect an account\",\n });\n\n return (\n <div className=\"px-1 py-3\">\n <div className=\"flex items-center justify-between gap-4\">\n <div className=\"flex min-w-0 flex-1 flex-wrap items-center gap-x-5 gap-y-1.5 text-xs font-semibold\">\n <span\n className={`flex items-center gap-2 ${vincentConnected ? \"text-ok\" : \"text-muted\"}`}\n >\n <StatusDot\n status={vincentConnected ? \"connected\" : \"muted\"}\n tone={vincentConnected ? \"success\" : \"muted\"}\n className=\"shrink-0\"\n />\n <span className=\"truncate\">\n {vincentConnected\n ? t(\"vincent.connected\", { defaultValue: \"Connected\" })\n : t(\"vincent.disconnected\", { defaultValue: \"Offline\" })}\n </span>\n </span>\n <span className=\"flex items-center gap-2 text-muted\">\n <KeyRound className=\"h-3.5 w-3.5 shrink-0\" />\n <span className=\"truncate\">OAuth</span>\n </span>\n <span className=\"hidden items-center gap-2 text-muted sm:flex\">\n <ShieldCheck className=\"h-3.5 w-3.5 shrink-0\" />\n <span className=\"truncate\">\n {vincentConnectedAt\n ? formatConnectedAt(vincentConnectedAt)\n : \"Ready\"}\n </span>\n </span>\n </div>\n\n <div className=\"flex shrink-0 items-center gap-2\">\n {vincentConnected ? (\n <Button\n ref={disconnect.ref}\n {...disconnect.agentProps}\n variant=\"outline\"\n size=\"sm\"\n className=\"h-9 px-4 text-xs font-semibold text-status-danger border-status-danger/30 hover:bg-status-danger-bg hover:text-status-danger\"\n onClick={() => void handleVincentDisconnect()}\n aria-label={disconnectLabel}\n >\n <LogOut className=\"h-3.5 w-3.5\" />\n {disconnectLabel}\n </Button>\n ) : (\n <Button\n ref={connect.ref}\n {...connect.agentProps}\n variant=\"default\"\n size=\"sm\"\n className=\"h-9 px-4 text-xs font-semibold\"\n onClick={() => void handleVincentLogin()}\n disabled={vincentLoginBusy}\n aria-label={connectLabel}\n >\n {vincentLoginBusy ? (\n <RefreshCw className=\"h-3.5 w-3.5 animate-spin\" />\n ) : (\n <LogIn className=\"h-3.5 w-3.5\" />\n )}\n {vincentLoginBusy\n ? t(\"vincent.connecting\", { defaultValue: \"Connecting…\" })\n : t(\"vincent.connect\", { defaultValue: \"Connect Vincent\" })}\n </Button>\n )}\n </div>\n </div>\n\n {vincentLoginError && (\n <div className=\"mt-3 px-1 py-2 text-xs text-status-danger\">\n {vincentLoginError}\n </div>\n )}\n </div>\n );\n});\n"],"mappings":"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","names":["VincentConnectionCard"]}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * VincentView — the single GUI/XR data wrapper for the Vincent surface.
3
+ *
4
+ * It owns the live Vincent data (OAuth status, agent wallet addresses, strategy
5
+ * and trading-profile polling, plus the connect/disconnect OAuth flow) and
6
+ * renders the one presentational {@link VincentSpatialView} inside a
7
+ * {@link SpatialSurface}. Omitting the `modality` prop lets `SpatialSurface`
8
+ * auto-detect GUI vs XR via `window.__elizaXRContext`, so the SAME component
9
+ * serves both surfaces. The TUI surface renders the same `VincentSpatialView`
10
+ * through the terminal registry (see `register-terminal-view.tsx`).
11
+ */
12
+ export declare function VincentView(): import("react/jsx-runtime").JSX.Element;
13
+ //# sourceMappingURL=VincentView.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"VincentView.d.ts","sourceRoot":"","sources":["../src/VincentView.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAsBH,wBAAgB,WAAW,4CA8E1B"}
@@ -0,0 +1,86 @@
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { openExternalUrl, useAppSelector } from "@elizaos/ui";
3
+ import { SpatialSurface } from "@elizaos/ui/spatial";
4
+ import { useCallback } from "react";
5
+ import {
6
+ VincentSpatialView
7
+ } from "./components/VincentSpatialView.js";
8
+ import { useVincentDashboard } from "./useVincentDashboard.js";
9
+ import { useVincentState } from "./useVincentState.js";
10
+ const VINCENT_DASHBOARD_URL = "https://heyvincent.ai";
11
+ function defaultTranslate(_key, opts) {
12
+ return typeof opts?.defaultValue === "string" ? opts.defaultValue : _key;
13
+ }
14
+ function VincentView() {
15
+ const setActionNotice = useAppSelector((s) => s.setActionNotice);
16
+ const {
17
+ vincentConnected,
18
+ vincentConnectedAt,
19
+ walletAddresses,
20
+ walletBalances,
21
+ strategy,
22
+ tradingProfile,
23
+ loading,
24
+ error,
25
+ refresh
26
+ } = useVincentDashboard();
27
+ const { handleVincentLogin, handleVincentDisconnect } = useVincentState({
28
+ setActionNotice,
29
+ t: defaultTranslate
30
+ });
31
+ const copyAddress = useCallback(
32
+ (address, label) => {
33
+ if (!address) return;
34
+ void navigator.clipboard.writeText(address).then(() => {
35
+ setActionNotice(`${label} address copied`, "success", 2e3);
36
+ });
37
+ },
38
+ [setActionNotice]
39
+ );
40
+ const onAction = useCallback(
41
+ (action) => {
42
+ switch (action) {
43
+ case "connect":
44
+ void handleVincentLogin();
45
+ return;
46
+ case "disconnect":
47
+ void handleVincentDisconnect();
48
+ return;
49
+ case "refresh":
50
+ refresh();
51
+ return;
52
+ case "open-vincent":
53
+ void openExternalUrl(VINCENT_DASHBOARD_URL);
54
+ return;
55
+ case "copy-evm":
56
+ copyAddress(walletAddresses?.evmAddress, "EVM");
57
+ return;
58
+ case "copy-solana":
59
+ copyAddress(walletAddresses?.solanaAddress, "Solana");
60
+ return;
61
+ }
62
+ },
63
+ [
64
+ copyAddress,
65
+ handleVincentDisconnect,
66
+ handleVincentLogin,
67
+ refresh,
68
+ walletAddresses
69
+ ]
70
+ );
71
+ const snapshot = {
72
+ vincentConnected,
73
+ vincentConnectedAt,
74
+ walletAddresses,
75
+ walletBalances,
76
+ strategy,
77
+ tradingProfile,
78
+ loading,
79
+ error
80
+ };
81
+ return /* @__PURE__ */ jsx(SpatialSurface, { children: /* @__PURE__ */ jsx(VincentSpatialView, { snapshot, onAction }) });
82
+ }
83
+ export {
84
+ VincentView
85
+ };
86
+ //# sourceMappingURL=VincentView.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/VincentView.tsx"],"sourcesContent":["/**\n * VincentView — the single GUI/XR data wrapper for the Vincent surface.\n *\n * It owns the live Vincent data (OAuth status, agent wallet addresses, strategy\n * and trading-profile polling, plus the connect/disconnect OAuth flow) and\n * renders the one presentational {@link VincentSpatialView} inside a\n * {@link SpatialSurface}. Omitting the `modality` prop lets `SpatialSurface`\n * auto-detect GUI vs XR via `window.__elizaXRContext`, so the SAME component\n * serves both surfaces. The TUI surface renders the same `VincentSpatialView`\n * through the terminal registry (see `register-terminal-view.tsx`).\n */\n\nimport { openExternalUrl, useAppSelector } from \"@elizaos/ui\";\nimport { SpatialSurface } from \"@elizaos/ui/spatial\";\nimport { useCallback } from \"react\";\nimport {\n type VincentSnapshot,\n VincentSpatialView,\n} from \"./components/VincentSpatialView.js\";\nimport { useVincentDashboard } from \"./useVincentDashboard.js\";\nimport { useVincentState } from \"./useVincentState.js\";\n\nconst VINCENT_DASHBOARD_URL = \"https://heyvincent.ai\";\n\n/** Minimal i18n passthrough — the standalone view has no OverlayAppContext. */\nfunction defaultTranslate(\n _key: string,\n opts?: Record<string, unknown>,\n): string {\n return typeof opts?.defaultValue === \"string\" ? opts.defaultValue : _key;\n}\n\nexport function VincentView() {\n const setActionNotice = useAppSelector((s) => s.setActionNotice);\n\n const {\n vincentConnected,\n vincentConnectedAt,\n walletAddresses,\n walletBalances,\n strategy,\n tradingProfile,\n loading,\n error,\n refresh,\n } = useVincentDashboard();\n\n const { handleVincentLogin, handleVincentDisconnect } = useVincentState({\n setActionNotice,\n t: defaultTranslate,\n });\n\n const copyAddress = useCallback(\n (address: string | null | undefined, label: string) => {\n if (!address) return;\n void navigator.clipboard.writeText(address).then(() => {\n setActionNotice(`${label} address copied`, \"success\", 2000);\n });\n },\n [setActionNotice],\n );\n\n const onAction = useCallback(\n (action: string) => {\n switch (action) {\n case \"connect\":\n void handleVincentLogin();\n return;\n case \"disconnect\":\n void handleVincentDisconnect();\n return;\n case \"refresh\":\n refresh();\n return;\n case \"open-vincent\":\n void openExternalUrl(VINCENT_DASHBOARD_URL);\n return;\n case \"copy-evm\":\n copyAddress(walletAddresses?.evmAddress, \"EVM\");\n return;\n case \"copy-solana\":\n copyAddress(walletAddresses?.solanaAddress, \"Solana\");\n return;\n }\n },\n [\n copyAddress,\n handleVincentDisconnect,\n handleVincentLogin,\n refresh,\n walletAddresses,\n ],\n );\n\n const snapshot: VincentSnapshot = {\n vincentConnected,\n vincentConnectedAt,\n walletAddresses,\n walletBalances,\n strategy,\n tradingProfile,\n loading,\n error,\n };\n\n return (\n <SpatialSurface>\n <VincentSpatialView snapshot={snapshot} onAction={onAction} />\n </SpatialSurface>\n );\n}\n"],"mappings":"AA2GM;AA/FN,SAAS,iBAAiB,sBAAsB;AAChD,SAAS,sBAAsB;AAC/B,SAAS,mBAAmB;AAC5B;AAAA,EAEE;AAAA,OACK;AACP,SAAS,2BAA2B;AACpC,SAAS,uBAAuB;AAEhC,MAAM,wBAAwB;AAG9B,SAAS,iBACP,MACA,MACQ;AACR,SAAO,OAAO,MAAM,iBAAiB,WAAW,KAAK,eAAe;AACtE;AAEO,SAAS,cAAc;AAC5B,QAAM,kBAAkB,eAAe,CAAC,MAAM,EAAE,eAAe;AAE/D,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI,oBAAoB;AAExB,QAAM,EAAE,oBAAoB,wBAAwB,IAAI,gBAAgB;AAAA,IACtE;AAAA,IACA,GAAG;AAAA,EACL,CAAC;AAED,QAAM,cAAc;AAAA,IAClB,CAAC,SAAoC,UAAkB;AACrD,UAAI,CAAC,QAAS;AACd,WAAK,UAAU,UAAU,UAAU,OAAO,EAAE,KAAK,MAAM;AACrD,wBAAgB,GAAG,KAAK,mBAAmB,WAAW,GAAI;AAAA,MAC5D,CAAC;AAAA,IACH;AAAA,IACA,CAAC,eAAe;AAAA,EAClB;AAEA,QAAM,WAAW;AAAA,IACf,CAAC,WAAmB;AAClB,cAAQ,QAAQ;AAAA,QACd,KAAK;AACH,eAAK,mBAAmB;AACxB;AAAA,QACF,KAAK;AACH,eAAK,wBAAwB;AAC7B;AAAA,QACF,KAAK;AACH,kBAAQ;AACR;AAAA,QACF,KAAK;AACH,eAAK,gBAAgB,qBAAqB;AAC1C;AAAA,QACF,KAAK;AACH,sBAAY,iBAAiB,YAAY,KAAK;AAC9C;AAAA,QACF,KAAK;AACH,sBAAY,iBAAiB,eAAe,QAAQ;AACpD;AAAA,MACJ;AAAA,IACF;AAAA,IACA;AAAA,MACE;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,QAAM,WAA4B;AAAA,IAChC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,SACE,oBAAC,kBACC,8BAAC,sBAAmB,UAAoB,UAAoB,GAC9D;AAEJ;","names":[]}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * WalletStatusCard — displays agent wallet addresses and token balances.
3
+ */
4
+ import type { WalletAddresses, WalletBalancesResponse } from "@elizaos/shared";
5
+ interface WalletStatusCardProps {
6
+ walletAddresses: WalletAddresses | null;
7
+ walletBalances: WalletBalancesResponse | null;
8
+ setActionNotice: (text: string, tone?: "info" | "success" | "error", ttlMs?: number) => void;
9
+ }
10
+ export declare const WalletStatusCard: import("react").MemoExoticComponent<({ walletAddresses, walletBalances, setActionNotice, }: WalletStatusCardProps) => import("react/jsx-runtime").JSX.Element>;
11
+ export {};
12
+ //# sourceMappingURL=WalletStatusCard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WalletStatusCard.d.ts","sourceRoot":"","sources":["../src/WalletStatusCard.tsx"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAM/E,UAAU,qBAAqB;IAC7B,eAAe,EAAE,eAAe,GAAG,IAAI,CAAC;IACxC,cAAc,EAAE,sBAAsB,GAAG,IAAI,CAAC;IAC9C,eAAe,EAAE,CACf,IAAI,EAAE,MAAM,EACZ,IAAI,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,EACnC,KAAK,CAAC,EAAE,MAAM,KACX,IAAI,CAAC;CACX;AA4ED,eAAO,MAAM,gBAAgB,8FAI1B,qBAAqB,6CAoKtB,CAAC"}
@@ -0,0 +1,203 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { Button, StatusBadge } from "@elizaos/ui";
3
+ import { useAgentElement } from "@elizaos/ui/agent-surface";
4
+ import { Check, Copy, Layers3, Network, Wallet } from "lucide-react";
5
+ import { memo, useCallback, useMemo, useState } from "react";
6
+ function CopyableAddress({
7
+ label,
8
+ address,
9
+ onCopy,
10
+ agentId,
11
+ agentLabel
12
+ }) {
13
+ const copy = useAgentElement({
14
+ id: agentId,
15
+ role: "button",
16
+ label: agentLabel,
17
+ group: "vincent-wallet",
18
+ description: `${agentLabel} to the clipboard`
19
+ });
20
+ const shortAddress = address.length > 18 ? `${address.slice(0, 6)}...${address.slice(-4)}` : address;
21
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3 px-1 py-1.5", children: [
22
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [
23
+ /* @__PURE__ */ jsx(Network, { className: "h-4 w-4 shrink-0 text-accent" }),
24
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
25
+ /* @__PURE__ */ jsx("div", { className: "text-xs-tight font-semibold text-txt", children: label }),
26
+ /* @__PURE__ */ jsx("div", { className: "mt-0.5 font-mono text-xs text-muted", children: shortAddress })
27
+ ] })
28
+ ] }),
29
+ /* @__PURE__ */ jsx(
30
+ Button,
31
+ {
32
+ ref: copy.ref,
33
+ ...copy.agentProps,
34
+ variant: "ghost",
35
+ size: "icon",
36
+ className: "h-7 w-7 shrink-0 text-muted hover:text-txt",
37
+ onClick: () => onCopy(address, label),
38
+ "aria-label": `Copy ${label}`,
39
+ children: label === "Copied!" ? /* @__PURE__ */ jsx(Check, { className: "h-3.5 w-3.5 text-ok" }) : /* @__PURE__ */ jsx(Copy, { className: "h-3.5 w-3.5" })
40
+ }
41
+ )
42
+ ] });
43
+ }
44
+ function BalancePill({ label, value }) {
45
+ return /* @__PURE__ */ jsxs("div", { className: "flex min-w-[88px] flex-col items-start gap-0.5 px-1 py-1", children: [
46
+ /* @__PURE__ */ jsx("span", { className: "text-2xs font-semibold tracking-wider text-muted/70", children: label }),
47
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold tabular-nums text-txt", children: value })
48
+ ] });
49
+ }
50
+ function isNonDust(valueUsd) {
51
+ const n = Number.parseFloat(valueUsd);
52
+ return Number.isFinite(n) && n >= 0.01;
53
+ }
54
+ const WalletStatusCard = memo(function WalletStatusCard2({
55
+ walletAddresses,
56
+ walletBalances,
57
+ setActionNotice
58
+ }) {
59
+ const [copiedField, setCopiedField] = useState(null);
60
+ const handleCopy = useCallback(
61
+ (text, label) => {
62
+ void navigator.clipboard.writeText(text).then(() => {
63
+ setCopiedField(label);
64
+ setActionNotice(`${label} copied`, "success", 2e3);
65
+ setTimeout(() => setCopiedField(null), 2e3);
66
+ });
67
+ },
68
+ [setActionNotice]
69
+ );
70
+ const evmAddress = walletAddresses?.evmAddress ?? null;
71
+ const solanaAddress = walletAddresses?.solanaAddress ?? null;
72
+ const { totalUsd, balancePills } = useMemo(() => {
73
+ if (!walletBalances) return { totalUsd: null, balancePills: [] };
74
+ let total = 0;
75
+ const pills = [];
76
+ if (walletBalances.evm) {
77
+ for (const chain of walletBalances.evm.chains) {
78
+ if (isNonDust(chain.nativeValueUsd)) {
79
+ total += Number.parseFloat(chain.nativeValueUsd);
80
+ pills.push({
81
+ label: chain.chain,
82
+ value: `$${Number.parseFloat(chain.nativeValueUsd).toFixed(2)}`
83
+ });
84
+ }
85
+ for (const token of chain.tokens) {
86
+ if (isNonDust(token.valueUsd)) {
87
+ total += Number.parseFloat(token.valueUsd);
88
+ pills.push({
89
+ label: token.symbol,
90
+ value: `$${Number.parseFloat(token.valueUsd).toFixed(2)}`
91
+ });
92
+ }
93
+ }
94
+ }
95
+ }
96
+ if (walletBalances.solana) {
97
+ if (isNonDust(walletBalances.solana.solValueUsd)) {
98
+ total += Number.parseFloat(walletBalances.solana.solValueUsd);
99
+ pills.push({
100
+ label: "SOL",
101
+ value: `$${Number.parseFloat(walletBalances.solana.solValueUsd).toFixed(2)}`
102
+ });
103
+ }
104
+ for (const token of walletBalances.solana.tokens) {
105
+ if (isNonDust(token.valueUsd)) {
106
+ total += Number.parseFloat(token.valueUsd);
107
+ pills.push({
108
+ label: token.symbol,
109
+ value: `$${Number.parseFloat(token.valueUsd).toFixed(2)}`
110
+ });
111
+ }
112
+ }
113
+ }
114
+ return {
115
+ totalUsd: total > 0 ? `$${total.toFixed(2)}` : null,
116
+ balancePills: pills.sort(
117
+ (a, b) => Number.parseFloat(b.value.replace("$", "")) - Number.parseFloat(a.value.replace("$", ""))
118
+ )
119
+ };
120
+ }, [walletBalances]);
121
+ const hasAddresses = evmAddress || solanaAddress;
122
+ const visibleBalancePills = balancePills.slice(0, 4);
123
+ const hiddenBalanceCount = Math.max(
124
+ 0,
125
+ balancePills.length - visibleBalancePills.length
126
+ );
127
+ if (!hasAddresses && !walletBalances) {
128
+ return /* @__PURE__ */ jsx("div", { "data-testid": "vincent-wallet-status-card", className: "px-1 py-3", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
129
+ /* @__PURE__ */ jsx(Wallet, { className: "h-4 w-4 text-muted/50" }),
130
+ /* @__PURE__ */ jsx("span", { className: "text-sm text-muted", children: "Wallet loading" })
131
+ ] }) });
132
+ }
133
+ return /* @__PURE__ */ jsxs(
134
+ "div",
135
+ {
136
+ "data-testid": "vincent-wallet-status-card",
137
+ className: "space-y-3 px-1 py-3",
138
+ children: [
139
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
140
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
141
+ /* @__PURE__ */ jsx(Wallet, { className: "h-4 w-4 text-accent" }),
142
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-semibold text-txt", children: "Wallet" })
143
+ ] }),
144
+ totalUsd && /* @__PURE__ */ jsx(StatusBadge, { label: totalUsd, tone: "success", withDot: true })
145
+ ] }),
146
+ hasAddresses && /* @__PURE__ */ jsxs("div", { className: "grid gap-2 sm:grid-cols-2", children: [
147
+ evmAddress && /* @__PURE__ */ jsx(
148
+ CopyableAddress,
149
+ {
150
+ label: copiedField === "EVM" ? "Copied!" : "EVM",
151
+ address: evmAddress,
152
+ onCopy: handleCopy,
153
+ agentId: "action-copy-evm-address",
154
+ agentLabel: "Copy EVM address"
155
+ }
156
+ ),
157
+ solanaAddress && /* @__PURE__ */ jsx(
158
+ CopyableAddress,
159
+ {
160
+ label: copiedField === "Solana" ? "Copied!" : "Solana",
161
+ address: solanaAddress,
162
+ onCopy: handleCopy,
163
+ agentId: "action-copy-solana-address",
164
+ agentLabel: "Copy Solana address"
165
+ }
166
+ )
167
+ ] }),
168
+ balancePills.length > 0 && /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap gap-2", children: [
169
+ visibleBalancePills.map((pill) => /* @__PURE__ */ jsx(
170
+ BalancePill,
171
+ {
172
+ label: pill.label,
173
+ value: pill.value
174
+ },
175
+ pill.label
176
+ )),
177
+ hiddenBalanceCount > 0 ? /* @__PURE__ */ jsxs(
178
+ "div",
179
+ {
180
+ className: "flex min-w-[88px] items-center gap-2 px-1 py-1 text-muted",
181
+ title: `${hiddenBalanceCount} more balances`,
182
+ children: [
183
+ /* @__PURE__ */ jsx(Layers3, { className: "h-4 w-4" }),
184
+ /* @__PURE__ */ jsxs("span", { className: "text-sm font-semibold tabular-nums", children: [
185
+ "+",
186
+ hiddenBalanceCount
187
+ ] })
188
+ ]
189
+ }
190
+ ) : null
191
+ ] }),
192
+ walletBalances && balancePills.length === 0 && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-1 py-1 text-xs text-muted", children: [
193
+ /* @__PURE__ */ jsx("span", { className: "h-2 w-2 rounded-full bg-muted/50" }),
194
+ "$0.01+"
195
+ ] })
196
+ ]
197
+ }
198
+ );
199
+ });
200
+ export {
201
+ WalletStatusCard
202
+ };
203
+ //# sourceMappingURL=WalletStatusCard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/WalletStatusCard.tsx"],"sourcesContent":["/**\n * WalletStatusCard — displays agent wallet addresses and token balances.\n */\n\nimport type { WalletAddresses, WalletBalancesResponse } from \"@elizaos/shared\";\nimport { Button, StatusBadge } from \"@elizaos/ui\";\nimport { useAgentElement } from \"@elizaos/ui/agent-surface\";\nimport { Check, Copy, Layers3, Network, Wallet } from \"lucide-react\";\nimport { memo, useCallback, useMemo, useState } from \"react\";\n\ninterface WalletStatusCardProps {\n walletAddresses: WalletAddresses | null;\n walletBalances: WalletBalancesResponse | null;\n setActionNotice: (\n text: string,\n tone?: \"info\" | \"success\" | \"error\",\n ttlMs?: number,\n ) => void;\n}\n\nfunction CopyableAddress({\n label,\n address,\n onCopy,\n agentId,\n agentLabel,\n}: {\n label: string;\n address: string;\n onCopy: (text: string, label: string) => void;\n agentId: string;\n agentLabel: string;\n}) {\n const copy = useAgentElement<HTMLButtonElement>({\n id: agentId,\n role: \"button\",\n label: agentLabel,\n group: \"vincent-wallet\",\n description: `${agentLabel} to the clipboard`,\n });\n const shortAddress =\n address.length > 18\n ? `${address.slice(0, 6)}...${address.slice(-4)}`\n : address;\n\n return (\n <div className=\"flex items-center justify-between gap-3 px-1 py-1.5\">\n <div className=\"flex min-w-0 items-center gap-2\">\n <Network className=\"h-4 w-4 shrink-0 text-accent\" />\n <div className=\"min-w-0\">\n <div className=\"text-xs-tight font-semibold text-txt\">{label}</div>\n <div className=\"mt-0.5 font-mono text-xs text-muted\">\n {shortAddress}\n </div>\n </div>\n </div>\n <Button\n ref={copy.ref}\n {...copy.agentProps}\n variant=\"ghost\"\n size=\"icon\"\n className=\"h-7 w-7 shrink-0 text-muted hover:text-txt\"\n onClick={() => onCopy(address, label)}\n aria-label={`Copy ${label}`}\n >\n {label === \"Copied!\" ? (\n <Check className=\"h-3.5 w-3.5 text-ok\" />\n ) : (\n <Copy className=\"h-3.5 w-3.5\" />\n )}\n </Button>\n </div>\n );\n}\n\nfunction BalancePill({ label, value }: { label: string; value: string }) {\n return (\n <div className=\"flex min-w-[88px] flex-col items-start gap-0.5 px-1 py-1\">\n <span className=\"text-2xs font-semibold tracking-wider text-muted/70\">\n {label}\n </span>\n <span className=\"text-sm font-semibold tabular-nums text-txt\">\n {value}\n </span>\n </div>\n );\n}\n\n/** Filter out dust balances (< $0.01 USD). */\nfunction isNonDust(valueUsd: string): boolean {\n const n = Number.parseFloat(valueUsd);\n return Number.isFinite(n) && n >= 0.01;\n}\n\nexport const WalletStatusCard = memo(function WalletStatusCard({\n walletAddresses,\n walletBalances,\n setActionNotice,\n}: WalletStatusCardProps) {\n const [copiedField, setCopiedField] = useState<string | null>(null);\n\n const handleCopy = useCallback(\n (text: string, label: string) => {\n void navigator.clipboard.writeText(text).then(() => {\n setCopiedField(label);\n setActionNotice(`${label} copied`, \"success\", 2000);\n setTimeout(() => setCopiedField(null), 2000);\n });\n },\n [setActionNotice],\n );\n\n const evmAddress = walletAddresses?.evmAddress ?? null;\n const solanaAddress = walletAddresses?.solanaAddress ?? null;\n\n // Compute total USD value across all chains, filtering dust\n const { totalUsd, balancePills } = useMemo(() => {\n if (!walletBalances) return { totalUsd: null, balancePills: [] };\n\n let total = 0;\n const pills: Array<{ label: string; value: string }> = [];\n\n // EVM chains\n if (walletBalances.evm) {\n for (const chain of walletBalances.evm.chains) {\n if (isNonDust(chain.nativeValueUsd)) {\n total += Number.parseFloat(chain.nativeValueUsd);\n pills.push({\n label: chain.chain,\n value: `$${Number.parseFloat(chain.nativeValueUsd).toFixed(2)}`,\n });\n }\n for (const token of chain.tokens) {\n if (isNonDust(token.valueUsd)) {\n total += Number.parseFloat(token.valueUsd);\n pills.push({\n label: token.symbol,\n value: `$${Number.parseFloat(token.valueUsd).toFixed(2)}`,\n });\n }\n }\n }\n }\n\n // Solana\n if (walletBalances.solana) {\n if (isNonDust(walletBalances.solana.solValueUsd)) {\n total += Number.parseFloat(walletBalances.solana.solValueUsd);\n pills.push({\n label: \"SOL\",\n value: `$${Number.parseFloat(walletBalances.solana.solValueUsd).toFixed(2)}`,\n });\n }\n for (const token of walletBalances.solana.tokens) {\n if (isNonDust(token.valueUsd)) {\n total += Number.parseFloat(token.valueUsd);\n pills.push({\n label: token.symbol,\n value: `$${Number.parseFloat(token.valueUsd).toFixed(2)}`,\n });\n }\n }\n }\n\n return {\n totalUsd: total > 0 ? `$${total.toFixed(2)}` : null,\n balancePills: pills.sort(\n (a, b) =>\n Number.parseFloat(b.value.replace(\"$\", \"\")) -\n Number.parseFloat(a.value.replace(\"$\", \"\")),\n ),\n };\n }, [walletBalances]);\n\n const hasAddresses = evmAddress || solanaAddress;\n const visibleBalancePills = balancePills.slice(0, 4);\n const hiddenBalanceCount = Math.max(\n 0,\n balancePills.length - visibleBalancePills.length,\n );\n\n if (!hasAddresses && !walletBalances) {\n return (\n <div data-testid=\"vincent-wallet-status-card\" className=\"px-1 py-3\">\n <div className=\"flex items-center gap-2\">\n <Wallet className=\"h-4 w-4 text-muted/50\" />\n <span className=\"text-sm text-muted\">Wallet loading</span>\n </div>\n </div>\n );\n }\n\n return (\n <div\n data-testid=\"vincent-wallet-status-card\"\n className=\"space-y-3 px-1 py-3\"\n >\n {/* Header row */}\n <div className=\"flex items-center justify-between gap-3\">\n <div className=\"flex items-center gap-2\">\n <Wallet className=\"h-4 w-4 text-accent\" />\n <span className=\"text-sm font-semibold text-txt\">Wallet</span>\n </div>\n {totalUsd && <StatusBadge label={totalUsd} tone=\"success\" withDot />}\n </div>\n\n {/* Addresses */}\n {hasAddresses && (\n <div className=\"grid gap-2 sm:grid-cols-2\">\n {evmAddress && (\n <CopyableAddress\n label={copiedField === \"EVM\" ? \"Copied!\" : \"EVM\"}\n address={evmAddress}\n onCopy={handleCopy}\n agentId=\"action-copy-evm-address\"\n agentLabel=\"Copy EVM address\"\n />\n )}\n {solanaAddress && (\n <CopyableAddress\n label={copiedField === \"Solana\" ? \"Copied!\" : \"Solana\"}\n address={solanaAddress}\n onCopy={handleCopy}\n agentId=\"action-copy-solana-address\"\n agentLabel=\"Copy Solana address\"\n />\n )}\n </div>\n )}\n\n {/* Balance pills — dust filtered */}\n {balancePills.length > 0 && (\n <div className=\"flex flex-wrap gap-2\">\n {visibleBalancePills.map((pill) => (\n <BalancePill\n key={pill.label}\n label={pill.label}\n value={pill.value}\n />\n ))}\n {hiddenBalanceCount > 0 ? (\n <div\n className=\"flex min-w-[88px] items-center gap-2 px-1 py-1 text-muted\"\n title={`${hiddenBalanceCount} more balances`}\n >\n <Layers3 className=\"h-4 w-4\" />\n <span className=\"text-sm font-semibold tabular-nums\">\n +{hiddenBalanceCount}\n </span>\n </div>\n ) : null}\n </div>\n )}\n\n {walletBalances && balancePills.length === 0 && (\n <div className=\"flex items-center gap-2 px-1 py-1 text-xs text-muted\">\n <span className=\"h-2 w-2 rounded-full bg-muted/50\" />\n $0.01+\n </div>\n )}\n </div>\n );\n});\n"],"mappings":"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","names":["WalletStatusCard"]}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Vincent domain methods — OAuth status, dashboard, and strategy settings.
3
+ */
4
+ /**
5
+ * Frontend client extensions are installed on the UI package's ElizaClient
6
+ * prototype and exposed through a locally typed client wrapper.
7
+ */
8
+ import { client as baseClient } from "@elizaos/ui";
9
+ import type { VincentStartLoginResponse, VincentStatusResponse, VincentStrategyResponse, VincentStrategyUpdateRequest, VincentStrategyUpdateResponse, VincentTradingProfileResponse } from "./vincent-contracts";
10
+ export interface VincentClientMethods {
11
+ vincentStartLogin(appName?: string): Promise<VincentStartLoginResponse>;
12
+ vincentStatus(): Promise<VincentStatusResponse>;
13
+ vincentDisconnect(): Promise<{
14
+ ok: boolean;
15
+ }>;
16
+ vincentStrategy(): Promise<VincentStrategyResponse>;
17
+ vincentUpdateStrategy(request: VincentStrategyUpdateRequest): Promise<VincentStrategyUpdateResponse>;
18
+ vincentTradingProfile(): Promise<VincentTradingProfileResponse>;
19
+ }
20
+ export declare const vincentClient: typeof baseClient & VincentClientMethods;
21
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;GAGG;AACH,OAAO,EAAE,MAAM,IAAI,UAAU,EAAe,MAAM,aAAa,CAAC;AAChE,OAAO,KAAK,EACV,yBAAyB,EACzB,qBAAqB,EACrB,uBAAuB,EACvB,4BAA4B,EAC5B,6BAA6B,EAC7B,6BAA6B,EAC9B,MAAM,qBAAqB,CAAC;AAE7B,MAAM,WAAW,oBAAoB;IACnC,iBAAiB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,yBAAyB,CAAC,CAAC;IACxE,aAAa,IAAI,OAAO,CAAC,qBAAqB,CAAC,CAAC;IAChD,iBAAiB,IAAI,OAAO,CAAC;QAAE,EAAE,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAC9C,eAAe,IAAI,OAAO,CAAC,uBAAuB,CAAC,CAAC;IACpD,qBAAqB,CACnB,OAAO,EAAE,4BAA4B,GACpC,OAAO,CAAC,6BAA6B,CAAC,CAAC;IAC1C,qBAAqB,IAAI,OAAO,CAAC,6BAA6B,CAAC,CAAC;CACjE;AAuCD,eAAO,MAAM,aAAa,EAAiB,OAAO,UAAU,GAC1D,oBAAoB,CAAC"}
package/dist/client.js ADDED
@@ -0,0 +1,31 @@
1
+ import { client as baseClient, ElizaClient } from "@elizaos/ui";
2
+ const vincentPrototype = ElizaClient.prototype;
3
+ vincentPrototype.vincentStartLogin = async function(appName) {
4
+ return this.fetch("/api/vincent/start-login", {
5
+ method: "POST",
6
+ body: JSON.stringify({ appName: appName ?? "Eliza" })
7
+ });
8
+ };
9
+ vincentPrototype.vincentStatus = async function() {
10
+ return this.fetch("/api/vincent/status");
11
+ };
12
+ vincentPrototype.vincentDisconnect = async function() {
13
+ return this.fetch("/api/vincent/disconnect", { method: "POST" });
14
+ };
15
+ vincentPrototype.vincentStrategy = async function() {
16
+ return this.fetch("/api/vincent/strategy");
17
+ };
18
+ vincentPrototype.vincentUpdateStrategy = async function(request) {
19
+ return this.fetch("/api/vincent/strategy", {
20
+ method: "POST",
21
+ body: JSON.stringify(request)
22
+ });
23
+ };
24
+ vincentPrototype.vincentTradingProfile = async function() {
25
+ return this.fetch("/api/vincent/trading-profile");
26
+ };
27
+ const vincentClient = baseClient;
28
+ export {
29
+ vincentClient
30
+ };
31
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/client.ts"],"sourcesContent":["/**\n * Vincent domain methods — OAuth status, dashboard, and strategy settings.\n */\n\n/**\n * Frontend client extensions are installed on the UI package's ElizaClient\n * prototype and exposed through a locally typed client wrapper.\n */\nimport { client as baseClient, ElizaClient } from \"@elizaos/ui\";\nimport type {\n VincentStartLoginResponse,\n VincentStatusResponse,\n VincentStrategyResponse,\n VincentStrategyUpdateRequest,\n VincentStrategyUpdateResponse,\n VincentTradingProfileResponse,\n} from \"./vincent-contracts.js\";\n\nexport interface VincentClientMethods {\n vincentStartLogin(appName?: string): Promise<VincentStartLoginResponse>;\n vincentStatus(): Promise<VincentStatusResponse>;\n vincentDisconnect(): Promise<{ ok: boolean }>;\n vincentStrategy(): Promise<VincentStrategyResponse>;\n vincentUpdateStrategy(\n request: VincentStrategyUpdateRequest,\n ): Promise<VincentStrategyUpdateResponse>;\n vincentTradingProfile(): Promise<VincentTradingProfileResponse>;\n}\n\nconst vincentPrototype = ElizaClient.prototype as ElizaClient &\n VincentClientMethods;\n\n// ── Implementation ────────────────────────────────────────────────────\n\nvincentPrototype.vincentStartLogin = async function (appName?: string) {\n return this.fetch(\"/api/vincent/start-login\", {\n method: \"POST\",\n body: JSON.stringify({ appName: appName ?? \"Eliza\" }),\n });\n};\n\nvincentPrototype.vincentStatus = async function () {\n return this.fetch(\"/api/vincent/status\");\n};\n\nvincentPrototype.vincentDisconnect = async function () {\n return this.fetch(\"/api/vincent/disconnect\", { method: \"POST\" });\n};\n\nvincentPrototype.vincentStrategy = async function () {\n return this.fetch(\"/api/vincent/strategy\");\n};\n\nvincentPrototype.vincentUpdateStrategy = async function (\n request: VincentStrategyUpdateRequest,\n) {\n return this.fetch(\"/api/vincent/strategy\", {\n method: \"POST\",\n body: JSON.stringify(request),\n });\n};\n\nvincentPrototype.vincentTradingProfile = async function () {\n return this.fetch(\"/api/vincent/trading-profile\");\n};\n\nexport const vincentClient = baseClient as typeof baseClient &\n VincentClientMethods;\n"],"mappings":"AAQA,SAAS,UAAU,YAAY,mBAAmB;AAqBlD,MAAM,mBAAmB,YAAY;AAKrC,iBAAiB,oBAAoB,eAAgB,SAAkB;AACrE,SAAO,KAAK,MAAM,4BAA4B;AAAA,IAC5C,QAAQ;AAAA,IACR,MAAM,KAAK,UAAU,EAAE,SAAS,WAAW,QAAQ,CAAC;AAAA,EACtD,CAAC;AACH;AAEA,iBAAiB,gBAAgB,iBAAkB;AACjD,SAAO,KAAK,MAAM,qBAAqB;AACzC;AAEA,iBAAiB,oBAAoB,iBAAkB;AACrD,SAAO,KAAK,MAAM,2BAA2B,EAAE,QAAQ,OAAO,CAAC;AACjE;AAEA,iBAAiB,kBAAkB,iBAAkB;AACnD,SAAO,KAAK,MAAM,uBAAuB;AAC3C;AAEA,iBAAiB,wBAAwB,eACvC,SACA;AACA,SAAO,KAAK,MAAM,yBAAyB;AAAA,IACzC,QAAQ;AAAA,IACR,MAAM,KAAK,UAAU,OAAO;AAAA,EAC9B,CAAC;AACH;AAEA,iBAAiB,wBAAwB,iBAAkB;AACzD,SAAO,KAAK,MAAM,8BAA8B;AAClD;AAEO,MAAM,gBAAgB;","names":[]}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * VincentSpatialView - the Vincent trading dashboard authored once with the
3
+ * spatial vocabulary, so it renders correctly wherever it is displayed:
4
+ *
5
+ * - GUI / XR - mounted in `<SpatialSurface>` (DOM; XR scales up).
6
+ * - TUI - rendered to real terminal lines by the agent terminal, via
7
+ * `registerSpatialTerminalView` (see `register-terminal-view.tsx`).
8
+ *
9
+ * It is purely presentational (a snapshot + an action callback in, primitives
10
+ * out) and imports only the cross-modality primitives plus type-only views of
11
+ * the wallet and Vincent contracts, so it is safe to render in the Node agent
12
+ * process where the terminal lives (no client/Capacitor runtime import).
13
+ */
14
+ import type { WalletAddresses, WalletBalancesResponse } from "@elizaos/shared";
15
+ import type { VincentStrategy, VincentTradingProfile } from "../vincent-contracts.ts";
16
+ export interface VincentSnapshot {
17
+ vincentConnected: boolean;
18
+ vincentConnectedAt: number | null;
19
+ walletAddresses: WalletAddresses | null;
20
+ walletBalances: WalletBalancesResponse | null;
21
+ strategy: VincentStrategy | null;
22
+ tradingProfile: VincentTradingProfile | null;
23
+ loading?: boolean;
24
+ error?: string | null;
25
+ }
26
+ export interface VincentSpatialViewProps {
27
+ snapshot: VincentSnapshot;
28
+ /** Dispatch by agent id: `connect`, `disconnect`, `refresh`, `start`, `stop`. */
29
+ onAction?: (action: string) => void;
30
+ }
31
+ export declare function VincentSpatialView({ snapshot, onAction, }: VincentSpatialViewProps): import("react/jsx-runtime").JSX.Element;
32
+ //# sourceMappingURL=VincentSpatialView.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"VincentSpatialView.d.ts","sourceRoot":"","sources":["../../src/components/VincentSpatialView.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAW/E,OAAO,KAAK,EACV,eAAe,EACf,qBAAqB,EACtB,MAAM,yBAAyB,CAAC;AAEjC,MAAM,WAAW,eAAe;IAC9B,gBAAgB,EAAE,OAAO,CAAC;IAC1B,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,eAAe,EAAE,eAAe,GAAG,IAAI,CAAC;IACxC,cAAc,EAAE,sBAAsB,GAAG,IAAI,CAAC;IAC9C,QAAQ,EAAE,eAAe,GAAG,IAAI,CAAC;IACjC,cAAc,EAAE,qBAAqB,GAAG,IAAI,CAAC;IAC7C,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAgCD,MAAM,WAAW,uBAAuB;IACtC,QAAQ,EAAE,eAAe,CAAC;IAC1B,iFAAiF;IACjF,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CACrC;AAED,wBAAgB,kBAAkB,CAAC,EACjC,QAAQ,EACR,QAAQ,GACT,EAAE,uBAAuB,2CAyMzB"}