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,90 @@
1
+ 'use client';
2
+
3
+ import { useEffect, useRef } from 'react';
4
+ import { PriceProvider } from '@/context/PriceContext';
5
+ import { WebSocketProvider, useWebSocket } from '@/context/WebSocketContext';
6
+ import { WorkspaceProvider } from '@/context/WorkspaceContext';
7
+ import { ThemeProvider } from '@/context/ThemeContext';
8
+ import { AuthProvider, useAuth } from '@/context/AuthContext';
9
+
10
+ function ServerRestartBanner() {
11
+ const { reconnected, dismissReconnected } = useWebSocket();
12
+ const timerRef = useRef<NodeJS.Timeout | null>(null);
13
+
14
+ useEffect(() => {
15
+ if (reconnected) {
16
+ timerRef.current = setTimeout(dismissReconnected, 30000);
17
+ return () => {
18
+ if (timerRef.current) clearTimeout(timerRef.current);
19
+ };
20
+ }
21
+ }, [reconnected, dismissReconnected]);
22
+
23
+ if (!reconnected) return null;
24
+
25
+ return (
26
+ <div
27
+ style={{
28
+ position: 'fixed',
29
+ top: 0,
30
+ left: 0,
31
+ right: 0,
32
+ zIndex: 9999,
33
+ padding: '10px 16px',
34
+ display: 'flex',
35
+ alignItems: 'center',
36
+ justifyContent: 'space-between',
37
+ gap: '12px',
38
+ fontFamily: 'monospace',
39
+ fontSize: '11px',
40
+ background: 'var(--color-warning, #ff4d00)',
41
+ color: '#fff',
42
+ }}
43
+ >
44
+ <span>
45
+ Server restarted. All agent tokens have been invalidated. Agents will need to re-request access.
46
+ </span>
47
+ <button
48
+ onClick={dismissReconnected}
49
+ style={{
50
+ background: 'rgba(255,255,255,0.2)',
51
+ border: 'none',
52
+ color: '#fff',
53
+ padding: '4px 10px',
54
+ cursor: 'pointer',
55
+ fontFamily: 'monospace',
56
+ fontSize: '10px',
57
+ flexShrink: 0,
58
+ }}
59
+ >
60
+ DISMISS
61
+ </button>
62
+ </div>
63
+ );
64
+ }
65
+
66
+ // Inner component that uses the auth context
67
+ function AuthenticatedProviders({ children }: { children: React.ReactNode }) {
68
+ const { token } = useAuth();
69
+
70
+ return (
71
+ <WebSocketProvider token={token}>
72
+ <ThemeProvider>
73
+ <PriceProvider>
74
+ <WorkspaceProvider>
75
+ <ServerRestartBanner />
76
+ {children}
77
+ </WorkspaceProvider>
78
+ </PriceProvider>
79
+ </ThemeProvider>
80
+ </WebSocketProvider>
81
+ );
82
+ }
83
+
84
+ export function Providers({ children }: { children: React.ReactNode }) {
85
+ return (
86
+ <AuthProvider>
87
+ <AuthenticatedProviders>{children}</AuthenticatedProviders>
88
+ </AuthProvider>
89
+ );
90
+ }
@@ -0,0 +1,295 @@
1
+ 'use client';
2
+
3
+ import React, { useCallback, useEffect, useMemo, useState } from 'react';
4
+ import { AlertTriangle, Eye, Link2, Loader2, Lock, ShieldCheck } from 'lucide-react';
5
+ import { getWalletBaseUrl } from '@/lib/api';
6
+ import Image from 'next/image';
7
+ import Link from 'next/link';
8
+ import { useParams } from 'next/navigation';
9
+
10
+ interface ShareMeta {
11
+ token: string;
12
+ credentialId: string;
13
+ credentialName: string;
14
+ credentialType: string;
15
+ expiresAt: number;
16
+ accessMode: 'anyone' | 'password';
17
+ passwordRequired: boolean;
18
+ oneTimeOnly: boolean;
19
+ viewCount: number;
20
+ maxViews: number | null;
21
+ }
22
+
23
+ interface SharedCredentialField {
24
+ key: string;
25
+ value: string;
26
+ }
27
+
28
+ interface SharedCredential {
29
+ id: string;
30
+ name: string;
31
+ type: string;
32
+ meta: Record<string, unknown>;
33
+ fields: SharedCredentialField[];
34
+ createdAt: string;
35
+ updatedAt: string;
36
+ }
37
+
38
+ export default function SharedCredentialPage() {
39
+ const params = useParams<{ token: string | string[] }>();
40
+ const [token, setToken] = useState('');
41
+ const [meta, setMeta] = useState<ShareMeta | null>(null);
42
+ const [credential, setCredential] = useState<SharedCredential | null>(null);
43
+ const [loadingMeta, setLoadingMeta] = useState(true);
44
+ const [reading, setReading] = useState(false);
45
+ const [password, setPassword] = useState('');
46
+ const [error, setError] = useState<string | null>(null);
47
+
48
+ const walletBaseUrl = useMemo(() => getWalletBaseUrl(), []);
49
+
50
+ const fetchMeta = useCallback(async (shareToken: string) => {
51
+ setLoadingMeta(true);
52
+ setError(null);
53
+ try {
54
+ const res = await fetch(`${walletBaseUrl}/credential-shares/${encodeURIComponent(shareToken)}`);
55
+ const data = await res.json().catch(() => ({ error: 'Invalid response' }));
56
+ if (!res.ok) {
57
+ setError(data.error || 'Failed to load share');
58
+ setMeta(null);
59
+ return;
60
+ }
61
+ setMeta(data.share as ShareMeta);
62
+ } catch {
63
+ setError('Failed to load share');
64
+ setMeta(null);
65
+ } finally {
66
+ setLoadingMeta(false);
67
+ }
68
+ }, [walletBaseUrl]);
69
+
70
+ const readShare = useCallback(async (shareToken: string, sharePassword?: string) => {
71
+ setReading(true);
72
+ setError(null);
73
+ try {
74
+ const res = await fetch(`${walletBaseUrl}/credential-shares/${encodeURIComponent(shareToken)}/read`, {
75
+ method: 'POST',
76
+ headers: { 'Content-Type': 'application/json' },
77
+ body: JSON.stringify(sharePassword ? { password: sharePassword } : {}),
78
+ });
79
+ const data = await res.json().catch(() => ({ error: 'Invalid response' }));
80
+ if (!res.ok) {
81
+ setError(data.error || 'Failed to read shared credential');
82
+ return;
83
+ }
84
+ setCredential(data.credential as SharedCredential);
85
+ } catch {
86
+ setError('Failed to read shared credential');
87
+ } finally {
88
+ setReading(false);
89
+ }
90
+ }, [walletBaseUrl]);
91
+
92
+ useEffect(() => {
93
+ const rawToken = params?.token;
94
+ const shareToken = Array.isArray(rawToken) ? rawToken[0] : rawToken;
95
+ setToken(shareToken || '');
96
+ if (shareToken) {
97
+ void fetchMeta(shareToken);
98
+ } else {
99
+ setLoadingMeta(false);
100
+ setError('Invalid share link');
101
+ }
102
+ }, [fetchMeta, params]);
103
+
104
+ useEffect(() => {
105
+ if (!token || !meta || meta.passwordRequired || credential || reading) return;
106
+ void readShare(token);
107
+ }, [token, meta, credential, reading, readShare]);
108
+
109
+ const metaEntries = useMemo(() => {
110
+ if (!credential) return [];
111
+ return Object.entries(credential.meta || {}).filter(([, value]) =>
112
+ typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean',
113
+ );
114
+ }, [credential]);
115
+
116
+ return (
117
+ <div className="min-h-screen bg-tyvek relative flex items-center justify-center p-4 py-8">
118
+ <div className="fixed inset-0 pointer-events-none z-0 overflow-hidden">
119
+ <div className="absolute inset-0 bg-grid-pattern bg-[size:4rem_4rem] opacity-30" />
120
+ <div className="absolute inset-0 tyvek-texture opacity-40 mix-blend-multiply" />
121
+
122
+ <div className="absolute top-[5%] left-[5%] opacity-5 select-none">
123
+ <h1 className="text-[15vw] font-bold leading-none text-void font-mono tracking-tighter">STERILE</h1>
124
+ </div>
125
+ <div className="absolute bottom-[5%] right-[5%] opacity-5 select-none">
126
+ <h1 className="text-[15vw] font-bold leading-none text-void font-mono tracking-tighter text-right">FIELD</h1>
127
+ </div>
128
+
129
+ <div className="absolute top-10 left-10 w-32 h-32 border-l-4 border-t-4 border-void opacity-10">
130
+ <div className="absolute top-2 left-2 w-4 h-4 bg-void" />
131
+ </div>
132
+ <div className="absolute bottom-10 right-10 w-32 h-32 border-r-4 border-b-4 border-void opacity-10 flex items-end justify-end">
133
+ <div className="absolute bottom-2 right-2 w-4 h-4 bg-void" />
134
+ </div>
135
+ </div>
136
+
137
+ <Link href="/" className="fixed top-6 left-6 z-50 flex items-center gap-3 hover:opacity-80 transition-opacity">
138
+ <div className="w-10 h-10">
139
+ <Image src="/logo.webp" alt="AuraWallet" width={40} height={40} className="w-full h-full object-contain" priority />
140
+ </div>
141
+ <div className="font-black text-xl tracking-tighter text-black">AURAWALLET</div>
142
+ </Link>
143
+
144
+ <div className="relative z-10 w-full max-w-2xl">
145
+ <div className="bg-[#f4f4f2] border border-black/10 shadow-lg overflow-hidden font-mono">
146
+ <div className="px-4 py-3 border-b border-black/10 bg-white/50 flex items-center justify-between">
147
+ <div className="flex items-center gap-2">
148
+ <Link2 size={15} className="text-black/70" />
149
+ <h1 className="text-[11px] font-bold uppercase tracking-[0.22em] text-black">SHARED CREDENTIAL</h1>
150
+ </div>
151
+ <span className="text-[9px] text-black/40 tracking-wider">
152
+ {credential ? 'ACCESS_GRANTED' : meta?.passwordRequired ? 'LOCKED' : 'PENDING'}
153
+ </span>
154
+ </div>
155
+
156
+ <div className="px-4 pb-4 pt-4 space-y-4">
157
+ {loadingMeta && (
158
+ <div className="flex items-center gap-2 text-[10px] text-black/60 uppercase tracking-widest">
159
+ <Loader2 size={12} className="animate-spin" />
160
+ LOADING SHARE
161
+ </div>
162
+ )}
163
+
164
+ {!loadingMeta && error && !credential && (
165
+ <div className="border border-red-300 bg-red-100/40 p-3">
166
+ <div className="flex items-center gap-2 text-[10px] text-red-700 uppercase tracking-wider">
167
+ <AlertTriangle size={12} />
168
+ {error}
169
+ </div>
170
+ </div>
171
+ )}
172
+
173
+ {!loadingMeta && meta && !credential && !meta.passwordRequired && reading && (
174
+ <div className="flex items-center gap-2 text-[10px] text-black/60 uppercase tracking-widest">
175
+ <Loader2 size={12} className="animate-spin" />
176
+ DECRYPTING PAYLOAD
177
+ </div>
178
+ )}
179
+
180
+ {!loadingMeta && meta && !credential && meta.passwordRequired && (
181
+ <div className="space-y-3">
182
+ <div className="text-[10px] text-black/60 uppercase tracking-wider">
183
+ This shared credential is password protected.
184
+ </div>
185
+ <div className="space-y-1">
186
+ <label htmlFor="share-password" className="block text-[8px] font-bold text-black/50 uppercase tracking-[0.14em]">
187
+ SHARE PASSWORD
188
+ </label>
189
+ <input
190
+ id="share-password"
191
+ type="password"
192
+ value={password}
193
+ onChange={(e) => setPassword(e.target.value)}
194
+ placeholder="Enter password"
195
+ className="w-full h-10 px-3 border border-black/20 bg-white text-[11px] text-black placeholder:text-black/35 outline-none focus:border-black transition-colors"
196
+ />
197
+ </div>
198
+
199
+ <button
200
+ type="button"
201
+ onClick={() => { void readShare(token, password); }}
202
+ disabled={!password || reading}
203
+ className="h-9 px-4 flex items-center justify-center gap-2 border border-black/20 bg-white hover:border-black text-[10px] font-bold uppercase tracking-wider text-black transition-colors disabled:opacity-40 disabled:cursor-not-allowed"
204
+ >
205
+ {reading ? <Loader2 size={12} className="animate-spin" /> : <Lock size={12} />}
206
+ UNLOCK
207
+ </button>
208
+
209
+ {error && (
210
+ <div className="text-[9px] text-red-700 uppercase tracking-wider">{error}</div>
211
+ )}
212
+ </div>
213
+ )}
214
+
215
+ {credential && (
216
+ <div className="space-y-4">
217
+ {meta && (
218
+ <div className="flex items-center gap-2 px-3 py-2 bg-voltage/10 border border-voltage/20">
219
+ <ShieldCheck size={13} className="text-green-600" />
220
+ <span className="text-[10px] font-bold text-black uppercase tracking-wider">AUTHENTIC SHARE</span>
221
+ <span className="text-[9px] text-black/50 ml-auto">
222
+ {meta.oneTimeOnly ? 'ONE_TIME' : 'MULTI_VIEW'}
223
+ </span>
224
+ </div>
225
+ )}
226
+
227
+ <div>
228
+ <div className="text-[14px] font-bold text-black uppercase tracking-tight">{credential.name}</div>
229
+ <div className="text-[9px] uppercase tracking-widest text-black/60">
230
+ {credential.type}
231
+ {meta?.oneTimeOnly ? ' · One-time view' : ''}
232
+ </div>
233
+ {meta && (
234
+ <div className="text-[9px] text-black/50 mt-1">Expires {new Date(meta.expiresAt).toLocaleString()}</div>
235
+ )}
236
+ </div>
237
+
238
+ {metaEntries.length > 0 && (
239
+ <div className="border border-dashed border-black/20 p-3 bg-white/40">
240
+ <div className="flex justify-between items-center mb-2 border-b border-black/10 pb-1">
241
+ <span className="text-[9px] font-bold uppercase tracking-widest text-black">METADATA</span>
242
+ <span className="text-[8px] text-black/40 font-bold">
243
+ QTY: {metaEntries.length.toString().padStart(2, '0')}
244
+ </span>
245
+ </div>
246
+ <div className="space-y-1.5">
247
+ {metaEntries.map(([key, value]) => (
248
+ <div key={key} className="grid grid-cols-[140px_1fr] gap-2 py-1 border-b border-black/10">
249
+ <div className="text-[9px] uppercase tracking-widest text-black/60">{key}</div>
250
+ <div className="text-[10px] break-all text-black">{String(value)}</div>
251
+ </div>
252
+ ))}
253
+ </div>
254
+ </div>
255
+ )}
256
+
257
+ <div className="border border-dashed border-black/20 p-3 bg-white/40">
258
+ <div className="flex justify-between items-center mb-2 border-b border-black/10 pb-1">
259
+ <span className="text-[9px] font-bold uppercase tracking-widest text-black">SHARED_FIELDS</span>
260
+ <span className="text-[8px] text-black/40 font-bold">
261
+ QTY: {credential.fields.length.toString().padStart(2, '0')}
262
+ </span>
263
+ </div>
264
+
265
+ {credential.fields.length === 0 ? (
266
+ <div className="text-[10px] text-black/60 uppercase tracking-wider">No sensitive fields.</div>
267
+ ) : (
268
+ <div className="space-y-1.5">
269
+ {credential.fields.map((field) => (
270
+ <div key={field.key} className="grid grid-cols-[140px_1fr] gap-2 py-1 border-b border-black/10">
271
+ <div className="text-[9px] uppercase tracking-widest text-black/60">{field.key}</div>
272
+ <div className="text-[10px] break-all flex items-start gap-1.5 text-black">
273
+ <Eye size={10} className="mt-0.5 shrink-0" />
274
+ <span>{field.value}</span>
275
+ </div>
276
+ </div>
277
+ ))}
278
+ </div>
279
+ )}
280
+ </div>
281
+ </div>
282
+ )}
283
+ </div>
284
+
285
+ {credential && (
286
+ <div className="px-4 py-2 border-t border-black/10 bg-white/30">
287
+ <div className="text-[8px] text-black/40 uppercase tracking-widest mb-1">SHARE TOKEN</div>
288
+ <div className="text-[9px] text-black font-bold break-all leading-relaxed">{token}</div>
289
+ </div>
290
+ )}
291
+ </div>
292
+ </div>
293
+ </div>
294
+ );
295
+ }
@@ -0,0 +1,144 @@
1
+ 'use client';
2
+
3
+ import React, { useState, useRef, useEffect } from 'react';
4
+ import { createPortal } from 'react-dom';
5
+ import { ChevronDown } from 'lucide-react';
6
+
7
+ // Supported chains with their config
8
+ export const SUPPORTED_CHAINS: Record<string, { name: string; chainId: number; color: string }> = {
9
+ base: { name: 'Base', chainId: 8453, color: '#0052FF' },
10
+ ethereum: { name: 'Ethereum', chainId: 1, color: '#627EEA' },
11
+ arbitrum: { name: 'Arbitrum', chainId: 42161, color: '#28A0F0' },
12
+ optimism: { name: 'Optimism', chainId: 10, color: '#FF0420' },
13
+ solana: { name: 'Solana', chainId: 0, color: '#9945FF' },
14
+ };
15
+
16
+ // Simple chain icons as colored circles with initials
17
+ function ChainIcon({ chain, size = 10 }: { chain: string; size?: number }) {
18
+ const config = SUPPORTED_CHAINS[chain];
19
+ const initial = chain.charAt(0).toUpperCase();
20
+
21
+ return (
22
+ <div
23
+ className="rounded-full flex items-center justify-center font-bold"
24
+ style={{
25
+ width: size,
26
+ height: size,
27
+ backgroundColor: config?.color || '#888',
28
+ color: '#fff',
29
+ fontSize: size * 0.5,
30
+ }}
31
+ >
32
+ {initial}
33
+ </div>
34
+ );
35
+ }
36
+
37
+ interface ChainSelectorProps {
38
+ value: string;
39
+ onChange: (chain: string) => void;
40
+ includeAll?: boolean;
41
+ disabled?: boolean;
42
+ size?: 'sm' | 'md';
43
+ }
44
+
45
+ export function ChainSelector({
46
+ value,
47
+ onChange,
48
+ includeAll = false,
49
+ disabled = false,
50
+ size = 'sm',
51
+ }: ChainSelectorProps) {
52
+ const [open, setOpen] = useState(false);
53
+ const [mounted, setMounted] = useState(false);
54
+ const buttonRef = useRef<HTMLButtonElement>(null);
55
+ const [position, setPosition] = useState<{ top: number; left: number; width: number } | null>(null);
56
+
57
+ useEffect(() => {
58
+ setMounted(true);
59
+ }, []);
60
+
61
+ useEffect(() => {
62
+ if (open && buttonRef.current) {
63
+ const rect = buttonRef.current.getBoundingClientRect();
64
+ setPosition({
65
+ top: rect.bottom + 2,
66
+ left: rect.left,
67
+ width: Math.max(rect.width, 100),
68
+ });
69
+ }
70
+ }, [open]);
71
+
72
+ const options = includeAll
73
+ ? [{ key: 'all', name: 'All Chains' }, ...Object.entries(SUPPORTED_CHAINS).map(([key, config]) => ({ key, name: config.name }))]
74
+ : Object.entries(SUPPORTED_CHAINS).map(([key, config]) => ({ key, name: config.name }));
75
+
76
+ const selectedLabel = value === 'all' ? 'ALL' : value.toUpperCase();
77
+
78
+ const padding = size === 'sm' ? 'px-1.5 py-1' : 'px-2 py-1.5';
79
+ const fontSize = size === 'sm' ? 'text-[8px]' : 'text-[9px]';
80
+
81
+ return (
82
+ <>
83
+ <button
84
+ ref={buttonRef}
85
+ onClick={() => !disabled && setOpen(!open)}
86
+ disabled={disabled}
87
+ className={`${padding} font-mono ${fontSize} tracking-wider flex items-center gap-1.5 transition-colors disabled:opacity-50`}
88
+ style={{
89
+ background: open ? 'var(--color-text, #0a0a0a)' : 'var(--color-background-alt, #f5f5f5)',
90
+ border: '1px solid var(--color-border, #e5e5e5)',
91
+ color: open ? 'var(--color-accent, #ccff00)' : 'var(--color-text, #0a0a0a)',
92
+ }}
93
+ >
94
+ {value !== 'all' && <ChainIcon chain={value} size={size === 'sm' ? 8 : 10} />}
95
+ <span>{selectedLabel}</span>
96
+ <ChevronDown size={size === 'sm' ? 8 : 10} className={`transition-transform ${open ? 'rotate-180' : ''}`} />
97
+ </button>
98
+
99
+ {open && mounted && createPortal(
100
+ <>
101
+ <div className="fixed inset-0 z-[9998]" onClick={() => setOpen(false)} />
102
+ <div
103
+ className="fixed z-[9999] shadow-lg"
104
+ style={{
105
+ top: position?.top,
106
+ left: position?.left,
107
+ minWidth: position?.width,
108
+ background: 'var(--color-surface, #fff)',
109
+ border: '1px solid var(--color-text, #0a0a0a)',
110
+ }}
111
+ >
112
+ {options.map((option) => (
113
+ <button
114
+ key={option.key}
115
+ onClick={() => {
116
+ onChange(option.key);
117
+ setOpen(false);
118
+ }}
119
+ className={`w-full text-left ${padding} font-mono ${fontSize} uppercase flex items-center gap-1.5 transition-colors`}
120
+ style={{
121
+ borderBottom: '1px solid var(--color-border-muted, #e5e5e5)',
122
+ background: option.key === value ? 'var(--color-background-alt, #f5f5f5)' : 'transparent',
123
+ color: 'var(--color-text, #0a0a0a)',
124
+ }}
125
+ onMouseEnter={(e) => {
126
+ e.currentTarget.style.background = 'var(--color-accent, #ccff00)';
127
+ }}
128
+ onMouseLeave={(e) => {
129
+ e.currentTarget.style.background = option.key === value ? 'var(--color-background-alt, #f5f5f5)' : 'transparent';
130
+ }}
131
+ >
132
+ {option.key !== 'all' && <ChainIcon chain={option.key} size={size === 'sm' ? 8 : 10} />}
133
+ {option.name}
134
+ </button>
135
+ ))}
136
+ </div>
137
+ </>,
138
+ document.body
139
+ )}
140
+ </>
141
+ );
142
+ }
143
+
144
+ export default ChainSelector;