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
package/src/lib/api.ts ADDED
@@ -0,0 +1,449 @@
1
+ /**
2
+ * Unified API client for calling both Express and Next.js backends
3
+ *
4
+ * Usage:
5
+ * import { api, Api, unlockWallet, setupWallet } from '@/lib/api';
6
+ *
7
+ * // Wallet operations (Express :4242)
8
+ * const wallets = await api.get(Api.Wallet, '/wallets');
9
+ * await api.post(Api.Wallet, '/wallet/rename', { address, name });
10
+ *
11
+ * // Encrypted unlock/setup (password encrypted with server's RSA key)
12
+ * const result = await unlockWallet(password);
13
+ * const result = await setupWallet(password);
14
+ *
15
+ * // Workspace operations (Next.js :4747)
16
+ * const workspaces = await api.get(Api.Workspace, '/workspace');
17
+ *
18
+ */
19
+
20
+ import { encryptPassword, getTokenMintPubkey } from './crypto';
21
+
22
+ // Derive ports from dashboard port at runtime (no env var coordination needed)
23
+ // Convention: wallet = dashboard - 505, WS = dashboard + 1
24
+ // Default: 4747 → wallet 4242, WS 4748
25
+ // Sandbox: 5747 → wallet 5242, WS 5748
26
+ const IS_LOCAL = typeof window !== 'undefined'
27
+ && (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1');
28
+
29
+ const DASHBOARD_PORT_NUM = typeof window !== 'undefined'
30
+ ? parseInt(window.location.port || '4747', 10)
31
+ : parseInt(process.env.DASHBOARD_PORT || '4747', 10);
32
+
33
+ // When accessed via tunnel (non-localhost), use sibling subdomains over HTTPS
34
+ // e.g. wallet.auramaxx.xyz → wallet-api.auramaxx.xyz for Express
35
+ const EXPRESS_URL = typeof window !== 'undefined'
36
+ ? IS_LOCAL
37
+ ? `http://${window.location.hostname}:${DASHBOARD_PORT_NUM - 505}`
38
+ : `https://wallet-api.${window.location.hostname.split('.').slice(1).join('.')}`
39
+ : `http://localhost:${DASHBOARD_PORT_NUM - 505}`;
40
+
41
+ const NEXTJS_URL = typeof window !== 'undefined'
42
+ ? IS_LOCAL
43
+ ? `http://${window.location.hostname}:${DASHBOARD_PORT_NUM}`
44
+ : `${window.location.protocol}//${window.location.host}`
45
+ : `http://localhost:${DASHBOARD_PORT_NUM}`;
46
+
47
+ /**
48
+ * API target enum - determines which backend to call
49
+ */
50
+ export enum Api {
51
+ /** Express :4242 - wallet operations, auth, agents, transactions */
52
+ Wallet = 'wallet',
53
+ /** Next.js :4747/api - workspace CRUD */
54
+ Workspace = 'workspace',
55
+ /** Next.js :4747/api - event logs from database */
56
+ Events = 'events',
57
+ /** Next.js :4747/api - agent dashboard (requests + tokens) */
58
+ AgentDashboard = 'agentDashboard',
59
+ }
60
+
61
+ const API_CONFIG: Record<Api, { baseUrl: string; pathPrefix: string }> = {
62
+ [Api.Wallet]: { baseUrl: EXPRESS_URL, pathPrefix: '' },
63
+ [Api.Workspace]: { baseUrl: NEXTJS_URL, pathPrefix: '/api' },
64
+ [Api.Events]: { baseUrl: NEXTJS_URL, pathPrefix: '/api' },
65
+ [Api.AgentDashboard]: { baseUrl: NEXTJS_URL, pathPrefix: '/api' },
66
+ };
67
+
68
+ const TOKEN_STORAGE_KEY = 'aurawallet_admin_token';
69
+
70
+ /**
71
+ * Get auth token from sessionStorage
72
+ */
73
+ function getToken(): string | null {
74
+ if (typeof window === 'undefined') return null;
75
+ return sessionStorage.getItem(TOKEN_STORAGE_KEY);
76
+ }
77
+
78
+ /**
79
+ * Make an authenticated request to the specified backend
80
+ */
81
+ async function request<T>(
82
+ target: Api,
83
+ path: string,
84
+ options: RequestInit = {}
85
+ ): Promise<T> {
86
+ const token = getToken();
87
+ const config = API_CONFIG[target];
88
+
89
+ const headers: HeadersInit = {
90
+ 'Content-Type': 'application/json',
91
+ ...options.headers,
92
+ };
93
+
94
+ if (token) {
95
+ (headers as Record<string, string>)['Authorization'] = `Bearer ${token}`;
96
+ }
97
+
98
+ const url = `${config.baseUrl}${config.pathPrefix}${path}`;
99
+
100
+ const res = await fetch(url, {
101
+ ...options,
102
+ headers,
103
+ });
104
+
105
+ const data = await res.json().catch(() => ({ error: res.statusText }));
106
+
107
+ if (!res.ok) {
108
+ const error = new Error(data.error || `Request failed: ${res.status}`);
109
+ (error as Error & { status: number }).status = res.status;
110
+ throw error;
111
+ }
112
+
113
+ return data as T;
114
+ }
115
+
116
+ /**
117
+ * API client with typed methods
118
+ */
119
+ /** Get the base URL for the wallet (Express) API */
120
+ export function getWalletBaseUrl(): string {
121
+ return EXPRESS_URL;
122
+ }
123
+
124
+ export const api = {
125
+ /**
126
+ * GET request
127
+ */
128
+ get: <T>(target: Api, path: string, params?: Record<string, string | number | boolean>, options?: RequestInit) => {
129
+ let url = path;
130
+ if (params) {
131
+ const searchParams = new URLSearchParams();
132
+ for (const [key, value] of Object.entries(params)) {
133
+ if (value !== undefined && value !== null) {
134
+ searchParams.set(key, String(value));
135
+ }
136
+ }
137
+ const queryString = searchParams.toString();
138
+ if (queryString) {
139
+ url = `${path}?${queryString}`;
140
+ }
141
+ }
142
+ return request<T>(target, url, { method: 'GET', ...options });
143
+ },
144
+
145
+ /**
146
+ * POST request
147
+ */
148
+ post: <T>(target: Api, path: string, body?: unknown) =>
149
+ request<T>(target, path, {
150
+ method: 'POST',
151
+ body: body ? JSON.stringify(body) : undefined,
152
+ }),
153
+
154
+ /**
155
+ * PUT request
156
+ */
157
+ put: <T>(target: Api, path: string, body?: unknown) =>
158
+ request<T>(target, path, {
159
+ method: 'PUT',
160
+ body: body ? JSON.stringify(body) : undefined,
161
+ }),
162
+
163
+ /**
164
+ * PATCH request
165
+ */
166
+ patch: <T>(target: Api, path: string, body?: unknown) =>
167
+ request<T>(target, path, {
168
+ method: 'PATCH',
169
+ body: body ? JSON.stringify(body) : undefined,
170
+ }),
171
+
172
+ /**
173
+ * DELETE request
174
+ */
175
+ delete: <T>(target: Api, path: string) =>
176
+ request<T>(target, path, { method: 'DELETE' }),
177
+
178
+ /**
179
+ * Get the base URL for a target (useful for debugging)
180
+ */
181
+ getBaseUrl: (target: Api = Api.Wallet) => {
182
+ const config = API_CONFIG[target];
183
+ return `${config.baseUrl}${config.pathPrefix}`;
184
+ },
185
+ };
186
+
187
+ // Type definitions for common API responses
188
+ export interface WalletData {
189
+ address: string;
190
+ tier: 'cold' | 'hot' | 'temp';
191
+ chain: string;
192
+ balance?: string;
193
+ name?: string;
194
+ color?: string;
195
+ emoji?: string;
196
+ description?: string;
197
+ hidden?: boolean;
198
+ tokenHash?: string;
199
+ createdAt?: string;
200
+ }
201
+
202
+ export interface WalletsResponse {
203
+ wallets: WalletData[];
204
+ unlocked: boolean;
205
+ agent?: { id: string; remaining: number };
206
+ }
207
+
208
+ export interface TrackedAsset {
209
+ id: string;
210
+ walletAddress: string;
211
+ tokenAddress: string;
212
+ symbol: string | null;
213
+ name: string | null;
214
+ decimals: number;
215
+ lastBalance: string | null;
216
+ lastBalanceAt: string | null;
217
+ isHidden: boolean;
218
+ chain: string;
219
+ poolAddress: string | null;
220
+ poolVersion: string | null;
221
+ icon: string | null;
222
+ createdAt: string;
223
+ updatedAt: string;
224
+ }
225
+
226
+ export interface AssetsResponse {
227
+ success: boolean;
228
+ assets: TrackedAsset[];
229
+ pagination: {
230
+ total: number;
231
+ limit: number;
232
+ offset: number;
233
+ hasMore: boolean;
234
+ };
235
+ }
236
+
237
+ export interface Transaction {
238
+ id: string;
239
+ walletAddress: string;
240
+ txHash: string | null;
241
+ type: string;
242
+ status: string;
243
+ amount: string | null;
244
+ tokenAddress: string | null;
245
+ tokenAmount: string | null;
246
+ from: string | null;
247
+ to: string | null;
248
+ description: string | null;
249
+ blockNumber: number | null;
250
+ chain: string;
251
+ createdAt: string;
252
+ updatedAt: string;
253
+ executedAt: string | null;
254
+ }
255
+
256
+ export interface TransactionsResponse {
257
+ success: boolean;
258
+ transactions: Transaction[];
259
+ pagination: {
260
+ total: number;
261
+ limit: number;
262
+ offset: number;
263
+ hasMore: boolean;
264
+ };
265
+ }
266
+
267
+ export interface DashboardResponse {
268
+ success: boolean;
269
+ requests: Array<{
270
+ id: string;
271
+ type: string;
272
+ status: string;
273
+ createdAt: string;
274
+ metadata?: string;
275
+ chain: string;
276
+ amount?: string;
277
+ }>;
278
+ tokens: {
279
+ active: Array<{
280
+ tokenHash: string;
281
+ agentId: string;
282
+ limit: number;
283
+ spent: number;
284
+ remaining: number;
285
+ permissions: string[];
286
+ expiresAt: number;
287
+ isActive: boolean;
288
+ isRevoked: boolean;
289
+ isExpired: boolean;
290
+ }>;
291
+ inactive: Array<{
292
+ tokenHash: string;
293
+ agentId: string;
294
+ limit: number;
295
+ spent: number;
296
+ remaining: number;
297
+ permissions: string[];
298
+ expiresAt: number;
299
+ isActive: boolean;
300
+ isRevoked: boolean;
301
+ isExpired: boolean;
302
+ }>;
303
+ };
304
+ counts: {
305
+ pendingActions: number;
306
+ activeTokens: number;
307
+ inactiveTokens: number;
308
+ };
309
+ }
310
+
311
+ // ============================================================================
312
+ // Encrypted Password Transport
313
+ // ============================================================================
314
+
315
+ // Cached server public key (cleared on failed decryption to force refetch)
316
+ let serverPublicKey: string | null = null;
317
+
318
+ /**
319
+ * Fetch the server's public key for encrypting passwords
320
+ * Key is cached until server restart (which invalidates it)
321
+ */
322
+ async function getServerPublicKey(): Promise<string> {
323
+ if (!serverPublicKey) {
324
+ const res = await api.get<{ publicKey: string }>(Api.Wallet, '/auth/connect');
325
+ serverPublicKey = res.publicKey;
326
+ }
327
+ return serverPublicKey;
328
+ }
329
+
330
+ /**
331
+ * Clear cached public key (call on decryption failure to force refetch)
332
+ */
333
+ export function clearServerPublicKey(): void {
334
+ serverPublicKey = null;
335
+ }
336
+
337
+ export interface UnlockResponse {
338
+ success: boolean;
339
+ message?: string;
340
+ address?: string;
341
+ token?: string;
342
+ error?: string;
343
+ }
344
+
345
+ export interface SetupResponse {
346
+ success: boolean;
347
+ address?: string;
348
+ mnemonic?: string;
349
+ token?: string;
350
+ message?: string;
351
+ error?: string;
352
+ }
353
+
354
+ export interface ChangePrimaryPasswordResponse {
355
+ success: boolean;
356
+ message?: string;
357
+ }
358
+
359
+ /**
360
+ * Unlock the wallet with encrypted password transport
361
+ * @param password - Plaintext password (will be encrypted before sending)
362
+ * @param vaultId - Optional vault ID (defaults to primary vault)
363
+ * @returns Unlock response with optional admin token
364
+ */
365
+ export async function unlockWallet(password: string, vaultId?: string, pubkey?: string): Promise<UnlockResponse> {
366
+ const publicKey = await getServerPublicKey();
367
+ const encrypted = await encryptPassword(password, publicKey);
368
+ const tokenPubkey = pubkey ?? await getTokenMintPubkey();
369
+ const path = vaultId ? `/unlock/${vaultId}` : '/unlock';
370
+
371
+ try {
372
+ return await api.post<UnlockResponse>(Api.Wallet, path, { encrypted, pubkey: tokenPubkey });
373
+ } catch (err) {
374
+ // If decryption failed on server, clear cached key and retry once
375
+ const error = err as Error & { status?: number };
376
+ if (error.message?.includes('decrypt') || error.message?.includes('refetch')) {
377
+ clearServerPublicKey();
378
+ const newKey = await getServerPublicKey();
379
+ const newEncrypted = await encryptPassword(password, newKey);
380
+ return await api.post<UnlockResponse>(Api.Wallet, path, { encrypted: newEncrypted, pubkey: tokenPubkey });
381
+ }
382
+ throw err;
383
+ }
384
+ }
385
+
386
+ /**
387
+ * Set up a new cold wallet with encrypted password transport
388
+ * @param password - Plaintext password (will be encrypted before sending)
389
+ * @returns Setup response with mnemonic
390
+ */
391
+ export async function setupWallet(password: string, pubkey?: string): Promise<SetupResponse> {
392
+ const publicKey = await getServerPublicKey();
393
+ const encrypted = await encryptPassword(password, publicKey);
394
+ const tokenPubkey = pubkey ?? await getTokenMintPubkey();
395
+
396
+ try {
397
+ return await api.post<SetupResponse>(Api.Wallet, '/setup', { encrypted, pubkey: tokenPubkey });
398
+ } catch (err) {
399
+ // If decryption failed on server, clear cached key and retry once
400
+ const error = err as Error & { status?: number };
401
+ if (error.message?.includes('decrypt') || error.message?.includes('refetch')) {
402
+ clearServerPublicKey();
403
+ const newKey = await getServerPublicKey();
404
+ const newEncrypted = await encryptPassword(password, newKey);
405
+ return await api.post<SetupResponse>(Api.Wallet, '/setup', { encrypted: newEncrypted, pubkey: tokenPubkey });
406
+ }
407
+ throw err;
408
+ }
409
+ }
410
+
411
+ /**
412
+ * Change the primary vault password with encrypted password transport.
413
+ */
414
+ export async function changePrimaryVaultPassword(
415
+ currentPassword: string,
416
+ newPassword: string,
417
+ ): Promise<ChangePrimaryPasswordResponse> {
418
+ const publicKey = await getServerPublicKey();
419
+ const currentEncrypted = await encryptPassword(currentPassword, publicKey);
420
+ const newEncrypted = await encryptPassword(newPassword, publicKey);
421
+
422
+ try {
423
+ return await api.post<ChangePrimaryPasswordResponse>(Api.Wallet, '/setup/password', {
424
+ currentEncrypted,
425
+ newEncrypted,
426
+ });
427
+ } catch (err) {
428
+ const error = err as Error & { status?: number };
429
+ if (error.message?.includes('decrypt') || error.message?.includes('refetch')) {
430
+ clearServerPublicKey();
431
+ const newKey = await getServerPublicKey();
432
+ const retryCurrentEncrypted = await encryptPassword(currentPassword, newKey);
433
+ const retryNewEncrypted = await encryptPassword(newPassword, newKey);
434
+ return await api.post<ChangePrimaryPasswordResponse>(Api.Wallet, '/setup/password', {
435
+ currentEncrypted: retryCurrentEncrypted,
436
+ newEncrypted: retryNewEncrypted,
437
+ });
438
+ }
439
+ throw err;
440
+ }
441
+ }
442
+
443
+ /**
444
+ * Re-key session with a new RSA public key (no password required).
445
+ * Used after page refresh when token survives but keypair is lost.
446
+ */
447
+ export async function rekeySession(pubkey: string): Promise<{ success: boolean; token: string }> {
448
+ return api.post<{ success: boolean; token: string }>(Api.Wallet, '/unlock/rekey', { pubkey });
449
+ }
@@ -0,0 +1,148 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+
4
+ export interface AppManifest {
5
+ id: string;
6
+ name: string;
7
+ icon: string;
8
+ category: string;
9
+ size: { width: number; height: number };
10
+ permissions: string[];
11
+ data: string[];
12
+ description: string;
13
+ path: string;
14
+ hasUi: boolean;
15
+ }
16
+
17
+ /**
18
+ * Parse simple YAML frontmatter from app.md
19
+ * Handles key: value lines and arrays (- item)
20
+ */
21
+ function parseFrontmatter(content: string): Record<string, unknown> {
22
+ const lines = content.split('\n');
23
+ const result: Record<string, unknown> = {};
24
+
25
+ let inFrontmatter = false;
26
+ let currentKey: string | null = null;
27
+ let currentArray: string[] | null = null;
28
+
29
+ for (const line of lines) {
30
+ const trimmed = line.trim();
31
+
32
+ if (trimmed === '---') {
33
+ if (inFrontmatter) break; // End of frontmatter
34
+ inFrontmatter = true;
35
+ continue;
36
+ }
37
+
38
+ if (!inFrontmatter) continue;
39
+
40
+ // Array item
41
+ if (trimmed.startsWith('- ') && currentKey) {
42
+ if (!currentArray) currentArray = [];
43
+ currentArray.push(trimmed.slice(2).trim());
44
+ continue;
45
+ }
46
+
47
+ // Save previous array
48
+ if (currentKey && currentArray) {
49
+ result[currentKey] = currentArray;
50
+ currentArray = null;
51
+ currentKey = null;
52
+ }
53
+
54
+ // Key: value line
55
+ const colonIdx = trimmed.indexOf(':');
56
+ if (colonIdx > 0) {
57
+ const key = trimmed.slice(0, colonIdx).trim();
58
+ const value = trimmed.slice(colonIdx + 1).trim();
59
+
60
+ if (value === '' || value === '[]') {
61
+ // Start of array or empty value or inline empty array
62
+ currentKey = key;
63
+ currentArray = [];
64
+ } else {
65
+ result[key] = value;
66
+ currentKey = null;
67
+ }
68
+ }
69
+ }
70
+
71
+ // Save trailing array
72
+ if (currentKey && currentArray) {
73
+ result[currentKey] = currentArray;
74
+ }
75
+
76
+ return result;
77
+ }
78
+
79
+ /**
80
+ * Extract description from markdown body (after frontmatter)
81
+ */
82
+ function extractDescription(content: string): string {
83
+ const parts = content.split('---');
84
+ if (parts.length < 3) return '';
85
+ // Everything after the second --- is the body
86
+ const body = parts.slice(2).join('---').trim();
87
+ // Take first paragraph
88
+ const firstParagraph = body.split('\n\n')[0];
89
+ return firstParagraph.replace(/^#+\s*/, '').trim();
90
+ }
91
+
92
+ /**
93
+ * Parse size string like "2x2" into { width, height }
94
+ * Grid units: 1 = 320px width, 280px height
95
+ */
96
+ function parseSize(size: string): { width: number; height: number } {
97
+ const match = size.match(/^(\d+)x(\d+)$/);
98
+ if (!match) return { width: 320, height: 280 };
99
+ return {
100
+ width: parseInt(match[1]) * 320,
101
+ height: parseInt(match[2]) * 280,
102
+ };
103
+ }
104
+
105
+ /**
106
+ * Load all app manifests from the apps/ directory
107
+ */
108
+ export function loadAppManifests(): AppManifest[] {
109
+ const appsDir = path.join(process.cwd(), 'apps');
110
+
111
+ if (!fs.existsSync(appsDir)) return [];
112
+
113
+ const manifests: AppManifest[] = [];
114
+
115
+ const entries = fs.readdirSync(appsDir, { withFileTypes: true });
116
+ for (const entry of entries) {
117
+ if (!entry.isDirectory()) continue;
118
+
119
+ const appMdPath = path.join(appsDir, entry.name, 'app.md');
120
+ const indexHtmlPath = path.join(appsDir, entry.name, 'index.html');
121
+
122
+ if (!fs.existsSync(appMdPath)) continue;
123
+ const hasUi = fs.existsSync(indexHtmlPath);
124
+
125
+ try {
126
+ const content = fs.readFileSync(appMdPath, 'utf-8');
127
+ const fm = parseFrontmatter(content);
128
+ const description = extractDescription(content);
129
+
130
+ manifests.push({
131
+ id: entry.name,
132
+ name: (fm.name as string) || entry.name,
133
+ icon: (fm.icon as string) || 'Box',
134
+ category: (fm.category as string) || 'general',
135
+ size: parseSize((fm.size as string) || '1x1'),
136
+ permissions: (fm.permissions as string[]) || [],
137
+ data: (fm.data as string[]) || [],
138
+ description,
139
+ path: entry.name,
140
+ hasUi,
141
+ });
142
+ } catch (err) {
143
+ console.error(`[app-loader] Failed to load app ${entry.name}:`, err);
144
+ }
145
+ }
146
+
147
+ return manifests;
148
+ }