auramaxx 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +26 -0
- package/README.md +77 -0
- package/apps/desktop-electron/main.js +428 -0
- package/bin/auramaxx.js +1063 -0
- package/docs/ADAPTERS.md +466 -0
- package/docs/AGENT_SETUP.md +159 -0
- package/docs/API.md +127 -0
- package/docs/APPS.md +199 -0
- package/docs/ARCHITECTURE.md +235 -0
- package/docs/AUTH.md +318 -0
- package/docs/BEST-PRACTICES.md +82 -0
- package/docs/CLI.md +141 -0
- package/docs/DESKTOP_ELECTRON.md +26 -0
- package/docs/DEVELOPING-APPS.md +453 -0
- package/docs/MCP.md +122 -0
- package/docs/PACKAGING_POLICY.md +19 -0
- package/docs/PERMISSION.md +137 -0
- package/docs/PROTOCOL.md +142 -0
- package/docs/README.md +50 -0
- package/docs/SKILLS.md +132 -0
- package/docs/TROUBLESHOOTING.md +376 -0
- package/docs/WORKSPACE.md +673 -0
- package/docs/agent-auth.md +14 -0
- package/docs/api/authentication.md +79 -0
- package/docs/api/secrets/api-keys.md +28 -0
- package/docs/api/secrets/credentials.md +80 -0
- package/docs/api/secrets/sharing.md +48 -0
- package/docs/api/system.md +41 -0
- package/docs/api/wallets/apps-strategies.md +66 -0
- package/docs/api/wallets/core.md +46 -0
- package/docs/api/wallets/data-portfolio.md +42 -0
- package/docs/aura-file.md +48 -0
- package/docs/core-concepts/FEATURES.md +114 -0
- package/docs/credentials.md +120 -0
- package/docs/external/HOW_TO_AURAMAXX/GETTING_SECRETS.md +33 -0
- package/docs/external/HOW_TO_AURAMAXX/README.md +45 -0
- package/docs/external/getting-started.md +10 -0
- package/docs/external/overview.md +19 -0
- package/docs/external/persona-paths.md +7 -0
- package/docs/external/share-secret.md +76 -0
- package/docs/external/why-aura.md +7 -0
- package/docs/security.md +227 -0
- package/docs/templates/RELEASE_NOTES_TEMPLATE.md +22 -0
- package/docs/wallet/AI.md +508 -0
- package/docs/wallet/DEVELOPING-STRATEGIES.md +713 -0
- package/docs/wallet/README.md +47 -0
- package/docs/wallet/STRATEGY.md +89 -0
- package/next.config.ts +28 -0
- package/package.json +167 -0
- package/postcss.config.mjs +8 -0
- package/prisma/migrations/20260214170000_baseline/migration.sql +511 -0
- package/prisma/migrations/20260216214537_add_passkey_model/migration.sql +18 -0
- package/prisma/migrations/20260217150500_add_credential_access_audit/migration.sql +31 -0
- package/prisma/migrations/20260222090000_update_admin_ttl_default/migration.sql +10 -0
- package/prisma/migrations/migration_lock.toml +3 -0
- package/prisma/schema.prisma +447 -0
- package/public/logo.webp +0 -0
- package/scripts/add-app.js +245 -0
- package/server/abi/SwapHelper.json +438 -0
- package/server/cli/approval.ts +447 -0
- package/server/cli/commands/actions.ts +474 -0
- package/server/cli/commands/api.ts +220 -0
- package/server/cli/commands/apikey.ts +277 -0
- package/server/cli/commands/app.ts +204 -0
- package/server/cli/commands/auth.ts +464 -0
- package/server/cli/commands/cron.ts +24 -0
- package/server/cli/commands/diary.ts +274 -0
- package/server/cli/commands/doctor.ts +1247 -0
- package/server/cli/commands/env.ts +476 -0
- package/server/cli/commands/experimental.ts +69 -0
- package/server/cli/commands/init.ts +798 -0
- package/server/cli/commands/lock.ts +157 -0
- package/server/cli/commands/mcp.ts +285 -0
- package/server/cli/commands/quickhack.ts +86 -0
- package/server/cli/commands/release-check.ts +231 -0
- package/server/cli/commands/restore.ts +314 -0
- package/server/cli/commands/service.ts +320 -0
- package/server/cli/commands/shell-hook.ts +512 -0
- package/server/cli/commands/skill.ts +216 -0
- package/server/cli/commands/start.ts +139 -0
- package/server/cli/commands/status.ts +59 -0
- package/server/cli/commands/stop.ts +36 -0
- package/server/cli/commands/token.ts +180 -0
- package/server/cli/commands/unlock.ts +50 -0
- package/server/cli/commands/vault.ts +1323 -0
- package/server/cli/commands/wallet.ts +209 -0
- package/server/cli/index.ts +280 -0
- package/server/cli/lib/approval-poll.ts +94 -0
- package/server/cli/lib/aura-parser.ts +64 -0
- package/server/cli/lib/credential-create.ts +74 -0
- package/server/cli/lib/credential-resolve.ts +280 -0
- package/server/cli/lib/dotenv-migrate.ts +116 -0
- package/server/cli/lib/dotenv-parser.ts +146 -0
- package/server/cli/lib/escalation.ts +57 -0
- package/server/cli/lib/http.ts +91 -0
- package/server/cli/lib/init-steps.ts +76 -0
- package/server/cli/lib/local-agent-trust.ts +45 -0
- package/server/cli/lib/lock-unlock-helper.ts +71 -0
- package/server/cli/lib/process.ts +162 -0
- package/server/cli/lib/prompt.ts +294 -0
- package/server/cli/lib/theme.ts +240 -0
- package/server/cli/socket.ts +579 -0
- package/server/cli/transport-client.ts +50 -0
- package/server/cron/index.ts +137 -0
- package/server/cron/job.ts +31 -0
- package/server/cron/jobs/balance-sync.ts +436 -0
- package/server/cron/jobs/incoming-scan.ts +506 -0
- package/server/cron/jobs/native-price.ts +70 -0
- package/server/cron/jobs/orphan-cleanup.ts +40 -0
- package/server/cron/jobs/strategy-runner.ts +175 -0
- package/server/cron/scheduler.ts +125 -0
- package/server/index.ts +420 -0
- package/server/lib/adapters/factory.ts +119 -0
- package/server/lib/adapters/index.ts +19 -0
- package/server/lib/adapters/router.ts +297 -0
- package/server/lib/adapters/telegram.ts +645 -0
- package/server/lib/adapters/types.ts +89 -0
- package/server/lib/adapters/webhook.ts +95 -0
- package/server/lib/address.ts +49 -0
- package/server/lib/agent-auth/contracts.ts +1194 -0
- package/server/lib/agent-profiles.ts +419 -0
- package/server/lib/ai.ts +285 -0
- package/server/lib/api-registry/contracts.ts +86 -0
- package/server/lib/api-registry/validation.ts +172 -0
- package/server/lib/apikey-migration.ts +258 -0
- package/server/lib/app-installer.ts +505 -0
- package/server/lib/app-tokens.ts +247 -0
- package/server/lib/approval-link.ts +27 -0
- package/server/lib/auth.ts +314 -0
- package/server/lib/auto-execute.ts +160 -0
- package/server/lib/batch.ts +242 -0
- package/server/lib/cold.ts +1048 -0
- package/server/lib/config.ts +408 -0
- package/server/lib/credential-access-audit.ts +85 -0
- package/server/lib/credential-access-policy.ts +111 -0
- package/server/lib/credential-health.ts +343 -0
- package/server/lib/credential-import.ts +608 -0
- package/server/lib/credential-scope.ts +102 -0
- package/server/lib/credential-shares.ts +190 -0
- package/server/lib/credential-transport.ts +533 -0
- package/server/lib/credential-vault.ts +77 -0
- package/server/lib/credentials.ts +422 -0
- package/server/lib/crypto.ts +8 -0
- package/server/lib/db.ts +58 -0
- package/server/lib/defaults.ts +386 -0
- package/server/lib/dex/index.ts +80 -0
- package/server/lib/dex/relay.ts +235 -0
- package/server/lib/dex/types.ts +59 -0
- package/server/lib/dex/uniswap.ts +370 -0
- package/server/lib/diary.ts +34 -0
- package/server/lib/dont-ask-again-policy.ts +41 -0
- package/server/lib/e2e-agent/artifacts.ts +36 -0
- package/server/lib/e2e-agent/contracts.ts +112 -0
- package/server/lib/e2e-agent/validation.ts +135 -0
- package/server/lib/encrypt.ts +114 -0
- package/server/lib/error.ts +20 -0
- package/server/lib/events.ts +217 -0
- package/server/lib/feature-flags.ts +93 -0
- package/server/lib/hot.ts +357 -0
- package/server/lib/human-action-summary.ts +80 -0
- package/server/lib/key-fingerprint.ts +28 -0
- package/server/lib/logger.ts +340 -0
- package/server/lib/network.ts +137 -0
- package/server/lib/notifications.ts +230 -0
- package/server/lib/oauth2-refresh.ts +241 -0
- package/server/lib/oursecret.ts +71 -0
- package/server/lib/passkey-credential.ts +360 -0
- package/server/lib/passkey.ts +68 -0
- package/server/lib/permissions.ts +299 -0
- package/server/lib/pino.ts +24 -0
- package/server/lib/policy-preview.ts +138 -0
- package/server/lib/price.ts +338 -0
- package/server/lib/prices.ts +34 -0
- package/server/lib/project-scope.ts +297 -0
- package/server/lib/resolve-action.ts +328 -0
- package/server/lib/resolve.ts +36 -0
- package/server/lib/secret-gist-share.ts +296 -0
- package/server/lib/sessions.ts +634 -0
- package/server/lib/socket-path.ts +56 -0
- package/server/lib/solana/connection.ts +26 -0
- package/server/lib/solana/jupiter.ts +128 -0
- package/server/lib/solana/transfer.ts +108 -0
- package/server/lib/solana/wallet.ts +136 -0
- package/server/lib/strategy/emits.ts +21 -0
- package/server/lib/strategy/engine.ts +1305 -0
- package/server/lib/strategy/executor.ts +115 -0
- package/server/lib/strategy/hook-context.ts +159 -0
- package/server/lib/strategy/hooks.ts +990 -0
- package/server/lib/strategy/index.ts +28 -0
- package/server/lib/strategy/installer.ts +305 -0
- package/server/lib/strategy/loader.ts +256 -0
- package/server/lib/strategy/message.ts +237 -0
- package/server/lib/strategy/repository.ts +218 -0
- package/server/lib/strategy/session-logger.ts +693 -0
- package/server/lib/strategy/sources.ts +288 -0
- package/server/lib/strategy/state.ts +189 -0
- package/server/lib/strategy/templates.ts +403 -0
- package/server/lib/strategy/tick.ts +404 -0
- package/server/lib/strategy/types.ts +230 -0
- package/server/lib/swap.ts +3 -0
- package/server/lib/temp.ts +86 -0
- package/server/lib/token-metadata.ts +86 -0
- package/server/lib/token-safety.ts +200 -0
- package/server/lib/token-search.ts +444 -0
- package/server/lib/totp.ts +194 -0
- package/server/lib/transactions.ts +123 -0
- package/server/lib/transport.ts +84 -0
- package/server/lib/txhistory/decoder.ts +262 -0
- package/server/lib/txhistory/enricher.ts +652 -0
- package/server/lib/txhistory/index.ts +391 -0
- package/server/lib/txhistory/signatures.ts +59 -0
- package/server/lib/update-check.ts +35 -0
- package/server/lib/verified-summary.ts +414 -0
- package/server/lib/view-registry.ts +80 -0
- package/server/mcp/profile-policy.ts +30 -0
- package/server/mcp/server.ts +1589 -0
- package/server/mcp/tools.ts +276 -0
- package/server/middleware/auth.ts +119 -0
- package/server/middleware/requestLogger.ts +84 -0
- package/server/routes/actions.ts +539 -0
- package/server/routes/adapters.ts +711 -0
- package/server/routes/addressbook.ts +113 -0
- package/server/routes/ai.ts +34 -0
- package/server/routes/apikeys.ts +343 -0
- package/server/routes/apps.ts +601 -0
- package/server/routes/auth.ts +406 -0
- package/server/routes/backup.ts +404 -0
- package/server/routes/batch.ts +270 -0
- package/server/routes/bookmarks.ts +162 -0
- package/server/routes/credential-shares.ts +380 -0
- package/server/routes/credential-vaults.ts +159 -0
- package/server/routes/credentials.ts +1782 -0
- package/server/routes/dashboard.ts +97 -0
- package/server/routes/defaults.ts +124 -0
- package/server/routes/flags.ts +11 -0
- package/server/routes/fund.ts +225 -0
- package/server/routes/heartbeat.ts +375 -0
- package/server/routes/import.ts +364 -0
- package/server/routes/launch.ts +665 -0
- package/server/routes/lock.ts +54 -0
- package/server/routes/logs.ts +68 -0
- package/server/routes/nuke.ts +111 -0
- package/server/routes/passkey-credentials.ts +99 -0
- package/server/routes/passkey.ts +366 -0
- package/server/routes/portfolio.ts +217 -0
- package/server/routes/price.ts +63 -0
- package/server/routes/resolve.ts +31 -0
- package/server/routes/security.ts +45 -0
- package/server/routes/send-evm.ts +241 -0
- package/server/routes/send-solana.ts +281 -0
- package/server/routes/send.ts +178 -0
- package/server/routes/setup.ts +210 -0
- package/server/routes/strategy.ts +894 -0
- package/server/routes/swap-evm.ts +352 -0
- package/server/routes/swap-solana.ts +176 -0
- package/server/routes/swap.ts +356 -0
- package/server/routes/token.ts +247 -0
- package/server/routes/unlock.ts +467 -0
- package/server/routes/views.ts +41 -0
- package/server/routes/wallet-assets.ts +361 -0
- package/server/routes/wallet-transactions.ts +515 -0
- package/server/routes/wallet.ts +709 -0
- package/server/types.ts +146 -0
- package/shared/credential-field-schema.ts +248 -0
- package/skills/auramaxx/HEARTBEAT.md +78 -0
- package/skills/auramaxx/SKILL.md +745 -0
- package/skills/auramaxx/docs/AGENT_SETUP.md +155 -0
- package/skills/auramaxx/docs/API.md +127 -0
- package/skills/auramaxx/docs/AUTH.md +318 -0
- package/skills/auramaxx/docs/CLI.md +130 -0
- package/skills/auramaxx/docs/MCP.md +122 -0
- package/skills/auramaxx/docs/TROUBLESHOOTING.md +357 -0
- package/skills/auramaxx/docs/WORKSPACE.md +673 -0
- package/skills/auramaxx/docs/security.md +227 -0
- package/skills/task-lifecycle/SKILL.md +378 -0
- package/src/app/api/[...doc]/page.tsx +36 -0
- package/src/app/api/agent-requests/route.ts +30 -0
- package/src/app/api/apps/install/route.ts +132 -0
- package/src/app/api/apps/manifests/route.ts +16 -0
- package/src/app/api/apps/static/[...path]/route.ts +57 -0
- package/src/app/api/docs/plain/route.ts +74 -0
- package/src/app/api/events/route.ts +92 -0
- package/src/app/api/page.tsx +290 -0
- package/src/app/api/workspace/[id]/apps/[wid]/route.ts +119 -0
- package/src/app/api/workspace/[id]/apps/route.ts +81 -0
- package/src/app/api/workspace/[id]/export/route.ts +67 -0
- package/src/app/api/workspace/[id]/route.ts +168 -0
- package/src/app/api/workspace/auth.ts +40 -0
- package/src/app/api/workspace/config/route.ts +121 -0
- package/src/app/api/workspace/import/route.ts +127 -0
- package/src/app/api/workspace/route.ts +116 -0
- package/src/app/app-legacy-do-not-use/page.tsx +2245 -0
- package/src/app/apple-icon.png +0 -0
- package/src/app/approve/[actionId]/page.tsx +409 -0
- package/src/app/docs/DocsPageContent.tsx +269 -0
- package/src/app/docs/[...doc]/page.tsx +41 -0
- package/src/app/docs/page.tsx +38 -0
- package/src/app/favicon.ico +0 -0
- package/src/app/globals.css +819 -0
- package/src/app/health/page.tsx +5 -0
- package/src/app/hello/page.tsx +102 -0
- package/src/app/icon.png +0 -0
- package/src/app/layout.tsx +39 -0
- package/src/app/page.tsx +1964 -0
- package/src/app/privacy/page.tsx +63 -0
- package/src/app/providers.tsx +87 -0
- package/src/app/share/[token]/page.tsx +295 -0
- package/src/app/terms/page.tsx +80 -0
- package/src/components/ChainSelector.tsx +44 -0
- package/src/components/HumanActionBar.tsx +697 -0
- package/src/components/NotificationDrawer.tsx +387 -0
- package/src/components/PasskeyEnrollmentPrompt.tsx +235 -0
- package/src/components/apps/AgentKeysApp.tsx +490 -0
- package/src/components/apps/App.tsx +153 -0
- package/src/components/apps/AppGrid.tsx +15 -0
- package/src/components/apps/DetailedAddressDrawer.tsx +325 -0
- package/src/components/apps/DraggableApp.tsx +562 -0
- package/src/components/apps/IFrameApp.tsx +73 -0
- package/src/components/apps/LogsApp.tsx +360 -0
- package/src/components/apps/SendApp.tsx +394 -0
- package/src/components/apps/SetupWizardApp.tsx +1004 -0
- package/src/components/apps/SystemDefaultsApp.tsx +845 -0
- package/src/components/apps/ThirdPartyApp.tsx +428 -0
- package/src/components/apps/TokenApp.tsx +319 -0
- package/src/components/apps/TransactionsApp.tsx +438 -0
- package/src/components/apps/WalletDetailApp.tsx +1505 -0
- package/src/components/apps/index.ts +13 -0
- package/src/components/design-system/Button.tsx +88 -0
- package/src/components/design-system/ChainIndicator.tsx +65 -0
- package/src/components/design-system/ChainSelector.tsx +147 -0
- package/src/components/design-system/ConfirmationModal.tsx +107 -0
- package/src/components/design-system/ConfirmationPopover.tsx +81 -0
- package/src/components/design-system/DownloadButton.tsx +149 -0
- package/src/components/design-system/Drawer.tsx +133 -0
- package/src/components/design-system/FilterDropdown.tsx +183 -0
- package/src/components/design-system/ItemPicker.tsx +157 -0
- package/src/components/design-system/Modal.tsx +296 -0
- package/src/components/design-system/Popover.tsx +142 -0
- package/src/components/design-system/TextInput.tsx +85 -0
- package/src/components/design-system/Toggle.tsx +65 -0
- package/src/components/design-system/TyvekCollapsibleSection.tsx +55 -0
- package/src/components/design-system/index.ts +14 -0
- package/src/components/docs/ClientSideMarkdown.tsx +51 -0
- package/src/components/docs/DocsSearchBar.tsx +118 -0
- package/src/components/docs/DocsThemeToggle.tsx +38 -0
- package/src/components/docs/PersistentDocGroup.tsx +91 -0
- package/src/components/docs/ShareUrlButton.tsx +33 -0
- package/src/components/docs/SidebarScrollMemory.tsx +56 -0
- package/src/components/health/CredentialHealthDashboard.tsx +214 -0
- package/src/components/icons/ChainIcons.tsx +72 -0
- package/src/components/layout/AppStoreDrawer.tsx +369 -0
- package/src/components/layout/ContentArea.tsx +21 -0
- package/src/components/layout/CreateViewModal.tsx +88 -0
- package/src/components/layout/LeftRail.tsx +114 -0
- package/src/components/layout/TabBar.tsx +284 -0
- package/src/components/layout/WalletSidebar.tsx +1030 -0
- package/src/components/layout/index.ts +6 -0
- package/src/components/marketing/AuraMaxxSpecOverlay.tsx +653 -0
- package/src/components/marketing/DeviceMorphExperience.tsx +216 -0
- package/src/components/vault/ApiKeysConsole.tsx +1272 -0
- package/src/components/vault/AuditConsole.tsx +600 -0
- package/src/components/vault/CredentialDetail.tsx +625 -0
- package/src/components/vault/CredentialEmpty.tsx +55 -0
- package/src/components/vault/CredentialField.tsx +583 -0
- package/src/components/vault/CredentialForm.tsx +1484 -0
- package/src/components/vault/CredentialList.tsx +265 -0
- package/src/components/vault/CredentialRow.tsx +130 -0
- package/src/components/vault/CredentialShareModal.tsx +273 -0
- package/src/components/vault/CredentialVault.tsx +1662 -0
- package/src/components/vault/CredentialWalletWidget.tsx +103 -0
- package/src/components/vault/DocsConsole.tsx +113 -0
- package/src/components/vault/ImportCredentialsModal.tsx +578 -0
- package/src/components/vault/LargeTypeModal.tsx +88 -0
- package/src/components/vault/PasswordGenerator.tsx +232 -0
- package/src/components/vault/TOTPDisplay.tsx +108 -0
- package/src/components/vault/TotpSetupPanel.tsx +198 -0
- package/src/components/vault/VaultSidebar.tsx +881 -0
- package/src/components/vault/credentialFormName.ts +91 -0
- package/src/components/vault/hooks/useVaultKeyboardShortcuts.ts +69 -0
- package/src/components/vault/types.ts +56 -0
- package/src/context/AuthContext.tsx +365 -0
- package/src/context/PriceContext.tsx +113 -0
- package/src/context/ThemeContext.tsx +164 -0
- package/src/context/WebSocketContext.tsx +269 -0
- package/src/context/WorkspaceContext.tsx +668 -0
- package/src/hooks/index.ts +4 -0
- package/src/hooks/useAgentActions.ts +552 -0
- package/src/hooks/useBalance.ts +103 -0
- package/src/hooks/useBalances.ts +129 -0
- package/src/hooks/useTheme.ts +156 -0
- package/src/instrumentation.ts +12 -0
- package/src/lib/api-docs.ts +154 -0
- package/src/lib/api.ts +474 -0
- package/src/lib/app-loader.ts +148 -0
- package/src/lib/app-registry.ts +178 -0
- package/src/lib/app-sdk.ts +157 -0
- package/src/lib/audit-console-adapter.ts +151 -0
- package/src/lib/auth-client.ts +75 -0
- package/src/lib/config.ts +74 -0
- package/src/lib/credential-field-schema.ts +11 -0
- package/src/lib/crypto.ts +112 -0
- package/src/lib/db.ts +21 -0
- package/src/lib/docs.ts +544 -0
- package/src/lib/events.ts +363 -0
- package/src/lib/pino.ts +24 -0
- package/src/lib/theme-handlers.ts +168 -0
- package/src/lib/theme.ts +351 -0
- package/src/lib/tokenData.ts +378 -0
- package/src/lib/totp-import.ts +57 -0
- package/src/lib/vault-crypto.ts +129 -0
- package/src/lib/view-registry.ts +57 -0
- package/src/lib/websocket-server.ts +302 -0
- package/src/lib/websocket-setup.ts +79 -0
- package/src/lib/wordlist.ts +2050 -0
- package/src/lib/workspace-handlers.ts +285 -0
- package/start.sh +170 -0
- package/tailwind.config.ts +99 -0
- package/tsconfig.json +42 -0
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Event logger helper for consistent event emission across Express routes
|
|
3
|
+
* Convenience wrapper around events.custom() for structured logging
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { events } from './events';
|
|
7
|
+
|
|
8
|
+
export type EventCategory = 'auth' | 'wallet' | 'transaction' | 'token' | 'request' | 'system' | 'agent';
|
|
9
|
+
|
|
10
|
+
export interface LogParams {
|
|
11
|
+
category: EventCategory;
|
|
12
|
+
action: string;
|
|
13
|
+
description: string;
|
|
14
|
+
agentId?: string;
|
|
15
|
+
walletAddress?: string;
|
|
16
|
+
txHash?: string;
|
|
17
|
+
metadata?: Record<string, unknown>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Log an event with structured data
|
|
22
|
+
* Emits to WebSocket and stores in database
|
|
23
|
+
*/
|
|
24
|
+
export function logEvent(params: LogParams): void {
|
|
25
|
+
const eventType = `${params.category}:${params.action}`;
|
|
26
|
+
events.custom(eventType, {
|
|
27
|
+
description: params.description,
|
|
28
|
+
agentId: params.agentId,
|
|
29
|
+
walletAddress: params.walletAddress,
|
|
30
|
+
txHash: params.txHash,
|
|
31
|
+
timestamp: Date.now(),
|
|
32
|
+
...params.metadata,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Convenience methods for common events
|
|
38
|
+
*/
|
|
39
|
+
export const logger = {
|
|
40
|
+
// ── Auth Events ──────────────────────────────────────────────
|
|
41
|
+
|
|
42
|
+
/** Wallet unlocked event */
|
|
43
|
+
unlocked: (address: string) =>
|
|
44
|
+
logEvent({
|
|
45
|
+
category: 'auth',
|
|
46
|
+
action: 'unlocked',
|
|
47
|
+
description: `Wallet unlocked: ${address.slice(0, 10)}...`,
|
|
48
|
+
walletAddress: address,
|
|
49
|
+
}),
|
|
50
|
+
|
|
51
|
+
/** Wallet locked event */
|
|
52
|
+
locked: () =>
|
|
53
|
+
logEvent({
|
|
54
|
+
category: 'auth',
|
|
55
|
+
action: 'locked',
|
|
56
|
+
description: 'Wallet locked',
|
|
57
|
+
}),
|
|
58
|
+
|
|
59
|
+
/** Auth failure (invalid/expired/revoked token, missing header) */
|
|
60
|
+
authFailed: (reason: string, path: string, metadata?: Record<string, unknown>) =>
|
|
61
|
+
logEvent({
|
|
62
|
+
category: 'auth',
|
|
63
|
+
action: 'auth_failed',
|
|
64
|
+
description: `Auth failed: ${reason}`,
|
|
65
|
+
metadata: { path, reason, ...metadata },
|
|
66
|
+
}),
|
|
67
|
+
|
|
68
|
+
/** Permission denied */
|
|
69
|
+
permissionDenied: (permission: string, agentId: string, path: string) =>
|
|
70
|
+
logEvent({
|
|
71
|
+
category: 'auth',
|
|
72
|
+
action: 'permission_denied',
|
|
73
|
+
description: `Permission denied: ${permission}`,
|
|
74
|
+
agentId,
|
|
75
|
+
metadata: { permission, path },
|
|
76
|
+
}),
|
|
77
|
+
|
|
78
|
+
/** Token validated successfully */
|
|
79
|
+
tokenValidated: (agentId: string, tokenHash: string) =>
|
|
80
|
+
logEvent({
|
|
81
|
+
category: 'auth',
|
|
82
|
+
action: 'token_validated',
|
|
83
|
+
description: `Token validated for ${agentId}`,
|
|
84
|
+
agentId,
|
|
85
|
+
metadata: { tokenHash },
|
|
86
|
+
}),
|
|
87
|
+
|
|
88
|
+
// ── Token Events ─────────────────────────────────────────────
|
|
89
|
+
|
|
90
|
+
/** Agent token created */
|
|
91
|
+
tokenCreated: (agentId: string, tokenHash: string, limit: number, permissions: string[]) =>
|
|
92
|
+
logEvent({
|
|
93
|
+
category: 'token',
|
|
94
|
+
action: 'created',
|
|
95
|
+
description: `Token created for ${agentId} (limit: ${limit} ETH)`,
|
|
96
|
+
agentId,
|
|
97
|
+
metadata: { tokenHash, limit, permissions },
|
|
98
|
+
}),
|
|
99
|
+
|
|
100
|
+
/** Agent token revoked */
|
|
101
|
+
tokenRevoked: (tokenHash: string, revokedBy?: string) =>
|
|
102
|
+
logEvent({
|
|
103
|
+
category: 'token',
|
|
104
|
+
action: 'revoked',
|
|
105
|
+
description: `Token revoked: ${tokenHash.slice(0, 12)}...`,
|
|
106
|
+
metadata: { tokenHash, revokedBy },
|
|
107
|
+
}),
|
|
108
|
+
|
|
109
|
+
/** Spending limit exceeded */
|
|
110
|
+
limitExceeded: (agentId: string, limitType: string, requested: number, remaining: number) =>
|
|
111
|
+
logEvent({
|
|
112
|
+
category: 'token',
|
|
113
|
+
action: 'limit_exceeded',
|
|
114
|
+
description: `${limitType} limit exceeded: requested ${requested}, remaining ${remaining}`,
|
|
115
|
+
agentId,
|
|
116
|
+
metadata: { limitType, requested, remaining },
|
|
117
|
+
}),
|
|
118
|
+
|
|
119
|
+
// ── Wallet Events ────────────────────────────────────────────
|
|
120
|
+
|
|
121
|
+
/** Cold wallet setup event */
|
|
122
|
+
setup: (address: string) =>
|
|
123
|
+
logEvent({
|
|
124
|
+
category: 'wallet',
|
|
125
|
+
action: 'setup',
|
|
126
|
+
description: `Cold wallet created: ${address.slice(0, 10)}...`,
|
|
127
|
+
walletAddress: address,
|
|
128
|
+
}),
|
|
129
|
+
|
|
130
|
+
/** Hot/temp wallet created event */
|
|
131
|
+
walletCreated: (address: string, tier: string, agentId?: string) =>
|
|
132
|
+
logEvent({
|
|
133
|
+
category: 'wallet',
|
|
134
|
+
action: 'created',
|
|
135
|
+
description: `${tier} wallet created`,
|
|
136
|
+
walletAddress: address,
|
|
137
|
+
agentId,
|
|
138
|
+
metadata: { tier },
|
|
139
|
+
}),
|
|
140
|
+
|
|
141
|
+
/** Wallet renamed/updated */
|
|
142
|
+
walletRenamed: (address: string, agentId?: string) =>
|
|
143
|
+
logEvent({
|
|
144
|
+
category: 'wallet',
|
|
145
|
+
action: 'renamed',
|
|
146
|
+
description: `Wallet updated: ${address.slice(0, 10)}...`,
|
|
147
|
+
walletAddress: address,
|
|
148
|
+
agentId,
|
|
149
|
+
}),
|
|
150
|
+
|
|
151
|
+
/** Wallet private key exported */
|
|
152
|
+
walletExported: (address: string, agentId?: string) =>
|
|
153
|
+
logEvent({
|
|
154
|
+
category: 'wallet',
|
|
155
|
+
action: 'exported',
|
|
156
|
+
description: `Private key exported: ${address.slice(0, 10)}...`,
|
|
157
|
+
walletAddress: address,
|
|
158
|
+
agentId,
|
|
159
|
+
}),
|
|
160
|
+
|
|
161
|
+
/** Seed phrase exported */
|
|
162
|
+
seedExported: (vaultId?: string) =>
|
|
163
|
+
logEvent({
|
|
164
|
+
category: 'wallet',
|
|
165
|
+
action: 'seed_exported',
|
|
166
|
+
description: `Seed phrase exported${vaultId ? ` (vault: ${vaultId})` : ''}`,
|
|
167
|
+
metadata: { vaultId },
|
|
168
|
+
}),
|
|
169
|
+
|
|
170
|
+
// ── Transaction Events ───────────────────────────────────────
|
|
171
|
+
|
|
172
|
+
/** ETH send event */
|
|
173
|
+
send: (from: string, to: string, amount: string, txHash: string, agentId?: string) =>
|
|
174
|
+
logEvent({
|
|
175
|
+
category: 'transaction',
|
|
176
|
+
action: 'send',
|
|
177
|
+
description: `Sent ${amount} ETH`,
|
|
178
|
+
walletAddress: from,
|
|
179
|
+
txHash,
|
|
180
|
+
agentId,
|
|
181
|
+
metadata: { to, amount },
|
|
182
|
+
}),
|
|
183
|
+
|
|
184
|
+
/** Cold to hot fund transfer event */
|
|
185
|
+
fund: (to: string, amount: string, txHash: string, agentId?: string) =>
|
|
186
|
+
logEvent({
|
|
187
|
+
category: 'transaction',
|
|
188
|
+
action: 'fund',
|
|
189
|
+
description: `Funded ${amount} ETH`,
|
|
190
|
+
walletAddress: to,
|
|
191
|
+
txHash,
|
|
192
|
+
agentId,
|
|
193
|
+
metadata: { amount },
|
|
194
|
+
}),
|
|
195
|
+
|
|
196
|
+
/** Token swap event */
|
|
197
|
+
swap: (wallet: string, fromToken: string, toToken: string, amount: string, txHash: string, agentId?: string) =>
|
|
198
|
+
logEvent({
|
|
199
|
+
category: 'transaction',
|
|
200
|
+
action: 'swap',
|
|
201
|
+
description: `Swapped ${amount} ${fromToken} to ${toToken}`,
|
|
202
|
+
walletAddress: wallet,
|
|
203
|
+
txHash,
|
|
204
|
+
agentId,
|
|
205
|
+
metadata: { fromToken, toToken, amount },
|
|
206
|
+
}),
|
|
207
|
+
|
|
208
|
+
// ── Agent Events ─────────────────────────────────────────────
|
|
209
|
+
|
|
210
|
+
/** Agent requested access */
|
|
211
|
+
agentRequested: (agentId: string, requestId: string, limit: number) =>
|
|
212
|
+
logEvent({
|
|
213
|
+
category: 'agent',
|
|
214
|
+
action: 'access_requested',
|
|
215
|
+
description: `${agentId} requested access (limit: ${limit} ETH)`,
|
|
216
|
+
agentId,
|
|
217
|
+
metadata: { requestId, limit },
|
|
218
|
+
}),
|
|
219
|
+
|
|
220
|
+
/** Agent polled for token */
|
|
221
|
+
agentPolled: (requestId: string) =>
|
|
222
|
+
logEvent({
|
|
223
|
+
category: 'agent',
|
|
224
|
+
action: 'polled',
|
|
225
|
+
description: `Token poll for request ${requestId}`,
|
|
226
|
+
metadata: { requestId },
|
|
227
|
+
}),
|
|
228
|
+
|
|
229
|
+
/** Permission update requested */
|
|
230
|
+
permissionRequested: (agentId: string, requestId: string, permissions: string[]) =>
|
|
231
|
+
logEvent({
|
|
232
|
+
category: 'agent',
|
|
233
|
+
action: 'permission_requested',
|
|
234
|
+
description: `${agentId} requested permission update`,
|
|
235
|
+
agentId,
|
|
236
|
+
metadata: { requestId, permissions },
|
|
237
|
+
}),
|
|
238
|
+
|
|
239
|
+
/** Action created by agent */
|
|
240
|
+
actionCreated: (agentId: string, requestId: string, type: string, summary: string) =>
|
|
241
|
+
logEvent({
|
|
242
|
+
category: 'agent',
|
|
243
|
+
action: 'action_created',
|
|
244
|
+
description: `${agentId} created ${type}: ${summary}`,
|
|
245
|
+
agentId,
|
|
246
|
+
metadata: { requestId, type, summary },
|
|
247
|
+
}),
|
|
248
|
+
|
|
249
|
+
/** Action resolved (approved/rejected) */
|
|
250
|
+
actionResolved: (requestId: string, type: string, approved: boolean, resolvedBy: string) =>
|
|
251
|
+
logEvent({
|
|
252
|
+
category: 'agent',
|
|
253
|
+
action: 'action_resolved',
|
|
254
|
+
description: `${type} ${approved ? 'approved' : 'rejected'} by ${resolvedBy}`,
|
|
255
|
+
metadata: { requestId, type, approved, resolvedBy },
|
|
256
|
+
}),
|
|
257
|
+
|
|
258
|
+
// ── System Events ────────────────────────────────────────────
|
|
259
|
+
|
|
260
|
+
/** System nuke (full reset) */
|
|
261
|
+
nuke: () =>
|
|
262
|
+
logEvent({
|
|
263
|
+
category: 'system',
|
|
264
|
+
action: 'nuke',
|
|
265
|
+
description: 'System nuke: all data wiped',
|
|
266
|
+
}),
|
|
267
|
+
|
|
268
|
+
/** Database backup */
|
|
269
|
+
backup: (filename: string) =>
|
|
270
|
+
logEvent({
|
|
271
|
+
category: 'system',
|
|
272
|
+
action: 'backup',
|
|
273
|
+
description: `Database backup created: ${filename}`,
|
|
274
|
+
metadata: { filename },
|
|
275
|
+
}),
|
|
276
|
+
|
|
277
|
+
/** API key created */
|
|
278
|
+
apiKeyCreated: (service: string, name: string) =>
|
|
279
|
+
logEvent({
|
|
280
|
+
category: 'system',
|
|
281
|
+
action: 'apikey_created',
|
|
282
|
+
description: `API key created: ${service}/${name}`,
|
|
283
|
+
metadata: { service, name },
|
|
284
|
+
}),
|
|
285
|
+
|
|
286
|
+
/** API key deleted */
|
|
287
|
+
apiKeyDeleted: (service: string, name: string) =>
|
|
288
|
+
logEvent({
|
|
289
|
+
category: 'system',
|
|
290
|
+
action: 'apikey_deleted',
|
|
291
|
+
description: `API key deleted: ${service}/${name}`,
|
|
292
|
+
metadata: { service, name },
|
|
293
|
+
}),
|
|
294
|
+
|
|
295
|
+
/** All API keys revoked */
|
|
296
|
+
apiKeysRevokedAll: (revokedCount: number) =>
|
|
297
|
+
logEvent({
|
|
298
|
+
category: 'system',
|
|
299
|
+
action: 'apikey_revoked_all',
|
|
300
|
+
description: `All API keys revoked (${revokedCount})`,
|
|
301
|
+
metadata: { revokedCount },
|
|
302
|
+
}),
|
|
303
|
+
|
|
304
|
+
/** Strategy toggled */
|
|
305
|
+
strategyToggled: (strategyId: string, enabled: boolean) =>
|
|
306
|
+
logEvent({
|
|
307
|
+
category: 'system',
|
|
308
|
+
action: 'strategy_toggled',
|
|
309
|
+
description: `Strategy ${strategyId} ${enabled ? 'enabled' : 'disabled'}`,
|
|
310
|
+
metadata: { strategyId, enabled },
|
|
311
|
+
}),
|
|
312
|
+
|
|
313
|
+
/** Adapter configuration changed */
|
|
314
|
+
adapterChanged: (action: string, type: string) =>
|
|
315
|
+
logEvent({
|
|
316
|
+
category: 'system',
|
|
317
|
+
action: 'adapter_changed',
|
|
318
|
+
description: `Adapter ${type}: ${action}`,
|
|
319
|
+
metadata: { action, type },
|
|
320
|
+
}),
|
|
321
|
+
|
|
322
|
+
/** App operation */
|
|
323
|
+
appOperation: (operation: string, appId: string, agentId?: string) =>
|
|
324
|
+
logEvent({
|
|
325
|
+
category: 'system',
|
|
326
|
+
action: 'app_operation',
|
|
327
|
+
description: `App ${operation}: ${appId}`,
|
|
328
|
+
agentId,
|
|
329
|
+
metadata: { operation, appId },
|
|
330
|
+
}),
|
|
331
|
+
|
|
332
|
+
/** Server error */
|
|
333
|
+
error: (message: string, path?: string, metadata?: Record<string, unknown>) =>
|
|
334
|
+
logEvent({
|
|
335
|
+
category: 'system',
|
|
336
|
+
action: 'error',
|
|
337
|
+
description: `Error: ${message}`,
|
|
338
|
+
metadata: { path, ...metadata },
|
|
339
|
+
}),
|
|
340
|
+
};
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Network / SSRF Utilities
|
|
3
|
+
* ========================
|
|
4
|
+
* Shared utilities for validating external URLs and preventing SSRF attacks.
|
|
5
|
+
* Used by the strategy executor, source fetcher, app fetch proxy,
|
|
6
|
+
* webhook adapter, and app installer.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { isIPv4, isIPv6 } from 'net';
|
|
10
|
+
import dns from 'dns';
|
|
11
|
+
import { getErrorMessage } from './error';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Check whether a resolved IP address is in a private/reserved range.
|
|
15
|
+
* Handles both IPv4 and IPv6 (including IPv4-mapped IPv6 addresses).
|
|
16
|
+
*/
|
|
17
|
+
export function isPrivateIp(ip: string): boolean {
|
|
18
|
+
if (isIPv4(ip)) {
|
|
19
|
+
const parts = ip.split('.').map(Number);
|
|
20
|
+
const [a, b] = parts;
|
|
21
|
+
if (a === 127) return true; // 127.0.0.0/8 loopback
|
|
22
|
+
if (a === 10) return true; // 10.0.0.0/8 private
|
|
23
|
+
if (a === 172 && b >= 16 && b <= 31) return true; // 172.16.0.0/12 private
|
|
24
|
+
if (a === 192 && b === 168) return true; // 192.168.0.0/16 private
|
|
25
|
+
if (a === 169 && b === 254) return true; // 169.254.0.0/16 link-local
|
|
26
|
+
if (a === 0) return true; // 0.0.0.0/8
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (isIPv6(ip)) {
|
|
31
|
+
const normalized = ip.toLowerCase();
|
|
32
|
+
|
|
33
|
+
// Loopback
|
|
34
|
+
if (normalized === '::1') return true;
|
|
35
|
+
|
|
36
|
+
// IPv4-mapped IPv6 — ::ffff:a.b.c.d
|
|
37
|
+
if (normalized.startsWith('::ffff:')) {
|
|
38
|
+
const embedded = normalized.slice(7);
|
|
39
|
+
if (isIPv4(embedded)) return isPrivateIp(embedded);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Link-local fe80::/10 — first 10 bits = 1111 1110 10
|
|
43
|
+
// Covers fe80:: through febf::
|
|
44
|
+
const firstSegment = normalized.split(':')[0];
|
|
45
|
+
if (firstSegment.length >= 3) {
|
|
46
|
+
const prefix = firstSegment.slice(0, 3);
|
|
47
|
+
if (prefix === 'fe8' || prefix === 'fe9' || prefix === 'fea' || prefix === 'feb') return true;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Unique local fc00::/7 — first 7 bits = 1111 110
|
|
51
|
+
// Covers fc00:: through fdff::
|
|
52
|
+
if (normalized.startsWith('fc') || normalized.startsWith('fd')) return true;
|
|
53
|
+
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Unknown format — treat as suspicious
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Resolve a hostname via DNS and verify the resolved IP is not private.
|
|
63
|
+
* Throws if the hostname resolves to a private/reserved IP.
|
|
64
|
+
*/
|
|
65
|
+
export async function resolveAndValidateHost(hostname: string): Promise<void> {
|
|
66
|
+
// If the hostname is already a raw IP, check directly
|
|
67
|
+
if (isIPv4(hostname) || isIPv6(hostname)) {
|
|
68
|
+
if (isPrivateIp(hostname)) {
|
|
69
|
+
throw new Error(`Address "${hostname}" is a private/reserved IP`);
|
|
70
|
+
}
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
let address: string;
|
|
75
|
+
try {
|
|
76
|
+
const result = await dns.promises.lookup(hostname);
|
|
77
|
+
address = result.address;
|
|
78
|
+
} catch (err) {
|
|
79
|
+
const msg = getErrorMessage(err);
|
|
80
|
+
throw new Error(`DNS lookup failed for "${hostname}": ${msg}`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (isPrivateIp(address)) {
|
|
84
|
+
throw new Error(`Host "${hostname}" resolves to private IP ${address}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Full validation pipeline for an external URL:
|
|
90
|
+
* 1. Parse the URL
|
|
91
|
+
* 2. Verify protocol is http: or https:
|
|
92
|
+
* 3. If allowedHosts is provided, verify hostname is in the list
|
|
93
|
+
* 4. DNS-resolve and verify the IP is not private
|
|
94
|
+
*/
|
|
95
|
+
// Reserved test TLDs (RFC 2606) — skip DNS resolution in test environments
|
|
96
|
+
const TEST_TLDS = ['.example.com', '.example.org', '.example.net', '.test', '.localhost'];
|
|
97
|
+
|
|
98
|
+
export async function validateExternalUrl(
|
|
99
|
+
url: string,
|
|
100
|
+
allowedHosts?: string[],
|
|
101
|
+
): Promise<void> {
|
|
102
|
+
let parsed: URL;
|
|
103
|
+
try {
|
|
104
|
+
parsed = new URL(url);
|
|
105
|
+
} catch {
|
|
106
|
+
throw new Error(`Invalid URL: ${url}`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {
|
|
110
|
+
throw new Error(`Protocol "${parsed.protocol}" is not allowed. Only http: and https: are permitted`);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (allowedHosts && allowedHosts.length > 0) {
|
|
114
|
+
if (!allowedHosts.includes(parsed.hostname)) {
|
|
115
|
+
throw new Error(`Host "${parsed.hostname}" is not in the allowed hosts list`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Skip DNS resolution for RFC 2606 reserved test domains in test environments
|
|
120
|
+
if (process.env.NODE_ENV === 'test' && TEST_TLDS.some(tld => parsed.hostname.endsWith(tld))) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
await resolveAndValidateHost(parsed.hostname);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Sanitize a path segment (e.g., strategyId, appId) to prevent
|
|
129
|
+
* path traversal when interpolated into REST API URLs.
|
|
130
|
+
* Throws if the segment contains forbidden characters.
|
|
131
|
+
*/
|
|
132
|
+
export function sanitizePathSegment(segment: string): string {
|
|
133
|
+
if (segment.includes('/') || segment.includes('..') || segment.includes('\\')) {
|
|
134
|
+
throw new Error(`Invalid path segment: "${segment}" contains forbidden characters`);
|
|
135
|
+
}
|
|
136
|
+
return segment;
|
|
137
|
+
}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
import { prisma } from './db';
|
|
2
|
+
import { loadConfig } from './config';
|
|
3
|
+
|
|
4
|
+
interface NotificationAction {
|
|
5
|
+
id: string;
|
|
6
|
+
label: string;
|
|
7
|
+
type: 'primary' | 'secondary' | 'danger' | 'link';
|
|
8
|
+
action: 'api' | 'navigate' | 'dismiss';
|
|
9
|
+
endpoint?: string;
|
|
10
|
+
method?: string;
|
|
11
|
+
body?: Record<string, unknown>;
|
|
12
|
+
href?: string;
|
|
13
|
+
external?: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface CreateNotificationParams {
|
|
17
|
+
type: 'pending_approval' | 'info' | 'warning' | 'success' | 'error' | 'system';
|
|
18
|
+
category?: 'transaction' | 'security' | 'token' | 'wallet' | 'general';
|
|
19
|
+
title: string;
|
|
20
|
+
message: string;
|
|
21
|
+
actions?: NotificationAction[];
|
|
22
|
+
metadata?: Record<string, unknown>;
|
|
23
|
+
humanActionId?: string;
|
|
24
|
+
expiresAt?: Date;
|
|
25
|
+
source?: 'system' | 'agent' | 'user';
|
|
26
|
+
agentId?: string;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export async function createNotification(params: CreateNotificationParams) {
|
|
30
|
+
return prisma.notification.create({
|
|
31
|
+
data: {
|
|
32
|
+
type: params.type,
|
|
33
|
+
category: params.category,
|
|
34
|
+
title: params.title,
|
|
35
|
+
message: params.message,
|
|
36
|
+
actions: params.actions ? JSON.stringify(params.actions) : null,
|
|
37
|
+
metadata: params.metadata ? JSON.stringify(params.metadata) : null,
|
|
38
|
+
humanActionId: params.humanActionId,
|
|
39
|
+
expiresAt: params.expiresAt,
|
|
40
|
+
source: params.source || 'system',
|
|
41
|
+
agentId: params.agentId,
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export async function createHumanActionNotification(request: {
|
|
47
|
+
id: string;
|
|
48
|
+
type: string;
|
|
49
|
+
fromTier: string;
|
|
50
|
+
toAddress: string | null;
|
|
51
|
+
amount: string | null;
|
|
52
|
+
chain: string;
|
|
53
|
+
metadata?: string | null;
|
|
54
|
+
}) {
|
|
55
|
+
const config = loadConfig();
|
|
56
|
+
const explorer = config.chains[request.chain]?.explorer || 'https://basescan.org';
|
|
57
|
+
|
|
58
|
+
const typeLabels: Record<string, string> = {
|
|
59
|
+
fund: 'Fund Request',
|
|
60
|
+
send: 'Send Request',
|
|
61
|
+
agent_access: 'Agent Access Request',
|
|
62
|
+
auth: 'Agent Auth Request',
|
|
63
|
+
action: 'Action Request',
|
|
64
|
+
notify: 'Token Alert',
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const title = typeLabels[request.type] || 'Pending Request';
|
|
68
|
+
const shortAddr = request.toAddress
|
|
69
|
+
? `${request.toAddress.slice(0, 6)}...${request.toAddress.slice(-4)}`
|
|
70
|
+
: 'Unknown';
|
|
71
|
+
|
|
72
|
+
let message: string;
|
|
73
|
+
if (request.type === 'agent_access' || request.type === 'auth') {
|
|
74
|
+
// Parse metadata to get agent info
|
|
75
|
+
let agentId = 'Unknown Agent';
|
|
76
|
+
let limit = 0;
|
|
77
|
+
let requestedLimitExplicit = false;
|
|
78
|
+
let summary = '';
|
|
79
|
+
if (request.metadata) {
|
|
80
|
+
try {
|
|
81
|
+
const meta = JSON.parse(request.metadata);
|
|
82
|
+
agentId = meta.agentId || agentId;
|
|
83
|
+
if (typeof meta.limit === 'number') limit = meta.limit;
|
|
84
|
+
if (typeof meta?.limits?.fund === 'number') limit = meta.limits.fund;
|
|
85
|
+
requestedLimitExplicit = meta.requestedLimitExplicit === true;
|
|
86
|
+
if (typeof meta.summary === 'string') summary = meta.summary;
|
|
87
|
+
} catch {
|
|
88
|
+
// ignore
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
if (summary) {
|
|
92
|
+
message = summary;
|
|
93
|
+
} else if (requestedLimitExplicit) {
|
|
94
|
+
message = `${agentId} requesting ${limit} ETH access`;
|
|
95
|
+
} else {
|
|
96
|
+
message = `${agentId} requesting access`;
|
|
97
|
+
}
|
|
98
|
+
} else if (request.type === 'action') {
|
|
99
|
+
let summary = 'Action pending';
|
|
100
|
+
let callerAgentId = 'app';
|
|
101
|
+
if (request.metadata) {
|
|
102
|
+
try {
|
|
103
|
+
const meta = JSON.parse(request.metadata);
|
|
104
|
+
const vs = meta.verifiedSummary;
|
|
105
|
+
summary = vs?.oneLiner || meta.summary || summary;
|
|
106
|
+
callerAgentId = meta.agentId || callerAgentId;
|
|
107
|
+
} catch {}
|
|
108
|
+
}
|
|
109
|
+
message = `${callerAgentId}: ${summary}`;
|
|
110
|
+
} else if (request.amount) {
|
|
111
|
+
message = `${request.amount} ETH to ${shortAddr}`;
|
|
112
|
+
} else {
|
|
113
|
+
message = `Request to ${shortAddr}`;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const actions: NotificationAction[] = [
|
|
117
|
+
{
|
|
118
|
+
id: 'approve',
|
|
119
|
+
label: 'APPROVE',
|
|
120
|
+
type: 'primary',
|
|
121
|
+
action: 'api',
|
|
122
|
+
endpoint: `/actions/${request.id}/resolve`,
|
|
123
|
+
method: 'POST',
|
|
124
|
+
body: { approved: true },
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
id: 'reject',
|
|
128
|
+
label: 'REJECT',
|
|
129
|
+
type: 'danger',
|
|
130
|
+
action: 'api',
|
|
131
|
+
endpoint: `/actions/${request.id}/resolve`,
|
|
132
|
+
method: 'POST',
|
|
133
|
+
body: { approved: false },
|
|
134
|
+
},
|
|
135
|
+
];
|
|
136
|
+
|
|
137
|
+
const metadata: Record<string, unknown> = {
|
|
138
|
+
requestType: request.type,
|
|
139
|
+
fromTier: request.fromTier,
|
|
140
|
+
toAddress: request.toAddress,
|
|
141
|
+
amount: request.amount,
|
|
142
|
+
chain: request.chain,
|
|
143
|
+
explorer,
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
if (request.metadata) {
|
|
147
|
+
try {
|
|
148
|
+
const parsed = JSON.parse(request.metadata);
|
|
149
|
+
Object.assign(metadata, parsed);
|
|
150
|
+
} catch {
|
|
151
|
+
// ignore parse errors
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return createNotification({
|
|
156
|
+
type: 'pending_approval',
|
|
157
|
+
category: 'transaction',
|
|
158
|
+
title,
|
|
159
|
+
message,
|
|
160
|
+
actions,
|
|
161
|
+
metadata,
|
|
162
|
+
humanActionId: request.id,
|
|
163
|
+
source: 'system',
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export async function resolveNotificationForRequest(
|
|
168
|
+
requestId: string,
|
|
169
|
+
resolution: 'approved' | 'rejected',
|
|
170
|
+
resultData?: { txHash?: string; explorer?: string }
|
|
171
|
+
) {
|
|
172
|
+
// Find and dismiss the pending notification
|
|
173
|
+
const notification = await prisma.notification.findFirst({
|
|
174
|
+
where: { humanActionId: requestId },
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
if (notification) {
|
|
178
|
+
await prisma.notification.update({
|
|
179
|
+
where: { id: notification.id },
|
|
180
|
+
data: { dismissed: true, read: true },
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Create a success/info notification about the resolution
|
|
185
|
+
if (resolution === 'approved' && resultData?.txHash) {
|
|
186
|
+
const actions: NotificationAction[] = [];
|
|
187
|
+
if (resultData.explorer) {
|
|
188
|
+
actions.push({
|
|
189
|
+
id: 'view_tx',
|
|
190
|
+
label: 'VIEW TX',
|
|
191
|
+
type: 'link',
|
|
192
|
+
action: 'navigate',
|
|
193
|
+
href: `${resultData.explorer}/tx/${resultData.txHash}`,
|
|
194
|
+
external: true,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
actions.push({
|
|
198
|
+
id: 'dismiss',
|
|
199
|
+
label: 'DISMISS',
|
|
200
|
+
type: 'secondary',
|
|
201
|
+
action: 'dismiss',
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
await createNotification({
|
|
205
|
+
type: 'success',
|
|
206
|
+
category: 'transaction',
|
|
207
|
+
title: 'Transaction Approved',
|
|
208
|
+
message: `TX: ${resultData.txHash.slice(0, 10)}...${resultData.txHash.slice(-6)}`,
|
|
209
|
+
actions,
|
|
210
|
+
metadata: { txHash: resultData.txHash, explorer: resultData.explorer },
|
|
211
|
+
source: 'system',
|
|
212
|
+
});
|
|
213
|
+
} else if (resolution === 'rejected') {
|
|
214
|
+
await createNotification({
|
|
215
|
+
type: 'info',
|
|
216
|
+
category: 'transaction',
|
|
217
|
+
title: 'Request Rejected',
|
|
218
|
+
message: 'The pending request was rejected.',
|
|
219
|
+
actions: [
|
|
220
|
+
{
|
|
221
|
+
id: 'dismiss',
|
|
222
|
+
label: 'DISMISS',
|
|
223
|
+
type: 'secondary',
|
|
224
|
+
action: 'dismiss',
|
|
225
|
+
},
|
|
226
|
+
],
|
|
227
|
+
source: 'system',
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
}
|