auramaxx 1.0.0-alpha.4

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 (363) hide show
  1. package/LICENSE +26 -0
  2. package/README.md +112 -0
  3. package/bin/aurawallet.js +121 -0
  4. package/docs/ADAPTERS.md +467 -0
  5. package/docs/API.md +2679 -0
  6. package/docs/APPS.md +198 -0
  7. package/docs/ARCHITECTURE.md +350 -0
  8. package/docs/AUTH.md +698 -0
  9. package/docs/BEST-PRACTICES.md +121 -0
  10. package/docs/CLI.md +61 -0
  11. package/docs/DEVELOPING-APPS.md +452 -0
  12. package/docs/EXTENSION.md +97 -0
  13. package/docs/JOBS.md +33 -0
  14. package/docs/MCP.md +76 -0
  15. package/docs/PROTOCOL.md +142 -0
  16. package/docs/SETUP.md +219 -0
  17. package/docs/WORKSPACE.md +672 -0
  18. package/docs/agent-auth.md +63 -0
  19. package/docs/aura-file.md +48 -0
  20. package/docs/credentials.md +53 -0
  21. package/docs/external/getting-started.md +65 -0
  22. package/docs/external/overview.md +45 -0
  23. package/docs/external/use-cases.md +48 -0
  24. package/docs/external/why-aura.md +35 -0
  25. package/docs/jobs/connect-agent.md +77 -0
  26. package/docs/jobs/migrate-from-dotenv.md +79 -0
  27. package/docs/jobs/recover-from-lockout.md +72 -0
  28. package/docs/jobs/secure-ci.md +63 -0
  29. package/docs/oauth2.md +42 -0
  30. package/docs/passkeys.md +60 -0
  31. package/docs/security.md +540 -0
  32. package/docs/specs/aura-open-protocol.md +61 -0
  33. package/docs/specs/aura-provider-plugin.md +24 -0
  34. package/docs/specs/aura-registry-model.md +31 -0
  35. package/docs/specs/fixtures/invalid-bad-key.aura +1 -0
  36. package/docs/specs/fixtures/invalid-bad-unicode-escape.aura +1 -0
  37. package/docs/specs/fixtures/invalid-duplicate-key.aura +2 -0
  38. package/docs/specs/fixtures/valid-basic.aura +4 -0
  39. package/docs/specs/fixtures/valid-provider-ref.aura +1 -0
  40. package/docs/specs/fixtures/valid-quoted-escapes.aura +2 -0
  41. package/docs/templates/RELEASE_NOTES_TEMPLATE.md +22 -0
  42. package/docs/totp.md +40 -0
  43. package/docs/wallet/AI.md +508 -0
  44. package/docs/wallet/DEVELOPING-STRATEGIES.md +713 -0
  45. package/docs/wallet/README.md +47 -0
  46. package/docs/wallet/STRATEGY.md +89 -0
  47. package/next.config.ts +21 -0
  48. package/package.json +151 -0
  49. package/postcss.config.mjs +8 -0
  50. package/prisma/migrations/20260214170000_baseline/migration.sql +511 -0
  51. package/prisma/migrations/20260216214537_add_passkey_model/migration.sql +18 -0
  52. package/prisma/migrations/20260217150500_add_credential_access_audit/migration.sql +31 -0
  53. package/prisma/migrations/migration_lock.toml +3 -0
  54. package/prisma/schema.prisma +447 -0
  55. package/public/logo-chevron.svg +31 -0
  56. package/public/logo-concentric.svg +31 -0
  57. package/public/logo-crosshatch.svg +39 -0
  58. package/public/logo-dashed.svg +39 -0
  59. package/public/logo-horizontal.svg +31 -0
  60. package/public/logo-m56.svg +64 -0
  61. package/public/logo.webp +0 -0
  62. package/scripts/add-app.js +245 -0
  63. package/scripts/init.sh +57 -0
  64. package/scripts/migrate-apikeys-to-credentials.ts +35 -0
  65. package/scripts/sandbox-agent-flow.sh +235 -0
  66. package/scripts/sandbox.sh +175 -0
  67. package/scripts/validate-job-docs.mjs +125 -0
  68. package/server/abi/SwapHelper.json +438 -0
  69. package/server/cli/approval.ts +447 -0
  70. package/server/cli/commands/app.ts +204 -0
  71. package/server/cli/commands/cron.ts +24 -0
  72. package/server/cli/commands/doctor.ts +1007 -0
  73. package/server/cli/commands/env.ts +456 -0
  74. package/server/cli/commands/init.ts +752 -0
  75. package/server/cli/commands/mcp.ts +125 -0
  76. package/server/cli/commands/restore.ts +314 -0
  77. package/server/cli/commands/shell-hook.ts +468 -0
  78. package/server/cli/commands/start.ts +62 -0
  79. package/server/cli/commands/status.ts +59 -0
  80. package/server/cli/commands/stop.ts +14 -0
  81. package/server/cli/commands/token.ts +180 -0
  82. package/server/cli/commands/unlock.ts +49 -0
  83. package/server/cli/commands/vault.ts +417 -0
  84. package/server/cli/index.ts +328 -0
  85. package/server/cli/lib/aura-parser.ts +64 -0
  86. package/server/cli/lib/credential-create.ts +74 -0
  87. package/server/cli/lib/credential-resolve.ts +254 -0
  88. package/server/cli/lib/dotenv-migrate.ts +116 -0
  89. package/server/cli/lib/dotenv-parser.ts +146 -0
  90. package/server/cli/lib/http.ts +91 -0
  91. package/server/cli/lib/init-steps.ts +76 -0
  92. package/server/cli/lib/local-agent-trust.ts +45 -0
  93. package/server/cli/lib/process.ts +136 -0
  94. package/server/cli/lib/prompt.ts +85 -0
  95. package/server/cli/lib/theme.ts +240 -0
  96. package/server/cli/socket.ts +570 -0
  97. package/server/cli/transport-client.ts +50 -0
  98. package/server/cron/index.ts +137 -0
  99. package/server/cron/job.ts +31 -0
  100. package/server/cron/jobs/balance-sync.ts +436 -0
  101. package/server/cron/jobs/incoming-scan.ts +506 -0
  102. package/server/cron/jobs/native-price.ts +70 -0
  103. package/server/cron/jobs/orphan-cleanup.ts +40 -0
  104. package/server/cron/jobs/strategy-runner.ts +175 -0
  105. package/server/cron/scheduler.ts +125 -0
  106. package/server/index.ts +406 -0
  107. package/server/lib/adapters/factory.ts +110 -0
  108. package/server/lib/adapters/index.ts +19 -0
  109. package/server/lib/adapters/router.ts +297 -0
  110. package/server/lib/adapters/telegram.ts +645 -0
  111. package/server/lib/adapters/types.ts +89 -0
  112. package/server/lib/adapters/webhook.ts +95 -0
  113. package/server/lib/address.ts +49 -0
  114. package/server/lib/agent-auth/contracts.ts +1194 -0
  115. package/server/lib/agent-profiles.ts +328 -0
  116. package/server/lib/ai.ts +285 -0
  117. package/server/lib/api-registry/contracts.ts +86 -0
  118. package/server/lib/api-registry/validation.ts +172 -0
  119. package/server/lib/apikey-migration.ts +189 -0
  120. package/server/lib/app-installer.ts +505 -0
  121. package/server/lib/app-tokens.ts +247 -0
  122. package/server/lib/auth.ts +314 -0
  123. package/server/lib/batch.ts +242 -0
  124. package/server/lib/cold.ts +874 -0
  125. package/server/lib/config.ts +381 -0
  126. package/server/lib/credential-access-audit.ts +85 -0
  127. package/server/lib/credential-access-policy.ts +110 -0
  128. package/server/lib/credential-health.ts +343 -0
  129. package/server/lib/credential-import.ts +487 -0
  130. package/server/lib/credential-scope.ts +87 -0
  131. package/server/lib/credential-shares.ts +190 -0
  132. package/server/lib/credential-transport.ts +342 -0
  133. package/server/lib/credential-vault.ts +77 -0
  134. package/server/lib/credentials.ts +333 -0
  135. package/server/lib/crypto.ts +8 -0
  136. package/server/lib/db.ts +15 -0
  137. package/server/lib/defaults.ts +366 -0
  138. package/server/lib/dex/index.ts +80 -0
  139. package/server/lib/dex/relay.ts +235 -0
  140. package/server/lib/dex/types.ts +59 -0
  141. package/server/lib/dex/uniswap.ts +370 -0
  142. package/server/lib/e2e-agent/artifacts.ts +36 -0
  143. package/server/lib/e2e-agent/contracts.ts +112 -0
  144. package/server/lib/e2e-agent/validation.ts +135 -0
  145. package/server/lib/encrypt.ts +128 -0
  146. package/server/lib/error.ts +20 -0
  147. package/server/lib/events.ts +205 -0
  148. package/server/lib/hot.ts +357 -0
  149. package/server/lib/key-fingerprint.ts +28 -0
  150. package/server/lib/logger.ts +331 -0
  151. package/server/lib/network.ts +137 -0
  152. package/server/lib/notifications.ts +219 -0
  153. package/server/lib/oauth2-refresh.ts +241 -0
  154. package/server/lib/oursecret.ts +54 -0
  155. package/server/lib/passkey-credential.ts +360 -0
  156. package/server/lib/passkey.ts +68 -0
  157. package/server/lib/permissions.ts +248 -0
  158. package/server/lib/pino.ts +24 -0
  159. package/server/lib/policy-preview.ts +138 -0
  160. package/server/lib/price.ts +338 -0
  161. package/server/lib/prices.ts +34 -0
  162. package/server/lib/project-scope.ts +239 -0
  163. package/server/lib/resolve-action.ts +427 -0
  164. package/server/lib/resolve.ts +36 -0
  165. package/server/lib/sessions.ts +632 -0
  166. package/server/lib/solana/connection.ts +26 -0
  167. package/server/lib/solana/jupiter.ts +128 -0
  168. package/server/lib/solana/transfer.ts +108 -0
  169. package/server/lib/solana/wallet.ts +136 -0
  170. package/server/lib/strategy/emits.ts +21 -0
  171. package/server/lib/strategy/engine.ts +1305 -0
  172. package/server/lib/strategy/executor.ts +115 -0
  173. package/server/lib/strategy/hook-context.ts +158 -0
  174. package/server/lib/strategy/hooks.ts +990 -0
  175. package/server/lib/strategy/index.ts +28 -0
  176. package/server/lib/strategy/installer.ts +305 -0
  177. package/server/lib/strategy/loader.ts +256 -0
  178. package/server/lib/strategy/message.ts +235 -0
  179. package/server/lib/strategy/repository.ts +218 -0
  180. package/server/lib/strategy/session-logger.ts +693 -0
  181. package/server/lib/strategy/sources.ts +288 -0
  182. package/server/lib/strategy/state.ts +189 -0
  183. package/server/lib/strategy/templates.ts +403 -0
  184. package/server/lib/strategy/tick.ts +404 -0
  185. package/server/lib/strategy/types.ts +230 -0
  186. package/server/lib/swap.ts +3 -0
  187. package/server/lib/temp.ts +86 -0
  188. package/server/lib/token-metadata.ts +86 -0
  189. package/server/lib/token-safety.ts +200 -0
  190. package/server/lib/token-search.ts +444 -0
  191. package/server/lib/totp.ts +194 -0
  192. package/server/lib/transactions.ts +123 -0
  193. package/server/lib/transport.ts +75 -0
  194. package/server/lib/txhistory/decoder.ts +262 -0
  195. package/server/lib/txhistory/enricher.ts +652 -0
  196. package/server/lib/txhistory/index.ts +391 -0
  197. package/server/lib/txhistory/signatures.ts +59 -0
  198. package/server/lib/verified-summary.ts +421 -0
  199. package/server/mcp/profile-policy.ts +30 -0
  200. package/server/mcp/server.ts +619 -0
  201. package/server/mcp/tools.ts +523 -0
  202. package/server/middleware/auth.ts +119 -0
  203. package/server/middleware/requestLogger.ts +84 -0
  204. package/server/routes/actions.ts +459 -0
  205. package/server/routes/adapters.ts +703 -0
  206. package/server/routes/addressbook.ts +113 -0
  207. package/server/routes/ai.ts +34 -0
  208. package/server/routes/apikeys.ts +295 -0
  209. package/server/routes/apps.ts +601 -0
  210. package/server/routes/auth.ts +457 -0
  211. package/server/routes/backup.ts +340 -0
  212. package/server/routes/batch.ts +270 -0
  213. package/server/routes/bookmarks.ts +162 -0
  214. package/server/routes/credential-shares.ts +198 -0
  215. package/server/routes/credential-vaults.ts +154 -0
  216. package/server/routes/credentials.ts +1290 -0
  217. package/server/routes/dashboard.ts +71 -0
  218. package/server/routes/defaults.ts +124 -0
  219. package/server/routes/fund.ts +229 -0
  220. package/server/routes/import.ts +352 -0
  221. package/server/routes/launch.ts +665 -0
  222. package/server/routes/lock.ts +54 -0
  223. package/server/routes/logs.ts +68 -0
  224. package/server/routes/nuke.ts +111 -0
  225. package/server/routes/passkey-credentials.ts +99 -0
  226. package/server/routes/passkey.ts +346 -0
  227. package/server/routes/portfolio.ts +217 -0
  228. package/server/routes/price.ts +63 -0
  229. package/server/routes/resolve.ts +31 -0
  230. package/server/routes/security.ts +45 -0
  231. package/server/routes/send-evm.ts +241 -0
  232. package/server/routes/send-solana.ts +281 -0
  233. package/server/routes/send.ts +178 -0
  234. package/server/routes/setup.ts +210 -0
  235. package/server/routes/strategy.ts +894 -0
  236. package/server/routes/swap-evm.ts +353 -0
  237. package/server/routes/swap-solana.ts +177 -0
  238. package/server/routes/swap.ts +356 -0
  239. package/server/routes/token.ts +247 -0
  240. package/server/routes/unlock.ts +403 -0
  241. package/server/routes/wallet-assets.ts +361 -0
  242. package/server/routes/wallet-transactions.ts +515 -0
  243. package/server/routes/wallet.ts +710 -0
  244. package/server/types.ts +146 -0
  245. package/skills/aurawallet/SKILL.md +739 -0
  246. package/skills/aurawallet-setup/SKILL.md +74 -0
  247. package/skills/security-review/SKILL.md +148 -0
  248. package/src/app/api/agent-requests/route.ts +30 -0
  249. package/src/app/api/apps/install/route.ts +126 -0
  250. package/src/app/api/apps/manifests/route.ts +16 -0
  251. package/src/app/api/apps/static/[...path]/route.ts +57 -0
  252. package/src/app/api/events/route.ts +92 -0
  253. package/src/app/api/page.tsx +212 -0
  254. package/src/app/api/workspace/[id]/apps/[wid]/route.ts +119 -0
  255. package/src/app/api/workspace/[id]/apps/route.ts +81 -0
  256. package/src/app/api/workspace/[id]/export/route.ts +67 -0
  257. package/src/app/api/workspace/[id]/route.ts +168 -0
  258. package/src/app/api/workspace/auth.ts +34 -0
  259. package/src/app/api/workspace/config/route.ts +106 -0
  260. package/src/app/api/workspace/import/route.ts +127 -0
  261. package/src/app/api/workspace/route.ts +116 -0
  262. package/src/app/app/page.tsx +2122 -0
  263. package/src/app/apple-icon.png +0 -0
  264. package/src/app/docs/page.tsx +178 -0
  265. package/src/app/favicon.ico +0 -0
  266. package/src/app/globals.css +572 -0
  267. package/src/app/health/page.tsx +5 -0
  268. package/src/app/hello/page.tsx +15 -0
  269. package/src/app/icon.png +0 -0
  270. package/src/app/layout.tsx +34 -0
  271. package/src/app/page.tsx +986 -0
  272. package/src/app/providers.tsx +90 -0
  273. package/src/app/share/[token]/page.tsx +295 -0
  274. package/src/components/ChainSelector.tsx +144 -0
  275. package/src/components/HumanActionBar.tsx +695 -0
  276. package/src/components/NotificationDrawer.tsx +129 -0
  277. package/src/components/apps/AgentKeysApp.tsx +490 -0
  278. package/src/components/apps/App.tsx +153 -0
  279. package/src/components/apps/AppGrid.tsx +15 -0
  280. package/src/components/apps/DetailedAddressDrawer.tsx +325 -0
  281. package/src/components/apps/DraggableApp.tsx +562 -0
  282. package/src/components/apps/IFrameApp.tsx +73 -0
  283. package/src/components/apps/LogsApp.tsx +360 -0
  284. package/src/components/apps/SendApp.tsx +394 -0
  285. package/src/components/apps/SetupWizardApp.tsx +1004 -0
  286. package/src/components/apps/SystemDefaultsApp.tsx +845 -0
  287. package/src/components/apps/ThirdPartyApp.tsx +428 -0
  288. package/src/components/apps/TokenApp.tsx +319 -0
  289. package/src/components/apps/TransactionsApp.tsx +438 -0
  290. package/src/components/apps/WalletDetailApp.tsx +1505 -0
  291. package/src/components/apps/index.ts +13 -0
  292. package/src/components/design-system/Button.tsx +53 -0
  293. package/src/components/design-system/ChainIndicator.tsx +65 -0
  294. package/src/components/design-system/ChainSelector.tsx +137 -0
  295. package/src/components/design-system/ConfirmationModal.tsx +106 -0
  296. package/src/components/design-system/ConfirmationPopover.tsx +81 -0
  297. package/src/components/design-system/Drawer.tsx +123 -0
  298. package/src/components/design-system/FilterDropdown.tsx +72 -0
  299. package/src/components/design-system/Modal.tsx +206 -0
  300. package/src/components/design-system/Popover.tsx +142 -0
  301. package/src/components/design-system/TextInput.tsx +85 -0
  302. package/src/components/design-system/Toggle.tsx +58 -0
  303. package/src/components/design-system/index.ts +11 -0
  304. package/src/components/docs/DocsThemeToggle.tsx +49 -0
  305. package/src/components/health/CredentialHealthDashboard.tsx +214 -0
  306. package/src/components/icons/ChainIcons.tsx +72 -0
  307. package/src/components/layout/AppStoreDrawer.tsx +369 -0
  308. package/src/components/layout/ContentArea.tsx +21 -0
  309. package/src/components/layout/TabBar.tsx +278 -0
  310. package/src/components/layout/WalletSidebar.tsx +1033 -0
  311. package/src/components/layout/index.ts +4 -0
  312. package/src/components/marketing/AuraWalletSpecOverlay.tsx +635 -0
  313. package/src/components/marketing/DeviceMorphExperience.tsx +216 -0
  314. package/src/components/vault/ApiKeysConsole.tsx +1080 -0
  315. package/src/components/vault/AuditConsole.tsx +584 -0
  316. package/src/components/vault/CredentialDetail.tsx +455 -0
  317. package/src/components/vault/CredentialEmpty.tsx +55 -0
  318. package/src/components/vault/CredentialField.tsx +361 -0
  319. package/src/components/vault/CredentialForm.tsx +1212 -0
  320. package/src/components/vault/CredentialList.tsx +165 -0
  321. package/src/components/vault/CredentialRow.tsx +97 -0
  322. package/src/components/vault/CredentialShareModal.tsx +178 -0
  323. package/src/components/vault/CredentialVault.tsx +754 -0
  324. package/src/components/vault/CredentialWalletWidget.tsx +103 -0
  325. package/src/components/vault/ImportCredentialsModal.tsx +515 -0
  326. package/src/components/vault/LargeTypeModal.tsx +64 -0
  327. package/src/components/vault/PasswordGenerator.tsx +224 -0
  328. package/src/components/vault/TOTPDisplay.tsx +123 -0
  329. package/src/components/vault/VaultSidebar.tsx +413 -0
  330. package/src/components/vault/types.ts +54 -0
  331. package/src/context/AuthContext.tsx +337 -0
  332. package/src/context/PriceContext.tsx +113 -0
  333. package/src/context/ThemeContext.tsx +164 -0
  334. package/src/context/WebSocketContext.tsx +269 -0
  335. package/src/context/WorkspaceContext.tsx +668 -0
  336. package/src/hooks/index.ts +3 -0
  337. package/src/hooks/useAgentActions.ts +368 -0
  338. package/src/hooks/useBalance.ts +103 -0
  339. package/src/hooks/useBalances.ts +129 -0
  340. package/src/instrumentation.ts +12 -0
  341. package/src/lib/api.ts +449 -0
  342. package/src/lib/app-loader.ts +148 -0
  343. package/src/lib/app-registry.ts +178 -0
  344. package/src/lib/app-sdk.ts +157 -0
  345. package/src/lib/audit-console-adapter.ts +151 -0
  346. package/src/lib/auth-client.ts +75 -0
  347. package/src/lib/config.ts +74 -0
  348. package/src/lib/crypto.ts +112 -0
  349. package/src/lib/db.ts +21 -0
  350. package/src/lib/docs.ts +390 -0
  351. package/src/lib/events.ts +361 -0
  352. package/src/lib/pino.ts +24 -0
  353. package/src/lib/theme-handlers.ts +168 -0
  354. package/src/lib/theme.ts +351 -0
  355. package/src/lib/tokenData.ts +378 -0
  356. package/src/lib/vault-crypto.ts +129 -0
  357. package/src/lib/websocket-server.ts +302 -0
  358. package/src/lib/websocket-setup.ts +79 -0
  359. package/src/lib/wordlist.ts +2050 -0
  360. package/src/lib/workspace-handlers.ts +285 -0
  361. package/start.sh +80 -0
  362. package/tailwind.config.ts +99 -0
  363. package/tsconfig.json +42 -0
