auramaxx 0.0.17 → 0.0.19

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 (296) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-build-manifest.json +70 -70
  3. package/.next/app-path-routes-manifest.json +2 -2
  4. package/.next/build-manifest.json +5 -5
  5. package/.next/prerender-manifest.json +30 -30
  6. package/.next/react-loadable-manifest.json +16 -16
  7. package/.next/server/app/_not-found/page.js.nft.json +1 -1
  8. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  9. package/.next/server/app/_not-found.html +1 -1
  10. package/.next/server/app/_not-found.rsc +5 -5
  11. package/.next/server/app/api/[...doc]/page.js.nft.json +1 -1
  12. package/.next/server/app/api/[...doc]/page_client-reference-manifest.js +1 -1
  13. package/.next/server/app/api/agent-requests/route_client-reference-manifest.js +1 -1
  14. package/.next/server/app/api/apps/install/route.js.nft.json +1 -1
  15. package/.next/server/app/api/apps/install/route_client-reference-manifest.js +1 -1
  16. package/.next/server/app/api/apps/manifests/route_client-reference-manifest.js +1 -1
  17. package/.next/server/app/api/apps/static/[...path]/route_client-reference-manifest.js +1 -1
  18. package/.next/server/app/api/docs/plain/route.js.nft.json +1 -1
  19. package/.next/server/app/api/docs/plain/route_client-reference-manifest.js +1 -1
  20. package/.next/server/app/api/events/route_client-reference-manifest.js +1 -1
  21. package/.next/server/app/api/import-from-openclaw/[channel]/route.js.nft.json +1 -1
  22. package/.next/server/app/api/import-from-openclaw/[channel]/route_client-reference-manifest.js +1 -1
  23. package/.next/server/app/api/import-from-openclaw/route.js.nft.json +1 -1
  24. package/.next/server/app/api/import-from-openclaw/route_client-reference-manifest.js +1 -1
  25. package/.next/server/app/api/import-from-openclaw/validate/[channel]/route.js.nft.json +1 -1
  26. package/.next/server/app/api/import-from-openclaw/validate/[channel]/route_client-reference-manifest.js +1 -1
  27. package/.next/server/app/api/page.js.nft.json +1 -1
  28. package/.next/server/app/api/page_client-reference-manifest.js +1 -1
  29. package/.next/server/app/api/restart/route_client-reference-manifest.js +1 -1
  30. package/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
  31. package/.next/server/app/api/version/route_client-reference-manifest.js +1 -1
  32. package/.next/server/app/api/workspace/[id]/apps/[wid]/route_client-reference-manifest.js +1 -1
  33. package/.next/server/app/api/workspace/[id]/apps/route_client-reference-manifest.js +1 -1
  34. package/.next/server/app/api/workspace/[id]/export/route_client-reference-manifest.js +1 -1
  35. package/.next/server/app/api/workspace/[id]/route_client-reference-manifest.js +1 -1
  36. package/.next/server/app/api/workspace/config/route_client-reference-manifest.js +1 -1
  37. package/.next/server/app/api/workspace/import/route_client-reference-manifest.js +1 -1
  38. package/.next/server/app/api/workspace/route_client-reference-manifest.js +1 -1
  39. package/.next/server/app/app-legacy-do-not-use/page.js +3 -3
  40. package/.next/server/app/app-legacy-do-not-use/page.js.nft.json +1 -1
  41. package/.next/server/app/app-legacy-do-not-use/page_client-reference-manifest.js +1 -1
  42. package/.next/server/app/app-legacy-do-not-use.html +1 -1
  43. package/.next/server/app/app-legacy-do-not-use.rsc +6 -6
  44. package/.next/server/app/approve/[actionId]/page.js.nft.json +1 -1
  45. package/.next/server/app/approve/[actionId]/page_client-reference-manifest.js +1 -1
  46. package/.next/server/app/docs/[...doc]/page.js.nft.json +1 -1
  47. package/.next/server/app/docs/[...doc]/page_client-reference-manifest.js +1 -1
  48. package/.next/server/app/docs/page.js.nft.json +1 -1
  49. package/.next/server/app/docs/page_client-reference-manifest.js +1 -1
  50. package/.next/server/app/health/page.js.nft.json +1 -1
  51. package/.next/server/app/health/page_client-reference-manifest.js +1 -1
  52. package/.next/server/app/health.html +1 -1
  53. package/.next/server/app/health.rsc +5 -5
  54. package/.next/server/app/hello/page.js.nft.json +1 -1
  55. package/.next/server/app/hello/page_client-reference-manifest.js +1 -1
  56. package/.next/server/app/hello.html +1 -1
  57. package/.next/server/app/hello.rsc +5 -5
  58. package/.next/server/app/index.html +1 -1
  59. package/.next/server/app/index.rsc +6 -6
  60. package/.next/server/app/page.js +9 -3
  61. package/.next/server/app/page.js.nft.json +1 -1
  62. package/.next/server/app/page_client-reference-manifest.js +1 -1
  63. package/.next/server/app/privacy/page.js.nft.json +1 -1
  64. package/.next/server/app/privacy/page_client-reference-manifest.js +1 -1
  65. package/.next/server/app/privacy.html +1 -1
  66. package/.next/server/app/privacy.rsc +5 -5
  67. package/.next/server/app/share/[token]/page.js.nft.json +1 -1
  68. package/.next/server/app/share/[token]/page_client-reference-manifest.js +1 -1
  69. package/.next/server/app/terms/page.js.nft.json +1 -1
  70. package/.next/server/app/terms/page_client-reference-manifest.js +1 -1
  71. package/.next/server/app/terms.html +1 -1
  72. package/.next/server/app/terms.rsc +5 -5
  73. package/.next/server/app/yo/page.js +1 -1
  74. package/.next/server/app/yo/page.js.nft.json +1 -1
  75. package/.next/server/app/yo/page_client-reference-manifest.js +1 -1
  76. package/.next/server/app/yo.html +1 -1
  77. package/.next/server/app/yo.rsc +6 -6
  78. package/.next/server/app-paths-manifest.json +2 -2
  79. package/.next/server/chunks/1196.js +1 -1
  80. package/.next/server/chunks/{6415.js → 2123.js} +2 -2
  81. package/.next/server/chunks/2234.js +1 -1
  82. package/.next/server/chunks/4224.js +1 -0
  83. package/.next/server/chunks/4600.js +1 -1
  84. package/.next/server/chunks/5678.js +1 -1
  85. package/.next/server/chunks/5991.js +7 -0
  86. package/.next/server/chunks/6470.js +1 -1
  87. package/.next/server/chunks/7585.js +1 -1
  88. package/.next/server/chunks/7935.js +19 -11
  89. package/.next/server/chunks/8468.js +1 -1
  90. package/.next/server/chunks/{1350.js → 9323.js} +3 -3
  91. package/.next/server/chunks/9971.js +1 -1
  92. package/.next/server/functions-config-manifest.json +1 -1
  93. package/.next/server/middleware-build-manifest.js +1 -1
  94. package/.next/server/middleware-react-loadable-manifest.js +1 -1
  95. package/.next/server/pages/404.html +1 -1
  96. package/.next/server/pages/500.html +1 -1
  97. package/.next/server/server-reference-manifest.json +1 -1
  98. package/.next/static/chunks/{1168.aaac1edbb597fe5a.js → 1168.bb1c93965872e980.js} +1 -1
  99. package/.next/static/chunks/2891-bf010357841c9ce9.js +1 -0
  100. package/.next/static/chunks/{2927.7e00cc878d9a3f52.js → 2927.3976471e61df966e.js} +1 -1
  101. package/.next/static/chunks/{3573-1b41d7b8a000d015.js → 3573-667bc4c8f56afcff.js} +1 -1
  102. package/.next/static/chunks/{3a91511d-ba215c0b5dc21ba9.js → 3a91511d-da2c74cad6c6fc33.js} +2 -2
  103. package/.next/static/chunks/4114-a375178fd94c4d8a.js +1 -0
  104. package/.next/static/chunks/{4256.48407d9abad5ea33.js → 4256.aa831e4d8c3f65bc.js} +1 -1
  105. package/.next/static/chunks/{4919-7e7cdd5efc9f2110.js → 4919-0e698cd95504e355.js} +1 -1
  106. package/.next/static/chunks/{5336-233ec7ab3807267c.js → 5336-b775f449d7433604.js} +1 -1
  107. package/.next/static/chunks/{7338-3ce17a93614f1d77.js → 5362-2e8fcdd0bc6273ca.js} +4 -4
  108. package/.next/static/chunks/67-cb9bbb430499e803.js +1 -0
  109. package/.next/static/chunks/{8273-8e92d34180669ca9.js → 8273-3b9d882c5dd6a9a8.js} +1 -1
  110. package/.next/static/chunks/8291-8f6fb6554e692552.js +1 -0
  111. package/.next/static/chunks/8370-6537b6b27df58b23.js +1 -0
  112. package/.next/static/chunks/app/app-legacy-do-not-use/page-efa348da049c5873.js +1 -0
  113. package/.next/static/chunks/app/{error-63cbe72522239cff.js → error-04a18fd49a89ee17.js} +1 -1
  114. package/.next/static/chunks/app/layout-7093dda0d106f34d.js +1 -0
  115. package/.next/static/chunks/app/page-0ec7cf48550f71e6.js +1 -0
  116. package/.next/static/chunks/app/yo/{page-cf5d6540ea31c31c.js → page-48930746b135037e.js} +1 -1
  117. package/.next/static/chunks/{webpack-79ad58260e9b10b4.js → webpack-15ec0114c9d658c2.js} +1 -1
  118. package/.next/static/css/35991998d1b5eff0.css +3 -0
  119. package/.next/trace +28 -28
  120. package/README.md +2 -2
  121. package/bin/auramaxx.js +87 -3
  122. package/package.json +8 -4
  123. package/prisma/migrations/20260301225214_social_tables/migration.sql +81 -0
  124. package/prisma/migrations/20260302120000_add_notification_hash/migration.sql +8 -0
  125. package/prisma/migrations/20260302160000_add_public_key_hex/migration.sql +5 -0
  126. package/prisma/migrations/20260302170000_add_inbound_author_public_key/migration.sql +5 -0
  127. package/prisma/schema.prisma +272 -177
  128. package/shared/credential-field-schema.ts +10 -0
  129. package/skills/auramaxx/SKILL.md +33 -4
  130. package/skills/auramaxx/docs/AGENT_SETUP.md +7 -2
  131. package/src/app/UnlockPageClient.tsx +448 -43
  132. package/src/app/app-legacy-do-not-use/page.tsx +5 -3
  133. package/src/app/globals.css +1 -1
  134. package/src/components/AgentPicker.tsx +278 -0
  135. package/src/components/CreateAgentModal.tsx +192 -0
  136. package/src/components/NotificationDrawer.tsx +15 -13
  137. package/src/components/agent/AgentSidebar.tsx +2 -19
  138. package/src/components/agent/CredentialAgent.tsx +70 -37
  139. package/src/components/agent/CredentialForm.tsx +32 -7
  140. package/src/components/agent/CredentialList.tsx +149 -72
  141. package/src/components/agents/AgentsView.tsx +1035 -0
  142. package/src/components/design-system/TextAreaInput.tsx +78 -0
  143. package/src/components/design-system/index.ts +1 -0
  144. package/src/components/hub/HubView.tsx +58 -0
  145. package/src/components/layout/LeftRail.tsx +333 -40
  146. package/src/components/layout/ViewShell.tsx +125 -0
  147. package/src/components/layout/index.ts +1 -0
  148. package/src/components/social/PrimaryHubRegistrationGate.tsx +260 -0
  149. package/src/components/social/SocialView.tsx +904 -0
  150. package/src/hooks/useSocialNotifications.ts +123 -0
  151. package/src/lib/events.ts +10 -1
  152. package/src/lib/social-client.ts +319 -0
  153. package/src/lib/view-registry.ts +22 -4
  154. package/src/server/cli/commands/agent.ts +33 -7
  155. package/src/server/cli/commands/create.ts +209 -0
  156. package/src/server/cli/commands/onboard.ts +39 -0
  157. package/src/server/cli/commands/play.ts +68 -0
  158. package/src/server/cli/commands/register-agent.ts +181 -0
  159. package/src/server/cli/commands/social.ts +648 -0
  160. package/src/server/cli/commands/start.ts +4 -3
  161. package/src/server/cron/index.ts +5 -9
  162. package/src/server/cron/jobs/social-inbound.ts +596 -0
  163. package/src/server/cron/jobs/social-outbound.ts +70 -0
  164. package/src/server/index.ts +14 -0
  165. package/src/server/lib/agent-profiles.ts +10 -1
  166. package/src/server/lib/cold.ts +0 -45
  167. package/src/server/lib/credential-import.ts +53 -47
  168. package/src/server/lib/defaults.ts +11 -0
  169. package/src/server/lib/feature-flags.ts +3 -0
  170. package/src/server/lib/hub-auth.ts +159 -0
  171. package/src/server/lib/migrate-agent-profiles.ts +96 -0
  172. package/src/server/lib/notifications.ts +12 -1
  173. package/src/server/lib/permissions.ts +10 -0
  174. package/src/server/lib/project-scope.ts +33 -5
  175. package/src/server/lib/social/create.ts +176 -0
  176. package/src/server/lib/social/global-aura-id.ts +115 -0
  177. package/src/server/lib/social/hub-rpc-client.ts +99 -0
  178. package/src/server/lib/social/hub-session.ts +6 -0
  179. package/src/server/lib/social/public-key.ts +32 -0
  180. package/src/server/lib/social/register.ts +162 -0
  181. package/src/server/lib/social/sign.ts +95 -0
  182. package/src/server/lib/social/sync.ts +301 -0
  183. package/src/server/mcp/server.ts +142 -18
  184. package/src/server/mcp/tools.ts +552 -0
  185. package/src/server/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
  186. package/src/server/routes/agent-hub.ts +276 -0
  187. package/src/server/routes/agent-profiles.ts +145 -19
  188. package/src/server/routes/credentials.ts +49 -15
  189. package/src/server/routes/heartbeat.ts +3 -7
  190. package/src/server/routes/passkey.ts +7 -1
  191. package/src/server/routes/social.ts +869 -0
  192. package/src/server/routes/unlock.ts +17 -1
  193. package/src/server/routes/verified-credentials.ts +312 -0
  194. package/src/server/test-data/template.db +0 -0
  195. package/src/server/test-data/worker-1/agent-primary.json +12 -0
  196. package/src/server/test-data/worker-1/auramaxx.db +0 -0
  197. package/src/server/test-data/worker-1/backups/auramaxx.db.20260305_011404.bak +0 -0
  198. package/src/server/test-data/worker-1/backups/credentials.20260305_011404.cred-00ntf5h3.json +24 -0
  199. package/src/server/test-data/worker-1/backups/credentials.20260305_011404.cred-dh8fa9k3.json +24 -0
  200. package/src/server/test-data/worker-1/backups/credentials.20260305_011404.cred-prfhlyow.json +25 -0
  201. package/src/server/test-data/worker-1/backups/credentials.20260305_011404.cred-w26jd6zi.json +22 -0
  202. package/src/server/test-data/worker-1/config.json +33 -0
  203. package/src/server/test-data/worker-1/test.db +0 -0
  204. package/src/server/test-data/worker-10/agent-primary.json +12 -0
  205. package/src/server/test-data/worker-10/config.json +33 -0
  206. package/src/server/test-data/worker-10/test.db +0 -0
  207. package/src/server/test-data/worker-11/agent-primary.json +12 -0
  208. package/src/server/test-data/worker-11/config.json +33 -0
  209. package/src/server/test-data/worker-11/test.db +0 -0
  210. package/src/server/test-data/worker-12/agent-primary.json +12 -0
  211. package/src/server/test-data/worker-12/config.json +33 -0
  212. package/src/server/test-data/worker-12/credentials/cred-hboagned.json +25 -0
  213. package/src/server/test-data/worker-12/credentials/cred-nt14xd9t.json +24 -0
  214. package/src/server/test-data/worker-12/credentials/cred-qn59xefc.json +24 -0
  215. package/src/server/test-data/worker-12/test.db +0 -0
  216. package/src/server/test-data/worker-13/agent-primary.json +12 -0
  217. package/src/server/test-data/worker-13/approval-context.json +56 -0
  218. package/src/server/test-data/worker-13/approval-context.key +1 -0
  219. package/src/server/test-data/worker-13/config.json +33 -0
  220. package/src/server/test-data/worker-13/credentials-recently-deleted/cred-jn9yqy2m.json +20 -0
  221. package/src/server/test-data/worker-13/test.db +0 -0
  222. package/src/server/test-data/worker-14/agent-primary.json +12 -0
  223. package/src/server/test-data/worker-14/config.json +33 -0
  224. package/src/server/test-data/worker-14/test.db +0 -0
  225. package/src/server/test-data/worker-15/agent-primary.json +12 -0
  226. package/src/server/test-data/worker-15/config.json +33 -0
  227. package/src/server/test-data/worker-15/test.db +0 -0
  228. package/src/server/test-data/worker-15/update-check-4b305e76.json +5 -0
  229. package/src/server/test-data/worker-2/agent-primary.json +12 -0
  230. package/src/server/test-data/worker-2/config.json +33 -0
  231. package/src/server/test-data/worker-2/test.db +0 -0
  232. package/src/server/test-data/worker-3/agent-primary.json +12 -0
  233. package/src/server/test-data/worker-3/config.json +33 -0
  234. package/src/server/test-data/worker-3/test.db +0 -0
  235. package/src/server/test-data/worker-4/agent-primary.json +12 -0
  236. package/src/server/test-data/worker-4/config.json +33 -0
  237. package/src/server/test-data/worker-4/credentials/cred-bm6p74u5.json +24 -0
  238. package/src/server/test-data/worker-4/credentials/cred-r8eqb07i.json +25 -0
  239. package/src/server/test-data/worker-4/credentials/cred-ziv49y9h.json +24 -0
  240. package/src/server/test-data/worker-4/test.db +0 -0
  241. package/src/server/test-data/worker-5/agent-primary.json +12 -0
  242. package/src/server/test-data/worker-5/config.json +33 -0
  243. package/src/server/test-data/worker-5/credentials/cred-15gyfeht.json +25 -0
  244. package/src/server/test-data/worker-5/credentials/cred-lp0ocnfc.json +24 -0
  245. package/src/server/test-data/worker-5/credentials/cred-zfbwfxw0.json +24 -0
  246. package/src/server/test-data/worker-5/test.db +0 -0
  247. package/src/server/test-data/worker-6/agent-primary.json +12 -0
  248. package/src/server/test-data/worker-6/config.json +33 -0
  249. package/src/server/test-data/worker-6/credentials/cred-dx8li3ue.json +24 -0
  250. package/src/server/test-data/worker-6/credentials/cred-myeskqky.json +24 -0
  251. package/src/server/test-data/worker-6/credentials/cred-tam01qdt.json +25 -0
  252. package/src/server/test-data/worker-6/credentials/cred-wdh6rslw.json +22 -0
  253. package/src/server/test-data/worker-6/credentials-archive/cred-4wg53v1y.json +25 -0
  254. package/src/server/test-data/worker-6/test.db +0 -0
  255. package/src/server/test-data/worker-7/agent-primary.json +12 -0
  256. package/src/server/test-data/worker-7/config.json +33 -0
  257. package/src/server/test-data/worker-7/credentials/cred-ji7ec57d.json +25 -0
  258. package/src/server/test-data/worker-7/credentials/cred-mmgj0ypo.json +24 -0
  259. package/src/server/test-data/worker-7/credentials/cred-nb0ic93x.json +24 -0
  260. package/src/server/test-data/worker-7/test.db +0 -0
  261. package/src/server/test-data/worker-8/agent-primary.json +12 -0
  262. package/src/server/test-data/worker-8/config.json +33 -0
  263. package/src/server/test-data/worker-8/test.db +0 -0
  264. package/src/server/test-data/worker-9/agent-primary.json +12 -0
  265. package/src/server/test-data/worker-9/test.db +0 -0
  266. package/src/server/tests/cli/bin-entrypoint.test.ts +99 -0
  267. package/src/server/tests/cli/social.test.ts +83 -0
  268. package/src/server/tests/cron/social-inbound-followers.unit.test.ts +164 -0
  269. package/src/server/tests/cron/social-inbound-staleness.test.ts +193 -0
  270. package/src/server/tests/cron/social-outbound.test.ts +252 -0
  271. package/src/server/tests/endpoints/agent.test.ts +0 -16
  272. package/src/server/tests/lib/credential-field-schema.test.ts +448 -0
  273. package/src/server/tests/lib/project-scope.test.ts +49 -0
  274. package/src/server/tests/lib/social/create.test.ts +207 -0
  275. package/src/server/tests/lib/social/sign.test.ts +163 -0
  276. package/src/server/tests/lib/social/sync.test.ts +198 -0
  277. package/src/server/tests/mcp/server.test.ts +1 -3
  278. package/src/server/tests/mcp/tools.test.ts +219 -7
  279. package/src/server/tsconfig.tsbuildinfo +1 -1
  280. package/tsconfig.json +1 -0
  281. package/.next/server/chunks/2460.js +0 -7
  282. package/.next/server/chunks/5246.js +0 -1
  283. package/.next/static/chunks/3667-1db7bd03948e60df.js +0 -1
  284. package/.next/static/chunks/5442-b5bb869e832e8967.js +0 -1
  285. package/.next/static/chunks/6872-a8f1ad5fc35a1c35.js +0 -1
  286. package/.next/static/chunks/7394-4bdb1feefad1a74a.js +0 -1
  287. package/.next/static/chunks/7587-8542eae6e5139d27.js +0 -1
  288. package/.next/static/chunks/app/app-legacy-do-not-use/page-569fed5d1a2bfe79.js +0 -1
  289. package/.next/static/chunks/app/layout-af8d9969c7aeb758.js +0 -1
  290. package/.next/static/chunks/app/page-9bd0ec3ba4aa6cea.js +0 -1
  291. package/.next/static/css/88f92e1578b1e5d9.css +0 -3
  292. package/shared/agent-profile-schema.ts +0 -81
  293. package/src/internal/security/frontend-api-authorization-audit.md +0 -206
  294. package/src/server/lib/agent-profile-records.ts +0 -72
  295. /package/.next/static/{QPzVnl7GToqgYBq14V_iC → yEruVJH_cuht5hu5_1R00}/_buildManifest.js +0 -0
  296. /package/.next/static/{QPzVnl7GToqgYBq14V_iC → yEruVJH_cuht5hu5_1R00}/_ssgManifest.js +0 -0
