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.
- package/.next/BUILD_ID +1 -1
- package/.next/app-build-manifest.json +70 -70
- package/.next/app-path-routes-manifest.json +2 -2
- package/.next/build-manifest.json +5 -5
- package/.next/prerender-manifest.json +30 -30
- package/.next/react-loadable-manifest.json +16 -16
- package/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_not-found.html +1 -1
- package/.next/server/app/_not-found.rsc +5 -5
- package/.next/server/app/api/[...doc]/page.js.nft.json +1 -1
- package/.next/server/app/api/[...doc]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/api/agent-requests/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/apps/install/route.js.nft.json +1 -1
- package/.next/server/app/api/apps/install/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/apps/manifests/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/apps/static/[...path]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/docs/plain/route.js.nft.json +1 -1
- package/.next/server/app/api/docs/plain/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/events/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/import-from-openclaw/[channel]/route.js.nft.json +1 -1
- package/.next/server/app/api/import-from-openclaw/[channel]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/import-from-openclaw/route.js.nft.json +1 -1
- package/.next/server/app/api/import-from-openclaw/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/import-from-openclaw/validate/[channel]/route.js.nft.json +1 -1
- package/.next/server/app/api/import-from-openclaw/validate/[channel]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/page.js.nft.json +1 -1
- package/.next/server/app/api/page_client-reference-manifest.js +1 -1
- package/.next/server/app/api/restart/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/version/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/workspace/[id]/apps/[wid]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/workspace/[id]/apps/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/workspace/[id]/export/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/workspace/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/workspace/config/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/workspace/import/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/workspace/route_client-reference-manifest.js +1 -1
- package/.next/server/app/app-legacy-do-not-use/page.js +3 -3
- package/.next/server/app/app-legacy-do-not-use/page.js.nft.json +1 -1
- package/.next/server/app/app-legacy-do-not-use/page_client-reference-manifest.js +1 -1
- package/.next/server/app/app-legacy-do-not-use.html +1 -1
- package/.next/server/app/app-legacy-do-not-use.rsc +6 -6
- package/.next/server/app/approve/[actionId]/page.js.nft.json +1 -1
- package/.next/server/app/approve/[actionId]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/docs/[...doc]/page.js.nft.json +1 -1
- package/.next/server/app/docs/[...doc]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/docs/page.js.nft.json +1 -1
- package/.next/server/app/docs/page_client-reference-manifest.js +1 -1
- package/.next/server/app/health/page.js.nft.json +1 -1
- package/.next/server/app/health/page_client-reference-manifest.js +1 -1
- package/.next/server/app/health.html +1 -1
- package/.next/server/app/health.rsc +5 -5
- package/.next/server/app/hello/page.js.nft.json +1 -1
- package/.next/server/app/hello/page_client-reference-manifest.js +1 -1
- package/.next/server/app/hello.html +1 -1
- package/.next/server/app/hello.rsc +5 -5
- package/.next/server/app/index.html +1 -1
- package/.next/server/app/index.rsc +6 -6
- package/.next/server/app/page.js +9 -3
- package/.next/server/app/page.js.nft.json +1 -1
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app/privacy/page.js.nft.json +1 -1
- package/.next/server/app/privacy/page_client-reference-manifest.js +1 -1
- package/.next/server/app/privacy.html +1 -1
- package/.next/server/app/privacy.rsc +5 -5
- package/.next/server/app/share/[token]/page.js.nft.json +1 -1
- package/.next/server/app/share/[token]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/terms/page.js.nft.json +1 -1
- package/.next/server/app/terms/page_client-reference-manifest.js +1 -1
- package/.next/server/app/terms.html +1 -1
- package/.next/server/app/terms.rsc +5 -5
- package/.next/server/app/yo/page.js +1 -1
- package/.next/server/app/yo/page.js.nft.json +1 -1
- package/.next/server/app/yo/page_client-reference-manifest.js +1 -1
- package/.next/server/app/yo.html +1 -1
- package/.next/server/app/yo.rsc +6 -6
- package/.next/server/app-paths-manifest.json +2 -2
- package/.next/server/chunks/1196.js +1 -1
- package/.next/server/chunks/{6415.js → 2123.js} +2 -2
- package/.next/server/chunks/2234.js +1 -1
- package/.next/server/chunks/4224.js +1 -0
- package/.next/server/chunks/4600.js +1 -1
- package/.next/server/chunks/5678.js +1 -1
- package/.next/server/chunks/5991.js +7 -0
- package/.next/server/chunks/6470.js +1 -1
- package/.next/server/chunks/7585.js +1 -1
- package/.next/server/chunks/7935.js +19 -11
- package/.next/server/chunks/8468.js +1 -1
- package/.next/server/chunks/{1350.js → 9323.js} +3 -3
- package/.next/server/chunks/9971.js +1 -1
- package/.next/server/functions-config-manifest.json +1 -1
- package/.next/server/middleware-build-manifest.js +1 -1
- package/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/.next/server/pages/404.html +1 -1
- package/.next/server/pages/500.html +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/chunks/{1168.aaac1edbb597fe5a.js → 1168.bb1c93965872e980.js} +1 -1
- package/.next/static/chunks/2891-bf010357841c9ce9.js +1 -0
- package/.next/static/chunks/{2927.7e00cc878d9a3f52.js → 2927.3976471e61df966e.js} +1 -1
- package/.next/static/chunks/{3573-1b41d7b8a000d015.js → 3573-667bc4c8f56afcff.js} +1 -1
- package/.next/static/chunks/{3a91511d-ba215c0b5dc21ba9.js → 3a91511d-da2c74cad6c6fc33.js} +2 -2
- package/.next/static/chunks/4114-a375178fd94c4d8a.js +1 -0
- package/.next/static/chunks/{4256.48407d9abad5ea33.js → 4256.aa831e4d8c3f65bc.js} +1 -1
- package/.next/static/chunks/{4919-7e7cdd5efc9f2110.js → 4919-0e698cd95504e355.js} +1 -1
- package/.next/static/chunks/{5336-233ec7ab3807267c.js → 5336-b775f449d7433604.js} +1 -1
- package/.next/static/chunks/{7338-3ce17a93614f1d77.js → 5362-2e8fcdd0bc6273ca.js} +4 -4
- package/.next/static/chunks/67-cb9bbb430499e803.js +1 -0
- package/.next/static/chunks/{8273-8e92d34180669ca9.js → 8273-3b9d882c5dd6a9a8.js} +1 -1
- package/.next/static/chunks/8291-8f6fb6554e692552.js +1 -0
- package/.next/static/chunks/8370-6537b6b27df58b23.js +1 -0
- package/.next/static/chunks/app/app-legacy-do-not-use/page-efa348da049c5873.js +1 -0
- package/.next/static/chunks/app/{error-63cbe72522239cff.js → error-04a18fd49a89ee17.js} +1 -1
- package/.next/static/chunks/app/layout-7093dda0d106f34d.js +1 -0
- package/.next/static/chunks/app/page-0ec7cf48550f71e6.js +1 -0
- package/.next/static/chunks/app/yo/{page-cf5d6540ea31c31c.js → page-48930746b135037e.js} +1 -1
- package/.next/static/chunks/{webpack-79ad58260e9b10b4.js → webpack-15ec0114c9d658c2.js} +1 -1
- package/.next/static/css/35991998d1b5eff0.css +3 -0
- package/.next/trace +28 -28
- package/README.md +2 -2
- package/bin/auramaxx.js +87 -3
- package/package.json +8 -4
- package/prisma/migrations/20260301225214_social_tables/migration.sql +81 -0
- package/prisma/migrations/20260302120000_add_notification_hash/migration.sql +8 -0
- package/prisma/migrations/20260302160000_add_public_key_hex/migration.sql +5 -0
- package/prisma/migrations/20260302170000_add_inbound_author_public_key/migration.sql +5 -0
- package/prisma/schema.prisma +272 -177
- package/shared/credential-field-schema.ts +10 -0
- package/skills/auramaxx/SKILL.md +33 -4
- package/skills/auramaxx/docs/AGENT_SETUP.md +7 -2
- package/src/app/UnlockPageClient.tsx +448 -43
- package/src/app/app-legacy-do-not-use/page.tsx +5 -3
- package/src/app/globals.css +1 -1
- package/src/components/AgentPicker.tsx +278 -0
- package/src/components/CreateAgentModal.tsx +192 -0
- package/src/components/NotificationDrawer.tsx +15 -13
- package/src/components/agent/AgentSidebar.tsx +2 -19
- package/src/components/agent/CredentialAgent.tsx +70 -37
- package/src/components/agent/CredentialForm.tsx +32 -7
- package/src/components/agent/CredentialList.tsx +149 -72
- package/src/components/agents/AgentsView.tsx +1035 -0
- package/src/components/design-system/TextAreaInput.tsx +78 -0
- package/src/components/design-system/index.ts +1 -0
- package/src/components/hub/HubView.tsx +58 -0
- package/src/components/layout/LeftRail.tsx +333 -40
- package/src/components/layout/ViewShell.tsx +125 -0
- package/src/components/layout/index.ts +1 -0
- package/src/components/social/PrimaryHubRegistrationGate.tsx +260 -0
- package/src/components/social/SocialView.tsx +904 -0
- package/src/hooks/useSocialNotifications.ts +123 -0
- package/src/lib/events.ts +10 -1
- package/src/lib/social-client.ts +319 -0
- package/src/lib/view-registry.ts +22 -4
- package/src/server/cli/commands/agent.ts +33 -7
- package/src/server/cli/commands/create.ts +209 -0
- package/src/server/cli/commands/onboard.ts +39 -0
- package/src/server/cli/commands/play.ts +68 -0
- package/src/server/cli/commands/register-agent.ts +181 -0
- package/src/server/cli/commands/social.ts +648 -0
- package/src/server/cli/commands/start.ts +4 -3
- package/src/server/cron/index.ts +5 -9
- package/src/server/cron/jobs/social-inbound.ts +596 -0
- package/src/server/cron/jobs/social-outbound.ts +70 -0
- package/src/server/index.ts +14 -0
- package/src/server/lib/agent-profiles.ts +10 -1
- package/src/server/lib/cold.ts +0 -45
- package/src/server/lib/credential-import.ts +53 -47
- package/src/server/lib/defaults.ts +11 -0
- package/src/server/lib/feature-flags.ts +3 -0
- package/src/server/lib/hub-auth.ts +159 -0
- package/src/server/lib/migrate-agent-profiles.ts +96 -0
- package/src/server/lib/notifications.ts +12 -1
- package/src/server/lib/permissions.ts +10 -0
- package/src/server/lib/project-scope.ts +33 -5
- package/src/server/lib/social/create.ts +176 -0
- package/src/server/lib/social/global-aura-id.ts +115 -0
- package/src/server/lib/social/hub-rpc-client.ts +99 -0
- package/src/server/lib/social/hub-session.ts +6 -0
- package/src/server/lib/social/public-key.ts +32 -0
- package/src/server/lib/social/register.ts +162 -0
- package/src/server/lib/social/sign.ts +95 -0
- package/src/server/lib/social/sync.ts +301 -0
- package/src/server/mcp/server.ts +142 -18
- package/src/server/mcp/tools.ts +552 -0
- package/src/server/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -1
- package/src/server/routes/agent-hub.ts +276 -0
- package/src/server/routes/agent-profiles.ts +145 -19
- package/src/server/routes/credentials.ts +49 -15
- package/src/server/routes/heartbeat.ts +3 -7
- package/src/server/routes/passkey.ts +7 -1
- package/src/server/routes/social.ts +869 -0
- package/src/server/routes/unlock.ts +17 -1
- package/src/server/routes/verified-credentials.ts +312 -0
- package/src/server/test-data/template.db +0 -0
- package/src/server/test-data/worker-1/agent-primary.json +12 -0
- package/src/server/test-data/worker-1/auramaxx.db +0 -0
- package/src/server/test-data/worker-1/backups/auramaxx.db.20260305_011404.bak +0 -0
- package/src/server/test-data/worker-1/backups/credentials.20260305_011404.cred-00ntf5h3.json +24 -0
- package/src/server/test-data/worker-1/backups/credentials.20260305_011404.cred-dh8fa9k3.json +24 -0
- package/src/server/test-data/worker-1/backups/credentials.20260305_011404.cred-prfhlyow.json +25 -0
- package/src/server/test-data/worker-1/backups/credentials.20260305_011404.cred-w26jd6zi.json +22 -0
- package/src/server/test-data/worker-1/config.json +33 -0
- package/src/server/test-data/worker-1/test.db +0 -0
- package/src/server/test-data/worker-10/agent-primary.json +12 -0
- package/src/server/test-data/worker-10/config.json +33 -0
- package/src/server/test-data/worker-10/test.db +0 -0
- package/src/server/test-data/worker-11/agent-primary.json +12 -0
- package/src/server/test-data/worker-11/config.json +33 -0
- package/src/server/test-data/worker-11/test.db +0 -0
- package/src/server/test-data/worker-12/agent-primary.json +12 -0
- package/src/server/test-data/worker-12/config.json +33 -0
- package/src/server/test-data/worker-12/credentials/cred-hboagned.json +25 -0
- package/src/server/test-data/worker-12/credentials/cred-nt14xd9t.json +24 -0
- package/src/server/test-data/worker-12/credentials/cred-qn59xefc.json +24 -0
- package/src/server/test-data/worker-12/test.db +0 -0
- package/src/server/test-data/worker-13/agent-primary.json +12 -0
- package/src/server/test-data/worker-13/approval-context.json +56 -0
- package/src/server/test-data/worker-13/approval-context.key +1 -0
- package/src/server/test-data/worker-13/config.json +33 -0
- package/src/server/test-data/worker-13/credentials-recently-deleted/cred-jn9yqy2m.json +20 -0
- package/src/server/test-data/worker-13/test.db +0 -0
- package/src/server/test-data/worker-14/agent-primary.json +12 -0
- package/src/server/test-data/worker-14/config.json +33 -0
- package/src/server/test-data/worker-14/test.db +0 -0
- package/src/server/test-data/worker-15/agent-primary.json +12 -0
- package/src/server/test-data/worker-15/config.json +33 -0
- package/src/server/test-data/worker-15/test.db +0 -0
- package/src/server/test-data/worker-15/update-check-4b305e76.json +5 -0
- package/src/server/test-data/worker-2/agent-primary.json +12 -0
- package/src/server/test-data/worker-2/config.json +33 -0
- package/src/server/test-data/worker-2/test.db +0 -0
- package/src/server/test-data/worker-3/agent-primary.json +12 -0
- package/src/server/test-data/worker-3/config.json +33 -0
- package/src/server/test-data/worker-3/test.db +0 -0
- package/src/server/test-data/worker-4/agent-primary.json +12 -0
- package/src/server/test-data/worker-4/config.json +33 -0
- package/src/server/test-data/worker-4/credentials/cred-bm6p74u5.json +24 -0
- package/src/server/test-data/worker-4/credentials/cred-r8eqb07i.json +25 -0
- package/src/server/test-data/worker-4/credentials/cred-ziv49y9h.json +24 -0
- package/src/server/test-data/worker-4/test.db +0 -0
- package/src/server/test-data/worker-5/agent-primary.json +12 -0
- package/src/server/test-data/worker-5/config.json +33 -0
- package/src/server/test-data/worker-5/credentials/cred-15gyfeht.json +25 -0
- package/src/server/test-data/worker-5/credentials/cred-lp0ocnfc.json +24 -0
- package/src/server/test-data/worker-5/credentials/cred-zfbwfxw0.json +24 -0
- package/src/server/test-data/worker-5/test.db +0 -0
- package/src/server/test-data/worker-6/agent-primary.json +12 -0
- package/src/server/test-data/worker-6/config.json +33 -0
- package/src/server/test-data/worker-6/credentials/cred-dx8li3ue.json +24 -0
- package/src/server/test-data/worker-6/credentials/cred-myeskqky.json +24 -0
- package/src/server/test-data/worker-6/credentials/cred-tam01qdt.json +25 -0
- package/src/server/test-data/worker-6/credentials/cred-wdh6rslw.json +22 -0
- package/src/server/test-data/worker-6/credentials-archive/cred-4wg53v1y.json +25 -0
- package/src/server/test-data/worker-6/test.db +0 -0
- package/src/server/test-data/worker-7/agent-primary.json +12 -0
- package/src/server/test-data/worker-7/config.json +33 -0
- package/src/server/test-data/worker-7/credentials/cred-ji7ec57d.json +25 -0
- package/src/server/test-data/worker-7/credentials/cred-mmgj0ypo.json +24 -0
- package/src/server/test-data/worker-7/credentials/cred-nb0ic93x.json +24 -0
- package/src/server/test-data/worker-7/test.db +0 -0
- package/src/server/test-data/worker-8/agent-primary.json +12 -0
- package/src/server/test-data/worker-8/config.json +33 -0
- package/src/server/test-data/worker-8/test.db +0 -0
- package/src/server/test-data/worker-9/agent-primary.json +12 -0
- package/src/server/test-data/worker-9/test.db +0 -0
- package/src/server/tests/cli/bin-entrypoint.test.ts +99 -0
- package/src/server/tests/cli/social.test.ts +83 -0
- package/src/server/tests/cron/social-inbound-followers.unit.test.ts +164 -0
- package/src/server/tests/cron/social-inbound-staleness.test.ts +193 -0
- package/src/server/tests/cron/social-outbound.test.ts +252 -0
- package/src/server/tests/endpoints/agent.test.ts +0 -16
- package/src/server/tests/lib/credential-field-schema.test.ts +448 -0
- package/src/server/tests/lib/project-scope.test.ts +49 -0
- package/src/server/tests/lib/social/create.test.ts +207 -0
- package/src/server/tests/lib/social/sign.test.ts +163 -0
- package/src/server/tests/lib/social/sync.test.ts +198 -0
- package/src/server/tests/mcp/server.test.ts +1 -3
- package/src/server/tests/mcp/tools.test.ts +219 -7
- package/src/server/tsconfig.tsbuildinfo +1 -1
- package/tsconfig.json +1 -0
- package/.next/server/chunks/2460.js +0 -7
- package/.next/server/chunks/5246.js +0 -1
- package/.next/static/chunks/3667-1db7bd03948e60df.js +0 -1
- package/.next/static/chunks/5442-b5bb869e832e8967.js +0 -1
- package/.next/static/chunks/6872-a8f1ad5fc35a1c35.js +0 -1
- package/.next/static/chunks/7394-4bdb1feefad1a74a.js +0 -1
- package/.next/static/chunks/7587-8542eae6e5139d27.js +0 -1
- package/.next/static/chunks/app/app-legacy-do-not-use/page-569fed5d1a2bfe79.js +0 -1
- package/.next/static/chunks/app/layout-af8d9969c7aeb758.js +0 -1
- package/.next/static/chunks/app/page-9bd0ec3ba4aa6cea.js +0 -1
- package/.next/static/css/88f92e1578b1e5d9.css +0 -3
- package/shared/agent-profile-schema.ts +0 -81
- package/src/internal/security/frontend-api-authorization-audit.md +0 -206
- package/src/server/lib/agent-profile-records.ts +0 -72
- /package/.next/static/{QPzVnl7GToqgYBq14V_iC → yEruVJH_cuht5hu5_1R00}/_buildManifest.js +0 -0
- /package/.next/static/{QPzVnl7GToqgYBq14V_iC → yEruVJH_cuht5hu5_1R00}/_ssgManifest.js +0 -0
package/src/server/index.ts
CHANGED
|
@@ -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: [
|
|
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'],
|
package/src/server/lib/cold.ts
CHANGED
|
@@ -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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
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:
|
|
445
|
-
if (password) fields.push({ key: 'password', value: password, sensitive:
|
|
446
|
-
if (notes) fields.push({ key: 'notes', value: notes, sensitive:
|
|
447
|
-
if (totp) fields.push({ key: 'totp', value: totp, sensitive:
|
|
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:
|
|
490
|
-
if (password) fields.push({ key: 'password', value: password, sensitive:
|
|
491
|
-
if (notes) fields.push({ key: 'notes', value: notes, sensitive:
|
|
492
|
-
if (totp) fields.push({ key: 'totp', value: totp, sensitive:
|
|
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:
|
|
528
|
-
if (login.password) fields.push({ key: 'password', value: String(login.password), sensitive:
|
|
529
|
-
if (item?.notesPlain || item?.notes) fields.push({ key: 'notes', value: String(item.notesPlain || item.notes), sensitive:
|
|
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:
|
|
550
|
-
if (login.password) fields.push({ key: 'password', value: String(login.password), sensitive:
|
|
551
|
-
if (item?.notes) fields.push({ key: 'notes', value: String(item.notes), sensitive:
|
|
552
|
-
if (login?.totp) fields.push({ key: 'totp', value: String(login.totp), sensitive:
|
|
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:
|
|
570
|
-
if (item?.password) fields.push({ key: 'password', value: String(item.password), sensitive:
|
|
571
|
-
if (item?.note || item?.notes) fields.push({ key: 'notes', value: String(item.note || item.notes), sensitive:
|
|
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:
|
|
589
|
-
if (item?.password) fields.push({ key: 'password', value: String(item.password), sensitive:
|
|
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
|
+
}
|