auramaxx 0.0.1

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 (418) hide show
  1. package/LICENSE +26 -0
  2. package/README.md +77 -0
  3. package/apps/desktop-electron/main.js +428 -0
  4. package/bin/auramaxx.js +1063 -0
  5. package/docs/ADAPTERS.md +466 -0
  6. package/docs/AGENT_SETUP.md +159 -0
  7. package/docs/API.md +127 -0
  8. package/docs/APPS.md +199 -0
  9. package/docs/ARCHITECTURE.md +235 -0
  10. package/docs/AUTH.md +318 -0
  11. package/docs/BEST-PRACTICES.md +82 -0
  12. package/docs/CLI.md +141 -0
  13. package/docs/DESKTOP_ELECTRON.md +26 -0
  14. package/docs/DEVELOPING-APPS.md +453 -0
  15. package/docs/MCP.md +122 -0
  16. package/docs/PACKAGING_POLICY.md +19 -0
  17. package/docs/PERMISSION.md +137 -0
  18. package/docs/PROTOCOL.md +142 -0
  19. package/docs/README.md +50 -0
  20. package/docs/SKILLS.md +132 -0
  21. package/docs/TROUBLESHOOTING.md +376 -0
  22. package/docs/WORKSPACE.md +673 -0
  23. package/docs/agent-auth.md +14 -0
  24. package/docs/api/authentication.md +79 -0
  25. package/docs/api/secrets/api-keys.md +28 -0
  26. package/docs/api/secrets/credentials.md +80 -0
  27. package/docs/api/secrets/sharing.md +48 -0
  28. package/docs/api/system.md +41 -0
  29. package/docs/api/wallets/apps-strategies.md +66 -0
  30. package/docs/api/wallets/core.md +46 -0
  31. package/docs/api/wallets/data-portfolio.md +42 -0
  32. package/docs/aura-file.md +48 -0
  33. package/docs/core-concepts/FEATURES.md +114 -0
  34. package/docs/credentials.md +120 -0
  35. package/docs/external/HOW_TO_AURAMAXX/GETTING_SECRETS.md +33 -0
  36. package/docs/external/HOW_TO_AURAMAXX/README.md +45 -0
  37. package/docs/external/getting-started.md +10 -0
  38. package/docs/external/overview.md +19 -0
  39. package/docs/external/persona-paths.md +7 -0
  40. package/docs/external/share-secret.md +76 -0
  41. package/docs/external/why-aura.md +7 -0
  42. package/docs/security.md +227 -0
  43. package/docs/templates/RELEASE_NOTES_TEMPLATE.md +22 -0
  44. package/docs/wallet/AI.md +508 -0
  45. package/docs/wallet/DEVELOPING-STRATEGIES.md +713 -0
  46. package/docs/wallet/README.md +47 -0
  47. package/docs/wallet/STRATEGY.md +89 -0
  48. package/next.config.ts +28 -0
  49. package/package.json +167 -0
  50. package/postcss.config.mjs +8 -0
  51. package/prisma/migrations/20260214170000_baseline/migration.sql +511 -0
  52. package/prisma/migrations/20260216214537_add_passkey_model/migration.sql +18 -0
  53. package/prisma/migrations/20260217150500_add_credential_access_audit/migration.sql +31 -0
  54. package/prisma/migrations/20260222090000_update_admin_ttl_default/migration.sql +10 -0
  55. package/prisma/migrations/migration_lock.toml +3 -0
  56. package/prisma/schema.prisma +447 -0
  57. package/public/logo.webp +0 -0
  58. package/scripts/add-app.js +245 -0
  59. package/server/abi/SwapHelper.json +438 -0
  60. package/server/cli/approval.ts +447 -0
  61. package/server/cli/commands/actions.ts +474 -0
  62. package/server/cli/commands/api.ts +220 -0
  63. package/server/cli/commands/apikey.ts +277 -0
  64. package/server/cli/commands/app.ts +204 -0
  65. package/server/cli/commands/auth.ts +464 -0
  66. package/server/cli/commands/cron.ts +24 -0
  67. package/server/cli/commands/diary.ts +274 -0
  68. package/server/cli/commands/doctor.ts +1247 -0
  69. package/server/cli/commands/env.ts +476 -0
  70. package/server/cli/commands/experimental.ts +69 -0
  71. package/server/cli/commands/init.ts +798 -0
  72. package/server/cli/commands/lock.ts +157 -0
  73. package/server/cli/commands/mcp.ts +285 -0
  74. package/server/cli/commands/quickhack.ts +86 -0
  75. package/server/cli/commands/release-check.ts +231 -0
  76. package/server/cli/commands/restore.ts +314 -0
  77. package/server/cli/commands/service.ts +320 -0
  78. package/server/cli/commands/shell-hook.ts +512 -0
  79. package/server/cli/commands/skill.ts +216 -0
  80. package/server/cli/commands/start.ts +139 -0
  81. package/server/cli/commands/status.ts +59 -0
  82. package/server/cli/commands/stop.ts +36 -0
  83. package/server/cli/commands/token.ts +180 -0
  84. package/server/cli/commands/unlock.ts +50 -0
  85. package/server/cli/commands/vault.ts +1323 -0
  86. package/server/cli/commands/wallet.ts +209 -0
  87. package/server/cli/index.ts +280 -0
  88. package/server/cli/lib/approval-poll.ts +94 -0
  89. package/server/cli/lib/aura-parser.ts +64 -0
  90. package/server/cli/lib/credential-create.ts +74 -0
  91. package/server/cli/lib/credential-resolve.ts +280 -0
  92. package/server/cli/lib/dotenv-migrate.ts +116 -0
  93. package/server/cli/lib/dotenv-parser.ts +146 -0
  94. package/server/cli/lib/escalation.ts +57 -0
  95. package/server/cli/lib/http.ts +91 -0
  96. package/server/cli/lib/init-steps.ts +76 -0
  97. package/server/cli/lib/local-agent-trust.ts +45 -0
  98. package/server/cli/lib/lock-unlock-helper.ts +71 -0
  99. package/server/cli/lib/process.ts +162 -0
  100. package/server/cli/lib/prompt.ts +294 -0
  101. package/server/cli/lib/theme.ts +240 -0
  102. package/server/cli/socket.ts +579 -0
  103. package/server/cli/transport-client.ts +50 -0
  104. package/server/cron/index.ts +137 -0
  105. package/server/cron/job.ts +31 -0
  106. package/server/cron/jobs/balance-sync.ts +436 -0
  107. package/server/cron/jobs/incoming-scan.ts +506 -0
  108. package/server/cron/jobs/native-price.ts +70 -0
  109. package/server/cron/jobs/orphan-cleanup.ts +40 -0
  110. package/server/cron/jobs/strategy-runner.ts +175 -0
  111. package/server/cron/scheduler.ts +125 -0
  112. package/server/index.ts +420 -0
  113. package/server/lib/adapters/factory.ts +119 -0
  114. package/server/lib/adapters/index.ts +19 -0
  115. package/server/lib/adapters/router.ts +297 -0
  116. package/server/lib/adapters/telegram.ts +645 -0
  117. package/server/lib/adapters/types.ts +89 -0
  118. package/server/lib/adapters/webhook.ts +95 -0
  119. package/server/lib/address.ts +49 -0
  120. package/server/lib/agent-auth/contracts.ts +1194 -0
  121. package/server/lib/agent-profiles.ts +419 -0
  122. package/server/lib/ai.ts +285 -0
  123. package/server/lib/api-registry/contracts.ts +86 -0
  124. package/server/lib/api-registry/validation.ts +172 -0
  125. package/server/lib/apikey-migration.ts +258 -0
  126. package/server/lib/app-installer.ts +505 -0
  127. package/server/lib/app-tokens.ts +247 -0
  128. package/server/lib/approval-link.ts +27 -0
  129. package/server/lib/auth.ts +314 -0
  130. package/server/lib/auto-execute.ts +160 -0
  131. package/server/lib/batch.ts +242 -0
  132. package/server/lib/cold.ts +1048 -0
  133. package/server/lib/config.ts +408 -0
  134. package/server/lib/credential-access-audit.ts +85 -0
  135. package/server/lib/credential-access-policy.ts +111 -0
  136. package/server/lib/credential-health.ts +343 -0
  137. package/server/lib/credential-import.ts +608 -0
  138. package/server/lib/credential-scope.ts +102 -0
  139. package/server/lib/credential-shares.ts +190 -0
  140. package/server/lib/credential-transport.ts +533 -0
  141. package/server/lib/credential-vault.ts +77 -0
  142. package/server/lib/credentials.ts +422 -0
  143. package/server/lib/crypto.ts +8 -0
  144. package/server/lib/db.ts +58 -0
  145. package/server/lib/defaults.ts +386 -0
  146. package/server/lib/dex/index.ts +80 -0
  147. package/server/lib/dex/relay.ts +235 -0
  148. package/server/lib/dex/types.ts +59 -0
  149. package/server/lib/dex/uniswap.ts +370 -0
  150. package/server/lib/diary.ts +34 -0
  151. package/server/lib/dont-ask-again-policy.ts +41 -0
  152. package/server/lib/e2e-agent/artifacts.ts +36 -0
  153. package/server/lib/e2e-agent/contracts.ts +112 -0
  154. package/server/lib/e2e-agent/validation.ts +135 -0
  155. package/server/lib/encrypt.ts +114 -0
  156. package/server/lib/error.ts +20 -0
  157. package/server/lib/events.ts +217 -0
  158. package/server/lib/feature-flags.ts +93 -0
  159. package/server/lib/hot.ts +357 -0
  160. package/server/lib/human-action-summary.ts +80 -0
  161. package/server/lib/key-fingerprint.ts +28 -0
  162. package/server/lib/logger.ts +340 -0
  163. package/server/lib/network.ts +137 -0
  164. package/server/lib/notifications.ts +230 -0
  165. package/server/lib/oauth2-refresh.ts +241 -0
  166. package/server/lib/oursecret.ts +71 -0
  167. package/server/lib/passkey-credential.ts +360 -0
  168. package/server/lib/passkey.ts +68 -0
  169. package/server/lib/permissions.ts +299 -0
  170. package/server/lib/pino.ts +24 -0
  171. package/server/lib/policy-preview.ts +138 -0
  172. package/server/lib/price.ts +338 -0
  173. package/server/lib/prices.ts +34 -0
  174. package/server/lib/project-scope.ts +297 -0
  175. package/server/lib/resolve-action.ts +328 -0
  176. package/server/lib/resolve.ts +36 -0
  177. package/server/lib/secret-gist-share.ts +296 -0
  178. package/server/lib/sessions.ts +634 -0
  179. package/server/lib/socket-path.ts +56 -0
  180. package/server/lib/solana/connection.ts +26 -0
  181. package/server/lib/solana/jupiter.ts +128 -0
  182. package/server/lib/solana/transfer.ts +108 -0
  183. package/server/lib/solana/wallet.ts +136 -0
  184. package/server/lib/strategy/emits.ts +21 -0
  185. package/server/lib/strategy/engine.ts +1305 -0
  186. package/server/lib/strategy/executor.ts +115 -0
  187. package/server/lib/strategy/hook-context.ts +159 -0
  188. package/server/lib/strategy/hooks.ts +990 -0
  189. package/server/lib/strategy/index.ts +28 -0
  190. package/server/lib/strategy/installer.ts +305 -0
  191. package/server/lib/strategy/loader.ts +256 -0
  192. package/server/lib/strategy/message.ts +237 -0
  193. package/server/lib/strategy/repository.ts +218 -0
  194. package/server/lib/strategy/session-logger.ts +693 -0
  195. package/server/lib/strategy/sources.ts +288 -0
  196. package/server/lib/strategy/state.ts +189 -0
  197. package/server/lib/strategy/templates.ts +403 -0
  198. package/server/lib/strategy/tick.ts +404 -0
  199. package/server/lib/strategy/types.ts +230 -0
  200. package/server/lib/swap.ts +3 -0
  201. package/server/lib/temp.ts +86 -0
  202. package/server/lib/token-metadata.ts +86 -0
  203. package/server/lib/token-safety.ts +200 -0
  204. package/server/lib/token-search.ts +444 -0
  205. package/server/lib/totp.ts +194 -0
  206. package/server/lib/transactions.ts +123 -0
  207. package/server/lib/transport.ts +84 -0
  208. package/server/lib/txhistory/decoder.ts +262 -0
  209. package/server/lib/txhistory/enricher.ts +652 -0
  210. package/server/lib/txhistory/index.ts +391 -0
  211. package/server/lib/txhistory/signatures.ts +59 -0
  212. package/server/lib/update-check.ts +35 -0
  213. package/server/lib/verified-summary.ts +414 -0
  214. package/server/lib/view-registry.ts +80 -0
  215. package/server/mcp/profile-policy.ts +30 -0
  216. package/server/mcp/server.ts +1589 -0
  217. package/server/mcp/tools.ts +276 -0
  218. package/server/middleware/auth.ts +119 -0
  219. package/server/middleware/requestLogger.ts +84 -0
  220. package/server/routes/actions.ts +539 -0
  221. package/server/routes/adapters.ts +711 -0
  222. package/server/routes/addressbook.ts +113 -0
  223. package/server/routes/ai.ts +34 -0
  224. package/server/routes/apikeys.ts +343 -0
  225. package/server/routes/apps.ts +601 -0
  226. package/server/routes/auth.ts +406 -0
  227. package/server/routes/backup.ts +404 -0
  228. package/server/routes/batch.ts +270 -0
  229. package/server/routes/bookmarks.ts +162 -0
  230. package/server/routes/credential-shares.ts +380 -0
  231. package/server/routes/credential-vaults.ts +159 -0
  232. package/server/routes/credentials.ts +1782 -0
  233. package/server/routes/dashboard.ts +97 -0
  234. package/server/routes/defaults.ts +124 -0
  235. package/server/routes/flags.ts +11 -0
  236. package/server/routes/fund.ts +225 -0
  237. package/server/routes/heartbeat.ts +375 -0
  238. package/server/routes/import.ts +364 -0
  239. package/server/routes/launch.ts +665 -0
  240. package/server/routes/lock.ts +54 -0
  241. package/server/routes/logs.ts +68 -0
  242. package/server/routes/nuke.ts +111 -0
  243. package/server/routes/passkey-credentials.ts +99 -0
  244. package/server/routes/passkey.ts +366 -0
  245. package/server/routes/portfolio.ts +217 -0
  246. package/server/routes/price.ts +63 -0
  247. package/server/routes/resolve.ts +31 -0
  248. package/server/routes/security.ts +45 -0
  249. package/server/routes/send-evm.ts +241 -0
  250. package/server/routes/send-solana.ts +281 -0
  251. package/server/routes/send.ts +178 -0
  252. package/server/routes/setup.ts +210 -0
  253. package/server/routes/strategy.ts +894 -0
  254. package/server/routes/swap-evm.ts +352 -0
  255. package/server/routes/swap-solana.ts +176 -0
  256. package/server/routes/swap.ts +356 -0
  257. package/server/routes/token.ts +247 -0
  258. package/server/routes/unlock.ts +467 -0
  259. package/server/routes/views.ts +41 -0
  260. package/server/routes/wallet-assets.ts +361 -0
  261. package/server/routes/wallet-transactions.ts +515 -0
  262. package/server/routes/wallet.ts +709 -0
  263. package/server/types.ts +146 -0
  264. package/shared/credential-field-schema.ts +248 -0
  265. package/skills/auramaxx/HEARTBEAT.md +78 -0
  266. package/skills/auramaxx/SKILL.md +745 -0
  267. package/skills/auramaxx/docs/AGENT_SETUP.md +155 -0
  268. package/skills/auramaxx/docs/API.md +127 -0
  269. package/skills/auramaxx/docs/AUTH.md +318 -0
  270. package/skills/auramaxx/docs/CLI.md +130 -0
  271. package/skills/auramaxx/docs/MCP.md +122 -0
  272. package/skills/auramaxx/docs/TROUBLESHOOTING.md +357 -0
  273. package/skills/auramaxx/docs/WORKSPACE.md +673 -0
  274. package/skills/auramaxx/docs/security.md +227 -0
  275. package/skills/task-lifecycle/SKILL.md +378 -0
  276. package/src/app/api/[...doc]/page.tsx +36 -0
  277. package/src/app/api/agent-requests/route.ts +30 -0
  278. package/src/app/api/apps/install/route.ts +132 -0
  279. package/src/app/api/apps/manifests/route.ts +16 -0
  280. package/src/app/api/apps/static/[...path]/route.ts +57 -0
  281. package/src/app/api/docs/plain/route.ts +74 -0
  282. package/src/app/api/events/route.ts +92 -0
  283. package/src/app/api/page.tsx +290 -0
  284. package/src/app/api/workspace/[id]/apps/[wid]/route.ts +119 -0
  285. package/src/app/api/workspace/[id]/apps/route.ts +81 -0
  286. package/src/app/api/workspace/[id]/export/route.ts +67 -0
  287. package/src/app/api/workspace/[id]/route.ts +168 -0
  288. package/src/app/api/workspace/auth.ts +40 -0
  289. package/src/app/api/workspace/config/route.ts +121 -0
  290. package/src/app/api/workspace/import/route.ts +127 -0
  291. package/src/app/api/workspace/route.ts +116 -0
  292. package/src/app/app-legacy-do-not-use/page.tsx +2245 -0
  293. package/src/app/apple-icon.png +0 -0
  294. package/src/app/approve/[actionId]/page.tsx +409 -0
  295. package/src/app/docs/DocsPageContent.tsx +269 -0
  296. package/src/app/docs/[...doc]/page.tsx +41 -0
  297. package/src/app/docs/page.tsx +38 -0
  298. package/src/app/favicon.ico +0 -0
  299. package/src/app/globals.css +819 -0
  300. package/src/app/health/page.tsx +5 -0
  301. package/src/app/hello/page.tsx +102 -0
  302. package/src/app/icon.png +0 -0
  303. package/src/app/layout.tsx +39 -0
  304. package/src/app/page.tsx +1964 -0
  305. package/src/app/privacy/page.tsx +63 -0
  306. package/src/app/providers.tsx +87 -0
  307. package/src/app/share/[token]/page.tsx +295 -0
  308. package/src/app/terms/page.tsx +80 -0
  309. package/src/components/ChainSelector.tsx +44 -0
  310. package/src/components/HumanActionBar.tsx +697 -0
  311. package/src/components/NotificationDrawer.tsx +387 -0
  312. package/src/components/PasskeyEnrollmentPrompt.tsx +235 -0
  313. package/src/components/apps/AgentKeysApp.tsx +490 -0
  314. package/src/components/apps/App.tsx +153 -0
  315. package/src/components/apps/AppGrid.tsx +15 -0
  316. package/src/components/apps/DetailedAddressDrawer.tsx +325 -0
  317. package/src/components/apps/DraggableApp.tsx +562 -0
  318. package/src/components/apps/IFrameApp.tsx +73 -0
  319. package/src/components/apps/LogsApp.tsx +360 -0
  320. package/src/components/apps/SendApp.tsx +394 -0
  321. package/src/components/apps/SetupWizardApp.tsx +1004 -0
  322. package/src/components/apps/SystemDefaultsApp.tsx +845 -0
  323. package/src/components/apps/ThirdPartyApp.tsx +428 -0
  324. package/src/components/apps/TokenApp.tsx +319 -0
  325. package/src/components/apps/TransactionsApp.tsx +438 -0
  326. package/src/components/apps/WalletDetailApp.tsx +1505 -0
  327. package/src/components/apps/index.ts +13 -0
  328. package/src/components/design-system/Button.tsx +88 -0
  329. package/src/components/design-system/ChainIndicator.tsx +65 -0
  330. package/src/components/design-system/ChainSelector.tsx +147 -0
  331. package/src/components/design-system/ConfirmationModal.tsx +107 -0
  332. package/src/components/design-system/ConfirmationPopover.tsx +81 -0
  333. package/src/components/design-system/DownloadButton.tsx +149 -0
  334. package/src/components/design-system/Drawer.tsx +133 -0
  335. package/src/components/design-system/FilterDropdown.tsx +183 -0
  336. package/src/components/design-system/ItemPicker.tsx +157 -0
  337. package/src/components/design-system/Modal.tsx +296 -0
  338. package/src/components/design-system/Popover.tsx +142 -0
  339. package/src/components/design-system/TextInput.tsx +85 -0
  340. package/src/components/design-system/Toggle.tsx +65 -0
  341. package/src/components/design-system/TyvekCollapsibleSection.tsx +55 -0
  342. package/src/components/design-system/index.ts +14 -0
  343. package/src/components/docs/ClientSideMarkdown.tsx +51 -0
  344. package/src/components/docs/DocsSearchBar.tsx +118 -0
  345. package/src/components/docs/DocsThemeToggle.tsx +38 -0
  346. package/src/components/docs/PersistentDocGroup.tsx +91 -0
  347. package/src/components/docs/ShareUrlButton.tsx +33 -0
  348. package/src/components/docs/SidebarScrollMemory.tsx +56 -0
  349. package/src/components/health/CredentialHealthDashboard.tsx +214 -0
  350. package/src/components/icons/ChainIcons.tsx +72 -0
  351. package/src/components/layout/AppStoreDrawer.tsx +369 -0
  352. package/src/components/layout/ContentArea.tsx +21 -0
  353. package/src/components/layout/CreateViewModal.tsx +88 -0
  354. package/src/components/layout/LeftRail.tsx +114 -0
  355. package/src/components/layout/TabBar.tsx +284 -0
  356. package/src/components/layout/WalletSidebar.tsx +1030 -0
  357. package/src/components/layout/index.ts +6 -0
  358. package/src/components/marketing/AuraMaxxSpecOverlay.tsx +653 -0
  359. package/src/components/marketing/DeviceMorphExperience.tsx +216 -0
  360. package/src/components/vault/ApiKeysConsole.tsx +1272 -0
  361. package/src/components/vault/AuditConsole.tsx +600 -0
  362. package/src/components/vault/CredentialDetail.tsx +625 -0
  363. package/src/components/vault/CredentialEmpty.tsx +55 -0
  364. package/src/components/vault/CredentialField.tsx +583 -0
  365. package/src/components/vault/CredentialForm.tsx +1484 -0
  366. package/src/components/vault/CredentialList.tsx +265 -0
  367. package/src/components/vault/CredentialRow.tsx +130 -0
  368. package/src/components/vault/CredentialShareModal.tsx +273 -0
  369. package/src/components/vault/CredentialVault.tsx +1662 -0
  370. package/src/components/vault/CredentialWalletWidget.tsx +103 -0
  371. package/src/components/vault/DocsConsole.tsx +113 -0
  372. package/src/components/vault/ImportCredentialsModal.tsx +578 -0
  373. package/src/components/vault/LargeTypeModal.tsx +88 -0
  374. package/src/components/vault/PasswordGenerator.tsx +232 -0
  375. package/src/components/vault/TOTPDisplay.tsx +108 -0
  376. package/src/components/vault/TotpSetupPanel.tsx +198 -0
  377. package/src/components/vault/VaultSidebar.tsx +881 -0
  378. package/src/components/vault/credentialFormName.ts +91 -0
  379. package/src/components/vault/hooks/useVaultKeyboardShortcuts.ts +69 -0
  380. package/src/components/vault/types.ts +56 -0
  381. package/src/context/AuthContext.tsx +365 -0
  382. package/src/context/PriceContext.tsx +113 -0
  383. package/src/context/ThemeContext.tsx +164 -0
  384. package/src/context/WebSocketContext.tsx +269 -0
  385. package/src/context/WorkspaceContext.tsx +668 -0
  386. package/src/hooks/index.ts +4 -0
  387. package/src/hooks/useAgentActions.ts +552 -0
  388. package/src/hooks/useBalance.ts +103 -0
  389. package/src/hooks/useBalances.ts +129 -0
  390. package/src/hooks/useTheme.ts +156 -0
  391. package/src/instrumentation.ts +12 -0
  392. package/src/lib/api-docs.ts +154 -0
  393. package/src/lib/api.ts +474 -0
  394. package/src/lib/app-loader.ts +148 -0
  395. package/src/lib/app-registry.ts +178 -0
  396. package/src/lib/app-sdk.ts +157 -0
  397. package/src/lib/audit-console-adapter.ts +151 -0
  398. package/src/lib/auth-client.ts +75 -0
  399. package/src/lib/config.ts +74 -0
  400. package/src/lib/credential-field-schema.ts +11 -0
  401. package/src/lib/crypto.ts +112 -0
  402. package/src/lib/db.ts +21 -0
  403. package/src/lib/docs.ts +544 -0
  404. package/src/lib/events.ts +363 -0
  405. package/src/lib/pino.ts +24 -0
  406. package/src/lib/theme-handlers.ts +168 -0
  407. package/src/lib/theme.ts +351 -0
  408. package/src/lib/tokenData.ts +378 -0
  409. package/src/lib/totp-import.ts +57 -0
  410. package/src/lib/vault-crypto.ts +129 -0
  411. package/src/lib/view-registry.ts +57 -0
  412. package/src/lib/websocket-server.ts +302 -0
  413. package/src/lib/websocket-setup.ts +79 -0
  414. package/src/lib/wordlist.ts +2050 -0
  415. package/src/lib/workspace-handlers.ts +285 -0
  416. package/start.sh +170 -0
  417. package/tailwind.config.ts +99 -0
  418. package/tsconfig.json +42 -0