@@ -28,6 +28,7 @@ import strategyRoutes from './routes/strategy';
28
28
  import appRoutes from './routes/apps';
29
29
  import actionsRoutes from './routes/actions';
30
30
  import credentialAgentRoutes from './routes/credential-agents';
31
+ import agentHubRoutes from './routes/agent-hub';
31
32
  import credentialsRoutes from './routes/credentials';
32
33
  import agentProfilesRoutes from './routes/agent-profiles';
33
34
  import credentialSharesRoutes from './routes/credential-shares';
@@ -49,6 +50,8 @@ import heartbeatRoutes from './routes/heartbeat';
49
50
  import securityRoutes from './routes/security';
50
51
  import flagsRoutes from './routes/flags';
51
52
  import viewsRoutes from './routes/views';
53
+ import socialRoutes from './routes/social';
54
+ import verifiedCredentialRoutes from './routes/verified-credentials';
52
55
  import { preloadCache, onDefaultChanged, parseRateLimit, getDefaultSync } from './lib/defaults';
53
56
  import { SocketServer } from './cli/socket';
54
57
 
@@ -196,6 +199,7 @@ app.use('/strategies', strategyRoutes);
196
199
  app.use('/apps', appRoutes);
197
200
  app.use('/actions', actionsRoutes);
