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,219 @@
1
+ import { prisma } from './db';
2
+ import { loadConfig } from './config';
3
+
4
+ interface NotificationAction {
5
+ id: string;
6
+ label: string;
7
+ type: 'primary' | 'secondary' | 'danger' | 'link';
8
+ action: 'api' | 'navigate' | 'dismiss';
9
+ endpoint?: string;
10
+ method?: string;
11
+ body?: Record<string, unknown>;
12
+ href?: string;
13
+ external?: boolean;
14
+ }
15
+
16
+ interface CreateNotificationParams {
17
+ type: 'pending_approval' | 'info' | 'warning' | 'success' | 'error' | 'system';
18
+ category?: 'transaction' | 'security' | 'token' | 'wallet' | 'general';
19
+ title: string;
20
+ message: string;
21
+ actions?: NotificationAction[];
22
+ metadata?: Record<string, unknown>;
23
+ humanActionId?: string;
24
+ expiresAt?: Date;
25
+ source?: 'system' | 'agent' | 'user';
26
+ agentId?: string;
27
+ }
28
+
29
+ export async function createNotification(params: CreateNotificationParams) {
30
+ return prisma.notification.create({
31
+ data: {
32
+ type: params.type,
33
+ category: params.category,
34
+ title: params.title,
35
+ message: params.message,
36
+ actions: params.actions ? JSON.stringify(params.actions) : null,
37
+ metadata: params.metadata ? JSON.stringify(params.metadata) : null,
38
+ humanActionId: params.humanActionId,
39
+ expiresAt: params.expiresAt,
40
+ source: params.source || 'system',
41
+ agentId: params.agentId,
42
+ },
43
+ });
44
+ }
45
+
46
+ export async function createHumanActionNotification(request: {
47
+ id: string;
48
+ type: string;
49
+ fromTier: string;
50
+ toAddress: string | null;
51
+ amount: string | null;
52
+ chain: string;
53
+ metadata?: string | null;
54
+ }) {
55
+ const config = loadConfig();
56
+ const explorer = config.chains[request.chain]?.explorer || 'https://basescan.org';
57
+
58
+ const typeLabels: Record<string, string> = {
59
+ fund: 'Fund Request',
60
+ send: 'Send Request',
61
+ agent_access: 'Agent Access Request',
62
+ auth: 'Agent Auth Request',
63
+ action: 'Action Request',
64
+ notify: 'Token Alert',
65
+ };
66
+
67
+ const title = typeLabels[request.type] || 'Pending Request';
68
+ const shortAddr = request.toAddress
69
+ ? `${request.toAddress.slice(0, 6)}...${request.toAddress.slice(-4)}`
70
+ : 'Unknown';
71
+
72
+ let message: string;
73
+ if (request.type === 'agent_access' || request.type === 'auth') {
74
+ // Parse metadata to get agent info
75
+ let agentId = 'Unknown Agent';
76
+ let limit = '0.1';
77
+ if (request.metadata) {
78
+ try {
79
+ const meta = JSON.parse(request.metadata);
80
+ agentId = meta.agentId || agentId;
81
+ limit = meta.limit?.toString() || limit;
82
+ } catch {
83
+ // ignore
84
+ }
85
+ }
86
+ message = `${agentId} requesting ${limit} ETH access`;
87
+ } else if (request.type === 'action') {
88
+ let summary = 'Action pending';
89
+ let callerAgentId = 'app';
90
+ if (request.metadata) {
91
+ try {
92
+ const meta = JSON.parse(request.metadata);
93
+ const vs = meta.verifiedSummary;
94
+ summary = vs?.oneLiner || meta.summary || summary;
95
+ callerAgentId = meta.agentId || callerAgentId;
96
+ } catch {}
97
+ }
98
+ message = `${callerAgentId}: ${summary}`;
99
+ } else if (request.amount) {
100
+ message = `${request.amount} ETH to ${shortAddr}`;
101
+ } else {
102
+ message = `Request to ${shortAddr}`;
103
+ }
104
+
105
+ const actions: NotificationAction[] = [
106
+ {
107
+ id: 'approve',
108
+ label: 'APPROVE',
109
+ type: 'primary',
110
+ action: 'api',
111
+ endpoint: `/actions/${request.id}/resolve`,
112
+ method: 'POST',
113
+ body: { approved: true },
114
+ },
115
+ {
116
+ id: 'reject',
117
+ label: 'REJECT',
118
+ type: 'danger',
119
+ action: 'api',
120
+ endpoint: `/actions/${request.id}/resolve`,
121
+ method: 'POST',
122
+ body: { approved: false },
123
+ },
124
+ ];
125
+
126
+ const metadata: Record<string, unknown> = {
127
+ requestType: request.type,
128
+ fromTier: request.fromTier,
129
+ toAddress: request.toAddress,
130
+ amount: request.amount,
131
+ chain: request.chain,
132
+ explorer,
133
+ };
134
+
135
+ if (request.metadata) {
136
+ try {
137
+ const parsed = JSON.parse(request.metadata);
138
+ Object.assign(metadata, parsed);
139
+ } catch {
140
+ // ignore parse errors
141
+ }
142
+ }
143
+
144
+ return createNotification({
145
+ type: 'pending_approval',
146
+ category: 'transaction',
147
+ title,
148
+ message,
149
+ actions,
150
+ metadata,
151
+ humanActionId: request.id,
152
+ source: 'system',
153
+ });
154
+ }
155
+
156
+ export async function resolveNotificationForRequest(
157
+ requestId: string,
158
+ resolution: 'approved' | 'rejected',
159
+ resultData?: { txHash?: string; explorer?: string }
160
+ ) {
161
+ // Find and dismiss the pending notification
162
+ const notification = await prisma.notification.findFirst({
163
+ where: { humanActionId: requestId },
164
+ });
165
+
166
+ if (notification) {
167
+ await prisma.notification.update({
168
+ where: { id: notification.id },
169
+ data: { dismissed: true, read: true },
170
+ });
171
+ }
172
+
173
+ // Create a success/info notification about the resolution
174
+ if (resolution === 'approved' && resultData?.txHash) {
175
+ const actions: NotificationAction[] = [];
176
+ if (resultData.explorer) {
177
+ actions.push({
178
+ id: 'view_tx',
179
+ label: 'VIEW TX',
180
+ type: 'link',
181
+ action: 'navigate',
182
+ href: `${resultData.explorer}/tx/${resultData.txHash}`,
183
+ external: true,
184
+ });
185
+ }
186
+ actions.push({
187
+ id: 'dismiss',
188
+ label: 'DISMISS',
189
+ type: 'secondary',
190
+ action: 'dismiss',
191
+ });
192
+
193
+ await createNotification({
194
+ type: 'success',
195
+ category: 'transaction',
196
+ title: 'Transaction Approved',
197
+ message: `TX: ${resultData.txHash.slice(0, 10)}...${resultData.txHash.slice(-6)}`,
198
+ actions,
199
+ metadata: { txHash: resultData.txHash, explorer: resultData.explorer },
200
+ source: 'system',
201
+ });
202
+ } else if (resolution === 'rejected') {
203
+ await createNotification({
204
+ type: 'info',
205
+ category: 'transaction',
206
+ title: 'Request Rejected',
207
+ message: 'The pending request was rejected.',
208
+ actions: [
209
+ {
210
+ id: 'dismiss',
211
+ label: 'DISMISS',
212
+ type: 'secondary',
213
+ action: 'dismiss',
214
+ },
215
+ ],
216
+ source: 'system',
217
+ });
218
+ }
219
+ }
@@ -0,0 +1,241 @@
1
+ /**
2
+ * OAuth2 Refresh Token Logic
3
+ * ==========================
4
+ *
5
+ * Handles transparent access_token refresh for oauth2 credential type.
6
+ * Called during credential read path — if access_token is expired,
7
+ * refreshes via token_endpoint and updates stored credential.
8
+ */
9
+
10
+ import { CredentialFile, CredentialField } from '../types';
11
+ import { getCredential, readCredentialSecrets, updateCredential } from './credentials';
12
+
13
+ /** Buffer in seconds before expiry to trigger refresh */
14
+ const EXPIRY_BUFFER_SECONDS = 60;
15
+
16
+ /** Minimum seconds between refreshes for the same credential */
17
+ const REFRESH_COOLDOWN_SECONDS = 5;
18
+
19
+ /** Track last refresh time per credential for rate limiting */
20
+ const lastRefreshTime = new Map<string, number>();
21
+
22
+ export interface OAuth2RefreshResult {
23
+ accessToken: string;
24
+ refreshToken?: string;
25
+ expiresIn?: number;
26
+ tokenType?: string;
27
+ }
28
+
29
+ /**
30
+ * Check if an oauth2 credential's access_token is expired or nearly expired.
31
+ */
32
+ export function isTokenExpired(credential: CredentialFile): boolean {
33
+ const rawExpiresAt = credential.meta.expires_at as number | string | null | undefined;
34
+ const expiresAt =
35
+ typeof rawExpiresAt === 'string' ? Number(rawExpiresAt) : rawExpiresAt;
36
+
37
+ if (!Number.isFinite(expiresAt)) return true; // Missing/invalid expiry info → assume expired
38
+ if (expiresAt <= 0) return true;
39
+
40
+ const now = Math.floor(Date.now() / 1000);
41
+ return now >= expiresAt - EXPIRY_BUFFER_SECONDS;
42
+ }
43
+
44
+ /**
45
+ * Check if refresh is rate-limited for this credential.
46
+ */
47
+ export function isRefreshRateLimited(credentialId: string): boolean {
48
+ const last = lastRefreshTime.get(credentialId);
49
+ if (!last) return false;
50
+ return (Date.now() - last) < REFRESH_COOLDOWN_SECONDS * 1000;
51
+ }
52
+
53
+ /**
54
+ * Perform OAuth2 token refresh against the token_endpoint.
55
+ */
56
+ export async function refreshAccessToken(
57
+ tokenEndpoint: string,
58
+ refreshToken: string,
59
+ clientId: string,
60
+ clientSecret: string,
61
+ authMethod: string = 'client_secret_post',
62
+ ): Promise<OAuth2RefreshResult> {
63
+ const params = new URLSearchParams({
64
+ grant_type: 'refresh_token',
65
+ refresh_token: refreshToken,
66
+ });
67
+
68
+ const headers: Record<string, string> = {
69
+ 'Content-Type': 'application/x-www-form-urlencoded',
70
+ Accept: 'application/json',
71
+ };
72
+
73
+ if (authMethod === 'client_secret_basic') {
74
+ const encoded = Buffer.from(`${clientId}:${clientSecret}`).toString('base64');
75
+ headers['Authorization'] = `Basic ${encoded}`;
76
+ } else {
77
+ // client_secret_post (default)
78
+ params.set('client_id', clientId);
79
+ params.set('client_secret', clientSecret);
80
+ }
81
+
82
+ const response = await fetch(tokenEndpoint, {
83
+ method: 'POST',
84
+ headers,
85
+ body: params.toString(),
86
+ });
87
+
88
+ if (!response.ok) {
89
+ const body = await response.text();
90
+ const status = response.status;
91
+ const loweredBody = body.toLowerCase();
92
+ const parsed = (() => {
93
+ try {
94
+ return JSON.parse(body) as Record<string, unknown>;
95
+ } catch {
96
+ return null;
97
+ }
98
+ })();
99
+ const hasInvalidGrant =
100
+ status === 400 &&
101
+ (loweredBody.includes('invalid_grant') ||
102
+ (typeof parsed?.error === 'string' && parsed.error.toLowerCase() === 'invalid_grant'));
103
+
104
+ // 401 or 400 with invalid_grant means refresh token is revoked
105
+ if (status === 401 || hasInvalidGrant) {
106
+ const err = new Error(`OAuth2 refresh token revoked (${status}): ${body}`);
107
+ (err as any).revoked = true;
108
+ (err as any).statusCode = status;
109
+ throw err;
110
+ }
111
+ throw new Error(`OAuth2 refresh failed (${status}): ${body}`);
112
+ }
113
+
114
+ const data = await response.json() as Record<string, unknown>;
115
+
116
+ if (typeof data.access_token !== 'string') {
117
+ throw new Error('OAuth2 refresh response missing access_token');
118
+ }
119
+
120
+ return {
121
+ accessToken: data.access_token,
122
+ refreshToken: typeof data.refresh_token === 'string' ? data.refresh_token : undefined,
123
+ expiresIn: typeof data.expires_in === 'number' ? data.expires_in : undefined,
124
+ tokenType: typeof data.token_type === 'string' ? data.token_type : undefined,
125
+ };
126
+ }
127
+
128
+ /**
129
+ * Attempt a fetch-based refresh with one retry on transient network errors.
130
+ */
131
+ async function refreshWithRetry(
132
+ tokenEndpoint: string,
133
+ refreshToken: string,
134
+ clientId: string,
135
+ clientSecret: string,
136
+ authMethod: string,
137
+ ): Promise<OAuth2RefreshResult> {
138
+ try {
139
+ return await refreshAccessToken(tokenEndpoint, refreshToken, clientId, clientSecret, authMethod);
140
+ } catch (err: any) {
141
+ // Don't retry on revoked tokens or HTTP errors — only network failures
142
+ if (err.revoked) throw err;
143
+ if (err.message?.startsWith('OAuth2 refresh failed')) throw err;
144
+ if (err.message?.startsWith('OAuth2 refresh response')) throw err;
145
+ // Transient network error — retry once
146
+ return await refreshAccessToken(tokenEndpoint, refreshToken, clientId, clientSecret, authMethod);
147
+ }
148
+ }
149
+
150
+ /**
151
+ * Read oauth2 credential secrets, auto-refreshing if expired.
152
+ * Returns the (possibly refreshed) sensitive fields.
153
+ */
154
+ export async function readOAuth2SecretsWithRefresh(
155
+ credentialId: string,
156
+ ): Promise<CredentialField[]> {
157
+ const credential = getCredential(credentialId);
158
+ if (!credential) throw new Error(`Credential not found: ${credentialId}`);
159
+ if (credential.type !== 'oauth2') throw new Error('Not an oauth2 credential');
160
+
161
+ let fields = readCredentialSecrets(credentialId);
162
+
163
+ if (isTokenExpired(credential)) {
164
+ // Rate limit check
165
+ if (isRefreshRateLimited(credentialId)) {
166
+ // Return current fields without refreshing
167
+ return fields;
168
+ }
169
+
170
+ const fieldMap = new Map(fields.map(f => [f.key, f.value]));
171
+ const refreshToken = fieldMap.get('refresh_token');
172
+ const clientId = fieldMap.get('client_id');
173
+ const clientSecret = fieldMap.get('client_secret');
174
+ const tokenEndpoint = credential.meta.token_endpoint as string;
175
+ const authMethod = (credential.meta.auth_method as string) || 'client_secret_post';
176
+
177
+ if (!refreshToken || !clientId || !clientSecret || !tokenEndpoint) {
178
+ throw new Error('OAuth2 credential missing required fields for refresh');
179
+ }
180
+
181
+ try {
182
+ const result = await refreshWithRetry(
183
+ tokenEndpoint,
184
+ refreshToken,
185
+ clientId,
186
+ clientSecret,
187
+ authMethod,
188
+ );
189
+
190
+ // Record refresh time for rate limiting
191
+ lastRefreshTime.set(credentialId, Date.now());
192
+
193
+ // Update fields with new tokens
194
+ const updatedFields = fields.map(f => {
195
+ if (f.key === 'access_token') return { ...f, value: result.accessToken };
196
+ if (f.key === 'refresh_token' && result.refreshToken) return { ...f, value: result.refreshToken };
197
+ return f;
198
+ });
199
+
200
+ // Update expires_at and last_refreshed in meta
201
+ const newExpiresAt = result.expiresIn
202
+ ? Math.floor(Date.now() / 1000) + result.expiresIn
203
+ : null;
204
+
205
+ updateCredential(credentialId, {
206
+ meta: {
207
+ ...credential.meta,
208
+ needs_reauth: false,
209
+ reauth_reason: null,
210
+ expires_at: newExpiresAt,
211
+ last_refreshed: new Date().toISOString(),
212
+ },
213
+ sensitiveFields: updatedFields,
214
+ });
215
+
216
+ fields = updatedFields;
217
+ } catch (err: any) {
218
+ if (err.revoked) {
219
+ // Mark credential as needing re-authentication
220
+ updateCredential(credentialId, {
221
+ meta: {
222
+ ...credential.meta,
223
+ needs_reauth: true,
224
+ reauth_reason: err.message,
225
+ last_refreshed: new Date().toISOString(),
226
+ },
227
+ });
228
+ throw new Error(`OAuth2 token revoked — credential ${credentialId} marked as needs_reauth`);
229
+ }
230
+ throw err;
231
+ }
232
+ }
233
+
234
+ return fields;
235
+ }
236
+
237
+ /**
238
+ * Default fields to exclude from oauth2 credential reads by agents.
239
+ * Agents only need the access_token — never the refresh machinery.
240
+ */
241
+ export const OAUTH2_DEFAULT_EXCLUDE_FIELDS = ['refresh_token', 'client_secret', 'client_id', 'token_endpoint'];
@@ -0,0 +1,54 @@
1
+ import { CredentialField } from '../types';
2
+ import { createCredential, listCredentials } from './credentials';
3
+
4
+ export const OURSECRET_NOTE_NAME = 'OURSECRET';
5
+
6
+ export const OURSECRET_NOTE_CONTENT = [
7
+ 'Aura quickstart',
8
+ '',
9
+ 'Start here:',
10
+ '- README.md',
11
+ '- docs/SETUP.md',
12
+ '',
13
+ 'Developers:',
14
+ '- docs/DEVELOPING-APPS.md',
15
+ '- docs/API.md',
16
+ '- docs/CLI.md',
17
+ '',
18
+ 'Agents:',
19
+ '- docs/MCP.md',
20
+ '- docs/agent-auth.md',
21
+ ].join('\n');
22
+
23
+ function buildOurSecretFields(): CredentialField[] {
24
+ return [
25
+ {
26
+ key: 'content',
27
+ value: OURSECRET_NOTE_CONTENT,
28
+ type: 'text',
29
+ sensitive: true,
30
+ },
31
+ ];
32
+ }
33
+
34
+ export function ensureOurSecretForVault(vaultId: string): { created: boolean } {
35
+ const alreadyExists = listCredentials({ vaultId, type: 'note' })
36
+ .some((credential) => credential.name.trim().toUpperCase() === OURSECRET_NOTE_NAME);
37
+
38
+ if (alreadyExists) {
39
+ return { created: false };
40
+ }
41
+
42
+ createCredential(
43
+ vaultId,
44
+ 'note',
45
+ OURSECRET_NOTE_NAME,
46
+ {
47
+ tags: ['onboarding', 'docs', 'agents'],
48
+ system: true,
49
+ },
50
+ buildOurSecretFields(),
51
+ );
52
+
53
+ return { created: true };
54
+ }