@@ -0,0 +1,1323 @@
1
+ /**
2
+ * auramaxx vault — Retrieve and manage credentials from the vault
3
+ */
4
+
5
+ import {
6
+ generateEphemeralKeypair,
7
+ bootstrapViaSocket,
8
+ bootstrapViaAuthRequest,
9
+ decryptWithPrivateKey,
10
+ createReadToken,
11
+ encryptToAgentPubkey,
12
+ type EphemeralKeypair,
13
+ type ProfileIssuanceSelection,
14
+ } from '../../lib/credential-transport';
15
+ import { spawn } from 'child_process';
16
+ import { serverUrl } from '../lib/http';
17
+ import { getErrorMessage } from '../../lib/error';
18
+ import {
19
+ evaluateProjectScopeAccess,
20
+ emitProjectScopeEvent,
21
+ type ProjectScopeMode,
22
+ } from '../../lib/project-scope';
23
+ import { printHelp } from '../lib/theme';
24
+ import { handlePermissionDenied } from '../lib/escalation';
25
+ import { maybeHandleLockError } from '../lib/lock-unlock-helper';
26
+ import {
27
+ canonicalizeCredentialFieldKey,
28
+ getCredentialPrimaryFieldKey,
29
+ normalizeCredentialFieldsForType,
30
+ } from '../../../shared/credential-field-schema';
31
+ import {
32
+ createSecretGist,
33
+ SecretGistError,
34
+ } from '../../lib/secret-gist-share';
35
+
36
+ // ── Auth ──────────────────────────────────────────────────────────────
37
+
38
+ async function getAuthToken(
39
+ keypair: EphemeralKeypair,
40
+ authSelection?: ProfileIssuanceSelection,
41
+ ): Promise<string> {
42
+ const envToken = process.env.AURA_TOKEN;
43
+ if (envToken) return envToken;
44
+
45
+ try {
46
+ return await bootstrapViaSocket('cli-vault', keypair);
47
+ } catch (socketErr) {
48
+ const socketMessage = getErrorMessage(socketErr).toLowerCase();
49
+ const indicatesLockedVault =
50
+ socketMessage.includes('wallet is locked') ||
51
+ socketMessage.includes('vault is locked') ||
52
+ socketMessage.includes('daemon is locked') ||
53
+ socketMessage.includes('unlock before socket auto-approve');
54
+ if (indicatesLockedVault) {
55
+ throw new Error('Vault is locked. Run `auramaxx unlock` and retry.');
56
+ }
57
+
58
+ if (authSelection?.profile) {
59
+ const result = await bootstrapViaAuthRequest(serverUrl(), 'cli-vault', keypair, {
60
+ ...authSelection,
61
+ noWait: true,
62
+ onStatus: (message) => console.error(message),
63
+ });
64
+ if (result.approveUrl) {
65
+ console.error(`Approve at: ${result.approveUrl}`);
66
+ }
67
+ console.error(`After approval, re-run with: AURA_TOKEN=<token> npx auramaxx vault ...`);
68
+ console.error(`Or use: npx auramaxx auth request --profile ${authSelection.profile} --raw-token`);
69
+ process.exit(1);
70
+ }
71
+
72
+ return bootstrapViaAuthRequest(serverUrl(), 'cli-vault', keypair, {
73
+ ...authSelection,
74
+ onStatus: (message) => console.error(message),
75
+ }).catch((authErr) => {
76
+ throw new Error(`${getErrorMessage(socketErr)}\n${getErrorMessage(authErr)}`);
77
+ });
78
+ }
79
+ }
80
+
81
+ async function getReadToken(
82
+ authToken: string,
83
+ keypair: EphemeralKeypair,
84
+ authSelection?: ProfileIssuanceSelection,
85
+ ): Promise<string> {
86
+ try {
87
+ return await createReadToken(serverUrl(), authToken, keypair, 'cli-vault-reader', authSelection);
88
+ } catch (error) {
89
+ const message = getErrorMessage(error);
90
+ if (message.includes('(401)') || message.includes('(403)')) {
91
+ // Profile-issued tokens may not have action:create; direct read can still succeed when secret:read exists.
92
+ return authToken;
93
+ }
94
+ throw error;
95
+ }
96
+ }
97
+
98
+ function decryptCredentialPayload(encrypted: string, keypair: EphemeralKeypair): DecryptedCredential {
99
+ const plaintext = decryptWithPrivateKey(encrypted, keypair.privateKeyPem);
100
+ return JSON.parse(plaintext) as DecryptedCredential;
101
+ }
102
+
103
+ // ── Types ─────────────────────────────────────────────────────────────
104
+
105
+ interface CredentialMeta {
106
+ id: string;
107
+ name: string;
108
+ type: string;
109
+ vaultId: string;
110
+ meta: Record<string, unknown>;
111
+ }
112
+
113
+ interface DecryptedCredential {
114
+ id: string;
115
+ vaultId: string;
116
+ type: string;
117
+ fields: Array<{ key: string; value: string; type?: string; sensitive?: boolean }>;
118
+ health?: {
119
+ status: string;
120
+ flags: { weak: boolean; reused: boolean; breached: boolean; unknown: boolean };
121
+ lastScannedAt: string | null;
122
+ };
123
+ }
124
+
125
+ interface CredentialHealthSummaryResponse {
126
+ summary: {
127
+ totalAnalyzed: number;
128
+ safe: number;
129
+ weak: number;
130
+ reused: number;
131
+ breached: number;
132
+ unknown: number;
133
+ lastScanAt: string | null;
134
+ };
135
+ }
136
+
137
+ interface VaultSummary {
138
+ id: string;
139
+ name?: string;
140
+ isPrimary?: boolean;
141
+ }
142
+
143
+ interface ShareCreateResponse {
144
+ success?: boolean;
145
+ share?: {
146
+ token: string;
147
+ credentialId: string;
148
+ expiresAt: number;
149
+ accessMode: 'anyone' | 'password';
150
+ oneTimeOnly: boolean;
151
+ };
152
+ error?: string;
153
+ }
154
+
155
+ // ── API helpers ───────────────────────────────────────────────────────
156
+
157
+ async function listCredentials(token: string, query?: string): Promise<CredentialMeta[]> {
158
+ const qs = query ? `?q=${encodeURIComponent(query)}` : '';
159
+ const res = await fetch(`${serverUrl()}/credentials${qs}`, {
160
+ headers: { 'Authorization': `Bearer ${token}` },
161
+ signal: AbortSignal.timeout(8_000),
162
+ });
163
+ if (!res.ok) {
164
+ const body = await res.json().catch(() => ({})) as unknown;
165
+ if (handlePermissionDenied(res.status, body)) process.exit(1);
166
+ throw new Error(`List failed (${res.status})`);
167
+ }
168
+ const data = await res.json() as { credentials?: CredentialMeta[] };
169
+ return data.credentials || [];
170
+ }
171
+
172
+ async function readCredential(
173
+ credentialId: string,
174
+ readToken: string,
175
+ keypair: EphemeralKeypair,
176
+ ): Promise<DecryptedCredential> {
177
+ const res = await fetch(`${serverUrl()}/credentials/${credentialId}/read`, {
178
+ method: 'POST',
179
+ headers: { 'Authorization': `Bearer ${readToken}` },
180
+ signal: AbortSignal.timeout(8_000),
181
+ });
182
+ if (!res.ok) {
183
+ const text = await res.text();
184
+ let body: unknown;
185
+ try { body = JSON.parse(text) as unknown; } catch { body = text; }
186
+ if (handlePermissionDenied(res.status, body)) process.exit(1);
187
+ throw new Error(`Read failed (${res.status}): ${text}`);
188
+ }
189
+
190
+ const data = await res.json() as { encrypted: string };
191
+ return decryptCredentialPayload(data.encrypted, keypair);
192
+ }
193
+
194
+ async function searchCredentials(token: string, name: string): Promise<CredentialMeta[]> {
195
+ for (const param of [`q=${encodeURIComponent(name)}`, `tag=${encodeURIComponent(name)}`]) {
196
+ const res = await fetch(`${serverUrl()}/credentials?${param}`, {
197
+ headers: { 'Authorization': `Bearer ${token}` },
198
+ signal: AbortSignal.timeout(8_000),
199
+ });
200
+ if (!res.ok) {
201
+ if (res.status === 403) {
202
+ const body = await res.json().catch(() => ({})) as unknown;
203
+ if (handlePermissionDenied(res.status, body)) process.exit(1);
204
+ }
205
+ continue;
206
+ }
207
+
208
+ const data = await res.json() as { credentials?: CredentialMeta[] };
209
+ if (data.credentials && data.credentials.length > 0) {
210
+ return data.credentials;
211
+ }
212
+ }
213
+ return [];
214
+ }
215
+
216
+ async function fetchTotpCode(credentialId: string, token: string): Promise<{ code: string; remaining: number }> {
217
+ const res = await fetch(`${serverUrl()}/credentials/${credentialId}/totp`, {
218
+ method: 'POST',
219
+ headers: { 'Authorization': `Bearer ${token}` },
220
+ signal: AbortSignal.timeout(8_000),
221
+ });
222
+ if (!res.ok) {
223
+ const data = await res.json().catch(() => ({ error: `HTTP ${res.status}` })) as { error?: string };
224
+ throw new Error(data.error || `TOTP request failed (${res.status})`);
225
+ }
226
+ return await res.json() as { code: string; remaining: number };
227
+ }
228
+
229
+ async function fetchVaults(): Promise<VaultSummary[]> {
230
+ const res = await fetch(`${serverUrl()}/setup/vaults`, {
231
+ signal: AbortSignal.timeout(8_000),
232
+ });
233
+ if (!res.ok) return [];
234
+ const data = await res.json() as { vaults?: VaultSummary[] };
235
+ return data.vaults || [];
236
+ }
237
+
238
+ async function fetchVaultNameMap(): Promise<Map<string, string>> {
239
+ const vaults = await fetchVaults();
240
+ return new Map(vaults.map((v) => [v.id, v.name || v.id]));
241
+ }
242
+
243
+ async function fetchHealthSummary(token: string): Promise<CredentialHealthSummaryResponse['summary']> {
244
+ const res = await fetch(`${serverUrl()}/credentials/health/summary`, {
245
+ headers: { 'Authorization': `Bearer ${token}` },
246
+ signal: AbortSignal.timeout(8_000),
247
+ });
248
+ if (!res.ok) {
249
+ const body = await res.json().catch(() => ({})) as unknown;
250
+ if (handlePermissionDenied(res.status, body)) process.exit(1);
251
+ throw new Error(`Health summary failed (${res.status})`);
252
+ }
253
+ const data = await res.json() as CredentialHealthSummaryResponse;
254
+ return data.summary;
255
+ }
256
+
257
+ async function createCredential(
258
+ token: string,
259
+ body: {
260
+ vaultId: string;
261
+ name: string;
262
+ type: string;
263
+ sensitiveFields: Array<{ key: string; value: string; sensitive?: boolean }>;
264
+ meta?: Record<string, unknown>;
265
+ },
266
+ ): Promise<CredentialMeta> {
267
+ const res = await fetch(`${serverUrl()}/credentials`, {
268
+ method: 'POST',
269
+ headers: {
270
+ 'Authorization': `Bearer ${token}`,
271
+ 'Content-Type': 'application/json',
272
+ },
273
+ body: JSON.stringify(body),
274
+ signal: AbortSignal.timeout(8_000),
275
+ });
276
+
277
+ const data = await res.json().catch(() => ({})) as {
278
+ success?: boolean;
279
+ error?: string;
280
+ credential?: CredentialMeta;
281
+ };
282
+
283
+ if (!res.ok || !data.success || !data.credential) {
284
+ if (handlePermissionDenied(res.status, data)) process.exit(1);
285
+ throw new Error(data.error || `Create failed (${res.status})`);
286
+ }
287
+
288
+ return data.credential;
289
+ }
290
+
291
+ async function updateCredential(
292
+ token: string,
293
+ credentialId: string,
294
+ body: {
295
+ sensitiveFields?: Array<{ key: string; value: string; sensitive?: boolean }>;
296
+ name?: string;
297
+ meta?: Record<string, unknown>;
298
+ },
299
+ ): Promise<CredentialMeta> {
300
+ const res = await fetch(`${serverUrl()}/credentials/${encodeURIComponent(credentialId)}`, {
301
+ method: 'PUT',
302
+ headers: {
303
+ 'Authorization': `Bearer ${token}`,
304
+ 'Content-Type': 'application/json',
305
+ },
306
+ body: JSON.stringify(body),
307
+ signal: AbortSignal.timeout(8_000),
308
+ });
309
+
310
+ const data = await res.json().catch(() => ({})) as {
311
+ success?: boolean;
312
+ error?: string;
313
+ credential?: CredentialMeta;
314
+ };
315
+
316
+ if (!res.ok || !data.success || !data.credential) {
317
+ if (handlePermissionDenied(res.status, data)) process.exit(1);
318
+ throw new Error(data.error || `Update failed (${res.status})`);
319
+ }
320
+
321
+ return data.credential;
322
+ }
323
+
324
+ async function deleteCredential(
325
+ token: string,
326
+ credentialId: string,
327
+ location: 'active' | 'archive' | 'recently_deleted' = 'active',
328
+ ): Promise<{ success: boolean; action?: string; deleted?: boolean; error?: string }> {
329
+ const qs = new URLSearchParams({ location }).toString();
330
+ const res = await fetch(`${serverUrl()}/credentials/${encodeURIComponent(credentialId)}?${qs}`, {
331
+ method: 'DELETE',
332
+ headers: { 'Authorization': `Bearer ${token}` },
333
+ signal: AbortSignal.timeout(8_000),
334
+ });
335
+ const data = await res.json().catch(() => ({})) as {
336
+ success?: boolean;
337
+ action?: string;
338
+ deleted?: boolean;
339
+ error?: string;
340
+ };
341
+ if (!res.ok) {
342
+ if (handlePermissionDenied(res.status, data)) process.exit(1);
343
+ throw new Error(data.error || `Delete failed (${res.status})`);
344
+ }
345
+ return { success: data.success === true, action: data.action, deleted: data.deleted };
346
+ }
347
+
348
+ async function createShare(
349
+ token: string,
350
+ body: {
351
+ credentialId: string;
352
+ expiresAfter?: string;
353
+ accessMode?: 'anyone' | 'password';
354
+ password?: string;
355
+ oneTimeOnly?: boolean;
356
+ },
357
+ ): Promise<ShareCreateResponse['share']> {
358
+ const res = await fetch(`${serverUrl()}/credential-shares`, {
359
+ method: 'POST',
360
+ headers: {
361
+ 'Authorization': `Bearer ${token}`,
362
+ 'Content-Type': 'application/json',
363
+ },
364
+ body: JSON.stringify(body),
365
+ signal: AbortSignal.timeout(8_000),
366
+ });
367
+
368
+ const data = await res.json().catch(() => ({})) as ShareCreateResponse;
369
+ if (!res.ok || !data.success || !data.share) {
370
+ if (handlePermissionDenied(res.status, data)) process.exit(1);
371
+ throw new Error(data.error || `Share create failed (${res.status})`);
372
+ }
373
+ return data.share;
374
+ }
375
+
376
+ function resolveShareUrlForGist(shareToken: string): string {
377
+ const configuredBase = String(process.env.AURA_SHARE_BASE_URL || '').trim();
378
+ if (configuredBase) {
379
+ try {
380
+ const parsed = new URL(configuredBase);
381
+ if (parsed.protocol === 'http:' || parsed.protocol === 'https:') {
382
+ const normalizedBase = configuredBase.replace(/\/+$/, '');
383
+ return `${normalizedBase}/share/${shareToken}`;
384
+ }
385
+ } catch {
386
+ // fall back to API share URL below when base URL is invalid
387
+ }
388
+ }
389
+ return `${serverUrl()}/credential-shares/${shareToken}`;
390
+ }
391
+
392
+ // ── Scope + target resolution ─────────────────────────────────────────
393
+
394
+ function normalizeProjectScopeMode(raw: unknown): ProjectScopeMode {
395
+ const value = String(raw || '').trim().toLowerCase();
396
+ if (value === 'strict') return 'strict';
397
+ if (value === 'off') return 'off';
398
+ return 'auto';
399
+ }
400
+
401
+ async function fetchProjectScopeMode(): Promise<ProjectScopeMode> {
402
+ try {
403
+ const res = await fetch(`${serverUrl()}/setup`, { signal: AbortSignal.timeout(5_000) });
404
+ if (!res.ok) return 'auto';
405
+ const data = await res.json() as { projectScopeMode?: unknown };
406
+ return normalizeProjectScopeMode(data.projectScopeMode);
407
+ } catch {
408
+ return 'auto';
409
+ }
410
+ }
411
+
412
+ async function resolveCredentialTarget(
413
+ token: string,
414
+ name: string,
415
+ options: {
416
+ vaultName?: string;
417
+ first?: boolean;
418
+ surface: string;
419
+ actor: string;
420
+ },
421
+ ): Promise<{ target: CredentialMeta; vaultNames: Map<string, string> }> {
422
+ const matches = await searchCredentials(token, name);
423
+ if (matches.length === 0) {
424
+ throw new Error(`No credential found matching "${name}"`);
425
+ }
426
+
427
+ const exactMatches = matches.filter((m) => m.name.toLowerCase() === name.toLowerCase());
428
+ const candidateMatches = exactMatches.length > 0 ? exactMatches : matches;
429
+
430
+ const vaultNames = await fetchVaultNameMap();
431
+ const projectScopeMode = await fetchProjectScopeMode();
432
+
433
+ const scopedDecision = evaluateProjectScopeAccess({
434
+ surface: options.surface,
435
+ requested: { vaultName: options.vaultName || null, credentialName: name },
436
+ candidates: candidateMatches.map((m) => ({
437
+ id: m.id,
438
+ name: m.name,
439
+ vaultName: vaultNames.get(m.vaultId) || null,
440
+ })),
441
+ actor: options.actor,
442
+ projectScopeMode,
443
+ });
444
+
445
+ emitProjectScopeEvent({
446
+ actor: options.actor,
447
+ surface: options.surface,
448
+ requestedCredential: { vaultName: options.vaultName || null, credentialName: name },
449
+ decision: scopedDecision,
450
+ });
451
+
452
+ if (!scopedDecision.allowed) {
453
+ throw new Error(`${scopedDecision.code}: ${scopedDecision.remediation}`);
454
+ }
455
+
456
+ const allowedIds = new Set(scopedDecision.allowedCandidates.map((c) => c.id).filter(Boolean));
457
+ const scopedMatches = candidateMatches.filter((m) => allowedIds.has(m.id));
458
+ const vaultFiltered = options.vaultName
459
+ ? scopedMatches.filter((m) => (vaultNames.get(m.vaultId) || '').toLowerCase() === options.vaultName!.toLowerCase())
460
+ : scopedMatches;
461
+
462
+ if (vaultFiltered.length === 0) {
463
+ throw new Error(`PROJECT_SCOPE_DENIED: No allowed credential found for "${name}" in vault "${options.vaultName}".`);
464
+ }
465
+
466
+ if (vaultFiltered.length > 1 && !options.first) {
467
+ const lines = [`Multiple credentials match "${name}":`];
468
+ for (const m of vaultFiltered) {
469
+ const resolvedVault = vaultNames.get(m.vaultId) || m.vaultId;
470
+ lines.push(` - ${m.name} (${m.type}, vault: ${resolvedVault}, id: ${m.id})`);
471
+ }
472
+ lines.push('Use --first to select the first match, or specify --vault.');
473
+ throw new Error(lines.join('\n'));
474
+ }
475
+
476
+ return { target: vaultFiltered[0], vaultNames };
477
+ }
478
+
479
+ function resolveVaultId(vaults: VaultSummary[], vaultName?: string): string {
480
+ if (vaults.length === 0) {
481
+ throw new Error('No vaults found. Initialize setup first.');
482
+ }
483
+
484
+ if (vaultName) {
485
+ const found = vaults.find((v) => (v.name || '').toLowerCase() === vaultName.toLowerCase());
486
+ if (!found) {
487
+ throw new Error(`Vault not found: ${vaultName}`);
488
+ }
489
+ return found.id;
490
+ }
491
+
492
+ const primary = vaults.find((v) => v.isPrimary);
493
+ if (primary) return primary.id;
494
+ return vaults[0].id;
495
+ }
496
+
497
+ // ── CLI parsing ────────────────────────────────────────────────────────
498
+
499
+ function showHelp(): void {
500
+ printHelp('VAULT', 'npx auramaxx vault <subcommand> [options]', [
501
+ { name: 'list', desc: 'List credential names (supports --name/--field filters)' },
502
+ { name: 'get <name>', desc: 'Print primary value only (use --json for full payload)' },
503
+ { name: 'inject <name> [-- <cmd>]', desc: 'Save primary secret to env var and optionally run command' },
504
+ { name: 'set <name> <value>', desc: 'Upsert secret (default type: api key, default field: value)' },
505
+ { name: 'share <name>', desc: 'Create GitHub secret gist share for credential by name' },
506
+ { name: 'delete <name>', desc: 'Delete credential by name (lifecycle delete)' },
507
+ { name: 'health', desc: 'Print credential health summary' },
508
+ ], [
509
+ 'Options:',
510
+ ' --field <f> get/set: field key override; list: field key/value contains query',
511
+ ' --name <v> list: credential name/title contains query',
512
+ ' --env <v> inject: env var override',
513
+ ' --type <t> Set type (set only; default: api key)',
514
+ ' --tags <a,b,c> Comma-separated tags (set only)',
515
+ ' --vault <v> Restrict to vault name',
516
+ ' --json JSON output',
517
+ ' --first Use first match if ambiguous',
518
+ ' --totp Print current TOTP code only (get)',
519
+ ' --expires-after <v> Share expiry: 15m|1h|24h|7d|30d (default: 24h)',
520
+ ' --password <pwd> Share password (implies accessMode=password)',
521
+ ' --one-time Share one-time-only access',
522
+ ' --location <v> Delete location: active|archive|recently_deleted',
523
+ ' --profile <p> /auth fallback profile when socket is blocked',
524
+ ' --profile-version <v> /auth fallback profile version',
525
+ ' --profile-overrides <j> Tighten-only profile override JSON',
526
+ '',
527
+ 'Examples:',
528
+ ' npx auramaxx vault list --name prod',
529
+ ' npx auramaxx vault list --name prod --field token',
530
+ '',
531
+ 'Auth: Uses Unix socket by default. Falls back to /auth polling when strict local mode blocks auto-approve.',
532
+ ' Set AURA_TOKEN for headless/CI.',
533
+ ]);
534
+ }
535
+
536
+ export function parseArgs(args: string[]): {
537
+ subcommand: string | undefined;
538
+ flagJson: boolean;
539
+ flagFirst: boolean;
540
+ flagTotp: boolean;
541
+ flagOneTime: boolean;
542
+ fieldName: string | undefined;
543
+ secretEnvName: string | undefined;
544
+ vaultName: string | undefined;
545
+ expiresAfter: string | undefined;
546
+ sharePassword: string | undefined;
547
+ deleteLocation: string | undefined;
548
+ authProfile: string | undefined;
549
+ authProfileVersion: string | undefined;
550
+ authProfileOverrides: string | undefined;
551
+ typeName: string | undefined;
552
+ tags: string[] | undefined;
553
+ extraFields: Array<{ key: string; value: string }>;
554
+ positional: string[];
555
+ execCommand: string[];
556
+ } {
557
+ const separatorIdx = args.indexOf('--');
558
+ const parseableArgs = separatorIdx === -1 ? args : args.slice(0, separatorIdx);
559
+ const execCommand = separatorIdx === -1 ? [] : args.slice(separatorIdx + 1);
560
+
561
+ const subcommand = parseableArgs[0];
562
+ const flagJson = parseableArgs.includes('--json');
563
+ const flagFirst = parseableArgs.includes('--first');
564
+ const flagTotp = parseableArgs.includes('--totp');
565
+ const flagOneTime = parseableArgs.includes('--one-time');
566
+ const fieldIdx = parseableArgs.indexOf('--field');
567
+ const fieldName = fieldIdx !== -1 ? parseableArgs[fieldIdx + 1] : undefined;
568
+ const secretEnvIdx = parseableArgs.indexOf('--env');
569
+ const legacySecretEnvIdx = parseableArgs.indexOf('--name');
570
+ const secretEnvName = secretEnvIdx !== -1
571
+ ? parseableArgs[secretEnvIdx + 1]
572
+ : (legacySecretEnvIdx !== -1 ? parseableArgs[legacySecretEnvIdx + 1] : undefined);
573
+ const vaultIdx = parseableArgs.indexOf('--vault');
574
+ const vaultName = vaultIdx !== -1 ? parseableArgs[vaultIdx + 1] : undefined;
575
+ const expiresAfterIdx = parseableArgs.indexOf('--expires-after');
576
+ const expiresAfter = expiresAfterIdx !== -1 ? parseableArgs[expiresAfterIdx + 1] : undefined;
577
+ const passwordIdx = parseableArgs.indexOf('--password');
578
+ const sharePassword = passwordIdx !== -1 ? parseableArgs[passwordIdx + 1] : undefined;
579
+ const locationIdx = parseableArgs.indexOf('--location');
580
+ const deleteLocation = locationIdx !== -1 ? parseableArgs[locationIdx + 1] : undefined;
581
+ const profileIdx = parseableArgs.indexOf('--profile');
582
+ const authProfile = profileIdx !== -1 ? parseableArgs[profileIdx + 1] : undefined;
583
+ const profileVersionIdx = parseableArgs.indexOf('--profile-version');
584
+ const authProfileVersion = profileVersionIdx !== -1 ? parseableArgs[profileVersionIdx + 1] : undefined;
585
+ const profileOverridesIdx = parseableArgs.indexOf('--profile-overrides');
586
+ const authProfileOverrides = profileOverridesIdx !== -1 ? parseableArgs[profileOverridesIdx + 1] : undefined;
587
+ const typeIdx = parseableArgs.indexOf('--type');
588
+ const typeName = typeIdx !== -1 ? parseableArgs[typeIdx + 1] : undefined;
589
+ const tagsIdx = parseableArgs.indexOf('--tags');
590
+ const tagsRaw = tagsIdx !== -1 ? parseableArgs[tagsIdx + 1] : undefined;
591
+ const tags = tagsRaw
592
+ ? Array.from(new Set(tagsRaw.split(',').map((t) => t.trim().toLowerCase()).filter(Boolean)))
593
+ : undefined;
594
+
595
+ const knownValueFlags = new Set([
596
+ '--field', '--name', '--env', '--vault', '--expires-after', '--password', '--location', '--profile', '--profile-version', '--profile-overrides', '--type', '--tags',
597
+ ]);
598
+ const knownBooleanFlags = new Set(['--json', '--first', '--totp', '--one-time']);
599
+
600
+ const positional: string[] = [];
601
+ const extraFields: Array<{ key: string; value: string }> = [];
602
+ for (let i = 1; i < parseableArgs.length; i++) {
603
+ const arg = parseableArgs[i];
604
+ if (!arg) continue;
605
+ if (!arg.startsWith('--')) {
606
+ positional.push(arg);
607
+ continue;
608
+ }
609
+
610
+ if (knownValueFlags.has(arg)) {
611
+ i++;
612
+ continue;
613
+ }
614
+
615
+ if (knownBooleanFlags.has(arg)) {
616
+ continue;
617
+ }
618
+
619
+ const key = arg.slice(2).trim();
620
+ if (!key) continue;
621
+
622
+ const value = parseableArgs[i + 1];
623
+ if (!value || value.startsWith('--')) {
624
+ continue;
625
+ }
626
+
627
+ extraFields.push({ key, value });
628
+ i++;
629
+ }
630
+
631
+ return {
632
+ subcommand,
633
+ flagJson,
634
+ flagFirst,
635
+ flagTotp,
636
+ flagOneTime,
637
+ fieldName,
638
+ secretEnvName,
639
+ vaultName,
640
+ expiresAfter,
641
+ sharePassword,
642
+ deleteLocation,
643
+ authProfile,
644
+ authProfileVersion,
645
+ authProfileOverrides,
646
+ typeName,
647
+ tags,
648
+ extraFields,
649
+ positional,
650
+ execCommand,
651
+ };
652
+ }
653
+
654
+ type CredentialField = { key: string; value: string; type?: string; sensitive?: boolean };
655
+
656
+ const TYPE_ALIASES: Record<string, string> = {
657
+ 'api key': 'apikey',
658
+ 'api-key': 'apikey',
659
+ api_key: 'apikey',
660
+ };
661
+
662
+ function normalizeCredentialType(type: string | undefined): string {
663
+ const normalized = String(type || '').trim().toLowerCase();
664
+ if (!normalized) return 'apikey';
665
+ return TYPE_ALIASES[normalized] || normalized;
666
+ }
667
+
668
+ function resolvePrimaryField(type: string, fields: CredentialField[]): CredentialField | undefined {
669
+ const defaultFieldKey = getCredentialPrimaryFieldKey(type);
670
+ return fields.find((f) => f.key.toLowerCase() === defaultFieldKey.toLowerCase())
671
+ || fields.find((f) => f.key.toLowerCase() === 'value')
672
+ || fields[0];
673
+ }
674
+
675
+ function resolvePrimarySecretField(type: string, fields: CredentialField[]): CredentialField | undefined {
676
+ const defaultFieldKey = getCredentialPrimaryFieldKey(type).toLowerCase();
677
+ return fields.find((f) => f.key.toLowerCase() === defaultFieldKey && f.sensitive !== false)
678
+ || fields.find((f) => f.sensitive !== false);
679
+ }
680
+
681
+ function shouldAutoDecryptSensitiveValues(): boolean {
682
+ return String(process.env.AUTO_DECRYPT || '').trim().toLowerCase() === 'true'
683
+ && String(process.env.AURA_VAULT_PASSWORD || '').trim().length > 0;
684
+ }
685
+
686
+ function isSensitiveField(field: CredentialField): boolean {
687
+ return field.sensitive !== false;
688
+ }
689
+
690
+ function toMetaBackedFields(meta: Record<string, unknown> | undefined): CredentialField[] {
691
+ if (!meta || typeof meta !== 'object') return [];
692
+ const out: CredentialField[] = [];
693
+ for (const [key, rawValue] of Object.entries(meta)) {
694
+ if (rawValue === undefined || rawValue === null) continue;
695
+
696
+ if (typeof rawValue === 'string' || typeof rawValue === 'number' || typeof rawValue === 'boolean') {
697
+ out.push({ key, value: String(rawValue), type: 'text', sensitive: false });
698
+ continue;
699
+ }
700
+
701
+ if (Array.isArray(rawValue) && rawValue.every((v) => typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean')) {
702
+ out.push({ key, value: rawValue.map((v) => String(v)).join(','), type: 'text', sensitive: false });
703
+ }
704
+ }
705
+ return out;
706
+ }
707
+
708
+ function mergeCredentialFields(
709
+ type: string,
710
+ decryptedFields: CredentialField[],
711
+ meta: Record<string, unknown> | undefined,
712
+ ): CredentialField[] {
713
+ const out = [...decryptedFields];
714
+ const known = new Set(out.map((field) => field.key.toLowerCase()));
715
+ for (const metaField of normalizeCredentialFieldsForType(type, toMetaBackedFields(meta))) {
716
+ const key = metaField.key.toLowerCase();
717
+ if (known.has(key)) continue;
718
+ known.add(key);
719
+ out.push(metaField);
720
+ }
721
+ return out;
722
+ }
723
+
724
+ function findField(type: string, fields: CredentialField[], key: string): CredentialField | undefined {
725
+ const normalizedKey = key.trim().toLowerCase();
726
+ if (!normalizedKey) return undefined;
727
+ const direct = fields.find((field) => field.key.toLowerCase() === normalizedKey);
728
+ if (direct) return direct;
729
+ const canonicalKey = canonicalizeCredentialFieldKey(type, key);
730
+ return fields.find((field) => field.key.toLowerCase() === canonicalKey.toLowerCase());
731
+ }
732
+
733
+ function normalizeSecretExecEnvName(input: string | undefined): string | null {
734
+ const candidate = String(input || '').trim();
735
+ if (!candidate) return null;
736
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(candidate)) return null;
737
+ return candidate;
738
+ }
739
+
740
+ function normalizeListFilterQuery(input: string | undefined): string | null {
741
+ const candidate = String(input || '').trim().toLowerCase();
742
+ return candidate.length > 0 ? candidate : null;
743
+ }
744
+
745
+ function matchesListNameFilter(credential: CredentialMeta, query: string): boolean {
746
+ if (credential.name.toLowerCase().includes(query)) return true;
747
+ const title = typeof credential.meta?.title === 'string' ? credential.meta.title.toLowerCase() : '';
748
+ return title.includes(query);
749
+ }
750
+
751
+ function matchesListFieldFilter(
752
+ credential: CredentialMeta,
753
+ decrypted: DecryptedCredential,
754
+ query: string,
755
+ ): boolean {
756
+ const normalizedType = normalizeCredentialType(decrypted.type || credential.type);
757
+ const mergedFields = mergeCredentialFields(normalizedType, decrypted.fields, credential.meta);
758
+ return mergedFields.some((field) => {
759
+ const keyMatch = field.key.toLowerCase().includes(query);
760
+ const valueMatch = field.value.toLowerCase().includes(query);
761
+ return keyMatch || valueMatch;
762
+ });
763
+ }
764
+
765
+ const SECRET_EXEC_USAGE = 'Usage: npx auramaxx secret exec <name> [--env ENV_VAR] [-- <command>]';
766
+ const SECRET_INJECT_USAGE = 'Usage: npx auramaxx inject <name> [--env ENV_VAR] [-- <command>]';
767
+
768
+ async function runSecretExec(command: string[], envVarName: string, secretValue: string): Promise<number> {
769
+ if (command.length === 0) {
770
+ process.env[envVarName] = secretValue;
771
+ console.log(`Saved to env variable ${envVarName}.`);
772
+ console.log("Scope: current CLI process only. Use '-- <command>' to inject into a child command.");
773
+ return 0;
774
+ }
775
+
776
+ return await new Promise<number>((resolve) => {
777
+ const child = spawn(command[0], command.slice(1), {
778
+ stdio: 'inherit',
779
+ env: {
780
+ ...process.env,
781
+ [envVarName]: secretValue,
782
+ },
783
+ });
784
+
785
+ child.on('error', (error) => {
786
+ console.error(`Failed to execute command: ${getErrorMessage(error)}`);
787
+ resolve(1);
788
+ });
789
+
790
+ child.on('exit', (code, signal) => {
791
+ if (signal) {
792
+ resolve(1);
793
+ return;
794
+ }
795
+ resolve(typeof code === 'number' ? code : 1);
796
+ });
797
+ });
798
+ }
799
+
800
+ function resolveSetType(typeName: string | undefined): { requestType: string; normalizedType: string; defaultFieldKey: string } {
801
+ const normalizedType = normalizeCredentialType(typeName);
802
+ const requestType = ['login', 'card', 'note', 'plain_note', 'hot_wallet', 'api', 'apikey', 'custom', 'passkey', 'oauth2', 'ssh', 'gpg'].includes(normalizedType)
803
+ ? normalizedType
804
+ : 'custom';
805
+ const defaultFieldKey = getCredentialPrimaryFieldKey(normalizedType);
806
+ return { requestType, normalizedType, defaultFieldKey };
807
+ }
808
+
809
+ // ── Output formatting ──────────────────────────────────────────────────
810
+
811
+ export function formatCredential(
812
+ target: { name: string; type: string; id: string; vaultId?: string; meta?: Record<string, unknown> },
813
+ decrypted: DecryptedCredential,
814
+ opts: {
815
+ json: boolean;
816
+ fieldName?: string;
817
+ autoDecryptSensitive?: boolean;
818
+ encryptSensitiveValue?: (value: string) => string;
819
+ },
820
+ ): { output: string; exitCode: number } {
821
+ if (opts.json) {
822
+ return {
823
+ output: JSON.stringify({
824
+ name: target.name,
825
+ type: target.type,
826
+ id: target.id,
827
+ vaultId: decrypted.vaultId || target.vaultId,
828
+ meta: target.meta || {},
829
+ health: decrypted.health,
830
+ fields: decrypted.fields,
831
+ }, null, 2),
832
+ exitCode: 0,
833
+ };
834
+ }
835
+
836
+ const resolvedType = normalizeCredentialType(decrypted.type || target.type);
837
+ const allFields = mergeCredentialFields(resolvedType, decrypted.fields, target.meta);
838
+ const autoDecryptSensitive = opts.autoDecryptSensitive ?? true;
839
+ const serializeFieldValue = (field: CredentialField): string => {
840
+ if (!isSensitiveField(field) || autoDecryptSensitive) {
841
+ return field.value;
842
+ }
843
+ if (!opts.encryptSensitiveValue) {
844
+ return field.value;
845
+ }
846
+ return opts.encryptSensitiveValue(field.value);
847
+ };
848
+
849
+ if (opts.fieldName) {
850
+ const field = findField(resolvedType, allFields, opts.fieldName);
851
+ if (!field) {
852
+ return {
853
+ output: `Field "${opts.fieldName}" not found. Available: ${allFields.map((f) => f.key).join(', ')}`,
854
+ exitCode: 1,
855
+ };
856
+ }
857
+ return { output: serializeFieldValue(field), exitCode: 0 };
858
+ }
859
+
860
+ const primary = resolvePrimaryField(resolvedType, allFields);
861
+ if (!primary) {
862
+ return {
863
+ output: 'No fields found on credential.',
864
+ exitCode: 1,
865
+ };
866
+ }
867
+
868
+ return {
869
+ output: serializeFieldValue(primary),
870
+ exitCode: 0,
871
+ };
872
+ }
873
+
874
+ // ── Main command ───────────────────────────────────────────────────────
875
+
876
+ export async function runVaultCli(args: string[]): Promise<number> {
877
+ const {
878
+ subcommand,
879
+ flagJson,
880
+ flagFirst,
881
+ flagTotp,
882
+ flagOneTime,
883
+ fieldName,
884
+ secretEnvName,
885
+ vaultName,
886
+ expiresAfter,
887
+ sharePassword,
888
+ deleteLocation,
889
+ authProfile,
890
+ authProfileVersion,
891
+ authProfileOverrides,
892
+ typeName,
893
+ tags,
894
+ extraFields,
895
+ positional,
896
+ execCommand,
897
+ } = parseArgs(args);
898
+
899
+ if (!subcommand || subcommand === '--help' || subcommand === '-h') {
900
+ showHelp();
901
+ return 0;
902
+ }
903
+
904
+ const keypair = generateEphemeralKeypair();
905
+ let profileOverrides: ProfileIssuanceSelection['profileOverrides'] | undefined;
906
+ if (authProfileOverrides) {
907
+ try {
908
+ const parsed = JSON.parse(authProfileOverrides) as unknown;
909
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
910
+ throw new Error('must be a JSON object');
911
+ }
912
+ profileOverrides = parsed as ProfileIssuanceSelection['profileOverrides'];
913
+ } catch (error) {
914
+ console.error(`Invalid --profile-overrides: ${getErrorMessage(error)}`);
915
+ return 1;
916
+ }
917
+ }
918
+
919
+ const authSelection: ProfileIssuanceSelection = {
920
+ ...(authProfile ? { profile: authProfile } : {}),
921
+ ...(authProfileVersion ? { profileVersion: authProfileVersion } : {}),
922
+ ...(profileOverrides ? { profileOverrides } : {}),
923
+ };
924
+
925
+ try {
926
+ const token = await getAuthToken(keypair, authSelection);
927
+
928
+ if (subcommand === 'list') {
929
+ const listNameQuery = normalizeListFilterQuery(secretEnvName);
930
+ const listFieldQuery = normalizeListFilterQuery(fieldName);
931
+
932
+ let credentials = await listCredentials(token);
933
+ if (listNameQuery) {
934
+ credentials = credentials.filter((credential) => matchesListNameFilter(credential, listNameQuery));
935
+ }
936
+
937
+ if (listFieldQuery) {
938
+ const readToken = await getReadToken(token, keypair, authSelection);
939
+ const fieldMatches = await Promise.all(credentials.map(async (credential) => {
940
+ try {
941
+ const decrypted = await readCredential(credential.id, readToken, keypair);
942
+ return matchesListFieldFilter(credential, decrypted, listFieldQuery);
943
+ } catch {
944
+ // Best effort: unreadable credentials are excluded from field-filtered results.
945
+ return false;
946
+ }
947
+ }));
948
+ credentials = credentials.filter((_credential, idx) => fieldMatches[idx]);
949
+ }
950
+
951
+ if (flagJson) {
952
+ console.log(JSON.stringify(credentials.map((c) => ({ name: c.name, type: c.type, id: c.id })), null, 2));
953
+ } else if (credentials.length === 0) {
954
+ console.log('No credentials found.');
955
+ } else {
956
+ for (const c of credentials) {
957
+ console.log(`${c.name} (${c.type})`);
958
+ }
959
+ }
960
+ return 0;
961
+ }
962
+
963
+ if (subcommand === 'health') {
964
+ const summary = await fetchHealthSummary(token);
965
+ if (flagJson) {
966
+ console.log(JSON.stringify(summary, null, 2));
967
+ } else {
968
+ console.log(`Analyzed: ${summary.totalAnalyzed}`);
969
+ console.log(`Safe: ${summary.safe}`);
970
+ console.log(`Weak: ${summary.weak}`);
971
+ console.log(`Reused: ${summary.reused}`);
972
+ console.log(`Breached: ${summary.breached}`);
973
+ console.log(`Unknown: ${summary.unknown}`);
974
+ }
975
+ return 0;
976
+ }
977
+
978
+ if (subcommand === 'secret' || subcommand === 'inject' || subcommand === 'use') {
979
+ const isInjectShortcut = subcommand === 'inject' || subcommand === 'use';
980
+ const action = isInjectShortcut ? 'exec' : positional[0];
981
+ const name = isInjectShortcut ? positional[0] : positional[1];
982
+ if (action !== 'exec' || !name) {
983
+ console.error(isInjectShortcut ? SECRET_INJECT_USAGE : SECRET_EXEC_USAGE);
984
+ return 1;
985
+ }
986
+
987
+ const envVarName = normalizeSecretExecEnvName(secretEnvName || 'AURA_SECRET');
988
+ if (!envVarName) {
989
+ console.error('Invalid --env. Expected shell env var format like AURA_SECRET or GITHUB_PAT.');
990
+ return 1;
991
+ }
992
+
993
+ const { target } = await resolveCredentialTarget(token, name, {
994
+ vaultName,
995
+ first: flagFirst,
996
+ surface: 'cli_secret_exec',
997
+ actor: 'cli-vault',
998
+ });
999
+
1000
+ const readToken = await getReadToken(token, keypair, authSelection);
1001
+ const decrypted = await readCredential(target.id, readToken, keypair);
1002
+ const primarySecret = resolvePrimarySecretField(
1003
+ normalizeCredentialType(decrypted.type || target.type),
1004
+ decrypted.fields,
1005
+ );
1006
+ if (!primarySecret) {
1007
+ console.error('No sensitive field found on credential.');
1008
+ return 1;
1009
+ }
1010
+
1011
+ return await runSecretExec(execCommand, envVarName, primarySecret.value);
1012
+ }
1013
+
1014
+ if (subcommand === 'get') {
1015
+ const name = positional[0];
1016
+ if (!name) {
1017
+ console.error('Usage: npx auramaxx vault get <name>');
1018
+ return 1;
1019
+ }
1020
+
1021
+ const { target } = await resolveCredentialTarget(token, name, {
1022
+ vaultName,
1023
+ first: flagFirst,
1024
+ surface: 'cli_vault_get',
1025
+ actor: 'cli-vault',
1026
+ });
1027
+
1028
+ if (flagTotp) {
1029
+ const totp = await fetchTotpCode(target.id, token);
1030
+ process.stdout.write(totp.code);
1031
+ return 0;
1032
+ }
1033
+
1034
+ const readToken = await getReadToken(token, keypair, authSelection);
1035
+ const decrypted = await readCredential(target.id, readToken, keypair);
1036
+
1037
+ const hasTotpField = decrypted.fields.some((f) => f.key === 'totp' || f.key === 'otp');
1038
+ if (hasTotpField) {
1039
+ try {
1040
+ const totp = await fetchTotpCode(target.id, token);
1041
+ decrypted.fields.push({ key: 'totp_code', value: totp.code, type: 'text', sensitive: false });
1042
+ } catch {
1043
+ // ignore TOTP enrichment failure
1044
+ }
1045
+ }
1046
+
1047
+ const autoDecryptSensitive = shouldAutoDecryptSensitiveValues();
1048
+ const result = formatCredential(target, decrypted, {
1049
+ json: flagJson,
1050
+ fieldName,
1051
+ autoDecryptSensitive,
1052
+ encryptSensitiveValue: (value: string) => encryptToAgentPubkey(value, keypair.publicKeyPem),
1053
+ });
1054
+ if (result.exitCode !== 0) {
1055
+ console.error(result.output);
1056
+ return result.exitCode;
1057
+ }
1058
+
1059
+ if (fieldName) {
1060
+ process.stdout.write(result.output);
1061
+ } else {
1062
+ console.log(result.output);
1063
+ }
1064
+ return 0;
1065
+ }
1066
+
1067
+ if (subcommand === 'set') {
1068
+ const name = positional[0];
1069
+ const value = positional[1];
1070
+ const resolvedType = resolveSetType(typeName);
1071
+ const fieldKey = fieldName || resolvedType.defaultFieldKey;
1072
+
1073
+ if (!name || value === undefined) {
1074
+ console.error('Usage: npx auramaxx vault set <name> <value> [--type <type>] [--field <key>] [--tags a,b,c] [--vault <name>] [--another-field <value>]');
1075
+ return 1;
1076
+ }
1077
+
1078
+ const setFields: Array<{ key: string; value: string; sensitive?: boolean }> = [
1079
+ { key: fieldKey, value, sensitive: true },
1080
+ ...extraFields.map((f) => ({ key: f.key, value: f.value, sensitive: true })),
1081
+ ];
1082
+ const nextMeta = tags ? { tags } : undefined;
1083
+
1084
+ const matches = await searchCredentials(token, name);
1085
+ if (matches.length > 0) {
1086
+ const { target } = await resolveCredentialTarget(token, name, {
1087
+ vaultName,
1088
+ first: flagFirst,
1089
+ surface: 'cli_vault_set',
1090
+ actor: 'cli-vault',
1091
+ });
1092
+
1093
+ let nextFields: Array<{ key: string; value: string; type?: string; sensitive?: boolean }> = [...setFields];
1094
+
1095
+ // Preserve existing fields when read scope is available.
1096
+ try {
1097
+ const readToken = await getReadToken(token, keypair, authSelection);
1098
+ const decrypted = await readCredential(target.id, readToken, keypair);
1099
+ const existing = [...decrypted.fields];
1100
+ for (const next of setFields) {
1101
+ const index = existing.findIndex((f) => f.key.toLowerCase() === next.key.toLowerCase());
1102
+ if (index >= 0) {
1103
+ existing[index] = { ...existing[index], key: next.key, value: next.value, sensitive: true };
1104
+ } else {
1105
+ existing.push({ key: next.key, value: next.value, sensitive: true });
1106
+ }
1107
+ }
1108
+ nextFields = existing;
1109
+ } catch {
1110
+ // write-only tokens may not be allowed to read; replace with single field in that case.
1111
+ }
1112
+
1113
+ const updated = await updateCredential(token, target.id, {
1114
+ sensitiveFields: nextFields.map((f) => ({ key: f.key, value: f.value, sensitive: true })),
1115
+ ...(nextMeta ? { meta: nextMeta } : {}),
1116
+ });
1117
+
1118
+ if (flagJson) {
1119
+ console.log(JSON.stringify({ success: true, action: 'updated', credential: updated }, null, 2));
1120
+ } else {
1121
+ console.log(`Updated ${updated.name} (${updated.id})`);
1122
+ }
1123
+ return 0;
1124
+ }
1125
+
1126
+ const vaults = await fetchVaults();
1127
+ const vaultId = resolveVaultId(vaults, vaultName);
1128
+ const created = await createCredential(token, {
1129
+ vaultId,
1130
+ type: resolvedType.requestType,
1131
+ name,
1132
+ sensitiveFields: setFields,
1133
+ ...(nextMeta ? { meta: nextMeta } : {}),
1134
+ });
1135
+
1136
+ if (flagJson) {
1137
+ console.log(JSON.stringify({ success: true, action: 'created', credential: created }, null, 2));
1138
+ } else {
1139
+ console.log(`Created ${created.name} (${created.id})`);
1140
+ }
1141
+ return 0;
1142
+ }
1143
+
1144
+ if (subcommand === 'share') {
1145
+ const name = positional[0];
1146
+ if (!name) {
1147
+ console.error('Usage: npx auramaxx vault share <name> [--expires-after 24h] [--password xxx] [--one-time]');
1148
+ return 1;
1149
+ }
1150
+
1151
+ const { target } = await resolveCredentialTarget(token, name, {
1152
+ vaultName,
1153
+ first: flagFirst,
1154
+ surface: 'cli_vault_share',
1155
+ actor: 'cli-vault',
1156
+ });
1157
+
1158
+ const accessMode: 'anyone' | 'password' = sharePassword ? 'password' : 'anyone';
1159
+ const share = await createShare(token, {
1160
+ credentialId: target.id,
1161
+ expiresAfter: expiresAfter || '24h',
1162
+ accessMode,
1163
+ ...(sharePassword ? { password: sharePassword } : {}),
1164
+ oneTimeOnly: flagOneTime,
1165
+ });
1166
+
1167
+ const link = resolveShareUrlForGist(share.token);
1168
+ const fallbackLink = `${serverUrl()}/credential-shares/${share.token}`;
1169
+ const normalizedType = normalizeCredentialType(target.type);
1170
+ let gistFields = normalizeCredentialFieldsForType(
1171
+ normalizedType,
1172
+ toMetaBackedFields(target.meta),
1173
+ )
1174
+ .map((field) => ({
1175
+ key: String(field.key || '').trim(),
1176
+ value: String(field.value || '').trim(),
1177
+ sensitive: field.sensitive !== false,
1178
+ }))
1179
+ .filter((field) => field.key.length > 0 && field.value.length > 0);
1180
+ try {
1181
+ const readToken = await getReadToken(token, keypair, authSelection);
1182
+ const decrypted = await readCredential(target.id, readToken, keypair);
1183
+ gistFields = mergeCredentialFields(
1184
+ normalizedType,
1185
+ decrypted.fields,
1186
+ target.meta,
1187
+ )
1188
+ .map((field) => ({
1189
+ key: String(field.key || '').trim(),
1190
+ value: String(field.value || '').trim(),
1191
+ sensitive: field.sensitive !== false,
1192
+ }))
1193
+ .filter((field) => field.key.length > 0 && field.value.length > 0);
1194
+ } catch {
1195
+ // Preserve share behavior for write-only tokens; gist may still include non-secret metadata.
1196
+ }
1197
+ let gist: Awaited<ReturnType<typeof createSecretGist>> | null = null;
1198
+ let gistError: SecretGistError | null = null;
1199
+ try {
1200
+ gist = await createSecretGist({
1201
+ credentialId: target.id,
1202
+ credentialName: target.name,
1203
+ credentialType: target.type,
1204
+ shareUrl: link,
1205
+ accessMode: share.accessMode,
1206
+ oneTimeOnly: share.oneTimeOnly,
1207
+ expiresAfter: expiresAfter || '24h',
1208
+ fields: gistFields,
1209
+ });
1210
+ } catch (error) {
1211
+ if (error instanceof SecretGistError) {
1212
+ gistError = error;
1213
+ } else {
1214
+ throw error;
1215
+ }
1216
+ }
1217
+
1218
+ if (flagJson) {
1219
+ console.log(JSON.stringify({
1220
+ success: true,
1221
+ share,
1222
+ link: gist ? link : fallbackLink,
1223
+ gist,
1224
+ fallback: gist ? null : 'local_link',
1225
+ gistError: gistError
1226
+ ? {
1227
+ code: gistError.code,
1228
+ message: gistError.message,
1229
+ remediation: gistError.remediation,
1230
+ detail: gistError.detail,
1231
+ }
1232
+ : null,
1233
+ }, null, 2));
1234
+ } else {
1235
+ if (gist) {
1236
+ console.log(`Secret gist created for ${target.name}`);
1237
+ console.log(` gist: ${gist.url}`);
1238
+ console.log(` marker: ${gist.marker}`);
1239
+ console.log(` title: ${gist.title}`);
1240
+ } else {
1241
+ console.log(`GitHub gist unavailable for ${target.name}; using local share link fallback.`);
1242
+ if (gistError) {
1243
+ console.log(` gistError: ${gistError.message}`);
1244
+ console.log(` remediation: ${gistError.remediation}`);
1245
+ if (gistError.detail) {
1246
+ console.log(` detail: ${gistError.detail}`);
1247
+ }
1248
+ }
1249
+ console.log(` shareLink: ${fallbackLink}`);
1250
+ }
1251
+ console.log(` token: ${share.token}`);
1252
+ console.log(` shareLink: ${gist ? link : fallbackLink}`);
1253
+ console.log(` accessMode: ${share.accessMode}`);
1254
+ console.log(` oneTimeOnly: ${share.oneTimeOnly}`);
1255
+ }
1256
+ return 0;
1257
+ }
1258
+
1259
+ if (subcommand === 'delete' || subcommand === 'del') {
1260
+ const name = positional[0];
1261
+ if (!name) {
1262
+ console.error('Usage: npx auramaxx vault delete <name> [--location active|archive|recently_deleted]');
1263
+ return 1;
1264
+ }
1265
+
1266
+ const location = (deleteLocation || 'active').toLowerCase();
1267
+ if (!['active', 'archive', 'recently_deleted'].includes(location)) {
1268
+ console.error('Invalid --location. Expected active, archive, or recently_deleted.');
1269
+ return 1;
1270
+ }
1271
+
1272
+ const { target } = await resolveCredentialTarget(token, name, {
1273
+ vaultName,
1274
+ first: flagFirst,
1275
+ surface: 'cli_vault_delete',
1276
+ actor: 'cli-vault',
1277
+ });
1278
+
1279
+ const result = await deleteCredential(
1280
+ token,
1281
+ target.id,
1282
+ location as 'active' | 'archive' | 'recently_deleted',
1283
+ );
1284
+
1285
+ if (flagJson) {
1286
+ console.log(JSON.stringify({ success: true, credentialId: target.id, result }, null, 2));
1287
+ } else {
1288
+ console.log(`Deleted ${target.name} (${target.id}) -> ${result.action || 'ok'}`);
1289
+ }
1290
+ return 0;
1291
+ }
1292
+
1293
+ console.error(`Unknown vault subcommand: ${subcommand}`);
1294
+ showHelp();
1295
+ return 1;
1296
+ } catch (error) {
1297
+ if (error instanceof SecretGistError) {
1298
+ console.error(`Error: ${error.message}`);
1299
+ console.error(`Remediation: ${error.remediation}`);
1300
+ if (error.detail) {
1301
+ console.error(`Detail: ${error.detail}`);
1302
+ }
1303
+ return 1;
1304
+ }
1305
+ const handledLock = await maybeHandleLockError({ context: 'vault command', error });
1306
+ if (!handledLock) {
1307
+ console.error(`Error: ${getErrorMessage(error)}`);
1308
+ }
1309
+ return 1;
1310
+ }
1311
+ }
1312
+
1313
+ async function main(): Promise<void> {
1314
+ const exitCode = await runVaultCli(process.argv.slice(2));
1315
+ process.exit(exitCode);
1316
+ }
1317
+
1318
+ if (require.main === module) {
1319
+ main().catch((error) => {
1320
+ console.error(`Error: ${getErrorMessage(error)}`);
1321
+ process.exit(1);
1322
+ });
1323
+ }