198
201
  app.use('/agents/credential', credentialAgentRoutes);
202
+ app.use('/agent-hub', agentHubRoutes);
199
203
  app.use('/credentials/import', importRoutes);
200
204
  app.use('/credentials/passkey', passkeyCredentialRoutes);
201
205
  app.use('/credentials', credentialsRoutes);
@@ -214,6 +218,8 @@ app.use('/bookmarks', bookmarkRoutes);
214
218
  app.use('/security', securityRoutes);
215
219
  app.use('/flags', flagsRoutes);
216
220
  app.use('/views', viewsRoutes);
221
+ app.use('/social', socialRoutes);
222
+ app.use('/verified-credentials', verifiedCredentialRoutes);
217
223
 
218
224
  // Health check
219
225
  app.get('/health', (_req, res) => {
@@ -264,6 +270,14 @@ async function startServer() {
264
270
  // Apply pending migrations before anything else
265
271
  await autoMigrate();
266
272
 
273
+ // One-time migration: agent-profiles.json → Prisma AgentProfile table
274
+ try {
275
+ const { migrateAgentProfilesToPrisma } = await import('./lib/migrate-agent-profiles');
276
+ await migrateAgentProfilesToPrisma();
277
+ } catch (err) {
278
+ log.warn({ err }, 'Agent profile migration failed (non-fatal)');
279
+ }
280
+
267
281
  // Load system defaults into memory cache before handling any requests
268
282
  await preloadCache().catch(err => {
269
283
  log.warn({ err }, 'Failed to preload defaults cache (will use seed values)');
@@ -92,7 +92,16 @@ const BUILTIN_PROFILES: AgentPolicyProfileV1[] = [
92
92
  displayName: 'Dev',
93
93
  description: 'Developer profile for local agent automation with non-financial scope.',
94
94
  rationale: 'Use for day-to-day local dev workflows without granting financial operations.',
95
- permissions: ['wallet:list', 'secret:read', 'secret:write', 'action:create', 'action:read', 'action:resolve'],
95
+ permissions: [
96
+ 'wallet:list',
97
+ 'secret:read',
98
+ 'secret:write',
99
+ 'action:create',
100
+ 'action:read',
101
+ 'action:resolve',
102
+ 'social:read',
103
+ 'social:write',
104
+ ],
96
105
  credentialAccess: {
97
106
  // Keep legacy `agent` alias for migrated installs while scoping to primary-only access.
98
107
  readScopes: ['agent:primary', 'agent:agent'],
@@ -15,7 +15,6 @@ const COLD_FILE = 'cold.json';
15
15
  const AGENT_PREFIX = 'agent-';
16
16
  const PRIMARY_AGENT_ID = 'primary';
17
17
  export const AGENT_AGENT_NAME = 'agent';
18
- export const DAILY_LOGS_AGENT_NAME = 'daily-logs';
19
18
 
20
19
  export type AgentMode = 'primary' | 'linked' | 'independent';
21
20
 
@@ -566,36 +565,6 @@ export function ensureDefaultLinkedAgentAgent(): { created: boolean; agentId: st
566
565
  return { created: true, agentId: created.id };
567
566
  }
568
567
 
569
- export function ensureDefaultDailyLogsAgent(): { created: boolean; agentId: string | null; parentAgentId: string | null } {
570
- ensureMigration();
571
- if (!primaryAgentId || !primaryAgentPassword) {
572
- return { created: false, agentId: null, parentAgentId: null };
573
- }
574
-
575
- const defaultParentAgentId = primaryAgentId;
576
-
577
- const agents = listAgents();
578
- const existingUnderPrimary = agents.find(
579
- v => (v.name || '').trim().toLowerCase() === DAILY_LOGS_AGENT_NAME && (v.parentAgentId || v.linkedTo) === defaultParentAgentId,
580
- );
581
- if (existingUnderPrimary) {
582
- return { created: false, agentId: existingUnderPrimary.id, parentAgentId: defaultParentAgentId };
583
- }
584
-
585
- const created = createAgent(primaryAgentPassword, DAILY_LOGS_AGENT_NAME, {
586
- mode: 'linked',
587
- parentAgentId: defaultParentAgentId,
588
- seedOnboardingSecret: false,
589
- });
590
- return { created: true, agentId: created.id, parentAgentId: defaultParentAgentId };
591
- }
592
-
593
- export function ensureDefaultAgentHierarchy(): { agentAgentId: string | null; dailyLogsAgentId: string | null } {
594
- // New default hierarchy is primary -> daily-logs.
595
- // We intentionally do not auto-create a linked "agent" vault anymore.
596
- const dailyLogs = ensureDefaultDailyLogsAgent();
597
- return { agentAgentId: null, dailyLogsAgentId: dailyLogs.agentId };
598
- }
599
568
 
600
569
  function sortAgentsByCreatedAt(a: AgentInfo, b: AgentInfo): number {
601
570
  const createdDelta = new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime();
@@ -788,13 +757,6 @@ export function createColdWallet(password: string): CreateWalletResult {
788
757
  console.warn('[cold] Failed to seed primary WORKING_WITH_SECRETS onboarding note:', err);
789
758
  }
790
759
 
791
- // Create default linked diary hierarchy once at setup time.
792
- try {
793
- ensureDefaultAgentHierarchy();
794
- } catch (err) {
795
- console.warn('[cold] Failed to create default diary agent hierarchy:', err);
796
- }
797
-
798
760
  clearNukeStateMarker();
799
761
 
800
762
  return {
@@ -1025,13 +987,6 @@ export function importColdWallet(mnemonic: string, password: string): WalletInfo
1025
987
  console.warn('[cold] Failed to seed primary WORKING_WITH_SECRETS onboarding note:', err);
1026
988
  }
1027
989
 
1028
- // Create default linked diary hierarchy once at import time.
1029
- try {
1030
- ensureDefaultAgentHierarchy();
1031
- } catch (err) {
1032
- console.warn('[cold] Failed to create default diary agent hierarchy:', err);
1033
- }
1034
-
1035
990
  clearNukeStateMarker();
1036
991
 
1037
992
  return {
@@ -9,6 +9,7 @@
9
9
  import { parse } from 'csv-parse/sync';
10
10
  import { CredentialType, CredentialField } from '../types';
11
11
  import { listCredentials } from './credentials';
12
+ import { getCredentialFieldSpec } from '../../../shared/credential-field-schema';
12
13
 
13
14
  // ---------------------------------------------------------------------------
14
15
  // Types
@@ -59,6 +60,11 @@ export type ImportFormat =
59
60
  // Helpers
60
61
  // ---------------------------------------------------------------------------
61
62
 
63
+ /** Derive field sensitivity from the credential schema; default to false for unknown fields. */
64
+ function fieldSensitive(type: CredentialType, key: string): boolean {
65
+ return getCredentialFieldSpec(type, key)?.sensitive ?? false;
66
+ }
67
+
62
68
  /** Strip UTF-8 BOM if present */
63
69
  function stripBOM(text: string): string {
64
70
  return text.charCodeAt(0) === 0xfeff ? text.slice(1) : text;
@@ -249,29 +255,29 @@ export function parse1PasswordCSV(csv: string): ImportedCredential[] {
249
255
  }) as Record<string, string>[];
250
256
 
251
257
  return records.map((row) => {
258
+ // Map 1Password types to AuraMaxx types
259
+ let type: CredentialType = 'login';
260
+ const rawType = (row.Type || '').toLowerCase();
261
+ if (rawType.includes('card') || rawType.includes('credit')) type = 'card';
262
+ else if (rawType.includes('note')) type = 'note';
263
+ else if (rawType.includes('api')) type = 'api';
264
+ else if (rawType.includes('identity')) type = 'custom';
265
+
252
266
  const fields: ImportedField[] = [];
253
267
 
254
268
  if (row.Username) {
255
- fields.push({ key: 'username', value: row.Username, sensitive: false });
269
+ fields.push({ key: 'username', value: row.Username, sensitive: fieldSensitive(type, 'username') });
256
270
  }
257
271
  if (row.Password) {
258
- fields.push({ key: 'password', value: row.Password, sensitive: true });
272
+ fields.push({ key: 'password', value: row.Password, sensitive: fieldSensitive(type, 'password') });
259
273
  }
260
274
  if (row.Notes) {
261
- fields.push({ key: 'notes', value: row.Notes, sensitive: false });
275
+ fields.push({ key: 'notes', value: row.Notes, sensitive: fieldSensitive(type, 'notes') });
262
276
  }
263
277
  if (row.OTP) {
264
- fields.push({ key: 'totp', value: row.OTP, sensitive: true });
278
+ fields.push({ key: 'totp', value: row.OTP, sensitive: fieldSensitive(type, 'totp') });
265
279
  }
266
280
 
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
281
  return {
276
282
  name: row.Title || 'Untitled',
277
283
  type,
@@ -300,27 +306,27 @@ export function parseBitwardenCSV(csv: string): ImportedCredential[] {
300
306
  }) as Record<string, string>[];
301
307
 
302
308
  return records.map((row) => {
309
+ let type: CredentialType = 'login';
310
+ const rawType = (row.type || '').toLowerCase();
311
+ if (rawType === 'card') type = 'card';
312
+ else if (rawType === 'securenote' || rawType === 'note') type = 'note';
313
+ else if (rawType === 'identity') type = 'custom';
314
+
303
315
  const fields: ImportedField[] = [];
304
316
 
305
317
  if (row.login_username) {
306
- fields.push({ key: 'username', value: row.login_username, sensitive: false });
318
+ fields.push({ key: 'username', value: row.login_username, sensitive: fieldSensitive(type, 'username') });
307
319
  }
308
320
  if (row.login_password) {
309
- fields.push({ key: 'password', value: row.login_password, sensitive: true });
321
+ fields.push({ key: 'password', value: row.login_password, sensitive: fieldSensitive(type, 'password') });
310
322
  }
311
323
  if (row.notes) {
312
- fields.push({ key: 'notes', value: row.notes, sensitive: false });
324
+ fields.push({ key: 'notes', value: row.notes, sensitive: fieldSensitive(type, 'notes') });
313
325
  }
314
326
  if (row.login_totp) {
315
- fields.push({ key: 'totp', value: row.login_totp, sensitive: true });
327
+ fields.push({ key: 'totp', value: row.login_totp, sensitive: fieldSensitive(type, 'totp') });
316
328
  }
317
329
 
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
330
  const tags: string[] = [];
325
331
  if (row.folder) tags.push(row.folder);
326
332
 
@@ -355,13 +361,13 @@ export function parseChromeCSV(csv: string): ImportedCredential[] {
355
361
  const fields: ImportedField[] = [];
356
362
 
357
363
  if (row.username) {
358
- fields.push({ key: 'username', value: row.username, sensitive: false });
364
+ fields.push({ key: 'username', value: row.username, sensitive: fieldSensitive('login', 'username') });
359
365
  }
360
366
  if (row.password) {
361
- fields.push({ key: 'password', value: row.password, sensitive: true });
367
+ fields.push({ key: 'password', value: row.password, sensitive: fieldSensitive('login', 'password') });
362
368
  }
363
369
  if (row.note) {
364
- fields.push({ key: 'notes', value: row.note, sensitive: false });
370
+ fields.push({ key: 'notes', value: row.note, sensitive: fieldSensitive('login', 'notes') });
365
371
  }
366
372
 
367
373
  return {
@@ -395,10 +401,10 @@ export function parseFirefoxCSV(csv: string): ImportedCredential[] {
395
401
  const fields: ImportedField[] = [];
396
402
 
397
403
  if (row.username) {
398
- fields.push({ key: 'username', value: row.username, sensitive: false });
404
+ fields.push({ key: 'username', value: row.username, sensitive: fieldSensitive('login', 'username') });
399
405
  }
400
406
  if (row.password) {
401
- fields.push({ key: 'password', value: row.password, sensitive: true });
407
+ fields.push({ key: 'password', value: row.password, sensitive: fieldSensitive('login', 'password') });
402
408
  }
403
409
 
404
410
  // Derive a name from the URL
@@ -441,10 +447,10 @@ export function parseICloudCSV(csv: string): ImportedCredential[] {
441
447
  const totp = getRowValue(row, ['OTPAuth', 'TOTP', 'One-Time Code', 'One-Time Password']);
442
448
 
443
449
  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 });
450
+ if (username) fields.push({ key: 'username', value: username, sensitive: fieldSensitive('login', 'username') });
451
+ if (password) fields.push({ key: 'password', value: password, sensitive: fieldSensitive('login', 'password') });
452
+ if (notes) fields.push({ key: 'notes', value: notes, sensitive: fieldSensitive('login', 'notes') });
453
+ if (totp) fields.push({ key: 'totp', value: totp, sensitive: fieldSensitive('login', 'totp') });
448
454
 
449
455
  let name = title || 'Untitled';
450
456
  if (!title && url) {
@@ -486,10 +492,10 @@ export function parseLastPassCSV(csv: string): ImportedCredential[] {
486
492
  const grouping = getRowValue(row, ['grouping', 'group', 'folder']);
487
493
 
488
494
  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 });
495
+ if (username) fields.push({ key: 'username', value: username, sensitive: fieldSensitive('login', 'username') });
496
+ if (password) fields.push({ key: 'password', value: password, sensitive: fieldSensitive('login', 'password') });
497
+ if (notes) fields.push({ key: 'notes', value: notes, sensitive: fieldSensitive('login', 'notes') });
498
+ if (totp) fields.push({ key: 'totp', value: totp, sensitive: fieldSensitive('login', 'totp') });
493
499
 
494
500
  let name = nameFromRow || 'Untitled';
495
501
  if (!nameFromRow && url) {
@@ -524,9 +530,9 @@ export function parse1PasswordJSON(json: string): ImportedCredential[] {
524
530
  const login = item?.login ?? {};
525
531
  const fields: ImportedField[] = [];
526
532
 
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 });
533
+ if (login.username) fields.push({ key: 'username', value: String(login.username), sensitive: fieldSensitive('login', 'username') });
534
+ if (login.password) fields.push({ key: 'password', value: String(login.password), sensitive: fieldSensitive('login', 'password') });
535
+ if (item?.notesPlain || item?.notes) fields.push({ key: 'notes', value: String(item.notesPlain || item.notes), sensitive: fieldSensitive('login', 'notes') });
530
536
 
531
537
  const url = login?.urls?.[0]?.href || login?.uris?.[0]?.uri || login?.uris?.[0]?.url || item?.url || undefined;
532
538
  return {
@@ -546,10 +552,10 @@ export function parseBitwardenJSON(json: string): ImportedCredential[] {
546
552
  const login = item?.login ?? {};
547
553
  const fields: ImportedField[] = [];
548
554
 
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 });
555
+ if (login.username) fields.push({ key: 'username', value: String(login.username), sensitive: fieldSensitive('login', 'username') });
556
+ if (login.password) fields.push({ key: 'password', value: String(login.password), sensitive: fieldSensitive('login', 'password') });
557
+ if (item?.notes) fields.push({ key: 'notes', value: String(item.notes), sensitive: fieldSensitive('login', 'notes') });
558
+ if (login?.totp) fields.push({ key: 'totp', value: String(login.totp), sensitive: fieldSensitive('login', 'totp') });
553
559
 
554
560
  return {
555
561
  name: String(item?.name || 'Untitled'),
@@ -566,9 +572,9 @@ export function parseChromeJSON(json: string): ImportedCredential[] {
566
572
 
567
573
  return items.map((item: any) => {
568
574
  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 });
575
+ if (item?.username) fields.push({ key: 'username', value: String(item.username), sensitive: fieldSensitive('login', 'username') });
576
+ if (item?.password) fields.push({ key: 'password', value: String(item.password), sensitive: fieldSensitive('login', 'password') });
577
+ if (item?.note || item?.notes) fields.push({ key: 'notes', value: String(item.note || item.notes), sensitive: fieldSensitive('login', 'notes') });
572
578
 
573
579
  return {
574
580
  name: String(item?.name || item?.title || 'Untitled'),
@@ -585,8 +591,8 @@ export function parseFirefoxJSON(json: string): ImportedCredential[] {
585
591
 
586
592
  return items.map((item: any) => {
587
593
  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 });
594
+ if (item?.username) fields.push({ key: 'username', value: String(item.username), sensitive: fieldSensitive('login', 'username') });
595
+ if (item?.password) fields.push({ key: 'password', value: String(item.password), sensitive: fieldSensitive('login', 'password') });
590
596
 
591
597
  const url = item?.url || item?.hostname || undefined;
592
598
  let name = 'Untitled';
@@ -105,6 +105,10 @@ export const SEED_DEFAULTS: SeedDefault[] = [
105
105
  { key: 'defaults.credential.excludeFields.login', value: ['password'], type: 'credential', label: 'Login Excluded Fields', description: 'Fields excluded by default when reading login credentials' },
106
106
  { key: 'defaults.credential.excludeFields.note', value: [], type: 'credential', label: 'Note Excluded Fields', description: 'Fields excluded by default when reading note credentials' },
107
107
 
108
+ // Social
109
+ { key: 'social.hub_url', value: 'https://hub.auramaxx.com', type: 'social', label: 'Hub URL', description: 'Base URL for the auramaxx hub (registration, sync, credentials)' },
110
+ { key: 'social.required_credentials', value: [], type: 'social', label: 'Required Credentials for Social Writes', description: 'Credential type slugs an agent must have verified before posting (empty = no requirement)' },
111
+
108
112
  // Balance sync
109
113
  { key: 'sync.active_interval', value: 15000, type: 'sync', label: 'Active Sync Interval (ms)', description: 'Interval for syncing active/cold wallets' },
110
114
  { key: 'sync.background_interval', value: 120000, type: 'sync', label: 'Background Sync Interval (ms)', description: 'Interval for syncing background wallets' },
@@ -376,6 +380,13 @@ export function parseRateLimit(value: unknown): { max: number; windowMs: number
376
380
  return { max: 10, windowMs: 60000 };
377
381
  }
378
382
 
383
+ // ─── Convenience Accessors ──────────────────────────────────────────
384
+
385
+ /** Hub URL from defaults — single source of truth for all social/credential routes + cron. */
386
+ export function getHubUrl(): string {
387
+ return getDefaultSync<string>('social.hub_url', 'https://hub.auramaxx.com');
388
+ }
389
+
379
390
  // ─── Test Helpers ───────────────────────────────────────────────────
380
391
 
381
392
  /** Reset cache state (for tests only) */
@@ -14,6 +14,9 @@ export interface FeatureFlags {
14
14
  const DEFAULT_FLAGS: FeatureFlags = {
15
15
  DEMO_FEATURE: false,
16
16
  EXPERIMENTAL_WALLET: false,
17
+ SOCIAL: false,
18
+ SHOW_DISCOVER_HUB: false,
19
+ CREDENTIALS: false,
17
20
  };
18
21
 
19
22
  const root = path.resolve(__dirname, '..', '..', '..');
@@ -0,0 +1,159 @@
1
+ import { ed25519 } from '@noble/curves/ed25519.js';
2
+ import { deriveSigningSeed } from './social/sign';
3
+ import { HubRpcClient, HubRpcError } from './social/hub-rpc-client';
4
+ import { getAgentMnemonic, getPrimaryAgentId } from './cold';
5
+
6
+ const REFRESH_SKEW_MS = 30_000;
7
+
8
+ interface HubSessionCacheEntry {
9
+ token: string;
10
+ publicKeyB64: string;
11
+ expiresAtMs: number;
12
+ }
13
+
14
+ interface HubTokenResponse {
15
+ token: string;
16
+ expiresAt: string;
17
+ publicKey: string;
18
+ }
19
+
20
+ export interface HubAuthIdentity {
21
+ agentId: string;
22
+ mnemonic: string;
23
+ }
24
+
25
+ type HubSessionLogger = {
26
+ debug?: (...args: unknown[]) => void;
27
+ warn?: (...args: unknown[]) => void;
28
+ };
29
+
30
+ const sessionCache = new Map<string, HubSessionCacheEntry>();
31
+
32
+ function toCacheKey(hubUrl: string, publicKeyB64: string): string {
33
+ return `${hubUrl}::${publicKeyB64}`;
34
+ }
35
+
36
+ function derivePublicKeyB64(mnemonic: string): string {
37
+ const seed = deriveSigningSeed(mnemonic);
38
+ const pubKeyBytes = ed25519.getPublicKey(seed);
39
+ return Buffer.from(pubKeyBytes).toString('base64');
40
+ }
41
+
42
+ function parseExpiry(expiresAt: string): number {
43
+ const parsed = Date.parse(expiresAt);
44
+ return Number.isFinite(parsed) ? parsed : (Date.now() + 5 * 60_000);
45
+ }
46
+
47
+ /**
48
+ * Resolve which unlocked local agent should authenticate to a hub.
49
+ * Primary agent is preferred, then optional caller-provided fallback.
50
+ */
51
+ export function resolveHubAuthIdentity(
52
+ preferredAgentId?: string,
53
+ ): HubAuthIdentity | null {
54
+ const primaryAgentId = getPrimaryAgentId() ?? 'primary';
55
+ const candidates = [primaryAgentId];
56
+
57
+ const preferred = typeof preferredAgentId === 'string' ? preferredAgentId.trim() : '';
58
+ if (preferred && !candidates.includes(preferred)) {
59
+ candidates.push(preferred);
60
+ }
61
+
62
+ for (const agentId of candidates) {
63
+ const mnemonic = getAgentMnemonic(agentId);
64
+ if (mnemonic) {
65
+ return { agentId, mnemonic };
66
+ }
67
+ }
68
+
69
+ return null;
70
+ }
71
+
72
+ async function mintSessionToken(
73
+ hubUrl: string,
74
+ mnemonic: string,
75
+ publicKeyB64: string,
76
+ ): Promise<HubSessionCacheEntry> {
77
+ const seed = deriveSigningSeed(mnemonic);
78
+ const rpc = new HubRpcClient(hubUrl);
79
+
80
+ const challengeRes = await rpc.call<{ challenge: string }>('auth.challenge', { publicKey: publicKeyB64 });
81
+ const challenge = challengeRes.challenge;
82
+ const signature = Buffer.from(ed25519.sign(new TextEncoder().encode(challenge), seed)).toString('base64');
83
+
84
+ const tokenRes = await rpc.call<HubTokenResponse>('auth.token', {
85
+ publicKey: publicKeyB64,
86
+ challenge,
87
+ signature,
88
+ });
89
+
90
+ return {
91
+ token: tokenRes.token,
92
+ publicKeyB64,
93
+ expiresAtMs: parseExpiry(tokenRes.expiresAt),
94
+ };
95
+ }
96
+
97
+ export function invalidateHubSessionToken(hubUrl: string, publicKeyB64: string): void {
98
+ sessionCache.delete(toCacheKey(hubUrl, publicKeyB64));
99
+ }
100
+
101
+ export async function getHubSessionToken(
102
+ hubUrl: string,
103
+ mnemonic: string,
104
+ opts?: { forceRefresh?: boolean; log?: HubSessionLogger },
105
+ ): Promise<HubSessionCacheEntry> {
106
+ const publicKeyB64 = derivePublicKeyB64(mnemonic);
107
+ const key = toCacheKey(hubUrl, publicKeyB64);
108
+
109
+ if (!opts?.forceRefresh) {
110
+ const cached = sessionCache.get(key);
111
+ if (cached && (cached.expiresAtMs - REFRESH_SKEW_MS) > Date.now()) {
112
+ return cached;
113
+ }
114
+ }
115
+
116
+ const fresh = await mintSessionToken(hubUrl, mnemonic, publicKeyB64);
117
+ sessionCache.set(key, fresh);
118
+ opts?.log?.debug?.({ hubUrl, expiresAtMs: fresh.expiresAtMs }, 'Minted hub session token');
119
+ return fresh;
120
+ }
121
+
122
+ export async function callHubWithSessionAuth<R>(
123
+ hubUrl: string,
124
+ method: string,
125
+ params: Record<string, unknown>,
126
+ mnemonic: string,
127
+ opts?: { log?: HubSessionLogger; timeoutMs?: number },
128
+ ): Promise<R> {
129
+ const rpc = new HubRpcClient(hubUrl);
130
+ let session = await getHubSessionToken(hubUrl, mnemonic, { log: opts?.log });
131
+ rpc.setBearerToken(session.token);
132
+
133
+ try {
134
+ return await rpc.call<R>(method, params, { timeoutMs: opts?.timeoutMs });
135
+ } catch (error) {
136
+ if (error instanceof HubRpcError && error.statusCode === 401) {
137
+ opts?.log?.warn?.({ hubUrl, method }, 'Hub session token expired/invalid; refreshing and retrying once');
138
+ invalidateHubSessionToken(hubUrl, session.publicKeyB64);
139
+ session = await getHubSessionToken(hubUrl, mnemonic, { forceRefresh: true, log: opts?.log });
140
+ rpc.setBearerToken(session.token);
141
+ return rpc.call<R>(method, params, { timeoutMs: opts?.timeoutMs });
142
+ }
143
+ throw error;
144
+ }
145
+ }
146
+
147
+ export async function tryCallHubWithSessionAuth<R>(
148
+ hubUrl: string,
149
+ method: string,
150
+ params: Record<string, unknown>,
151
+ mnemonic: string,
152
+ opts?: { log?: HubSessionLogger; timeoutMs?: number },
153
+ ): Promise<R | null> {
154
+ try {
155
+ return await callHubWithSessionAuth<R>(hubUrl, method, params, mnemonic, opts);
156
+ } catch {
157
+ return null;
158
+ }
159
+ }
@@ -0,0 +1,96 @@
1
+ /**
2
+ * One-time boot migration: agent-profiles.json → Prisma AgentProfile table.
3
+ *
4
+ * Runs on server startup. If the JSON file exists, reads all entries,
5
+ * inserts them into the AgentProfile table, then deletes the JSON file.
6
+ * Idempotent: if the file doesn't exist, this is a no-op.
7
+ */
8
+ import fs from 'fs';
9
+ import path from 'path';
10
+ import { prisma } from './db';
11
+ import { DATA_PATHS } from './config';
12
+ import { log } from './pino';
13
+
14
+ const AGENT_PROFILES_FILE = 'agent-profiles.json';
15
+
16
+ interface LegacyProfileRecord {
17
+ agentId: string;
18
+ email?: string;
19
+ phone?: string;
20
+ address?: string;
21
+ profileImage?: string;
22
+ attributes?: Record<string, string>;
23
+ createdAt?: string;
24
+ updatedAt?: string;
25
+ }
26
+
27
+ interface LegacyProfilesFile {
28
+ version: number;
29
+ profiles: Record<string, LegacyProfileRecord>;
30
+ }
31
+
32
+ export async function migrateAgentProfilesToPrisma(): Promise<void> {
33
+ const filePath = path.join(DATA_PATHS.wallets, AGENT_PROFILES_FILE);
34
+
35
+ if (!fs.existsSync(filePath)) {
36
+ return; // Nothing to migrate
37
+ }
38
+
39
+ let store: LegacyProfilesFile;
40
+ try {
41
+ const raw = fs.readFileSync(filePath, 'utf8');
42
+ store = JSON.parse(raw) as LegacyProfilesFile;
43
+ } catch (err) {
44
+ log.warn({ err, filePath }, 'Failed to read agent-profiles.json for migration, skipping');
45
+ return;
46
+ }
47
+
48
+ if (!store || typeof store !== 'object' || !store.profiles || typeof store.profiles !== 'object') {
49
+ log.warn({ filePath }, 'agent-profiles.json has unexpected format, skipping migration');
50
+ return;
51
+ }
52
+
53
+ const entries = Object.values(store.profiles);
54
+ if (entries.length === 0) {
55
+ // Empty file, just delete it
56
+ fs.unlinkSync(filePath);
57
+ log.info('Deleted empty agent-profiles.json');
58
+ return;
59
+ }
60
+
61
+ let migrated = 0;
62
+ for (const entry of entries) {
63
+ if (!entry.agentId) continue;
64
+ try {
65
+ await prisma.agentProfile.upsert({
66
+ where: { agentId: entry.agentId },
67
+ create: {
68
+ agentId: entry.agentId,
69
+ email: entry.email ?? null,
70
+ phone: entry.phone ?? null,
71
+ address: entry.address ?? null,
72
+ profileImage: entry.profileImage ?? null,
73
+ attributes: entry.attributes ? JSON.stringify(entry.attributes) : null,
74
+ },
75
+ update: {
76
+ email: entry.email ?? null,
77
+ phone: entry.phone ?? null,
78
+ address: entry.address ?? null,
79
+ profileImage: entry.profileImage ?? null,
80
+ attributes: entry.attributes ? JSON.stringify(entry.attributes) : null,
81
+ },
82
+ });
83
+ migrated++;
84
+ } catch (err) {
85
+ log.error({ err, agentId: entry.agentId }, 'Failed to migrate agent profile');
86
+ }
87
+ }
88
+
89
+ // Delete the JSON file after successful migration
90
+ try {
91
+ fs.unlinkSync(filePath);
92
+ log.info({ migrated, total: entries.length }, 'Migrated agent profiles from JSON to Prisma');
93
+ } catch (err) {
94
+ log.warn({ err, filePath }, 'Failed to delete agent-profiles.json after migration');
95
+ }
96
+ }