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,608 @@
1
+ /**
2
+ * Credential Import — Parse external password manager exports
3
+ * ============================================================
4
+ *
5
+ * Normalizes CSV exports from 1Password, Bitwarden, LastPass, Chrome,
6
+ * and generic CSV into ImportedCredential[], ready for vault import.
7
+ */
8
+
9
+ import { parse } from 'csv-parse/sync';
10
+ import { CredentialType, CredentialField } from '../types';
11
+ import { listCredentials } from './credentials';
12
+
13
+ // ---------------------------------------------------------------------------
14
+ // Types
15
+ // ---------------------------------------------------------------------------
16
+
17
+ export interface ImportedField {
18
+ key: string;
19
+ value: string;
20
+ sensitive: boolean;
21
+ }
22
+
23
+ export interface ImportedCredential {
24
+ name: string;
25
+ type: CredentialType;
26
+ url?: string;
27
+ fields: ImportedField[];
28
+ tags?: string[];
29
+ }
30
+
31
+ export interface ColumnMapping {
32
+ title?: string;
33
+ url?: string;
34
+ username?: string;
35
+ password?: string;
36
+ notes?: string;
37
+ }
38
+
39
+ export interface SplitResult {
40
+ meta: Record<string, unknown>;
41
+ sensitiveFields: CredentialField[];
42
+ }
43
+
44
+ export type ImportFormat =
45
+ | '1password-csv'
46
+ | '1password-json'
47
+ | '1password-1pux'
48
+ | 'bitwarden-csv'
49
+ | 'bitwarden-json'
50
+ | 'lastpass-csv'
51
+ | 'icloud-csv'
52
+ | 'chrome-csv'
53
+ | 'chrome-json'
54
+ | 'firefox-csv'
55
+ | 'firefox-json'
56
+ | 'generic-csv';
57
+
58
+ // ---------------------------------------------------------------------------
59
+ // Helpers
60
+ // ---------------------------------------------------------------------------
61
+
62
+ /** Strip UTF-8 BOM if present */
63
+ function stripBOM(text: string): string {
64
+ return text.charCodeAt(0) === 0xfeff ? text.slice(1) : text;
65
+ }
66
+
67
+ function parseCsvRows(csv: string): Record<string, string>[] {
68
+ const cleaned = stripBOM(csv);
69
+ return parse(cleaned, {
70
+ columns: true,
71
+ skip_empty_lines: true,
72
+ trim: true,
73
+ relax_column_count: true,
74
+ }) as Record<string, string>[];
75
+ }
76
+
77
+ function normalizeColumnName(value: string): string {
78
+ return value.toLowerCase().replace(/[^a-z0-9]/g, '');
79
+ }
80
+
81
+ function getRowValue(row: Record<string, string>, aliases: string[]): string {
82
+ const lookup = new Map<string, string>();
83
+ for (const [key, value] of Object.entries(row)) {
84
+ lookup.set(normalizeColumnName(key), value);
85
+ }
86
+ for (const alias of aliases) {
87
+ const found = lookup.get(normalizeColumnName(alias));
88
+ if (found !== undefined && String(found).trim() !== '') {
89
+ return String(found).trim();
90
+ }
91
+ }
92
+ return '';
93
+ }
94
+
95
+ /** Normalize URL for duplicate comparison: strip protocol, www., trailing slash */
96
+ export function normalizeUrl(url: string): string {
97
+ if (!url) return '';
98
+ let u = url.trim().toLowerCase();
99
+ // strip protocol
100
+ u = u.replace(/^https?:\/\//, '');
101
+ // strip www.
102
+ u = u.replace(/^www\./, '');
103
+ // strip trailing slash
104
+ u = u.replace(/\/+$/, '');
105
+ return u;
106
+ }
107
+
108
+ /**
109
+ * Split ImportedField[] into meta (non-sensitive) + sensitiveFields (sensitive).
110
+ * The CredentialField objects get a default type based on key.
111
+ */
112
+ export function splitFields(
113
+ fields: ImportedField[],
114
+ url?: string,
115
+ tags?: string[]
116
+ ): SplitResult {
117
+ const meta: Record<string, unknown> = {};
118
+ const sensitiveFields: CredentialField[] = [];
119
+
120
+ if (url) meta.url = url;
121
+ if (tags && tags.length > 0) meta.tags = tags;
122
+
123
+ for (const f of fields) {
124
+ if (!f.value && f.value !== '') continue; // skip undefined/null
125
+ if (f.sensitive) {
126
+ sensitiveFields.push({
127
+ key: f.key,
128
+ value: f.value,
129
+ type: 'secret',
130
+ sensitive: true,
131
+ });
132
+ } else {
133
+ // Store non-sensitive fields in meta for searchability
134
+ meta[f.key] = f.value;
135
+ }
136
+ }
137
+
138
+ return { meta, sensitiveFields };
139
+ }
140
+
141
+ // ---------------------------------------------------------------------------
142
+ // Duplicate detection
143
+ // ---------------------------------------------------------------------------
144
+
145
+ export interface DuplicateMatch {
146
+ index: number;
147
+ existingId: string;
148
+ existingName: string;
149
+ matchType: 'exact' | 'name-only';
150
+ }
151
+
152
+ /**
153
+ * Check imported credentials against existing vault credentials for duplicates.
154
+ * Returns a map of import index → duplicate match info.
155
+ */
156
+ export function detectDuplicates(
157
+ imported: ImportedCredential[],
158
+ vaultId: string
159
+ ): Map<number, DuplicateMatch> {
160
+ const existing = listCredentials({ vaultId });
161
+ const duplicates = new Map<number, DuplicateMatch>();
162
+
163
+ // Build lookup maps from existing credentials
164
+ const existingByNameAndUrl = new Map<string, { id: string; name: string }>();
165
+ const existingByName = new Map<string, { id: string; name: string }>();
166
+
167
+ for (const cred of existing) {
168
+ const name = cred.name.trim().toLowerCase();
169
+ const url = normalizeUrl((cred.meta?.url as string) || '');
170
+ const key = `${name}|${url}`;
171
+ existingByNameAndUrl.set(key, { id: cred.id, name: cred.name });
172
+ existingByName.set(name, { id: cred.id, name: cred.name });
173
+ }
174
+
175
+ for (let i = 0; i < imported.length; i++) {
176
+ const cred = imported[i];
177
+ const name = cred.name.trim().toLowerCase();
178
+ const url = normalizeUrl(cred.url || '');
179
+
180
+ // Check exact match (name + URL)
181
+ const exactKey = `${name}|${url}`;
182
+ const exactMatch = existingByNameAndUrl.get(exactKey);
183
+ if (exactMatch && url) {
184
+ duplicates.set(i, {
185
+ index: i,
186
+ existingId: exactMatch.id,
187
+ existingName: exactMatch.name,
188
+ matchType: 'exact',
189
+ });
190
+ continue;
191
+ }
192
+
193
+ // Check name-only match
194
+ const nameMatch = existingByName.get(name);
195
+ if (nameMatch) {
196
+ duplicates.set(i, {
197
+ index: i,
198
+ existingId: nameMatch.id,
199
+ existingName: nameMatch.name,
200
+ matchType: 'name-only',
201
+ });
202
+ }
203
+ }
204
+
205
+ return duplicates;
206
+ }
207
+
208
+
209
+
210
+ function parseJsonObject(input: string): any {
211
+ const cleaned = stripBOM(input).trim();
212
+ if (!cleaned) return [];
213
+
214
+ let parsed: any;
215
+ try {
216
+ parsed = JSON.parse(cleaned);
217
+ } catch {
218
+ throw new Error('Invalid JSON payload');
219
+ }
220
+
221
+ if (Array.isArray(parsed)) return parsed;
222
+ if (parsed && typeof parsed === 'object') return parsed;
223
+ throw new Error('Invalid JSON payload');
224
+ }
225
+
226
+ function coerceItems(root: any, keys: string[]): any[] {
227
+ if (Array.isArray(root)) return root;
228
+ for (const key of keys) {
229
+ if (Array.isArray(root?.[key])) return root[key];
230
+ }
231
+ return [];
232
+ }
233
+
234
+ // ---------------------------------------------------------------------------
235
+ // 1Password CSV Parser
236
+ // ---------------------------------------------------------------------------
237
+
238
+ /**
239
+ * Parse 1Password CSV export.
240
+ * Expected columns: Title, URL, Username, Password, Notes, Type, OTP
241
+ */
242
+ export function parse1PasswordCSV(csv: string): ImportedCredential[] {
243
+ const cleaned = stripBOM(csv);
244
+ const records = parse(cleaned, {
245
+ columns: true,
246
+ skip_empty_lines: true,
247
+ trim: true,
248
+ relax_column_count: true,
249
+ }) as Record<string, string>[];
250
+
251
+ return records.map((row) => {
252
+ const fields: ImportedField[] = [];
253
+
254
+ if (row.Username) {
255
+ fields.push({ key: 'username', value: row.Username, sensitive: false });
256
+ }
257
+ if (row.Password) {
258
+ fields.push({ key: 'password', value: row.Password, sensitive: true });
259
+ }
260
+ if (row.Notes) {
261
+ fields.push({ key: 'notes', value: row.Notes, sensitive: false });
262
+ }
263
+ if (row.OTP) {
264
+ fields.push({ key: 'totp', value: row.OTP, sensitive: true });
265
+ }
266
+
267
+ // Map 1Password types to AuraMaxx types
268
+ let type: CredentialType = 'login';
269
+ const rawType = (row.Type || '').toLowerCase();
270
+ if (rawType.includes('card') || rawType.includes('credit')) type = 'card';
271
+ else if (rawType.includes('note')) type = 'note';
272
+ else if (rawType.includes('api')) type = 'api';
273
+ else if (rawType.includes('identity')) type = 'custom';
274
+
275
+ return {
276
+ name: row.Title || 'Untitled',
277
+ type,
278
+ url: row.URL || undefined,
279
+ fields,
280
+ };
281
+ });
282
+ }
283
+
284
+ // ---------------------------------------------------------------------------
285
+ // Bitwarden CSV Parser
286
+ // ---------------------------------------------------------------------------
287
+
288
+ /**
289
+ * Parse Bitwarden CSV export.
290
+ * Expected columns: folder, favorite, type, name, notes, fields, reprompt,
291
+ * login_uri, login_username, login_password, login_totp
292
+ */
293
+ export function parseBitwardenCSV(csv: string): ImportedCredential[] {
294
+ const cleaned = stripBOM(csv);
295
+ const records = parse(cleaned, {
296
+ columns: true,
297
+ skip_empty_lines: true,
298
+ trim: true,
299
+ relax_column_count: true,
300
+ }) as Record<string, string>[];
301
+
302
+ return records.map((row) => {
303
+ const fields: ImportedField[] = [];
304
+
305
+ if (row.login_username) {
306
+ fields.push({ key: 'username', value: row.login_username, sensitive: false });
307
+ }
308
+ if (row.login_password) {
309
+ fields.push({ key: 'password', value: row.login_password, sensitive: true });
310
+ }
311
+ if (row.notes) {
312
+ fields.push({ key: 'notes', value: row.notes, sensitive: false });
313
+ }
314
+ if (row.login_totp) {
315
+ fields.push({ key: 'totp', value: row.login_totp, sensitive: true });
316
+ }
317
+
318
+ let type: CredentialType = 'login';
319
+ const rawType = (row.type || '').toLowerCase();
320
+ if (rawType === 'card') type = 'card';
321
+ else if (rawType === 'securenote' || rawType === 'note') type = 'note';
322
+ else if (rawType === 'identity') type = 'custom';
323
+
324
+ const tags: string[] = [];
325
+ if (row.folder) tags.push(row.folder);
326
+
327
+ return {
328
+ name: row.name || 'Untitled',
329
+ type,
330
+ url: row.login_uri || undefined,
331
+ fields,
332
+ tags: tags.length > 0 ? tags : undefined,
333
+ };
334
+ });
335
+ }
336
+
337
+ // ---------------------------------------------------------------------------
338
+ // Chrome CSV Parser
339
+ // ---------------------------------------------------------------------------
340
+
341
+ /**
342
+ * Parse Chrome password CSV export.
343
+ * Expected columns: name, url, username, password, note
344
+ */
345
+ export function parseChromeCSV(csv: string): ImportedCredential[] {
346
+ const cleaned = stripBOM(csv);
347
+ const records = parse(cleaned, {
348
+ columns: true,
349
+ skip_empty_lines: true,
350
+ trim: true,
351
+ relax_column_count: true,
352
+ }) as Record<string, string>[];
353
+
354
+ return records.map((row) => {
355
+ const fields: ImportedField[] = [];
356
+
357
+ if (row.username) {
358
+ fields.push({ key: 'username', value: row.username, sensitive: false });
359
+ }
360
+ if (row.password) {
361
+ fields.push({ key: 'password', value: row.password, sensitive: true });
362
+ }
363
+ if (row.note) {
364
+ fields.push({ key: 'notes', value: row.note, sensitive: false });
365
+ }
366
+
367
+ return {
368
+ name: row.name || 'Untitled',
369
+ type: 'login' as CredentialType,
370
+ url: row.url || undefined,
371
+ fields,
372
+ };
373
+ });
374
+ }
375
+
376
+ // ---------------------------------------------------------------------------
377
+ // Firefox CSV Parser
378
+ // ---------------------------------------------------------------------------
379
+
380
+ /**
381
+ * Parse Firefox password CSV export.
382
+ * Expected columns: url, username, password, httpRealm, formActionOrigin,
383
+ * guid, timeCreated, timeLastUsed, timePasswordChanged
384
+ */
385
+ export function parseFirefoxCSV(csv: string): ImportedCredential[] {
386
+ const cleaned = stripBOM(csv);
387
+ const records = parse(cleaned, {
388
+ columns: true,
389
+ skip_empty_lines: true,
390
+ trim: true,
391
+ relax_column_count: true,
392
+ }) as Record<string, string>[];
393
+
394
+ return records.map((row) => {
395
+ const fields: ImportedField[] = [];
396
+
397
+ if (row.username) {
398
+ fields.push({ key: 'username', value: row.username, sensitive: false });
399
+ }
400
+ if (row.password) {
401
+ fields.push({ key: 'password', value: row.password, sensitive: true });
402
+ }
403
+
404
+ // Derive a name from the URL
405
+ let name = 'Untitled';
406
+ if (row.url) {
407
+ try {
408
+ name = new URL(row.url).hostname.replace(/^www\./, '');
409
+ } catch {
410
+ name = row.url;
411
+ }
412
+ }
413
+
414
+ return {
415
+ name,
416
+ type: 'login' as CredentialType,
417
+ url: row.url || undefined,
418
+ fields,
419
+ };
420
+ });
421
+ }
422
+
423
+ // ---------------------------------------------------------------------------
424
+ // iCloud Keychain CSV Parser
425
+ // ---------------------------------------------------------------------------
426
+
427
+ /**
428
+ * Parse iCloud Keychain / Apple Passwords CSV export.
429
+ * Common columns include:
430
+ * Title, URL, Username, Password, Notes, OTPAuth
431
+ */
432
+ export function parseICloudCSV(csv: string): ImportedCredential[] {
433
+ const records = parseCsvRows(csv);
434
+
435
+ return records.map((row) => {
436
+ const title = getRowValue(row, ['Title', 'Name', 'Site', 'Website']);
437
+ const url = getRowValue(row, ['URL', 'Website', 'Site URL', 'Site']);
438
+ const username = getRowValue(row, ['Username', 'User Name', 'Account', 'Login']);
439
+ const password = getRowValue(row, ['Password', 'Passcode']);
440
+ const notes = getRowValue(row, ['Notes', 'Note', 'Comments']);
441
+ const totp = getRowValue(row, ['OTPAuth', 'TOTP', 'One-Time Code', 'One-Time Password']);
442
+
443
+ const fields: ImportedField[] = [];
444
+ if (username) fields.push({ key: 'username', value: username, sensitive: false });
445
+ if (password) fields.push({ key: 'password', value: password, sensitive: true });
446
+ if (notes) fields.push({ key: 'notes', value: notes, sensitive: false });
447
+ if (totp) fields.push({ key: 'totp', value: totp, sensitive: true });
448
+
449
+ let name = title || 'Untitled';
450
+ if (!title && url) {
451
+ try {
452
+ name = new URL(url).hostname.replace(/^www\./, '');
453
+ } catch {
454
+ name = url;
455
+ }
456
+ }
457
+
458
+ return {
459
+ name,
460
+ type: 'login' as CredentialType,
461
+ url: url || undefined,
462
+ fields,
463
+ };
464
+ });
465
+ }
466
+
467
+ // ---------------------------------------------------------------------------
468
+ // LastPass CSV Parser
469
+ // ---------------------------------------------------------------------------
470
+
471
+ /**
472
+ * Parse LastPass CSV export.
473
+ * Common columns include:
474
+ * url, username, password, totp, extra, name, grouping, fav
475
+ */
476
+ export function parseLastPassCSV(csv: string): ImportedCredential[] {
477
+ const records = parseCsvRows(csv);
478
+
479
+ return records.map((row) => {
480
+ const nameFromRow = getRowValue(row, ['name', 'title']);
481
+ const url = getRowValue(row, ['url', 'uri', 'website']);
482
+ const username = getRowValue(row, ['username', 'user name', 'login']);
483
+ const password = getRowValue(row, ['password', 'passcode']);
484
+ const notes = getRowValue(row, ['extra', 'notes', 'note']);
485
+ const totp = getRowValue(row, ['totp', 'otp']);
486
+ const grouping = getRowValue(row, ['grouping', 'group', 'folder']);
487
+
488
+ const fields: ImportedField[] = [];
489
+ if (username) fields.push({ key: 'username', value: username, sensitive: false });
490
+ if (password) fields.push({ key: 'password', value: password, sensitive: true });
491
+ if (notes) fields.push({ key: 'notes', value: notes, sensitive: false });
492
+ if (totp) fields.push({ key: 'totp', value: totp, sensitive: true });
493
+
494
+ let name = nameFromRow || 'Untitled';
495
+ if (!nameFromRow && url) {
496
+ try {
497
+ name = new URL(url).hostname.replace(/^www\./, '');
498
+ } catch {
499
+ name = url;
500
+ }
501
+ }
502
+
503
+ const tags = grouping ? [grouping] : undefined;
504
+
505
+ return {
506
+ name,
507
+ type: 'login' as CredentialType,
508
+ url: url || undefined,
509
+ fields,
510
+ tags,
511
+ };
512
+ });
513
+ }
514
+
515
+ // ---------------------------------------------------------------------------
516
+ // JSON Parsers
517
+ // ---------------------------------------------------------------------------
518
+
519
+ export function parse1PasswordJSON(json: string): ImportedCredential[] {
520
+ const root = parseJsonObject(json);
521
+ const items = coerceItems(root, ['items']);
522
+
523
+ return items.map((item: any) => {
524
+ const login = item?.login ?? {};
525
+ const fields: ImportedField[] = [];
526
+
527
+ if (login.username) fields.push({ key: 'username', value: String(login.username), sensitive: false });
528
+ if (login.password) fields.push({ key: 'password', value: String(login.password), sensitive: true });
529
+ if (item?.notesPlain || item?.notes) fields.push({ key: 'notes', value: String(item.notesPlain || item.notes), sensitive: false });
530
+
531
+ const url = login?.urls?.[0]?.href || login?.uris?.[0]?.uri || login?.uris?.[0]?.url || item?.url || undefined;
532
+ return {
533
+ name: String(item?.title || item?.name || 'Untitled'),
534
+ type: 'login' as CredentialType,
535
+ url,
536
+ fields,
537
+ };
538
+ });
539
+ }
540
+
541
+ export function parseBitwardenJSON(json: string): ImportedCredential[] {
542
+ const root = parseJsonObject(json);
543
+ const items = coerceItems(root, ['items']);
544
+
545
+ return items.map((item: any) => {
546
+ const login = item?.login ?? {};
547
+ const fields: ImportedField[] = [];
548
+
549
+ if (login.username) fields.push({ key: 'username', value: String(login.username), sensitive: false });
550
+ if (login.password) fields.push({ key: 'password', value: String(login.password), sensitive: true });
551
+ if (item?.notes) fields.push({ key: 'notes', value: String(item.notes), sensitive: false });
552
+ if (login?.totp) fields.push({ key: 'totp', value: String(login.totp), sensitive: true });
553
+
554
+ return {
555
+ name: String(item?.name || 'Untitled'),
556
+ type: 'login' as CredentialType,
557
+ url: login?.uris?.[0]?.uri || undefined,
558
+ fields,
559
+ };
560
+ });
561
+ }
562
+
563
+ export function parseChromeJSON(json: string): ImportedCredential[] {
564
+ const root = parseJsonObject(json);
565
+ const items = coerceItems(root, ['items', 'logins', 'passwords', 'credentials']);
566
+
567
+ return items.map((item: any) => {
568
+ const fields: ImportedField[] = [];
569
+ if (item?.username) fields.push({ key: 'username', value: String(item.username), sensitive: false });
570
+ if (item?.password) fields.push({ key: 'password', value: String(item.password), sensitive: true });
571
+ if (item?.note || item?.notes) fields.push({ key: 'notes', value: String(item.note || item.notes), sensitive: false });
572
+
573
+ return {
574
+ name: String(item?.name || item?.title || 'Untitled'),
575
+ type: 'login' as CredentialType,
576
+ url: item?.url || item?.origin || item?.signon_realm || undefined,
577
+ fields,
578
+ };
579
+ });
580
+ }
581
+
582
+ export function parseFirefoxJSON(json: string): ImportedCredential[] {
583
+ const root = parseJsonObject(json);
584
+ const items = coerceItems(root, ['logins', 'items', 'credentials']);
585
+
586
+ return items.map((item: any) => {
587
+ const fields: ImportedField[] = [];
588
+ if (item?.username) fields.push({ key: 'username', value: String(item.username), sensitive: false });
589
+ if (item?.password) fields.push({ key: 'password', value: String(item.password), sensitive: true });
590
+
591
+ const url = item?.url || item?.hostname || undefined;
592
+ let name = 'Untitled';
593
+ if (url) {
594
+ try {
595
+ name = new URL(String(url)).hostname.replace(/^www\./, '');
596
+ } catch {
597
+ name = String(url);
598
+ }
599
+ }
600
+
601
+ return {
602
+ name,
603
+ type: 'login' as CredentialType,
604
+ url,
605
+ fields,
606
+ };
607
+ });
608
+ }
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Credential Scope — Matching & Field Exclusion Resolution
3
+ * ========================================================
4
+ *
5
+ * Handles scope normalization, credential-to-scope matching for access control,
6
+ * and resolution of which sensitive fields to exclude from reads.
7
+ */
8
+
9
+ import { CredentialFile } from '../types';
10
+ import { getDefaultSync } from './defaults';
11
+
12
+ /**
13
+ * Normalize a scope string for consistent matching.
14
+ * Trims whitespace, applies NFKC normalization, and lowercases.
15
+ */
16
+ export function normalizeScope(scope: string): string {
17
+ return scope.trim().normalize('NFKC').toLowerCase();
18
+ }
19
+
20
+ /**
21
+ * Check if a credential matches any of the given scopes.
22
+ *
23
+ * Scope types:
24
+ * - `*` — matches everything
25
+ * - `cred-xxxxx` — exact credential ID match
26
+ * - `tag:X` — matches if credential's meta.tags contains X
27
+ * - `vault:X` — matches if credential's vaultId equals X
28
+ *
29
+ * Selector wildcards:
30
+ * - `tag:*` / `vault:*` — match any tag / any vault
31
+ * - trailing `*` in tag/vault selectors — prefix wildcard
32
+ * (e.g. `tag:generated/*`, `vault:pri*`)
33
+ *
34
+ * All comparisons use normalized values.
35
+ * Empty scopes array matches nothing.
36
+ */
37
+ function matchesSelector(value: string, selector: string): boolean {
38
+ if (selector === '*') return true;
39
+ if (selector.endsWith('*')) {
40
+ const prefix = selector.slice(0, -1);
41
+ return value.startsWith(prefix);
42
+ }
43
+ return value === selector;
44
+ }
45
+
46
+ export function matchesScope(credential: CredentialFile, scopes: string[]): boolean {
47
+ if (scopes.length === 0) return false;
48
+
49
+ const normalizedScopes = scopes.map(normalizeScope);
50
+
51
+ for (const scope of normalizedScopes) {
52
+ // Wildcard
53
+ if (scope === '*') return true;
54
+
55
+ // Exact ID match
56
+ if (scope === normalizeScope(credential.id)) return true;
57
+
58
+ // Tag match
59
+ if (scope.startsWith('tag:')) {
60
+ const tagValue = scope.slice(4);
61
+ const tags = (credential.meta.tags as string[] | undefined) || [];
62
+ if (tagValue === '*' && tags.length > 0) return true;
63
+ if (tags.some(t => matchesSelector(normalizeScope(t), tagValue))) return true;
64
+ }
65
+
66
+ // Vault match
67
+ if (scope.startsWith('vault:')) {
68
+ const vaultValue = scope.slice(6);
69
+ if (matchesSelector(normalizeScope(credential.vaultId), vaultValue)) return true;
70
+ }
71
+ }
72
+
73
+ return false;
74
+ }
75
+
76
+ /**
77
+ * Resolve which fields to exclude from a credential read.
78
+ *
79
+ * Resolution order:
80
+ * 1. Token's explicit excludeFields (even if empty array []) → use as-is
81
+ * 2. Type default from `defaults.credential.excludeFields.{type}` → use if found
82
+ * 3. Fall back to empty array (exclude nothing)
83
+ */
84
+ export function resolveExcludeFields(
85
+ tokenExcludes: string[] | undefined,
86
+ credentialType: string
87
+ ): string[] {
88
+ // Token explicitly set excludeFields (even [] means "show everything")
89
+ if (tokenExcludes !== undefined) {
90
+ if (Array.isArray(tokenExcludes) && tokenExcludes.length === 0) {
91
+ console.warn('[credential-scope] Token has excludeFields: [] — all sensitive fields will be exposed. Ensure this is intentional.');
92
+ }
93
+ return tokenExcludes;
94
+ }
95
+
96
+ // Type default
97
+ const typeDefault = getDefaultSync<string[]>(
98
+ `defaults.credential.excludeFields.${credentialType}`,
99
+ []
100
+ );
101
+ return typeDefault;
102
+ }