@@ -0,0 +1,368 @@
1
+ 'use client';
2
+
3
+ import { useState, useEffect, useCallback, useRef } from 'react';
4
+ import { useWebSocket } from '@/context/WebSocketContext';
5
+ import { WALLET_EVENTS, type WalletEvent, type TokenCreatedData, type TokenRevokedData, type TokenSpentData, type ActionCreatedData, type ActionResolvedData } from '@/lib/events';
6
+ import { api, Api } from '@/lib/api';
7
+
8
+ export interface HumanAction {
9
+ id: string;
10
+ type: string;
11
+ fromTier: string;
12
+ toAddress: string | null;
13
+ amount: string | null;
14
+ chain: string;
15
+ status: string;
16
+ createdAt: string;
17
+ metadata?: string;
18
+ }
19
+
20
+ export interface AgentToken {
21
+ tokenHash: string;
22
+ agentId: string;
23
+ limit: number;
24
+ spent: number;
25
+ remaining: number;
26
+ permissions: string[];
27
+ expiresAt: number;
28
+ isExpired: boolean;
29
+ isRevoked: boolean;
30
+ isActive: boolean; // true = valid in memory, false = DB record only (server restarted)
31
+ isAdmin?: boolean; // true = admin token (UI session token)
32
+ }
33
+
34
+ interface DashboardData {
35
+ requests: HumanAction[];
36
+ tokens: {
37
+ active: AgentToken[];
38
+ inactive: AgentToken[];
39
+ };
40
+ counts: {
41
+ pendingActions: number;
42
+ activeTokens: number;
43
+ inactiveTokens: number;
44
+ };
45
+ }
46
+
47
+ interface UseAgentActionsOptions {
48
+ autoFetch?: boolean;
49
+ }
50
+
51
+ interface ApprovalResult {
52
+ success: boolean;
53
+ token?: string;
54
+ agentId?: string;
55
+ limit?: number;
56
+ permissions?: string[];
57
+ expiresIn?: number;
58
+ txHash?: string;
59
+ message?: string;
60
+ }
61
+
62
+ interface UseAgentActionsReturn {
63
+ requests: HumanAction[];
64
+ notifications: HumanAction[];
65
+ dismissNotification: (id: string) => void;
66
+ activeTokens: AgentToken[];
67
+ inactiveTokens: AgentToken[];
68
+ loading: boolean;
69
+ error: string | null;
70
+ counts: {
71
+ pendingActions: number;
72
+ activeTokens: number;
73
+ };
74
+ refresh: () => Promise<void>;
75
+ resolveAction: (id: string, approved: boolean) => Promise<ApprovalResult>;
76
+ revokeToken: (tokenHash: string) => Promise<boolean>;
77
+ actionLoading: string | null;
78
+ lastApprovalResult: ApprovalResult | null;
79
+ clearApprovalResult: () => void;
80
+ connected: boolean;
81
+ }
82
+
83
+ export function useAgentActions(options: UseAgentActionsOptions = {}): UseAgentActionsReturn {
84
+ const { autoFetch = true } = options;
85
+
86
+ const [requests, setRequests] = useState<HumanAction[]>([]);
87
+ const [notifications, setNotifications] = useState<HumanAction[]>([]);
88
+ const [activeTokens, setActiveTokens] = useState<AgentToken[]>([]);
89
+ const [inactiveTokens, setInactiveTokens] = useState<AgentToken[]>([]);
90
+ const [loading, setLoading] = useState(true);
91
+ const [error, setError] = useState<string | null>(null);
92
+ const [actionLoading, setActionLoading] = useState<string | null>(null);
93
+ const [counts, setCounts] = useState({ pendingActions: 0, activeTokens: 0 });
94
+ const [lastApprovalResult, setLastApprovalResult] = useState<ApprovalResult | null>(null);
95
+
96
+ const { subscribe, connected } = useWebSocket();
97
+ const mountedRef = useRef(true);
98
+
99
+ const clearApprovalResult = useCallback(() => {
100
+ setLastApprovalResult(null);
101
+ }, []);
102
+
103
+ const dismissNotification = useCallback((id: string) => {
104
+ setNotifications(prev => prev.filter(n => n.id !== id));
105
+ }, []);
106
+
107
+ const fetchDashboard = useCallback(async () => {
108
+ try {
109
+ const data = await api.get<{ success: boolean; error?: string } & DashboardData>(Api.AgentDashboard, '/agent-requests');
110
+
111
+ if (!mountedRef.current) return;
112
+
113
+ if (data.success) {
114
+ setRequests(data.requests || []);
115
+ setActiveTokens(data.tokens?.active || []);
116
+ setInactiveTokens(data.tokens?.inactive || []);
117
+ setCounts({
118
+ pendingActions: data.counts?.pendingActions || 0,
119
+ activeTokens: data.counts?.activeTokens || 0,
120
+ });
121
+ setError(null);
122
+ } else {
123
+ setError(data.error || 'Failed to fetch dashboard');
124
+ }
125
+ } catch (err) {
126
+ if (mountedRef.current) {
127
+ setError(err instanceof Error ? err.message : 'Failed to fetch dashboard');
128
+ }
129
+ } finally {
130
+ if (mountedRef.current) {
131
+ setLoading(false);
132
+ }
133
+ }
134
+ }, []);
135
+
136
+ const refresh = useCallback(async () => {
137
+ setLoading(true);
138
+ await fetchDashboard();
139
+ }, [fetchDashboard]);
140
+
141
+ const resolveAction = useCallback(async (id: string, approved: boolean): Promise<ApprovalResult> => {
142
+ setActionLoading(`resolve-${id}`);
143
+ try {
144
+ const data = await api.post<{
145
+ success: boolean;
146
+ error?: string;
147
+ token?: string;
148
+ agentId?: string;
149
+ limit?: number;
150
+ permissions?: string[];
151
+ expiresIn?: number;
152
+ txHash?: string;
153
+ message?: string;
154
+ }>(Api.Wallet, `/actions/${id}/resolve`, { approved });
155
+
156
+ if (data.success) {
157
+ const result: ApprovalResult = {
158
+ success: true,
159
+ token: data.token,
160
+ agentId: data.agentId,
161
+ limit: data.limit,
162
+ permissions: data.permissions,
163
+ expiresIn: data.expiresIn,
164
+ txHash: data.txHash,
165
+ message: data.message,
166
+ };
167
+ // Set lastApprovalResult when a token is returned
168
+ if (data.token) {
169
+ setLastApprovalResult(result);
170
+ }
171
+ await refresh();
172
+ return result;
173
+ } else {
174
+ setError(data.error || 'Action resolution failed');
175
+ return { success: false, message: data.error };
176
+ }
177
+ } catch (err) {
178
+ const message = err instanceof Error ? err.message : 'Action resolution failed';
179
+ setError(message);
180
+ return { success: false, message };
181
+ } finally {
182
+ setActionLoading(null);
183
+ }
184
+ }, [refresh]);
185
+
186
+ const revokeToken = useCallback(async (tokenHash: string): Promise<boolean> => {
187
+ setActionLoading(`revoke-${tokenHash}`);
188
+ try {
189
+ const data = await api.post<{ success: boolean; error?: string }>(Api.Wallet, '/actions/tokens/revoke', { tokenHash });
190
+
191
+ if (data.success) {
192
+ // WebSocket will update the UI, but refresh as fallback
193
+ await refresh();
194
+ return true;
195
+ } else {
196
+ setError(data.error || 'Revoke failed');
197
+ return false;
198
+ }
199
+ } catch (err) {
200
+ setError(err instanceof Error ? err.message : 'Revoke failed');
201
+ return false;
202
+ } finally {
203
+ setActionLoading(null);
204
+ }
205
+ }, [refresh]);
206
+
207
+ // Initial fetch
208
+ useEffect(() => {
209
+ mountedRef.current = true;
210
+ if (autoFetch) {
211
+ refresh();
212
+ }
213
+ return () => {
214
+ mountedRef.current = false;
215
+ };
216
+ }, [autoFetch, refresh]);
217
+
218
+ // Subscribe to WebSocket events
219
+ useEffect(() => {
220
+ // Token created - add to active tokens
221
+ const unsubTokenCreated = subscribe(WALLET_EVENTS.TOKEN_CREATED, (event) => {
222
+ const data = (event as WalletEvent).data as TokenCreatedData;
223
+ setActiveTokens((prev) => {
224
+ // Check if already exists
225
+ if (prev.some((t) => t.tokenHash === data.tokenHash)) return prev;
226
+ return [
227
+ {
228
+ tokenHash: data.tokenHash,
229
+ agentId: data.agentId,
230
+ limit: data.limit,
231
+ spent: 0,
232
+ remaining: data.limit,
233
+ permissions: data.permissions,
234
+ expiresAt: data.expiresAt,
235
+ isExpired: false,
236
+ isRevoked: false,
237
+ isActive: true,
238
+ },
239
+ ...prev,
240
+ ];
241
+ });
242
+ setCounts((prev) => ({ ...prev, activeTokens: prev.activeTokens + 1 }));
243
+ });
244
+
245
+ // Token revoked - move from active to inactive
246
+ const unsubTokenRevoked = subscribe(WALLET_EVENTS.TOKEN_REVOKED, (event) => {
247
+ const data = (event as WalletEvent).data as TokenRevokedData;
248
+ setActiveTokens((prev) => {
249
+ const token = prev.find((t) => t.tokenHash === data.tokenHash);
250
+ if (token) {
251
+ // Move to inactive
252
+ setInactiveTokens((inactive) => [
253
+ { ...token, isRevoked: true, isActive: false },
254
+ ...inactive,
255
+ ]);
256
+ }
257
+ return prev.filter((t) => t.tokenHash !== data.tokenHash);
258
+ });
259
+ setCounts((prev) => ({
260
+ ...prev,
261
+ activeTokens: Math.max(0, prev.activeTokens - 1),
262
+ }));
263
+ });
264
+
265
+ // Token spent - update spent/remaining
266
+ const unsubTokenSpent = subscribe(WALLET_EVENTS.TOKEN_SPENT, (event) => {
267
+ const data = (event as WalletEvent).data as TokenSpentData;
268
+ setActiveTokens((prev) =>
269
+ prev.map((t) =>
270
+ t.tokenHash === data.tokenHash
271
+ ? { ...t, spent: data.newSpent, remaining: data.remaining }
272
+ : t
273
+ )
274
+ );
275
+ });
276
+
277
+ // Action created - route notify types to notifications, others to pending requests
278
+ const unsubActionCreated = subscribe(WALLET_EVENTS.ACTION_CREATED, (event) => {
279
+ const data = (event as WalletEvent).data as ActionCreatedData;
280
+
281
+ // Notify actions go to the notifications list, not pending requests
282
+ if (data.type === 'notify') {
283
+ setNotifications((prev) => {
284
+ if (prev.some((n) => n.id === data.id)) return prev;
285
+ return [
286
+ {
287
+ id: data.id,
288
+ type: data.type,
289
+ fromTier: 'system',
290
+ toAddress: null,
291
+ amount: null,
292
+ chain: 'base',
293
+ status: 'acknowledged',
294
+ createdAt: new Date().toISOString(),
295
+ metadata: JSON.stringify({
296
+ ...data.metadata,
297
+ source: data.source,
298
+ summary: data.summary,
299
+ }),
300
+ },
301
+ ...prev,
302
+ ];
303
+ });
304
+ return;
305
+ }
306
+
307
+ setRequests((prev) => {
308
+ if (prev.some((r) => r.id === data.id)) return prev;
309
+ return [
310
+ {
311
+ id: data.id,
312
+ type: data.type,
313
+ fromTier: 'system',
314
+ toAddress: null,
315
+ amount: null,
316
+ chain: 'base',
317
+ status: 'pending',
318
+ createdAt: new Date().toISOString(),
319
+ metadata: JSON.stringify({
320
+ ...data.metadata,
321
+ source: data.source,
322
+ summary: data.summary,
323
+ expiresAt: data.expiresAt,
324
+ }),
325
+ },
326
+ ...prev,
327
+ ];
328
+ });
329
+ setCounts((prev) => ({ ...prev, pendingActions: prev.pendingActions + 1 }));
330
+ });
331
+
332
+ // Action resolved - remove from pending requests
333
+ const unsubActionResolved = subscribe(WALLET_EVENTS.ACTION_RESOLVED, (event) => {
334
+ const data = (event as WalletEvent).data as ActionResolvedData;
335
+ setRequests((prev) => prev.filter((r) => r.id !== data.id));
336
+ setCounts((prev) => ({
337
+ ...prev,
338
+ pendingActions: Math.max(0, prev.pendingActions - 1),
339
+ }));
340
+ });
341
+
342
+ return () => {
343
+ unsubTokenCreated();
344
+ unsubTokenRevoked();
345
+ unsubTokenSpent();
346
+ unsubActionCreated();
347
+ unsubActionResolved();
348
+ };
349
+ }, [subscribe]);
350
+
351
+ return {
352
+ requests,
353
+ notifications,
354
+ dismissNotification,
355
+ activeTokens,
356
+ inactiveTokens,
357
+ loading,
358
+ error,
359
+ counts,
360
+ refresh,
361
+ resolveAction,
362
+ revokeToken,
363
+ actionLoading,
364
+ lastApprovalResult,
365
+ clearApprovalResult,
366
+ connected,
367
+ };
368
+ }
@@ -0,0 +1,103 @@
1
+ import { useState, useEffect, useCallback, useRef } from 'react';
2
+ import { ethers } from 'ethers';
3
+ import { Connection, PublicKey, LAMPORTS_PER_SOL } from '@solana/web3.js';
4
+ import { useAuth } from '@/context/AuthContext';
5
+
6
+ function isSolanaChain(chain: string): boolean {
7
+ return chain === 'solana' || chain === 'solana-devnet';
8
+ }
9
+
10
+ interface UseBalanceResult {
11
+ balance: string | null;
12
+ balanceWei: bigint | null;
13
+ loading: boolean;
14
+ error: string | null;
15
+ refetch: () => Promise<void>;
16
+ /** Native currency symbol for the chain */
17
+ currency: string;
18
+ /** Number of decimals for the native currency */
19
+ decimals: number;
20
+ }
21
+
22
+ export function useBalance(address: string | undefined, chain?: string): UseBalanceResult {
23
+ const { getRpcUrl, getConfiguredChains } = useAuth();
24
+ const [balance, setBalance] = useState<string | null>(null);
25
+ const [balanceWei, setBalanceWei] = useState<bigint | null>(null);
26
+ const [loading, setLoading] = useState(false);
27
+ const [error, setError] = useState<string | null>(null);
28
+
29
+ // Track previous values to detect changes
30
+ const prevAddressRef = useRef<string>('');
31
+ const prevRpcUrlRef = useRef<string>('');
32
+
33
+ const chains = getConfiguredChains();
34
+ const defaultChain = Object.keys(chains)[0] || 'base';
35
+ const targetChain = chain || defaultChain;
36
+
37
+ const isSolana = isSolanaChain(targetChain);
38
+ const currency = isSolana ? 'SOL' : 'ETH';
39
+ const decimals = isSolana ? 9 : 18;
40
+
41
+ // Get current RPC URL for comparison
42
+ const rpcUrl = getRpcUrl(targetChain);
43
+
44
+ const fetchBalance = useCallback(async () => {
45
+ if (!address) {
46
+ setBalance(null);
47
+ setBalanceWei(null);
48
+ return;
49
+ }
50
+
51
+ setLoading(true);
52
+ setError(null);
53
+
54
+ try {
55
+ const currentRpcUrl = getRpcUrl(targetChain);
56
+
57
+ if (isSolanaChain(targetChain)) {
58
+ // Solana balance fetch
59
+ const connection = new Connection(currentRpcUrl, 'confirmed');
60
+ const pubkey = new PublicKey(address);
61
+ const lamports = await connection.getBalance(pubkey);
62
+ setBalanceWei(BigInt(lamports));
63
+ setBalance((lamports / LAMPORTS_PER_SOL).toString());
64
+ } else {
65
+ // EVM balance fetch
66
+ const provider = new ethers.JsonRpcProvider(currentRpcUrl);
67
+ const wei = await provider.getBalance(address);
68
+ setBalanceWei(wei);
69
+ setBalance(ethers.formatEther(wei));
70
+ }
71
+ } catch (err) {
72
+ console.error('[useBalance] Failed to fetch balance:', err);
73
+ setError(err instanceof Error ? err.message : 'Failed to fetch balance');
74
+ setBalance(null);
75
+ setBalanceWei(null);
76
+ } finally {
77
+ setLoading(false);
78
+ }
79
+ }, [address, targetChain, getRpcUrl]);
80
+
81
+ // Refetch when address, chain, or RPC URL changes
82
+ useEffect(() => {
83
+ const shouldRefetch =
84
+ address !== prevAddressRef.current ||
85
+ rpcUrl !== prevRpcUrlRef.current;
86
+
87
+ if (shouldRefetch) {
88
+ prevAddressRef.current = address || '';
89
+ prevRpcUrlRef.current = rpcUrl;
90
+ fetchBalance();
91
+ }
92
+ }, [address, rpcUrl, fetchBalance]);
93
+
94
+ return {
95
+ balance,
96
+ balanceWei,
97
+ loading,
98
+ error,
99
+ refetch: fetchBalance,
100
+ currency,
101
+ decimals,
102
+ };
103
+ }
@@ -0,0 +1,129 @@
1
+ import { useState, useEffect, useCallback, useRef } from 'react';
2
+ import { ethers } from 'ethers';
3
+ import { useAuth } from '@/context/AuthContext';
4
+
5
+ interface BalanceMap {
6
+ [address: string]: string;
7
+ }
8
+
9
+ interface UseBalancesResult {
10
+ balances: BalanceMap;
11
+ loading: boolean;
12
+ error: string | null;
13
+ refetch: () => Promise<void>;
14
+ }
15
+
16
+ /**
17
+ * Hook to fetch ETH balances for multiple addresses using batch RPC calls
18
+ * Uses eth_getBalance with JSON-RPC batch request for efficiency
19
+ */
20
+ export function useBalances(addresses: string[], chain?: string): UseBalancesResult {
21
+ const { getRpcUrl, getConfiguredChains } = useAuth();
22
+ const [balances, setBalances] = useState<BalanceMap>({});
23
+ const [loading, setLoading] = useState(false);
24
+ const [error, setError] = useState<string | null>(null);
25
+
26
+ // Track previous values to detect changes
27
+ const prevAddressesRef = useRef<string>('');
28
+ const prevRpcUrlRef = useRef<string>('');
29
+
30
+ const chains = getConfiguredChains();
31
+ const defaultChain = Object.keys(chains)[0] || 'base';
32
+ const targetChain = chain || defaultChain;
33
+
34
+ // Get current RPC URL for comparison
35
+ const rpcUrl = getRpcUrl(targetChain);
36
+
37
+ const fetchBalances = useCallback(async () => {
38
+ if (!addresses || addresses.length === 0) {
39
+ setBalances({});
40
+ return;
41
+ }
42
+
43
+ const currentRpcUrl = getRpcUrl(targetChain);
44
+
45
+ setLoading(true);
46
+ setError(null);
47
+
48
+ try {
49
+ // Build batch request for all addresses
50
+ const batchRequest = addresses.map((address, index) => ({
51
+ jsonrpc: '2.0',
52
+ id: index,
53
+ method: 'eth_getBalance',
54
+ params: [address, 'latest'],
55
+ }));
56
+
57
+ const response = await fetch(currentRpcUrl, {
58
+ method: 'POST',
59
+ headers: { 'Content-Type': 'application/json' },
60
+ body: JSON.stringify(batchRequest),
61
+ });
62
+
63
+ if (!response.ok) {
64
+ throw new Error(`RPC request failed: ${response.status}`);
65
+ }
66
+
67
+ const results = await response.json();
68
+
69
+ // Handle both array response (batch) and single response
70
+ const resultsArray = Array.isArray(results) ? results : [results];
71
+
72
+ // Sort by id to match original order
73
+ resultsArray.sort((a, b) => a.id - b.id);
74
+
75
+ const newBalances: BalanceMap = {};
76
+ resultsArray.forEach((result, index) => {
77
+ const address = addresses[index];
78
+ if (result.result) {
79
+ const wei = BigInt(result.result);
80
+ newBalances[address.toLowerCase()] = ethers.formatEther(wei);
81
+ } else {
82
+ newBalances[address.toLowerCase()] = '0';
83
+ }
84
+ });
85
+
86
+ setBalances(newBalances);
87
+ } catch (err) {
88
+ console.error('[useBalances] Failed to fetch balances:', err);
89
+ setError(err instanceof Error ? err.message : 'Failed to fetch balances');
90
+
91
+ // Set all balances to '0' on error
92
+ const fallbackBalances: BalanceMap = {};
93
+ addresses.forEach(addr => {
94
+ fallbackBalances[addr.toLowerCase()] = '0';
95
+ });
96
+ setBalances(fallbackBalances);
97
+ } finally {
98
+ setLoading(false);
99
+ }
100
+ }, [addresses, targetChain, getRpcUrl]);
101
+
102
+ // Refetch when addresses, chain, or RPC URL changes
103
+ useEffect(() => {
104
+ const addressKey = addresses.map(a => a.toLowerCase()).sort().join(',');
105
+ const shouldRefetch =
106
+ addressKey !== prevAddressesRef.current ||
107
+ rpcUrl !== prevRpcUrlRef.current;
108
+
109
+ if (shouldRefetch && addresses.length > 0) {
110
+ prevAddressesRef.current = addressKey;
111
+ prevRpcUrlRef.current = rpcUrl;
112
+ fetchBalances();
113
+ }
114
+ }, [addresses, rpcUrl, fetchBalances]);
115
+
116
+ return {
117
+ balances,
118
+ loading,
119
+ error,
120
+ refetch: fetchBalances,
121
+ };
122
+ }
123
+
124
+ /**
125
+ * Helper to get balance for a specific address from the balances map
126
+ */
127
+ export function getBalance(balances: BalanceMap, address: string): string {
128
+ return balances[address.toLowerCase()] || '0';
129
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Next.js instrumentation hook
3
+ * Sets up WebSocket server for real-time events
4
+ */
5
+
6
+ export async function register() {
7
+ // Only run on server
8
+ if (process.env.NEXT_RUNTIME === 'nodejs') {
9
+ const { setupWebSocketServer } = await import('./lib/websocket-setup');
10
+ setupWebSocketServer();
11
+ }
12
+ }