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
|
@@ -15,18 +15,28 @@ interface NotificationAction {
|
|
|
15
15
|
|
|
16
16
|
interface CreateNotificationParams {
|
|
17
17
|
type: 'pending_approval' | 'info' | 'warning' | 'success' | 'error' | 'system';
|
|
18
|
-
category?: 'transaction' | 'security' | 'token' | 'wallet' | 'general';
|
|
18
|
+
category?: 'transaction' | 'security' | 'token' | 'wallet' | 'general' | 'social';
|
|
19
19
|
title: string;
|
|
20
20
|
message: string;
|
|
21
21
|
actions?: NotificationAction[];
|
|
22
22
|
metadata?: Record<string, unknown>;
|
|
23
23
|
humanActionId?: string;
|
|
24
|
+
hash?: string;
|
|
24
25
|
expiresAt?: Date;
|
|
25
26
|
source?: 'system' | 'agent' | 'user';
|
|
26
27
|
agentId?: string;
|
|
27
28
|
}
|
|
28
29
|
|
|
29
30
|
export async function createNotification(params: CreateNotificationParams) {
|
|
31
|
+
// If hash is provided, skip if notification with this hash already exists (dedup)
|
|
32
|
+
if (params.hash) {
|
|
33
|
+
const existing = await prisma.notification.findUnique({
|
|
34
|
+
where: { hash: params.hash },
|
|
35
|
+
select: { id: true },
|
|
36
|
+
});
|
|
37
|
+
if (existing) return null;
|
|
38
|
+
}
|
|
39
|
+
|
|
30
40
|
return prisma.notification.create({
|
|
31
41
|
data: {
|
|
32
42
|
type: params.type,
|
|
@@ -36,6 +46,7 @@ export async function createNotification(params: CreateNotificationParams) {
|
|
|
36
46
|
actions: params.actions ? JSON.stringify(params.actions) : null,
|
|
37
47
|
metadata: params.metadata ? JSON.stringify(params.metadata) : null,
|
|
38
48
|
humanActionId: params.humanActionId,
|
|
49
|
+
hash: params.hash,
|
|
39
50
|
expiresAt: params.expiresAt,
|
|
40
51
|
source: params.source || 'system',
|
|
41
52
|
agentId: params.agentId,
|
|
@@ -63,6 +63,14 @@ export type Permission =
|
|
|
63
63
|
| 'secret:write' // Create/update/delete credentials in the agent
|
|
64
64
|
| 'totp:read' // Generate TOTP codes from credential secrets
|
|
65
65
|
|
|
66
|
+
// Social operations
|
|
67
|
+
| 'social:read' // Read feed, messages, followers, following, sync status
|
|
68
|
+
| 'social:write' // Create posts, reactions, follows, profile updates, removals
|
|
69
|
+
|
|
70
|
+
// Verified credential operations
|
|
71
|
+
| 'credential:read' // View verified credentials and verification status
|
|
72
|
+
| 'credential:write' // Initiate new credential verifications
|
|
73
|
+
|
|
66
74
|
// Compound permissions (expand to multiple permissions)
|
|
67
75
|
| 'trade:all' // All trading permissions + apikey:get + strategy:read
|
|
68
76
|
| 'wallet:write' // All wallet write operations
|
|
@@ -88,6 +96,8 @@ const COMPOUND_PERMISSIONS: Record<string, Permission[]> = {
|
|
|
88
96
|
'app:storage', 'app:storage:all', 'app:accesskey',
|
|
89
97
|
'adapter:manage',
|
|
90
98
|
'addressbook:write', 'bookmark:write',
|
|
99
|
+
'social:read', 'social:write',
|
|
100
|
+
'credential:read', 'credential:write',
|
|
91
101
|
],
|
|
92
102
|
'trade:all': [
|
|
93
103
|
'wallet:list',
|
|
@@ -190,7 +190,20 @@ export function evaluateProjectScopeAccess(input: {
|
|
|
190
190
|
|
|
191
191
|
const cwd = input.cwd || process.cwd();
|
|
192
192
|
const { projectRoot, auraPath } = resolveAuraPath({ cwd, projectRootOverride: input.projectRootOverride });
|
|
193
|
-
|
|
193
|
+
const auraExists = Boolean(auraPath && fs.existsSync(auraPath));
|
|
194
|
+
const auraIsFile = auraExists
|
|
195
|
+
? (() => {
|
|
196
|
+
try {
|
|
197
|
+
return fs.statSync(auraPath!).isFile();
|
|
198
|
+
} catch {
|
|
199
|
+
return false;
|
|
200
|
+
}
|
|
201
|
+
})()
|
|
202
|
+
: false;
|
|
203
|
+
|
|
204
|
+
const auraFilePath = auraExists && auraIsFile && auraPath ? auraPath : null;
|
|
205
|
+
|
|
206
|
+
if (!auraFilePath) {
|
|
194
207
|
if (projectScopeMode === 'auto') {
|
|
195
208
|
return {
|
|
196
209
|
allowed: true,
|
|
@@ -203,10 +216,13 @@ export function evaluateProjectScopeAccess(input: {
|
|
|
203
216
|
allowedCandidates: input.candidates,
|
|
204
217
|
};
|
|
205
218
|
}
|
|
219
|
+
const auraWrongType = auraExists && !auraIsFile;
|
|
206
220
|
return {
|
|
207
221
|
allowed: false,
|
|
208
|
-
code: 'PROJECT_SCOPE_MISSING_AURA',
|
|
209
|
-
remediation:
|
|
222
|
+
code: auraWrongType ? 'PROJECT_SCOPE_INVALID_AURA' : 'PROJECT_SCOPE_MISSING_AURA',
|
|
223
|
+
remediation: auraWrongType
|
|
224
|
+
? '.aura must be a file (not a directory). Use ENV=@agent/name/field or ENV=name/field lines.'
|
|
225
|
+
: 'Add a .aura file in the project root (or set AURA_PROJECT_ROOT / --project-root).',
|
|
210
226
|
projectScopeMode,
|
|
211
227
|
normalizedIdentity,
|
|
212
228
|
projectRoot,
|
|
@@ -218,10 +234,22 @@ export function evaluateProjectScopeAccess(input: {
|
|
|
218
234
|
let refs: ParsedRef[];
|
|
219
235
|
let auraFingerprint: string;
|
|
220
236
|
try {
|
|
221
|
-
const content = fs.readFileSync(
|
|
237
|
+
const content = fs.readFileSync(auraFilePath, 'utf-8');
|
|
222
238
|
auraFingerprint = fingerprint(content);
|
|
223
|
-
refs = parseAuraAllowlist(
|
|
239
|
+
refs = parseAuraAllowlist(auraFilePath);
|
|
224
240
|
} catch {
|
|
241
|
+
if (projectScopeMode === 'auto') {
|
|
242
|
+
return {
|
|
243
|
+
allowed: true,
|
|
244
|
+
code: null,
|
|
245
|
+
remediation: '',
|
|
246
|
+
projectScopeMode,
|
|
247
|
+
normalizedIdentity,
|
|
248
|
+
projectRoot,
|
|
249
|
+
auraFingerprint: null,
|
|
250
|
+
allowedCandidates: input.candidates,
|
|
251
|
+
};
|
|
252
|
+
}
|
|
225
253
|
return {
|
|
226
254
|
allowed: false,
|
|
227
255
|
code: 'PROJECT_SCOPE_INVALID_AURA',
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Social message creation -- sign + INSERT into SocialMessage.
|
|
3
|
+
*
|
|
4
|
+
* Each wrapper: look up auraId -> build MessageData -> sign -> INSERT.
|
|
5
|
+
* Returns immediately; caller can trigger async hub submit and cron retries remain as fallback.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { SocialMessage } from '@prisma/client';
|
|
9
|
+
import { prisma } from '../db';
|
|
10
|
+
import { signMessage, type MessageData } from './sign';
|
|
11
|
+
|
|
12
|
+
export interface CreatePostOpts {
|
|
13
|
+
embeds?: string[];
|
|
14
|
+
parentPostHash?: string;
|
|
15
|
+
mentions?: number[];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// --- Internal helpers ---
|
|
19
|
+
|
|
20
|
+
async function getOrDerivePublicKeyHex(agentId: string, mnemonic: string): Promise<string> {
|
|
21
|
+
const profile = await prisma.agentProfile.findUnique({ where: { agentId } });
|
|
22
|
+
if (profile?.publicKeyHex) return profile.publicKeyHex;
|
|
23
|
+
|
|
24
|
+
// Derive and cache
|
|
25
|
+
const { deriveSigningSeed } = await import('./sign');
|
|
26
|
+
const { ed25519 } = await import('@noble/curves/ed25519.js');
|
|
27
|
+
const seed = deriveSigningSeed(mnemonic);
|
|
28
|
+
const pubKey = Buffer.from(ed25519.getPublicKey(seed)).toString('hex');
|
|
29
|
+
|
|
30
|
+
await prisma.agentProfile.upsert({
|
|
31
|
+
where: { agentId },
|
|
32
|
+
update: { publicKeyHex: pubKey },
|
|
33
|
+
create: { agentId, publicKeyHex: pubKey },
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
return pubKey;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async function createMessage(
|
|
40
|
+
agentId: string,
|
|
41
|
+
mnemonic: string,
|
|
42
|
+
type: string,
|
|
43
|
+
body: Record<string, unknown>,
|
|
44
|
+
hubUrl: string = '',
|
|
45
|
+
): Promise<SocialMessage> {
|
|
46
|
+
await getOrDerivePublicKeyHex(agentId, mnemonic);
|
|
47
|
+
|
|
48
|
+
const data: MessageData = {
|
|
49
|
+
type,
|
|
50
|
+
timestamp: Math.floor(Date.now() / 1000),
|
|
51
|
+
network: 'mainnet',
|
|
52
|
+
body,
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const envelope = signMessage(data, mnemonic);
|
|
56
|
+
|
|
57
|
+
const insertData = {
|
|
58
|
+
agentId,
|
|
59
|
+
hash: envelope.hash,
|
|
60
|
+
type,
|
|
61
|
+
body: JSON.stringify(body),
|
|
62
|
+
timestamp: data.timestamp,
|
|
63
|
+
network: data.network,
|
|
64
|
+
signature: envelope.signature,
|
|
65
|
+
signer: envelope.signer,
|
|
66
|
+
syncStatus: 'pending' as const,
|
|
67
|
+
hubUrl,
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
return await prisma.socialMessage.create({ data: insertData });
|
|
72
|
+
} catch (error) {
|
|
73
|
+
// Idempotent write: if the same signed payload was already inserted,
|
|
74
|
+
// return that existing row instead of surfacing a unique-hash error.
|
|
75
|
+
const isUniqueViolation =
|
|
76
|
+
typeof error === 'object'
|
|
77
|
+
&& error !== null
|
|
78
|
+
&& 'code' in error
|
|
79
|
+
&& (error as { code?: string }).code === 'P2002';
|
|
80
|
+
|
|
81
|
+
if (!isUniqueViolation) throw error;
|
|
82
|
+
|
|
83
|
+
const existing = await prisma.socialMessage.findUnique({
|
|
84
|
+
where: { hash: envelope.hash },
|
|
85
|
+
});
|
|
86
|
+
if (existing) return existing;
|
|
87
|
+
throw error;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// --- Public wrappers ---
|
|
92
|
+
|
|
93
|
+
export async function createPost(
|
|
94
|
+
agentId: string,
|
|
95
|
+
mnemonic: string,
|
|
96
|
+
text: string,
|
|
97
|
+
opts?: CreatePostOpts & { hubUrl?: string },
|
|
98
|
+
): Promise<SocialMessage> {
|
|
99
|
+
const body: Record<string, unknown> = { text };
|
|
100
|
+
if (opts?.embeds?.length) body.embeds = opts.embeds;
|
|
101
|
+
if (opts?.parentPostHash) body.parentPostHash = opts.parentPostHash;
|
|
102
|
+
if (opts?.mentions?.length) body.mentions = opts.mentions;
|
|
103
|
+
return createMessage(agentId, mnemonic, 'post_add', body, opts?.hubUrl);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export async function createReaction(
|
|
107
|
+
agentId: string,
|
|
108
|
+
mnemonic: string,
|
|
109
|
+
postHash: string,
|
|
110
|
+
reactionType: string,
|
|
111
|
+
hubUrl?: string,
|
|
112
|
+
): Promise<SocialMessage> {
|
|
113
|
+
return createMessage(agentId, mnemonic, 'reaction_add', {
|
|
114
|
+
postHash,
|
|
115
|
+
reactionType,
|
|
116
|
+
}, hubUrl);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export async function createFollow(
|
|
120
|
+
agentId: string,
|
|
121
|
+
mnemonic: string,
|
|
122
|
+
targetPublicKey: string,
|
|
123
|
+
hubUrl?: string,
|
|
124
|
+
): Promise<SocialMessage> {
|
|
125
|
+
return createMessage(agentId, mnemonic, 'link_add', {
|
|
126
|
+
followeePublicKey: targetPublicKey,
|
|
127
|
+
linkType: 'follow',
|
|
128
|
+
}, hubUrl);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export async function createUnfollow(
|
|
132
|
+
agentId: string,
|
|
133
|
+
mnemonic: string,
|
|
134
|
+
targetPublicKey: string,
|
|
135
|
+
hubUrl?: string,
|
|
136
|
+
): Promise<SocialMessage> {
|
|
137
|
+
return createMessage(agentId, mnemonic, 'link_remove', {
|
|
138
|
+
followeePublicKey: targetPublicKey,
|
|
139
|
+
linkType: 'follow',
|
|
140
|
+
}, hubUrl);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export async function createPostRemove(
|
|
144
|
+
agentId: string,
|
|
145
|
+
mnemonic: string,
|
|
146
|
+
targetPostHash: string,
|
|
147
|
+
hubUrl?: string,
|
|
148
|
+
): Promise<SocialMessage> {
|
|
149
|
+
return createMessage(agentId, mnemonic, 'post_remove', { targetPostHash }, hubUrl);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export async function createReactionRemove(
|
|
153
|
+
agentId: string,
|
|
154
|
+
mnemonic: string,
|
|
155
|
+
postHash: string,
|
|
156
|
+
reactionType: string,
|
|
157
|
+
hubUrl?: string,
|
|
158
|
+
): Promise<SocialMessage> {
|
|
159
|
+
return createMessage(agentId, mnemonic, 'reaction_remove', {
|
|
160
|
+
postHash,
|
|
161
|
+
reactionType,
|
|
162
|
+
}, hubUrl);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
export async function createProfileUpdate(
|
|
166
|
+
agentId: string,
|
|
167
|
+
mnemonic: string,
|
|
168
|
+
field: string,
|
|
169
|
+
value: string,
|
|
170
|
+
hubUrl?: string,
|
|
171
|
+
): Promise<SocialMessage> {
|
|
172
|
+
return createMessage(agentId, mnemonic, 'user_data_add', {
|
|
173
|
+
type: field,
|
|
174
|
+
value,
|
|
175
|
+
}, hubUrl);
|
|
176
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { ed25519 } from '@noble/curves/ed25519.js';
|
|
2
|
+
import { prisma } from '../db';
|
|
3
|
+
import { log } from '../pino';
|
|
4
|
+
import { deriveSigningSeed } from './sign';
|
|
5
|
+
|
|
6
|
+
const DEFAULT_GLOBAL_HUB_URL = 'https://hub.auramaxx.now';
|
|
7
|
+
const IDENTITY_MAP_PATH = '/api/identity-map';
|
|
8
|
+
const LOOKUP_TIMEOUT_MS = 2_500;
|
|
9
|
+
|
|
10
|
+
interface IdentityMapResponse {
|
|
11
|
+
auraId?: unknown;
|
|
12
|
+
found?: unknown;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function getGlobalHubUrl(): string {
|
|
16
|
+
const raw = process.env.GLOBAL_AURA_HUB_URL;
|
|
17
|
+
if (typeof raw === 'string' && raw.trim()) {
|
|
18
|
+
return raw.trim().replace(/\/+$/, '');
|
|
19
|
+
}
|
|
20
|
+
return DEFAULT_GLOBAL_HUB_URL;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function parseAuraId(value: unknown): number | null {
|
|
24
|
+
if (typeof value === 'number' && Number.isFinite(value) && value > 0) {
|
|
25
|
+
return Math.floor(value);
|
|
26
|
+
}
|
|
27
|
+
if (typeof value === 'string') {
|
|
28
|
+
const parsed = Number.parseInt(value, 10);
|
|
29
|
+
if (Number.isFinite(parsed) && parsed > 0) {
|
|
30
|
+
return parsed;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function derivePublicKeys(mnemonic: string): { publicKeyHex: string; publicKeyBase64: string } {
|
|
37
|
+
const seed = deriveSigningSeed(mnemonic);
|
|
38
|
+
const pubKeyBytes = ed25519.getPublicKey(seed);
|
|
39
|
+
return {
|
|
40
|
+
publicKeyHex: Buffer.from(pubKeyBytes).toString('hex'),
|
|
41
|
+
publicKeyBase64: Buffer.from(pubKeyBytes).toString('base64'),
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async function cachePublicKey(agentId: string, publicKeyHex: string): Promise<void> {
|
|
46
|
+
await prisma.agentProfile.upsert({
|
|
47
|
+
where: { agentId },
|
|
48
|
+
create: { agentId, publicKeyHex },
|
|
49
|
+
update: { publicKeyHex },
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
async function fetchGlobalAuraId(publicKeyBase64: string): Promise<number | null> {
|
|
54
|
+
const endpoint = new URL(IDENTITY_MAP_PATH, `${getGlobalHubUrl()}/`);
|
|
55
|
+
endpoint.searchParams.set('address', publicKeyBase64);
|
|
56
|
+
|
|
57
|
+
const response = await fetch(endpoint.toString(), {
|
|
58
|
+
method: 'GET',
|
|
59
|
+
signal: AbortSignal.timeout(LOOKUP_TIMEOUT_MS),
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
if (!response.ok) return null;
|
|
63
|
+
|
|
64
|
+
const payload = await response.json() as IdentityMapResponse;
|
|
65
|
+
const found = payload.found;
|
|
66
|
+
if (found === false) return null;
|
|
67
|
+
return parseAuraId(payload.auraId);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Best-effort sync of the global auraId from hub.auramaxx.now.
|
|
72
|
+
* - Caches pubkey locally always
|
|
73
|
+
* - Caches auraId locally only when remote lookup succeeds
|
|
74
|
+
* - Never throws (safe to call during login / register flows)
|
|
75
|
+
*/
|
|
76
|
+
export async function syncGlobalAuraIdForAgent(agentId: string, mnemonic: string): Promise<number | null> {
|
|
77
|
+
const { publicKeyHex, publicKeyBase64 } = derivePublicKeys(mnemonic);
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
await cachePublicKey(agentId, publicKeyHex);
|
|
81
|
+
} catch (error) {
|
|
82
|
+
log.debug({ agentId, error }, 'Failed to cache agent public key while syncing global auraId');
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Keep tests deterministic and offline.
|
|
87
|
+
if (process.env.NODE_ENV === 'test') {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
let auraId: number | null = null;
|
|
92
|
+
try {
|
|
93
|
+
auraId = await fetchGlobalAuraId(publicKeyBase64);
|
|
94
|
+
} catch (error) {
|
|
95
|
+
log.debug({ agentId, error }, 'Global auraId lookup failed');
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (!auraId) {
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
try {
|
|
104
|
+
await prisma.agentProfile.upsert({
|
|
105
|
+
where: { agentId },
|
|
106
|
+
create: { agentId, publicKeyHex, auraId },
|
|
107
|
+
update: { publicKeyHex, auraId },
|
|
108
|
+
});
|
|
109
|
+
return auraId;
|
|
110
|
+
} catch (error) {
|
|
111
|
+
// auraId has a unique constraint; ignore conflicts across agents.
|
|
112
|
+
log.warn({ agentId, auraId, error }, 'Failed to cache global auraId locally');
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Typed RPC client for hub communication.
|
|
3
|
+
*
|
|
4
|
+
* Replaces per-file fetch() calls with a single `call(method, params)`.
|
|
5
|
+
* Envelope: { method, params } → { ok: true, result } | { ok: false, error, detail }
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export class HubRpcError extends Error {
|
|
9
|
+
readonly code: string;
|
|
10
|
+
readonly detail: string;
|
|
11
|
+
readonly statusCode: number;
|
|
12
|
+
|
|
13
|
+
constructor(code: string, detail: string, statusCode = 400) {
|
|
14
|
+
super(`${code}: ${detail}`);
|
|
15
|
+
this.name = 'HubRpcError';
|
|
16
|
+
this.code = code;
|
|
17
|
+
this.detail = detail;
|
|
18
|
+
this.statusCode = statusCode;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface HubRpcCallOptions {
|
|
23
|
+
/** Override default timeout (ms). Default: 15_000. */
|
|
24
|
+
timeoutMs?: number;
|
|
25
|
+
/** AbortSignal to cancel the request. */
|
|
26
|
+
signal?: AbortSignal;
|
|
27
|
+
/** Override bearer token for this call only. */
|
|
28
|
+
bearerToken?: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface HubRpcClientOptions {
|
|
32
|
+
/** Optional bearer token sent as Authorization header. */
|
|
33
|
+
bearerToken?: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export class HubRpcClient {
|
|
37
|
+
private bearerToken?: string;
|
|
38
|
+
|
|
39
|
+
constructor(private readonly hubUrl: string, opts?: HubRpcClientOptions) {
|
|
40
|
+
this.bearerToken = opts?.bearerToken;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
setBearerToken(token: string | undefined): void {
|
|
44
|
+
this.bearerToken = token;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Call an RPC method on the hub. Returns the `result` on success.
|
|
49
|
+
* Throws `HubRpcError` on RPC-level errors, or a plain `Error` on network failure.
|
|
50
|
+
*/
|
|
51
|
+
async call<R = unknown>(
|
|
52
|
+
method: string,
|
|
53
|
+
params: Record<string, unknown> = {},
|
|
54
|
+
opts?: HubRpcCallOptions,
|
|
55
|
+
): Promise<R> {
|
|
56
|
+
const timeoutMs = opts?.timeoutMs ?? 15_000;
|
|
57
|
+
const signal = opts?.signal ?? AbortSignal.timeout(timeoutMs);
|
|
58
|
+
const bearerToken = opts?.bearerToken ?? this.bearerToken;
|
|
59
|
+
const headers: Record<string, string> = { 'Content-Type': 'application/json' };
|
|
60
|
+
if (bearerToken) {
|
|
61
|
+
headers.Authorization = `Bearer ${bearerToken}`;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const res = await fetch(`${this.hubUrl}/rpc`, {
|
|
65
|
+
method: 'POST',
|
|
66
|
+
headers,
|
|
67
|
+
body: JSON.stringify({ method, params }),
|
|
68
|
+
signal,
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
const json = (await res.json()) as Record<string, unknown>;
|
|
72
|
+
|
|
73
|
+
if (!json.ok) {
|
|
74
|
+
throw new HubRpcError(
|
|
75
|
+
(json.error as string) ?? 'unknown_error',
|
|
76
|
+
(json.detail as string) ?? 'Hub returned an error',
|
|
77
|
+
res.status,
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return json.result as R;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Like `call()` but returns `null` instead of throwing on any error.
|
|
86
|
+
* Useful for cron jobs and graceful degradation paths.
|
|
87
|
+
*/
|
|
88
|
+
async tryCall<R = unknown>(
|
|
89
|
+
method: string,
|
|
90
|
+
params: Record<string, unknown> = {},
|
|
91
|
+
opts?: HubRpcCallOptions,
|
|
92
|
+
): Promise<R | null> {
|
|
93
|
+
try {
|
|
94
|
+
return await this.call<R>(method, params, opts);
|
|
95
|
+
} catch {
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public-key format helpers for hub RPC interoperability.
|
|
3
|
+
*
|
|
4
|
+
* AuraWallet stores signer keys as 32-byte hex.
|
|
5
|
+
* AuraHub identity/publicKey fields are base64.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const HEX_ED25519_RE = /^[0-9a-f]{64}$/i;
|
|
9
|
+
|
|
10
|
+
export function normalizeHubPublicKey(publicKey: string): string {
|
|
11
|
+
const trimmed = publicKey.trim();
|
|
12
|
+
if (!trimmed) return '';
|
|
13
|
+
if (HEX_ED25519_RE.test(trimmed)) {
|
|
14
|
+
return Buffer.from(trimmed.toLowerCase(), 'hex').toString('base64');
|
|
15
|
+
}
|
|
16
|
+
return trimmed;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function normalizeFollowBodyForHub(
|
|
20
|
+
type: string,
|
|
21
|
+
body: Record<string, unknown>,
|
|
22
|
+
): Record<string, unknown> {
|
|
23
|
+
if (type !== 'link_add' && type !== 'link_remove') return body;
|
|
24
|
+
|
|
25
|
+
const followee = body.followeePublicKey;
|
|
26
|
+
if (typeof followee !== 'string') return body;
|
|
27
|
+
|
|
28
|
+
const normalized = normalizeHubPublicKey(followee);
|
|
29
|
+
if (!normalized || normalized === followee) return body;
|
|
30
|
+
return { ...body, followeePublicKey: normalized };
|
|
31
|
+
}
|
|
32
|
+
|