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,254 @@
1
+ /**
2
+ * Shared credential resolution logic for env.ts and shell-hook.ts
3
+ *
4
+ * Consolidates search → read → decrypt flow to prevent drift between
5
+ * the two consumers (audit finding #3).
6
+ */
7
+
8
+ import { getErrorMessage } from '../../lib/error';
9
+ import { evaluateProjectScopeAccess, emitProjectScopeEvent } from '../../lib/project-scope';
10
+
11
+ // ── Types ──
12
+
13
+ export interface CredentialMeta {
14
+ id: string;
15
+ name: string;
16
+ type: string;
17
+ vaultId: string;
18
+ }
19
+
20
+ export interface DecryptedCredential {
21
+ id: string;
22
+ vaultId: string;
23
+ type: string;
24
+ fields: Array<{ key: string; value: string }>;
25
+ }
26
+
27
+ export interface AuraMapping {
28
+ envVar: string;
29
+ vault: string | null;
30
+ credentialName: string;
31
+ field: string;
32
+ }
33
+
34
+ export interface ResolveResult {
35
+ resolved: Map<string, string>;
36
+ errors: string[];
37
+ missing: AuraMapping[];
38
+ }
39
+
40
+ // ── Env var name validation (audit finding #6) ──
41
+
42
+ const ENV_VAR_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
43
+
44
+ export function isValidEnvVarName(name: string): boolean {
45
+ return ENV_VAR_NAME_RE.test(name);
46
+ }
47
+
48
+ export function validateEnvVarName(name: string): void {
49
+ if (!isValidEnvVarName(name)) {
50
+ throw new Error(
51
+ `Invalid env var name '${name}': must match [A-Za-z_][A-Za-z0-9_]*`
52
+ );
53
+ }
54
+ }
55
+
56
+ // ── Shell escaping (audit finding #1) ──
57
+
58
+ /**
59
+ * Escape a value for safe use in shell export statements.
60
+ * Uses ANSI-C quoting ($'...') to safely handle newlines, tabs,
61
+ * single quotes, backslashes, and other control characters.
62
+ */
63
+ export function escapeForShell(value: string): string {
64
+ // Use ANSI-C $'...' quoting which handles all special chars
65
+ const escaped = value
66
+ .replace(/\\/g, '\\\\')
67
+ .replace(/'/g, "\\'")
68
+ .replace(/\n/g, '\\n')
69
+ .replace(/\r/g, '\\r')
70
+ .replace(/\t/g, '\\t')
71
+ .replace(/\0/g, '\\0')
72
+ // Escape any other control characters
73
+ .replace(/[\x01-\x08\x0b\x0c\x0e-\x1f\x7f]/g, (ch) => {
74
+ return '\\x' + ch.charCodeAt(0).toString(16).padStart(2, '0');
75
+ });
76
+ return `$'${escaped}'`;
77
+ }
78
+
79
+ // ── Credential search with exact-match preference (audit finding #4) ──
80
+
81
+ export async function searchCredential(
82
+ baseUrl: string,
83
+ token: string,
84
+ name: string,
85
+ ): Promise<CredentialMeta | null> {
86
+ for (const param of [
87
+ `q=${encodeURIComponent(name)}`,
88
+ `tag=${encodeURIComponent(name)}`,
89
+ ]) {
90
+ const res = await fetch(`${baseUrl}/credentials?${param}`, {
91
+ headers: { Authorization: `Bearer ${token}` },
92
+ signal: AbortSignal.timeout(5000),
93
+ });
94
+ if (!res.ok) continue;
95
+
96
+ const data = (await res.json()) as { credentials: CredentialMeta[] };
97
+ const creds = data.credentials;
98
+ if (!creds || creds.length === 0) continue;
99
+
100
+ // Prefer exact name match (audit finding #4: ambiguous matching)
101
+ const exact = creds.find(
102
+ (c) => c.name.toLowerCase() === name.toLowerCase(),
103
+ );
104
+ if (exact) return exact;
105
+
106
+ // Warn if multiple non-exact matches
107
+ if (creds.length > 1) {
108
+ console.error(
109
+ `aura: warning: '${name}' matched ${creds.length} credentials, using first. ` +
110
+ `Matches: ${creds.map((c) => c.name).join(', ')}`,
111
+ );
112
+ }
113
+ return creds[0];
114
+ }
115
+ return null;
116
+ }
117
+
118
+ // ── Credential read + decrypt ──
119
+
120
+ export async function readCredential(
121
+ baseUrl: string,
122
+ readToken: string,
123
+ credentialId: string,
124
+ decryptFn: (encrypted: string) => string,
125
+ ): Promise<DecryptedCredential> {
126
+ const res = await fetch(`${baseUrl}/credentials/${credentialId}/read`, {
127
+ method: 'POST',
128
+ headers: { Authorization: `Bearer ${readToken}` },
129
+ signal: AbortSignal.timeout(5000),
130
+ });
131
+ if (!res.ok) {
132
+ const text = await res.text();
133
+ throw new Error(`Read failed (${res.status}): ${text}`);
134
+ }
135
+ const data = (await res.json()) as { encrypted: string };
136
+ const plaintext = decryptFn(data.encrypted);
137
+ return JSON.parse(plaintext);
138
+ }
139
+
140
+ // ── Resolve mappings to env vars ──
141
+
142
+ export async function resolveMappings(
143
+ mappings: AuraMapping[],
144
+ baseUrl: string,
145
+ token: string,
146
+ readToken: string,
147
+ decryptFn: (encrypted: string) => string,
148
+ ): Promise<ResolveResult> {
149
+ const resolved = new Map<string, string>();
150
+ const errors: string[] = [];
151
+ const missing: AuraMapping[] = [];
152
+
153
+ const credentialCache = new Map<string, DecryptedCredential | null>();
154
+ const CONCURRENCY = 5;
155
+
156
+ let vaultNameById = new Map<string, string>();
157
+ try {
158
+ const vaultRes = await fetch(`${baseUrl}/setup/vaults`, { signal: AbortSignal.timeout(5000) });
159
+ if (vaultRes.ok) {
160
+ const vaultData = await vaultRes.json() as { vaults?: Array<{ id: string; name: string }> };
161
+ vaultNameById = new Map((vaultData.vaults || []).map((v) => [v.id, v.name]));
162
+ }
163
+ } catch {
164
+ // best effort only
165
+ }
166
+
167
+ const uniqueTargets = new Map<string, AuraMapping>();
168
+ for (const mapping of mappings) {
169
+ const key = `${(mapping.vault || '').toLowerCase()}::${mapping.credentialName.toLowerCase()}`;
170
+ if (!uniqueTargets.has(key)) uniqueTargets.set(key, mapping);
171
+ }
172
+
173
+ const targetList = [...uniqueTargets.values()];
174
+
175
+ for (let i = 0; i < targetList.length; i += CONCURRENCY) {
176
+ const batch = targetList.slice(i, i + CONCURRENCY);
177
+ await Promise.all(
178
+ batch.map(async (mapping) => {
179
+ const cacheKey = `${(mapping.vault || '').toLowerCase()}::${mapping.credentialName.toLowerCase()}`;
180
+ const meta = await searchCredential(baseUrl, token, mapping.credentialName);
181
+ if (!meta) {
182
+ credentialCache.set(cacheKey, null);
183
+ return;
184
+ }
185
+
186
+ const decision = evaluateProjectScopeAccess({
187
+ surface: 'cli_env',
188
+ requested: { vaultName: mapping.vault, credentialName: mapping.credentialName },
189
+ candidates: [{ id: meta.id, name: meta.name, vaultName: vaultNameById.get(meta.vaultId) || null }],
190
+ actor: 'cli-env',
191
+ });
192
+ emitProjectScopeEvent({
193
+ actor: 'cli-env',
194
+ surface: 'cli_env',
195
+ requestedCredential: { vaultName: mapping.vault, credentialName: mapping.credentialName },
196
+ decision,
197
+ });
198
+ if (!decision.allowed) {
199
+ credentialCache.set(cacheKey, null);
200
+ errors.push(`credential '${mapping.credentialName}': ${decision.code}: ${decision.remediation}`);
201
+ return;
202
+ }
203
+
204
+ try {
205
+ const decrypted = await readCredential(
206
+ baseUrl,
207
+ readToken,
208
+ meta.id,
209
+ decryptFn,
210
+ );
211
+ credentialCache.set(cacheKey, decrypted);
212
+ } catch (err) {
213
+ credentialCache.set(cacheKey, null);
214
+ errors.push(`credential '${mapping.credentialName}': ${getErrorMessage(err)}`);
215
+ }
216
+ }),
217
+ );
218
+ }
219
+
220
+ for (const mapping of mappings) {
221
+ const cacheKey = `${(mapping.vault || '').toLowerCase()}::${mapping.credentialName.toLowerCase()}`;
222
+ const cred = credentialCache.get(cacheKey);
223
+ if (!cred) {
224
+ if (
225
+ !errors.some((e) =>
226
+ e.startsWith(`credential '${mapping.credentialName}'`),
227
+ )
228
+ ) {
229
+ errors.push(
230
+ `${mapping.envVar}: credential '${mapping.credentialName}' not found`,
231
+ );
232
+ } else {
233
+ errors.push(
234
+ `${mapping.envVar}: credential '${mapping.credentialName}' failed to resolve`,
235
+ );
236
+ }
237
+ missing.push(mapping);
238
+ continue;
239
+ }
240
+ const field = cred.fields.find(
241
+ (f) => f.key.toLowerCase() === mapping.field.toLowerCase(),
242
+ );
243
+ if (!field) {
244
+ const available = cred.fields.map((f) => f.key).join(', ');
245
+ errors.push(
246
+ `${mapping.envVar}: field '${mapping.field}' not found in '${mapping.credentialName}' (available: ${available})`,
247
+ );
248
+ continue;
249
+ }
250
+ resolved.set(mapping.envVar, field.value);
251
+ }
252
+
253
+ return { resolved, errors, missing };
254
+ }
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Shared dotenv → vault migration logic.
3
+ * Used by both `aura init --from-dotenv` and `aura env init`.
4
+ */
5
+
6
+ import * as fs from 'fs';
7
+ import * as path from 'path';
8
+ import { parseDotenv, groupByPrefix, noGrouping, generateAuraFile, type CredentialGroup } from './dotenv-parser';
9
+ import { createCredentialViaApi, getPrimaryVaultId } from './credential-create';
10
+
11
+ export interface MigrateOptions {
12
+ token: string;
13
+ envPath: string;
14
+ noGroup?: boolean;
15
+ dryRun?: boolean;
16
+ }
17
+
18
+ export interface MigrateResult {
19
+ groups: CredentialGroup[];
20
+ created: number;
21
+ failed: number;
22
+ auraPath: string;
23
+ }
24
+
25
+ /**
26
+ * Migrate a .env file into the vault and generate a .aura file.
27
+ * Returns summary of what was done.
28
+ */
29
+ export async function migrateDotenv(opts: MigrateOptions): Promise<MigrateResult> {
30
+ const { token, envPath, noGroup, dryRun } = opts;
31
+
32
+ if (!fs.existsSync(envPath)) {
33
+ throw new Error(`No .env file found at ${envPath}`);
34
+ }
35
+
36
+ const auraPath = path.join(path.dirname(envPath), '.aura');
37
+ const shouldWriteAura = !fs.existsSync(auraPath);
38
+
39
+ if (!shouldWriteAura && !dryRun) {
40
+ console.log(`\n Note: existing .aura file found at ${auraPath} — skipping overwrite.`);
41
+ }
42
+
43
+ const envContent = fs.readFileSync(envPath, 'utf-8');
44
+ const vars = parseDotenv(envContent);
45
+
46
+ if (vars.size === 0) {
47
+ throw new Error('No variables found in .env file.');
48
+ }
49
+
50
+ const groups = noGroup ? noGrouping(vars) : groupByPrefix(vars);
51
+
52
+ if (dryRun) {
53
+ console.log(`\n .env → .aura migration plan (${vars.size} variables → ${groups.length} credentials)\n`);
54
+ for (const group of groups) {
55
+ console.log(` 📦 ${group.name} (${group.fields.length} field${group.fields.length > 1 ? 's' : ''})`);
56
+ for (const field of group.fields) {
57
+ const preview = field.value.length > 20 ? field.value.substring(0, 20) + '...' : field.value;
58
+ console.log(` ${field.envVar} → ${group.name}/${field.key} (${preview})`);
59
+ }
60
+ }
61
+ console.log(`\n Run without --dry-run to execute.\n`);
62
+ return { groups, created: 0, failed: 0, auraPath };
63
+ }
64
+
65
+ const vaultId = await getPrimaryVaultId(token);
66
+
67
+ console.log(`\n Migrating ${vars.size} variables → ${groups.length} credentials\n`);
68
+
69
+ let created = 0;
70
+ let failed = 0;
71
+
72
+ for (const group of groups) {
73
+ const result = await createCredentialViaApi({
74
+ token,
75
+ vaultId,
76
+ name: group.name,
77
+ fields: group.fields.map(f => ({ key: f.key, value: f.value })),
78
+ });
79
+
80
+ if (result.success) {
81
+ console.log(` ✓ Created credential: ${group.name} (${group.fields.length} field${group.fields.length > 1 ? 's' : ''})`);
82
+ created++;
83
+ } else if (result.error?.includes('already exists') || result.error?.includes('duplicate')) {
84
+ console.log(` ⚠ Skipped credential: ${group.name} (already exists)`);
85
+ } else {
86
+ console.error(` ✗ Failed to create ${group.name}: ${result.error}`);
87
+ failed++;
88
+ }
89
+ }
90
+
91
+ // Generate .aura file when needed
92
+ if (shouldWriteAura) {
93
+ const auraContent = generateAuraFile(groups);
94
+ fs.writeFileSync(auraPath, auraContent, 'utf-8');
95
+ console.log(`\n ✓ Generated .aura file (${groups.length} credential${groups.length > 1 ? 's' : ''})`);
96
+ }
97
+
98
+ // Add .env to .gitignore
99
+ const gitignorePath = path.join(path.dirname(envPath), '.gitignore');
100
+ if (fs.existsSync(gitignorePath)) {
101
+ const gitignore = fs.readFileSync(gitignorePath, 'utf-8');
102
+ if (!gitignore.includes('.env')) {
103
+ fs.appendFileSync(gitignorePath, '\n.env\n');
104
+ console.log(' ✓ Added .env to .gitignore');
105
+ }
106
+ }
107
+
108
+ console.log(`\n Summary: ${created} created, ${failed} failed`);
109
+ if (created > 0) {
110
+ console.log(' Your .env variables are now in the vault.');
111
+ console.log(' Use `aura env -- <cmd>` to run commands with vault-injected env vars.');
112
+ console.log(' You can safely delete your .env file.\n');
113
+ }
114
+
115
+ return { groups, created, failed, auraPath };
116
+ }
@@ -0,0 +1,146 @@
1
+ /**
2
+ * .env file parser and credential grouping for `aura env init`
3
+ */
4
+
5
+ import { isValidEnvVarName } from './credential-resolve';
6
+
7
+ export interface CredentialGroup {
8
+ name: string;
9
+ fields: Array<{ key: string; value: string; envVar: string }>;
10
+ }
11
+
12
+ /**
13
+ * Parse a .env file into key-value pairs.
14
+ * Handles: comments, blank lines, `export` prefix, single/double quotes, escaped chars.
15
+ * Does NOT handle: multiline values, variable expansion.
16
+ */
17
+ export function parseDotenv(content: string): Map<string, string> {
18
+ const result = new Map<string, string>();
19
+
20
+ for (const rawLine of content.split('\n')) {
21
+ const line = rawLine.trim();
22
+
23
+ // Skip empty lines and comments
24
+ if (!line || line.startsWith('#')) continue;
25
+
26
+ // Strip optional `export ` prefix
27
+ const stripped = line.startsWith('export ') ? line.slice(7).trim() : line;
28
+
29
+ const eqIdx = stripped.indexOf('=');
30
+ if (eqIdx === -1) continue;
31
+
32
+ const key = stripped.substring(0, eqIdx).trim();
33
+ if (!key) continue;
34
+
35
+ // Validate env var name (audit finding #6)
36
+ if (!isValidEnvVarName(key)) {
37
+ console.error(`warning: skipping invalid env var name '${key}'`);
38
+ continue;
39
+ }
40
+
41
+ let value = stripped.substring(eqIdx + 1).trim();
42
+
43
+ // Handle quoted values
44
+ if ((value.startsWith('"') && value.endsWith('"')) ||
45
+ (value.startsWith("'") && value.endsWith("'"))) {
46
+ const quote = value[0];
47
+ value = value.slice(1, -1);
48
+ if (quote === '"') {
49
+ // Unescape double-quoted values
50
+ value = value
51
+ .replace(/\\n/g, '\n')
52
+ .replace(/\\r/g, '\r')
53
+ .replace(/\\t/g, '\t')
54
+ .replace(/\\"/g, '"')
55
+ .replace(/\\\\/g, '\\');
56
+ }
57
+ // Single-quoted: literal, no escaping
58
+ } else {
59
+ // Unquoted: strip inline comment
60
+ const commentIdx = value.indexOf(' #');
61
+ if (commentIdx !== -1) {
62
+ value = value.substring(0, commentIdx).trim();
63
+ }
64
+ }
65
+
66
+ result.set(key, value);
67
+ }
68
+
69
+ return result;
70
+ }
71
+
72
+ /**
73
+ * Group env vars by common prefix for credential creation.
74
+ *
75
+ * Rules:
76
+ * - Split var name by `_`, use first segment as group prefix
77
+ * - Groups with 1 member: credential name = full lowercased var name, field = "value"
78
+ * - Groups with 2+ members: credential name = prefix, field = rest lowercased
79
+ */
80
+ export function groupByPrefix(vars: Map<string, string>): CredentialGroup[] {
81
+ const prefixGroups = new Map<string, Array<{ key: string; value: string; envVar: string }>>();
82
+
83
+ for (const [envVar, value] of vars) {
84
+ const underscoreIdx = envVar.indexOf('_');
85
+ const prefix = underscoreIdx === -1
86
+ ? envVar.toLowerCase()
87
+ : envVar.substring(0, underscoreIdx).toLowerCase();
88
+
89
+ if (!prefixGroups.has(prefix)) {
90
+ prefixGroups.set(prefix, []);
91
+ }
92
+
93
+ const fieldName = underscoreIdx === -1
94
+ ? 'value'
95
+ : envVar.substring(underscoreIdx + 1).toLowerCase();
96
+
97
+ prefixGroups.get(prefix)!.push({ key: fieldName, value, envVar });
98
+ }
99
+
100
+ const groups: CredentialGroup[] = [];
101
+
102
+ for (const [prefix, fields] of prefixGroups) {
103
+ if (fields.length === 1 && fields[0].key !== 'value') {
104
+ groups.push({
105
+ name: fields[0].envVar.toLowerCase(),
106
+ fields: [{ key: 'value', value: fields[0].value, envVar: fields[0].envVar }],
107
+ });
108
+ } else {
109
+ groups.push({ name: prefix, fields });
110
+ }
111
+ }
112
+
113
+ return groups;
114
+ }
115
+
116
+ /**
117
+ * No grouping — one credential per env var, field always "value".
118
+ */
119
+ export function noGrouping(vars: Map<string, string>): CredentialGroup[] {
120
+ const groups: CredentialGroup[] = [];
121
+ for (const [envVar, value] of vars) {
122
+ groups.push({
123
+ name: envVar.toLowerCase(),
124
+ fields: [{ key: 'value', value, envVar }],
125
+ });
126
+ }
127
+ return groups;
128
+ }
129
+
130
+ /**
131
+ * Generate .aura file content from credential groups.
132
+ */
133
+ export function generateAuraFile(groups: CredentialGroup[]): string {
134
+ const lines = ['# Generated by aura env init'];
135
+
136
+ for (const group of groups) {
137
+ for (const field of group.fields) {
138
+ const ref = field.key === 'value'
139
+ ? `${group.name}/value`
140
+ : `${group.name}/${field.key}`;
141
+ lines.push(`${field.envVar}=${ref}`);
142
+ }
143
+ }
144
+
145
+ return lines.join('\n') + '\n';
146
+ }
@@ -0,0 +1,91 @@
1
+ /**
2
+ * HTTP helpers for CLI commands
3
+ */
4
+
5
+ const DEFAULT_SERVER_URL = 'http://localhost:4242';
6
+
7
+ /**
8
+ * Get the wallet server URL from env or default
9
+ */
10
+ export function serverUrl(): string {
11
+ return process.env.WALLET_SERVER_URL || DEFAULT_SERVER_URL;
12
+ }
13
+
14
+ /**
15
+ * Fetch JSON from the wallet server
16
+ */
17
+ export async function fetchJson<T = unknown>(
18
+ path: string,
19
+ opts: { method?: string; body?: unknown; token?: string } = {}
20
+ ): Promise<T> {
21
+ const url = `${serverUrl()}${path}`;
22
+ const headers: Record<string, string> = { 'Content-Type': 'application/json' };
23
+ if (opts.token) {
24
+ headers['Authorization'] = `Bearer ${opts.token}`;
25
+ }
26
+
27
+ const response = await fetch(url, {
28
+ method: opts.method || (opts.body ? 'POST' : 'GET'),
29
+ headers,
30
+ body: opts.body ? JSON.stringify(opts.body) : undefined,
31
+ });
32
+
33
+ const data = await response.json() as T & { error?: string };
34
+
35
+ if (!response.ok) {
36
+ throw new Error((data as { error?: string }).error || `HTTP ${response.status}`);
37
+ }
38
+
39
+ return data;
40
+ }
41
+
42
+ /**
43
+ * Fetch the server's RSA public key for password encryption
44
+ */
45
+ export async function fetchPublicKey(): Promise<string> {
46
+ const data = await fetchJson<{ publicKey: string }>('/auth/connect');
47
+ return data.publicKey;
48
+ }
49
+
50
+ /**
51
+ * Check if the wallet server is running
52
+ */
53
+ export async function isServerRunning(): Promise<boolean> {
54
+ try {
55
+ await fetchJson('/health');
56
+ return true;
57
+ } catch {
58
+ return false;
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Poll /health until the server is up, or timeout
64
+ */
65
+ export async function waitForServer(timeoutMs: number = 15000): Promise<void> {
66
+ const start = Date.now();
67
+ while (Date.now() - start < timeoutMs) {
68
+ if (await isServerRunning()) return;
69
+ await new Promise((r) => setTimeout(r, 500));
70
+ }
71
+ throw new Error(`Server did not start within ${timeoutMs / 1000}s`);
72
+ }
73
+
74
+ /**
75
+ * Setup status from GET /setup
76
+ */
77
+ export interface SetupStatus {
78
+ hasWallet: boolean;
79
+ unlocked: boolean;
80
+ address: string | null;
81
+ adapters?: { telegram: boolean; webhook: boolean };
82
+ apiKeys?: { alchemy: boolean; anthropic: boolean };
83
+ defaultChain?: string;
84
+ }
85
+
86
+ /**
87
+ * Fetch setup status from the wallet server
88
+ */
89
+ export async function fetchSetupStatus(): Promise<SetupStatus> {
90
+ return fetchJson<SetupStatus>('/setup');
91
+ }
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Filesystem + Prisma setup steps for init command
3
+ */
4
+
5
+ import * as path from 'path';
6
+ import * as fs from 'fs';
7
+ import * as os from 'os';
8
+ import { execSync } from 'child_process';
9
+ import { findProjectRoot } from './process';
10
+
11
+ function getDataDir(): string {
12
+ return process.env.WALLET_DATA_DIR || path.join(os.homedir(), '.aurawallet');
13
+ }
14
+
15
+ /**
16
+ * Create the ~/.aurawallet/ directory structure
17
+ */
18
+ export function ensureDirectories(): void {
19
+ const dataDir = getDataDir();
20
+ const dirs = [
21
+ dataDir,
22
+ path.join(dataDir, 'hot'),
23
+ path.join(dataDir, 'pending'),
24
+ ];
25
+
26
+ for (const dir of dirs) {
27
+ if (!fs.existsSync(dir)) {
28
+ fs.mkdirSync(dir, { recursive: true });
29
+ }
30
+ }
31
+
32
+ // Root-level tmp/backups directories are no longer created here.
33
+ }
34
+
35
+ /**
36
+ * Run Prisma migrations against ~/.aurawallet/aurawallet.db
37
+ */
38
+ export function runMigrations(root?: string): void {
39
+ const projectRoot = root || findProjectRoot();
40
+ const dbUrl = process.env.DATABASE_URL || `file:${getDataDir()}/aurawallet.db`;
41
+ const env = { ...process.env, DATABASE_URL: dbUrl };
42
+
43
+ try {
44
+ // Try deploy first (production-style, no prompts)
45
+ execSync('npx prisma migrate deploy', {
46
+ cwd: projectRoot,
47
+ env,
48
+ stdio: 'pipe',
49
+ });
50
+ } catch {
51
+ // Fallback to dev migration (creates migration if needed)
52
+ execSync('npx prisma migrate dev --name init', {
53
+ cwd: projectRoot,
54
+ env,
55
+ stdio: 'pipe',
56
+ });
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Generate the Prisma client
62
+ */
63
+ export function generatePrismaClient(root?: string): void {
64
+ const projectRoot = root || findProjectRoot();
65
+ execSync('npx prisma generate', {
66
+ cwd: projectRoot,
67
+ stdio: 'pipe',
68
+ });
69
+ }
70
+
71
+ /**
72
+ * Check if a primary vault file already exists
73
+ */
74
+ export function hasVault(): boolean {
75
+ return fs.existsSync(path.join(getDataDir(), 'vault-primary.json'));
76
+ }