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,455 @@
1
+ 'use client';
2
+
3
+ import React, { useState, useRef, useEffect, useCallback } from 'react';
4
+ import { Button, ConfirmationPopover } from '@/components/design-system';
5
+ import { api, Api } from '@/lib/api';
6
+ import { CredentialField } from './CredentialField';
7
+ import { TOTPDisplay } from './TOTPDisplay';
8
+ import { LargeTypeModal } from './LargeTypeModal';
9
+ import { CredentialShareModal } from './CredentialShareModal';
10
+ import { CredentialWalletWidget } from './CredentialWalletWidget';
11
+ import type { CredentialMeta, CredentialLifecycleFilter, WalletLinkMetaV1 } from './types';
12
+
13
+ interface CredentialDetailProps {
14
+ credential: CredentialMeta;
15
+ vaultName: string;
16
+ lifecycle: CredentialLifecycleFilter;
17
+ onEdit: () => void;
18
+ onDelete: () => void;
19
+ onRestore: () => void;
20
+ }
21
+
22
+ function formatDate(iso: string): string {
23
+ const d = new Date(iso);
24
+ return d.toLocaleDateString('en-US', { year: 'numeric', month: 'short', day: 'numeric' });
25
+ }
26
+
27
+ export const CredentialDetail: React.FC<CredentialDetailProps> = ({
28
+ credential,
29
+ vaultName,
30
+ lifecycle,
31
+ onEdit,
32
+ onDelete,
33
+ onRestore,
34
+ }) => {
35
+ const [largeTypeValue, setLargeTypeValue] = useState<string | null>(null);
36
+ const [deleteOpen, setDeleteOpen] = useState(false);
37
+ const [shareOpen, setShareOpen] = useState(false);
38
+ const [reauthMessage, setReauthMessage] = useState<string | null>(null);
39
+ const [reauthLoading, setReauthLoading] = useState(false);
40
+ const deleteAnchorRef = useRef<HTMLDivElement>(null);
41
+
42
+ const handleShowLargeType = (value: string) => {
43
+ setLargeTypeValue(value);
44
+ };
45
+
46
+ useEffect(() => {
47
+ setReauthMessage(null);
48
+ setReauthLoading(false);
49
+ }, [credential.id]);
50
+
51
+ const handleReauth = useCallback(async () => {
52
+ setReauthLoading(true);
53
+ setReauthMessage(null);
54
+ try {
55
+ const res = await api.post<{
56
+ success: boolean;
57
+ message: string;
58
+ authorization_endpoint: string | null;
59
+ token_endpoint: string | null;
60
+ needs_reauth: boolean;
61
+ reauth_reason: string | null;
62
+ }>(Api.Wallet, `/credentials/${credential.id}/reauth`);
63
+
64
+ setReauthMessage(
65
+ res.message
66
+ || `Re-auth info loaded. ${res.authorization_endpoint ? `Authorization: ${res.authorization_endpoint}` : 'Update credential manually after new tokens.'}`,
67
+ );
68
+ } catch (err) {
69
+ setReauthMessage(err instanceof Error ? err.message : 'Failed to start re-auth');
70
+ } finally {
71
+ setReauthLoading(false);
72
+ }
73
+ }, [credential.id]);
74
+
75
+ const typeLabelMap: Record<string, string> = {
76
+ login: 'Login',
77
+ card: 'Card',
78
+ note: 'Note',
79
+ apikey: 'API Key',
80
+ oauth2: 'OAuth2',
81
+ ssh: 'SSH Key',
82
+ gpg: 'GPG Key',
83
+ };
84
+ const typeLabel = typeLabelMap[credential.type] || credential.type.charAt(0).toUpperCase() + credential.type.slice(1);
85
+ const walletLink = (credential.meta.walletLink as WalletLinkMetaV1 | undefined);
86
+ const isActiveLifecycle = lifecycle === 'active';
87
+
88
+ const deleteButtonLabel = isActiveLifecycle
89
+ ? 'ARCHIVE'
90
+ : lifecycle === 'archive'
91
+ ? 'DELETE'
92
+ : 'PURGE';
93
+ const deleteConfirmLabel = lifecycle === 'recently_deleted' ? 'PURGE' : 'CONFIRM';
94
+ const deleteMessage = isActiveLifecycle
95
+ ? 'This credential will be moved to Archive.'
96
+ : lifecycle === 'archive'
97
+ ? 'This credential will be moved to Recently Deleted.'
98
+ : 'This credential will be permanently deleted now.';
99
+ const restoreLabel = lifecycle === 'archive' ? 'RESTORE' : 'RESTORE TO ARCHIVE';
100
+
101
+ return (
102
+ <div className="p-6 overflow-y-auto h-full flex flex-col">
103
+ {/* Header */}
104
+ <div className="mb-6">
105
+ <h2 className="font-mono font-bold text-lg text-[var(--color-text,#0a0a0a)]">
106
+ {credential.name}
107
+ </h2>
108
+ <div className="font-mono text-[9px] uppercase tracking-widest text-[var(--color-text-muted,#6b7280)] mt-1">
109
+ {typeLabel} &middot; {vaultName}
110
+ </div>
111
+ </div>
112
+
113
+ {/* Fields */}
114
+ <div className="flex-1 space-y-0.5">
115
+ {credential.type === 'login' && (
116
+ <>
117
+ <CredentialField
118
+ label="URL"
119
+ value={credential.meta.url}
120
+ credentialId={credential.id}
121
+ fieldKey="url"
122
+ isSensitive={false}
123
+ onShowLargeType={handleShowLargeType}
124
+ />
125
+ <CredentialField
126
+ label="Username"
127
+ value={credential.meta.username}
128
+ credentialId={credential.id}
129
+ fieldKey="username"
130
+ isSensitive={false}
131
+ onShowLargeType={handleShowLargeType}
132
+ />
133
+ <CredentialField
134
+ label="Password"
135
+ credentialId={credential.id}
136
+ fieldKey="password"
137
+ isSensitive={true}
138
+ onShowLargeType={handleShowLargeType}
139
+ />
140
+ {isActiveLifecycle && <TOTPDisplay credentialId={credential.id} />}
141
+ <CredentialField
142
+ label="Notes"
143
+ credentialId={credential.id}
144
+ fieldKey="notes"
145
+ isSensitive={true}
146
+ onShowLargeType={handleShowLargeType}
147
+ />
148
+ </>
149
+ )}
150
+
151
+ {credential.type === 'card' && (
152
+ <>
153
+ <CredentialField
154
+ label="Cardholder"
155
+ value={credential.meta.cardholder}
156
+ credentialId={credential.id}
157
+ fieldKey="cardholder"
158
+ isSensitive={false}
159
+ onShowLargeType={handleShowLargeType}
160
+ />
161
+ <CredentialField
162
+ label="Brand"
163
+ value={credential.meta.brand}
164
+ credentialId={credential.id}
165
+ fieldKey="brand"
166
+ isSensitive={false}
167
+ onShowLargeType={handleShowLargeType}
168
+ />
169
+ <CredentialField
170
+ label="Number"
171
+ credentialId={credential.id}
172
+ fieldKey="number"
173
+ isSensitive={true}
174
+ onShowLargeType={handleShowLargeType}
175
+ />
176
+ <CredentialField
177
+ label="Expiry"
178
+ credentialId={credential.id}
179
+ fieldKey="expiry"
180
+ isSensitive={true}
181
+ onShowLargeType={handleShowLargeType}
182
+ />
183
+ <CredentialField
184
+ label="CVV"
185
+ credentialId={credential.id}
186
+ fieldKey="cvv"
187
+ isSensitive={true}
188
+ onShowLargeType={handleShowLargeType}
189
+ />
190
+ <CredentialField
191
+ label="Billing ZIP"
192
+ value={credential.meta.billing_zip as string | undefined}
193
+ credentialId={credential.id}
194
+ fieldKey="billing_zip"
195
+ isSensitive={false}
196
+ onShowLargeType={handleShowLargeType}
197
+ />
198
+ <CredentialField
199
+ label="Notes"
200
+ credentialId={credential.id}
201
+ fieldKey="notes"
202
+ isSensitive={true}
203
+ onShowLargeType={handleShowLargeType}
204
+ />
205
+ </>
206
+ )}
207
+
208
+ {credential.type === 'note' && (
209
+ <CredentialField
210
+ label="Content"
211
+ credentialId={credential.id}
212
+ fieldKey="content"
213
+ isSensitive={true}
214
+ onShowLargeType={handleShowLargeType}
215
+ sensitiveInteractionMode="markdown-hover-copy"
216
+ />
217
+ )}
218
+
219
+ {credential.type === 'apikey' && (
220
+ <>
221
+ <CredentialField
222
+ label="Key"
223
+ value={credential.meta.key as string | undefined}
224
+ credentialId={credential.id}
225
+ fieldKey="key"
226
+ isSensitive={false}
227
+ onShowLargeType={handleShowLargeType}
228
+ />
229
+ <CredentialField
230
+ label="Value"
231
+ credentialId={credential.id}
232
+ fieldKey="value"
233
+ isSensitive={true}
234
+ onShowLargeType={handleShowLargeType}
235
+ />
236
+ </>
237
+ )}
238
+
239
+
240
+ {credential.type === 'ssh' && (
241
+ <>
242
+ <CredentialField label="Fingerprint" value={credential.meta.fingerprint as string | undefined} credentialId={credential.id} fieldKey="fingerprint" isSensitive={false} onShowLargeType={handleShowLargeType} />
243
+ <CredentialField label="Key Type" value={credential.meta.key_type as string | undefined} credentialId={credential.id} fieldKey="key_type" isSensitive={false} onShowLargeType={handleShowLargeType} />
244
+ <CredentialField label="Hosts" value={Array.isArray(credential.meta.hosts) ? (credential.meta.hosts as string[]).join(', ') : undefined} credentialId={credential.id} fieldKey="hosts" isSensitive={false} onShowLargeType={handleShowLargeType} />
245
+ <CredentialField label="Public Key" value={credential.meta.public_key as string | undefined} credentialId={credential.id} fieldKey="public_key" isSensitive={false} onShowLargeType={handleShowLargeType} />
246
+ <CredentialField label="Private Key" credentialId={credential.id} fieldKey="private_key" isSensitive={true} onShowLargeType={handleShowLargeType} />
247
+ <CredentialField label="Passphrase" credentialId={credential.id} fieldKey="passphrase" isSensitive={true} onShowLargeType={handleShowLargeType} />
248
+ </>
249
+ )}
250
+
251
+ {credential.type === 'gpg' && (
252
+ <>
253
+ <CredentialField label="Fingerprint" value={credential.meta.fingerprint as string | undefined} credentialId={credential.id} fieldKey="fingerprint" isSensitive={false} onShowLargeType={handleShowLargeType} />
254
+ <CredentialField label="Key ID" value={credential.meta.key_id as string | undefined} credentialId={credential.id} fieldKey="key_id" isSensitive={false} onShowLargeType={handleShowLargeType} />
255
+ <CredentialField label="UID Email" value={credential.meta.uid_email as string | undefined} credentialId={credential.id} fieldKey="uid_email" isSensitive={false} onShowLargeType={handleShowLargeType} />
256
+ <CredentialField label="Expires At" value={credential.meta.expires_at as string | undefined} credentialId={credential.id} fieldKey="expires_at" isSensitive={false} onShowLargeType={handleShowLargeType} />
257
+ <CredentialField label="Public Key" value={credential.meta.public_key as string | undefined} credentialId={credential.id} fieldKey="public_key" isSensitive={false} onShowLargeType={handleShowLargeType} />
258
+ <CredentialField label="Private Key" credentialId={credential.id} fieldKey="private_key" isSensitive={true} onShowLargeType={handleShowLargeType} />
259
+ </>
260
+ )}
261
+ {credential.type === 'oauth2' && (
262
+ <>
263
+ <CredentialField
264
+ label="Token Endpoint"
265
+ value={credential.meta.token_endpoint as string | undefined}
266
+ credentialId={credential.id}
267
+ fieldKey="token_endpoint"
268
+ isSensitive={false}
269
+ onShowLargeType={handleShowLargeType}
270
+ />
271
+ <CredentialField
272
+ label="Scopes"
273
+ value={credential.meta.scopes as string | undefined}
274
+ credentialId={credential.id}
275
+ fieldKey="scopes"
276
+ isSensitive={false}
277
+ onShowLargeType={handleShowLargeType}
278
+ />
279
+ <CredentialField
280
+ label="Auth Method"
281
+ value={credential.meta.auth_method as string | undefined}
282
+ credentialId={credential.id}
283
+ fieldKey="auth_method"
284
+ isSensitive={false}
285
+ onShowLargeType={handleShowLargeType}
286
+ />
287
+ {credential.meta.needs_reauth && (
288
+ <div className="px-2 py-2 my-1 border border-red-500/30 bg-red-500/10 text-red-400 font-mono text-[10px]">
289
+ ⚠️ Re-authentication required
290
+ {!!credential.meta.reauth_reason && (
291
+ <span className="block text-[9px] text-red-400/70 mt-0.5">
292
+ {String(credential.meta.reauth_reason as string)}
293
+ </span>
294
+ )}
295
+ <button
296
+ className="mt-1 px-2 py-0.5 bg-red-500/20 hover:bg-red-500/30 text-red-300 text-[9px] font-bold uppercase tracking-wider disabled:opacity-50"
297
+ onClick={handleReauth}
298
+ disabled={reauthLoading}
299
+ >
300
+ {reauthLoading ? 'Loading…' : 'Re-authenticate'}
301
+ </button>
302
+ {reauthMessage && (
303
+ <div className="mt-1 text-[9px] text-red-300/90 break-words">
304
+ {reauthMessage}
305
+ </div>
306
+ )}
307
+ </div>
308
+ )}
309
+ {credential.meta.last_refreshed && (
310
+ <div className="px-1 py-1">
311
+ <span className="font-mono text-[9px] font-bold uppercase tracking-widest text-[var(--color-text-muted,#6b7280)]">
312
+ Last Refreshed:{' '}
313
+ </span>
314
+ <span className="font-mono text-[10px] text-[var(--color-text-muted,#6b7280)]">
315
+ {new Date(String(credential.meta.last_refreshed)).toLocaleString()}
316
+ </span>
317
+ </div>
318
+ )}
319
+ {(() => {
320
+ const expiresAt = credential.meta.expires_at as number | null | undefined;
321
+ const now = Math.floor(Date.now() / 1000);
322
+ const isExpired = !expiresAt || now >= expiresAt;
323
+ const expiryLabel = !expiresAt
324
+ ? 'No token yet'
325
+ : isExpired
326
+ ? 'Expired — will auto-refresh on next read'
327
+ : `Valid for ${Math.floor((expiresAt - now) / 60)}m`;
328
+ return (
329
+ <div className="px-1 py-2">
330
+ <span className="font-mono text-[9px] font-bold uppercase tracking-widest text-[var(--color-text-muted,#6b7280)]">
331
+ Token Status:{' '}
332
+ </span>
333
+ <span
334
+ className="font-mono text-[10px] font-bold"
335
+ style={{ color: isExpired ? '#ef4444' : '#22c55e' }}
336
+ >
337
+ {expiryLabel}
338
+ </span>
339
+ </div>
340
+ );
341
+ })()}
342
+ <CredentialField
343
+ label="Access Token"
344
+ credentialId={credential.id}
345
+ fieldKey="access_token"
346
+ isSensitive={true}
347
+ onShowLargeType={handleShowLargeType}
348
+ />
349
+ <CredentialField
350
+ label="Refresh Token"
351
+ credentialId={credential.id}
352
+ fieldKey="refresh_token"
353
+ isSensitive={true}
354
+ onShowLargeType={handleShowLargeType}
355
+ />
356
+ <CredentialField
357
+ label="Client ID"
358
+ credentialId={credential.id}
359
+ fieldKey="client_id"
360
+ isSensitive={true}
361
+ onShowLargeType={handleShowLargeType}
362
+ />
363
+ <CredentialField
364
+ label="Client Secret"
365
+ credentialId={credential.id}
366
+ fieldKey="client_secret"
367
+ isSensitive={true}
368
+ onShowLargeType={handleShowLargeType}
369
+ />
370
+ </>
371
+ )}
372
+ </div>
373
+
374
+ {walletLink && <CredentialWalletWidget walletLink={walletLink} />}
375
+
376
+ {/* Tags */}
377
+ {credential.meta.tags && credential.meta.tags.length > 0 && (
378
+ <div className="mt-4 flex flex-wrap gap-1.5">
379
+ {credential.meta.tags.map((tag) => (
380
+ <span
381
+ key={tag}
382
+ className="inline-block font-mono text-[9px] px-2 py-0.5 bg-[var(--color-accent,#ccff00)]/10 text-[var(--color-text-muted,#6b7280)]"
383
+ >
384
+ {tag}
385
+ </span>
386
+ ))}
387
+ </div>
388
+ )}
389
+
390
+ {/* Metadata footer */}
391
+ <div className="mt-4 pt-3 border-t border-[var(--color-border,#d4d4d8)]">
392
+ <div className="font-mono text-[8px] text-[var(--color-text-faint,#9ca3af)] space-y-0.5">
393
+ <div>Created {formatDate(credential.createdAt)}</div>
394
+ <div>Updated {formatDate(credential.updatedAt)}</div>
395
+ {credential.archivedAt && <div>Archived {formatDate(credential.archivedAt)}</div>}
396
+ {credential.deletedAt && <div>Deleted {formatDate(credential.deletedAt)}</div>}
397
+ <div>Vault: {vaultName}</div>
398
+ </div>
399
+ </div>
400
+
401
+ {/* Actions bar */}
402
+ <div className="mt-4 flex gap-2">
403
+ {isActiveLifecycle && (
404
+ <Button variant="secondary" size="sm" onClick={() => setShareOpen(true)}>
405
+ SHARE
406
+ </Button>
407
+ )}
408
+ {isActiveLifecycle && (
409
+ <Button variant="secondary" size="sm" onClick={onEdit}>
410
+ EDIT
411
+ </Button>
412
+ )}
413
+ {!isActiveLifecycle && (
414
+ <Button variant="secondary" size="sm" onClick={onRestore}>
415
+ {restoreLabel}
416
+ </Button>
417
+ )}
418
+ <div ref={deleteAnchorRef} className="relative">
419
+ <Button
420
+ variant="danger"
421
+ size="sm"
422
+ onClick={() => setDeleteOpen(true)}
423
+ >
424
+ {deleteButtonLabel}
425
+ </Button>
426
+ <ConfirmationPopover
427
+ isOpen={deleteOpen}
428
+ onClose={() => setDeleteOpen(false)}
429
+ onConfirm={() => {
430
+ setDeleteOpen(false);
431
+ onDelete();
432
+ }}
433
+ message={deleteMessage}
434
+ confirmLabel={deleteConfirmLabel}
435
+ anchorEl={deleteAnchorRef.current}
436
+ />
437
+ </div>
438
+ </div>
439
+
440
+ {/* Large type modal */}
441
+ <LargeTypeModal
442
+ isOpen={largeTypeValue != null}
443
+ onClose={() => setLargeTypeValue(null)}
444
+ value={largeTypeValue ?? ''}
445
+ />
446
+
447
+ <CredentialShareModal
448
+ isOpen={shareOpen}
449
+ onClose={() => setShareOpen(false)}
450
+ credentialId={credential.id}
451
+ credentialName={credential.name}
452
+ />
453
+ </div>
454
+ );
455
+ };
@@ -0,0 +1,55 @@
1
+ 'use client';
2
+
3
+ import React from 'react';
4
+ import { ShieldCheck, Plus } from 'lucide-react';
5
+ import { Button } from '@/components/design-system';
6
+
7
+ interface CredentialEmptyProps {
8
+ variant: 'no-selection' | 'empty-vault' | 'empty-lifecycle';
9
+ onAdd?: () => void;
10
+ }
11
+
12
+ export const CredentialEmpty: React.FC<CredentialEmptyProps> = ({ variant, onAdd }) => {
13
+ if (variant === 'no-selection') {
14
+ return (
15
+ <div className="flex-1 flex items-center justify-center h-full">
16
+ <div className="text-center">
17
+ <ShieldCheck size={32} className="mx-auto mb-3 text-[var(--color-text-faint,#9ca3af)]" />
18
+ <div className="font-mono text-[11px] tracking-widest uppercase text-[var(--color-text-muted,#6b7280)]">
19
+ Select a credential from the list
20
+ </div>
21
+ </div>
22
+ </div>
23
+ );
24
+ }
25
+
26
+ if (variant === 'empty-lifecycle') {
27
+ return (
28
+ <div className="flex-1 flex items-center justify-center h-full">
29
+ <div className="text-center">
30
+ <ShieldCheck size={32} className="mx-auto mb-3 text-[var(--color-text-faint,#9ca3af)]" />
31
+ <div className="font-mono text-[11px] tracking-widest uppercase text-[var(--color-text-muted,#6b7280)] mb-1">
32
+ No credentials in this section
33
+ </div>
34
+ </div>
35
+ </div>
36
+ );
37
+ }
38
+
39
+ return (
40
+ <div className="flex-1 flex items-center justify-center h-full">
41
+ <div className="text-center">
42
+ <ShieldCheck size={32} className="mx-auto mb-3 text-[var(--color-text-faint,#9ca3af)]" />
43
+ <div className="font-mono text-[11px] tracking-widest uppercase text-[var(--color-text-muted,#6b7280)] mb-1">
44
+ Your vault is empty
45
+ </div>
46
+ <div className="font-mono text-[9px] text-[var(--color-text-faint,#9ca3af)] mb-4">
47
+ Create your first credential
48
+ </div>
49
+ <Button size="sm" onClick={onAdd} icon={<Plus size={10} />}>
50
+ ADD
51
+ </Button>
52
+ </div>
53
+ </div>
54
+ );
55
+ };