auramaxx 1.0.0-alpha.4
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 +112 -0
- package/bin/aurawallet.js +121 -0
- package/docs/ADAPTERS.md +467 -0
- package/docs/API.md +2679 -0
- package/docs/APPS.md +198 -0
- package/docs/ARCHITECTURE.md +350 -0
- package/docs/AUTH.md +698 -0
- package/docs/BEST-PRACTICES.md +121 -0
- package/docs/CLI.md +61 -0
- package/docs/DEVELOPING-APPS.md +452 -0
- package/docs/EXTENSION.md +97 -0
- package/docs/JOBS.md +33 -0
- package/docs/MCP.md +76 -0
- package/docs/PROTOCOL.md +142 -0
- package/docs/SETUP.md +219 -0
- package/docs/WORKSPACE.md +672 -0
- package/docs/agent-auth.md +63 -0
- package/docs/aura-file.md +48 -0
- package/docs/credentials.md +53 -0
- package/docs/external/getting-started.md +65 -0
- package/docs/external/overview.md +45 -0
- package/docs/external/use-cases.md +48 -0
- package/docs/external/why-aura.md +35 -0
- package/docs/jobs/connect-agent.md +77 -0
- package/docs/jobs/migrate-from-dotenv.md +79 -0
- package/docs/jobs/recover-from-lockout.md +72 -0
- package/docs/jobs/secure-ci.md +63 -0
- package/docs/oauth2.md +42 -0
- package/docs/passkeys.md +60 -0
- package/docs/security.md +540 -0
- package/docs/specs/aura-open-protocol.md +61 -0
- package/docs/specs/aura-provider-plugin.md +24 -0
- package/docs/specs/aura-registry-model.md +31 -0
- package/docs/specs/fixtures/invalid-bad-key.aura +1 -0
- package/docs/specs/fixtures/invalid-bad-unicode-escape.aura +1 -0
- package/docs/specs/fixtures/invalid-duplicate-key.aura +2 -0
- package/docs/specs/fixtures/valid-basic.aura +4 -0
- package/docs/specs/fixtures/valid-provider-ref.aura +1 -0
- package/docs/specs/fixtures/valid-quoted-escapes.aura +2 -0
- package/docs/templates/RELEASE_NOTES_TEMPLATE.md +22 -0
- package/docs/totp.md +40 -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 +21 -0
- package/package.json +151 -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/migration_lock.toml +3 -0
- package/prisma/schema.prisma +447 -0
- package/public/logo-chevron.svg +31 -0
- package/public/logo-concentric.svg +31 -0
- package/public/logo-crosshatch.svg +39 -0
- package/public/logo-dashed.svg +39 -0
- package/public/logo-horizontal.svg +31 -0
- package/public/logo-m56.svg +64 -0
- package/public/logo.webp +0 -0
- package/scripts/add-app.js +245 -0
- package/scripts/init.sh +57 -0
- package/scripts/migrate-apikeys-to-credentials.ts +35 -0
- package/scripts/sandbox-agent-flow.sh +235 -0
- package/scripts/sandbox.sh +175 -0
- package/scripts/validate-job-docs.mjs +125 -0
- package/server/abi/SwapHelper.json +438 -0
- package/server/cli/approval.ts +447 -0
- package/server/cli/commands/app.ts +204 -0
- package/server/cli/commands/cron.ts +24 -0
- package/server/cli/commands/doctor.ts +1007 -0
- package/server/cli/commands/env.ts +456 -0
- package/server/cli/commands/init.ts +752 -0
- package/server/cli/commands/mcp.ts +125 -0
- package/server/cli/commands/restore.ts +314 -0
- package/server/cli/commands/shell-hook.ts +468 -0
- package/server/cli/commands/start.ts +62 -0
- package/server/cli/commands/status.ts +59 -0
- package/server/cli/commands/stop.ts +14 -0
- package/server/cli/commands/token.ts +180 -0
- package/server/cli/commands/unlock.ts +49 -0
- package/server/cli/commands/vault.ts +417 -0
- package/server/cli/index.ts +328 -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 +254 -0
- package/server/cli/lib/dotenv-migrate.ts +116 -0
- package/server/cli/lib/dotenv-parser.ts +146 -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/process.ts +136 -0
- package/server/cli/lib/prompt.ts +85 -0
- package/server/cli/lib/theme.ts +240 -0
- package/server/cli/socket.ts +570 -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 +406 -0
- package/server/lib/adapters/factory.ts +110 -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 +328 -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 +189 -0
- package/server/lib/app-installer.ts +505 -0
- package/server/lib/app-tokens.ts +247 -0
- package/server/lib/auth.ts +314 -0
- package/server/lib/batch.ts +242 -0
- package/server/lib/cold.ts +874 -0
- package/server/lib/config.ts +381 -0
- package/server/lib/credential-access-audit.ts +85 -0
- package/server/lib/credential-access-policy.ts +110 -0
- package/server/lib/credential-health.ts +343 -0
- package/server/lib/credential-import.ts +487 -0
- package/server/lib/credential-scope.ts +87 -0
- package/server/lib/credential-shares.ts +190 -0
- package/server/lib/credential-transport.ts +342 -0
- package/server/lib/credential-vault.ts +77 -0
- package/server/lib/credentials.ts +333 -0
- package/server/lib/crypto.ts +8 -0
- package/server/lib/db.ts +15 -0
- package/server/lib/defaults.ts +366 -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/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 +128 -0
- package/server/lib/error.ts +20 -0
- package/server/lib/events.ts +205 -0
- package/server/lib/hot.ts +357 -0
- package/server/lib/key-fingerprint.ts +28 -0
- package/server/lib/logger.ts +331 -0
- package/server/lib/network.ts +137 -0
- package/server/lib/notifications.ts +219 -0
- package/server/lib/oauth2-refresh.ts +241 -0
- package/server/lib/oursecret.ts +54 -0
- package/server/lib/passkey-credential.ts +360 -0
- package/server/lib/passkey.ts +68 -0
- package/server/lib/permissions.ts +248 -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 +239 -0
- package/server/lib/resolve-action.ts +427 -0
- package/server/lib/resolve.ts +36 -0
- package/server/lib/sessions.ts +632 -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 +158 -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 +235 -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 +75 -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/verified-summary.ts +421 -0
- package/server/mcp/profile-policy.ts +30 -0
- package/server/mcp/server.ts +619 -0
- package/server/mcp/tools.ts +523 -0
- package/server/middleware/auth.ts +119 -0
- package/server/middleware/requestLogger.ts +84 -0
- package/server/routes/actions.ts +459 -0
- package/server/routes/adapters.ts +703 -0
- package/server/routes/addressbook.ts +113 -0
- package/server/routes/ai.ts +34 -0
- package/server/routes/apikeys.ts +295 -0
- package/server/routes/apps.ts +601 -0
- package/server/routes/auth.ts +457 -0
- package/server/routes/backup.ts +340 -0
- package/server/routes/batch.ts +270 -0
- package/server/routes/bookmarks.ts +162 -0
- package/server/routes/credential-shares.ts +198 -0
- package/server/routes/credential-vaults.ts +154 -0
- package/server/routes/credentials.ts +1290 -0
- package/server/routes/dashboard.ts +71 -0
- package/server/routes/defaults.ts +124 -0
- package/server/routes/fund.ts +229 -0
- package/server/routes/import.ts +352 -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 +346 -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 +353 -0
- package/server/routes/swap-solana.ts +177 -0
- package/server/routes/swap.ts +356 -0
- package/server/routes/token.ts +247 -0
- package/server/routes/unlock.ts +403 -0
- package/server/routes/wallet-assets.ts +361 -0
- package/server/routes/wallet-transactions.ts +515 -0
- package/server/routes/wallet.ts +710 -0
- package/server/types.ts +146 -0
- package/skills/aurawallet/SKILL.md +739 -0
- package/skills/aurawallet-setup/SKILL.md +74 -0
- package/skills/security-review/SKILL.md +148 -0
- package/src/app/api/agent-requests/route.ts +30 -0
- package/src/app/api/apps/install/route.ts +126 -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/events/route.ts +92 -0
- package/src/app/api/page.tsx +212 -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 +34 -0
- package/src/app/api/workspace/config/route.ts +106 -0
- package/src/app/api/workspace/import/route.ts +127 -0
- package/src/app/api/workspace/route.ts +116 -0
- package/src/app/app/page.tsx +2122 -0
- package/src/app/apple-icon.png +0 -0
- package/src/app/docs/page.tsx +178 -0
- package/src/app/favicon.ico +0 -0
- package/src/app/globals.css +572 -0
- package/src/app/health/page.tsx +5 -0
- package/src/app/hello/page.tsx +15 -0
- package/src/app/icon.png +0 -0
- package/src/app/layout.tsx +34 -0
- package/src/app/page.tsx +986 -0
- package/src/app/providers.tsx +90 -0
- package/src/app/share/[token]/page.tsx +295 -0
- package/src/components/ChainSelector.tsx +144 -0
- package/src/components/HumanActionBar.tsx +695 -0
- package/src/components/NotificationDrawer.tsx +129 -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 +53 -0
- package/src/components/design-system/ChainIndicator.tsx +65 -0
- package/src/components/design-system/ChainSelector.tsx +137 -0
- package/src/components/design-system/ConfirmationModal.tsx +106 -0
- package/src/components/design-system/ConfirmationPopover.tsx +81 -0
- package/src/components/design-system/Drawer.tsx +123 -0
- package/src/components/design-system/FilterDropdown.tsx +72 -0
- package/src/components/design-system/Modal.tsx +206 -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 +58 -0
- package/src/components/design-system/index.ts +11 -0
- package/src/components/docs/DocsThemeToggle.tsx +49 -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/TabBar.tsx +278 -0
- package/src/components/layout/WalletSidebar.tsx +1033 -0
- package/src/components/layout/index.ts +4 -0
- package/src/components/marketing/AuraWalletSpecOverlay.tsx +635 -0
- package/src/components/marketing/DeviceMorphExperience.tsx +216 -0
- package/src/components/vault/ApiKeysConsole.tsx +1080 -0
- package/src/components/vault/AuditConsole.tsx +584 -0
- package/src/components/vault/CredentialDetail.tsx +455 -0
- package/src/components/vault/CredentialEmpty.tsx +55 -0
- package/src/components/vault/CredentialField.tsx +361 -0
- package/src/components/vault/CredentialForm.tsx +1212 -0
- package/src/components/vault/CredentialList.tsx +165 -0
- package/src/components/vault/CredentialRow.tsx +97 -0
- package/src/components/vault/CredentialShareModal.tsx +178 -0
- package/src/components/vault/CredentialVault.tsx +754 -0
- package/src/components/vault/CredentialWalletWidget.tsx +103 -0
- package/src/components/vault/ImportCredentialsModal.tsx +515 -0
- package/src/components/vault/LargeTypeModal.tsx +64 -0
- package/src/components/vault/PasswordGenerator.tsx +224 -0
- package/src/components/vault/TOTPDisplay.tsx +123 -0
- package/src/components/vault/VaultSidebar.tsx +413 -0
- package/src/components/vault/types.ts +54 -0
- package/src/context/AuthContext.tsx +337 -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 +3 -0
- package/src/hooks/useAgentActions.ts +368 -0
- package/src/hooks/useBalance.ts +103 -0
- package/src/hooks/useBalances.ts +129 -0
- package/src/instrumentation.ts +12 -0
- package/src/lib/api.ts +449 -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/crypto.ts +112 -0
- package/src/lib/db.ts +21 -0
- package/src/lib/docs.ts +390 -0
- package/src/lib/events.ts +361 -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/vault-crypto.ts +129 -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 +80 -0
- package/tailwind.config.ts +99 -0
- package/tsconfig.json +42 -0
package/server/index.ts
ADDED
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import cors from 'cors';
|
|
3
|
+
import { createHash } from 'crypto';
|
|
4
|
+
import rateLimit, { ipKeyGenerator } from 'express-rate-limit';
|
|
5
|
+
import { SERVER_PORT, ensureDataDir } from './lib/config';
|
|
6
|
+
import { unlock, hasColdWallet, autoUnlockLinkedVaults, isVaultUnlocked, getPrimaryVaultId } from './lib/cold';
|
|
7
|
+
import { ApprovalRouter, loadAdaptersFromDb } from './lib/adapters';
|
|
8
|
+
import { requestLogger } from './middleware/requestLogger';
|
|
9
|
+
import { logger } from './lib/logger';
|
|
10
|
+
import { log } from './lib/pino';
|
|
11
|
+
|
|
12
|
+
// Routes
|
|
13
|
+
import setupRoutes from './routes/setup';
|
|
14
|
+
import unlockRoutes, { unlockPageHandler } from './routes/unlock';
|
|
15
|
+
import lockRoutes from './routes/lock';
|
|
16
|
+
import walletRoutes from './routes/wallet';
|
|
17
|
+
import sendRoutes from './routes/send';
|
|
18
|
+
import authRoutes from './routes/auth';
|
|
19
|
+
import passkeyRoutes from './routes/passkey';
|
|
20
|
+
import nukeRoutes from './routes/nuke';
|
|
21
|
+
import fundRoutes from './routes/fund';
|
|
22
|
+
import swapRoutes from './routes/swap';
|
|
23
|
+
import launchRoutes from './routes/launch';
|
|
24
|
+
import apikeysRoutes from './routes/apikeys';
|
|
25
|
+
import backupRoutes from './routes/backup';
|
|
26
|
+
import strategyRoutes from './routes/strategy';
|
|
27
|
+
import appRoutes from './routes/apps';
|
|
28
|
+
import actionsRoutes from './routes/actions';
|
|
29
|
+
import credentialVaultRoutes from './routes/credential-vaults';
|
|
30
|
+
import credentialsRoutes from './routes/credentials';
|
|
31
|
+
import credentialSharesRoutes from './routes/credential-shares';
|
|
32
|
+
import passkeyCredentialRoutes from './routes/passkey-credentials';
|
|
33
|
+
import importRoutes from './routes/import';
|
|
34
|
+
import adaptersRoutes, { setApprovalRouter, getApprovalRouter } from './routes/adapters';
|
|
35
|
+
import defaultsRoutes from './routes/defaults';
|
|
36
|
+
import aiRoutes from './routes/ai';
|
|
37
|
+
import portfolioRoutes from './routes/portfolio';
|
|
38
|
+
import resolveRoutes from './routes/resolve';
|
|
39
|
+
import priceRoutes from './routes/price';
|
|
40
|
+
import tokenRoutes from './routes/token';
|
|
41
|
+
import batchRoutes from './routes/batch';
|
|
42
|
+
import addressbookRoutes from './routes/addressbook';
|
|
43
|
+
import bookmarkRoutes from './routes/bookmarks';
|
|
44
|
+
import logsRoutes from './routes/logs';
|
|
45
|
+
import dashboardRoutes from './routes/dashboard';
|
|
46
|
+
import securityRoutes from './routes/security';
|
|
47
|
+
import { preloadCache, onDefaultChanged, parseRateLimit, getDefaultSync } from './lib/defaults';
|
|
48
|
+
import { SocketServer } from './cli/socket';
|
|
49
|
+
|
|
50
|
+
// Ensure data directory exists
|
|
51
|
+
ensureDataDir();
|
|
52
|
+
|
|
53
|
+
const app = express();
|
|
54
|
+
|
|
55
|
+
// Middleware
|
|
56
|
+
// Restrict CORS to localhost origins. Bearer token is the primary auth mechanism,
|
|
57
|
+
// but limiting origins provides defense-in-depth.
|
|
58
|
+
const ALLOWED_ORIGINS = [
|
|
59
|
+
/^https?:\/\/localhost(:\d+)?$/,
|
|
60
|
+
/^https?:\/\/127\.0\.0\.1(:\d+)?$/,
|
|
61
|
+
/^https:\/\/[a-z0-9-]+\.auramaxx\.xyz$/, // Cloudflare tunnel subdomains
|
|
62
|
+
'null', // blob URL iframes have opaque origin "null"
|
|
63
|
+
];
|
|
64
|
+
app.use(cors({
|
|
65
|
+
origin: (origin, callback) => {
|
|
66
|
+
// Allow requests with no origin (e.g., server-to-server, curl, mobile apps)
|
|
67
|
+
if (!origin) return callback(null, true);
|
|
68
|
+
if (origin === 'null') return callback(null, true);
|
|
69
|
+
if (ALLOWED_ORIGINS.some(p => p instanceof RegExp ? p.test(origin) : p === origin)) {
|
|
70
|
+
return callback(null, true);
|
|
71
|
+
}
|
|
72
|
+
callback(new Error(`Origin ${origin} not allowed by CORS`));
|
|
73
|
+
},
|
|
74
|
+
credentials: true,
|
|
75
|
+
}));
|
|
76
|
+
app.use(express.json());
|
|
77
|
+
|
|
78
|
+
// Rate limiting — set BYPASS_RATE_LIMIT=true to disable (useful for local dev)
|
|
79
|
+
const bypassRateLimit = process.env.BYPASS_RATE_LIMIT === 'true';
|
|
80
|
+
|
|
81
|
+
const rateLimitResponse = (_req: express.Request, res: express.Response) => {
|
|
82
|
+
res.status(429).json({ success: false, error: 'Too many requests, please try again later' });
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const noopMiddleware: express.RequestHandler = (_req, _res, next) => next();
|
|
86
|
+
|
|
87
|
+
// Hot-reloadable rate limiter: wraps a mutable inner limiter that gets swapped on config change
|
|
88
|
+
function createHotLimiter(
|
|
89
|
+
defaultKey: string,
|
|
90
|
+
fallback: string,
|
|
91
|
+
): express.RequestHandler {
|
|
92
|
+
if (bypassRateLimit) return noopMiddleware;
|
|
93
|
+
|
|
94
|
+
const { max, windowMs } = parseRateLimit(getDefaultSync(defaultKey, fallback));
|
|
95
|
+
let inner = rateLimit({ windowMs, max, standardHeaders: true, legacyHeaders: false, handler: rateLimitResponse });
|
|
96
|
+
|
|
97
|
+
onDefaultChanged(defaultKey, (_key, value) => {
|
|
98
|
+
const updated = parseRateLimit(value);
|
|
99
|
+
inner = rateLimit({ windowMs: updated.windowMs, max: updated.max, standardHeaders: true, legacyHeaders: false, handler: rateLimitResponse });
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
return (req, res, next) => inner(req, res, next);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Strict limit for password/setup endpoints (brute force protection)
|
|
106
|
+
const authBruteForceLimit = createHotLimiter('rate.brute_force', '5,900000');
|
|
107
|
+
|
|
108
|
+
// Registration/token request limit
|
|
109
|
+
const authRequestLimit = createHotLimiter('rate.auth_request', '10,60000');
|
|
110
|
+
|
|
111
|
+
// Transaction endpoints - keyed by hashed Bearer token
|
|
112
|
+
const txLimit = bypassRateLimit ? noopMiddleware : rateLimit({
|
|
113
|
+
windowMs: 60 * 1000, // 1 minute
|
|
114
|
+
max: 30,
|
|
115
|
+
standardHeaders: true,
|
|
116
|
+
legacyHeaders: false,
|
|
117
|
+
keyGenerator: (req) => {
|
|
118
|
+
const authHeader = req.headers.authorization;
|
|
119
|
+
if (authHeader && authHeader.startsWith('Bearer ')) {
|
|
120
|
+
// Hash the token so plaintext tokens are never stored in the rate limit memory store
|
|
121
|
+
return createHash('sha256').update(authHeader.slice(7)).digest('hex').slice(0, 16);
|
|
122
|
+
}
|
|
123
|
+
return ipKeyGenerator(req);
|
|
124
|
+
},
|
|
125
|
+
handler: rateLimitResponse,
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
// Paths that already have specific rate limits (skip general limit to avoid double-counting)
|
|
129
|
+
const rateLimitedPaths = new Set(['/unlock', '/setup', '/auth', '/send', '/swap', '/fund', '/launch', '/actions', '/nuke']);
|
|
130
|
+
|
|
131
|
+
// General rate limit for all other endpoints
|
|
132
|
+
const generalLimit = bypassRateLimit ? noopMiddleware : rateLimit({
|
|
133
|
+
windowMs: 60 * 1000, // 1 minute
|
|
134
|
+
max: 100,
|
|
135
|
+
standardHeaders: true,
|
|
136
|
+
legacyHeaders: false,
|
|
137
|
+
skip: (req) => rateLimitedPaths.has('/' + req.path.split('/')[1]),
|
|
138
|
+
handler: rateLimitResponse,
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// Request/response logging (structured, with timing, agent identification, and event storage)
|
|
142
|
+
app.use(requestLogger);
|
|
143
|
+
|
|
144
|
+
// Serve unlock page before rate limiting (GET is not a brute force vector)
|
|
145
|
+
app.get('/unlock', unlockPageHandler);
|
|
146
|
+
|
|
147
|
+
// Apply rate limits to specific routes before general middleware
|
|
148
|
+
if (bypassRateLimit) {
|
|
149
|
+
log.warn('Rate limiting BYPASSED (BYPASS_RATE_LIMIT=true)');
|
|
150
|
+
}
|
|
151
|
+
// NOTE: Rate limiting assumes direct connections (no reverse proxy).
|
|
152
|
+
// If deploying behind a proxy, set app.set('trust proxy', 1) and configure accordingly.
|
|
153
|
+
app.use('/unlock', authBruteForceLimit);
|
|
154
|
+
// Only brute-force-limit setup writes; GET /setup (status check) uses the general limiter
|
|
155
|
+
app.use('/setup', (req, res, next) => {
|
|
156
|
+
if (req.method === 'GET') return next();
|
|
157
|
+
return authBruteForceLimit(req, res, next);
|
|
158
|
+
});
|
|
159
|
+
app.use('/actions', authBruteForceLimit);
|
|
160
|
+
app.use('/nuke', authBruteForceLimit);
|
|
161
|
+
// Only brute-force-limit backup writes; GET /backup (list) uses the general limiter
|
|
162
|
+
app.use('/backup', (req, res, next) => {
|
|
163
|
+
if (req.method === 'GET') return next();
|
|
164
|
+
return authBruteForceLimit(req, res, next);
|
|
165
|
+
});
|
|
166
|
+
app.use('/auth', authRequestLimit);
|
|
167
|
+
app.use('/send', txLimit);
|
|
168
|
+
app.use('/swap', txLimit);
|
|
169
|
+
app.use('/fund', txLimit);
|
|
170
|
+
app.use('/launch', txLimit);
|
|
171
|
+
|
|
172
|
+
// General rate limit for everything else
|
|
173
|
+
app.use(generalLimit);
|
|
174
|
+
|
|
175
|
+
// Routes
|
|
176
|
+
app.use('/setup', setupRoutes);
|
|
177
|
+
app.use('/unlock', unlockRoutes);
|
|
178
|
+
app.use('/lock', lockRoutes);
|
|
179
|
+
app.use('/wallets', walletRoutes);
|
|
180
|
+
app.use('/wallet', walletRoutes);
|
|
181
|
+
app.use('/send', sendRoutes);
|
|
182
|
+
app.use('/auth', authRoutes);
|
|
183
|
+
app.use('/auth/passkey', passkeyRoutes);
|
|
184
|
+
app.use('/nuke', nukeRoutes);
|
|
185
|
+
app.use('/fund', fundRoutes);
|
|
186
|
+
app.use('/swap', swapRoutes);
|
|
187
|
+
app.use('/launch', launchRoutes);
|
|
188
|
+
app.use('/apikeys', apikeysRoutes);
|
|
189
|
+
app.use('/backup', backupRoutes);
|
|
190
|
+
app.use('/strategies', strategyRoutes);
|
|
191
|
+
app.use('/apps', appRoutes);
|
|
192
|
+
app.use('/actions', actionsRoutes);
|
|
193
|
+
app.use('/vaults/credential', credentialVaultRoutes);
|
|
194
|
+
app.use('/credentials/import', importRoutes);
|
|
195
|
+
app.use('/credentials/passkey', passkeyCredentialRoutes);
|
|
196
|
+
app.use('/credentials', credentialsRoutes);
|
|
197
|
+
app.use('/credential-shares', credentialSharesRoutes);
|
|
198
|
+
app.use('/adapters', adaptersRoutes);
|
|
199
|
+
app.use('/defaults', defaultsRoutes);
|
|
200
|
+
app.use('/ai', aiRoutes);
|
|
201
|
+
app.use('/portfolio', portfolioRoutes);
|
|
202
|
+
app.use('/resolve', resolveRoutes);
|
|
203
|
+
app.use('/price', priceRoutes);
|
|
204
|
+
app.use('/token', tokenRoutes);
|
|
205
|
+
app.use('/batch', batchRoutes);
|
|
206
|
+
app.use('/address-labels', addressbookRoutes);
|
|
207
|
+
app.use('/bookmarks', bookmarkRoutes);
|
|
208
|
+
app.use('/security', securityRoutes);
|
|
209
|
+
|
|
210
|
+
// Health check
|
|
211
|
+
app.get('/health', (_req, res) => {
|
|
212
|
+
res.json({ status: 'ok', timestamp: new Date().toISOString() });
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
app.use('/logs', logsRoutes);
|
|
216
|
+
app.use('/dashboard', dashboardRoutes);
|
|
217
|
+
|
|
218
|
+
// Catch-all 404 — return JSON instead of Express default HTML
|
|
219
|
+
app.use((_req: express.Request, res: express.Response) => {
|
|
220
|
+
res.status(404).json({ error: 'Not found' });
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
// Error handler
|
|
224
|
+
app.use((err: Error, req: express.Request, res: express.Response, _next: express.NextFunction) => {
|
|
225
|
+
log.error({ err, method: req.method, url: req.originalUrl || req.path, agentId: req.auth?.token?.agentId }, 'server error');
|
|
226
|
+
logger.error(err.message, req.originalUrl || req.path, {
|
|
227
|
+
method: req.method,
|
|
228
|
+
agentId: req.auth?.token?.agentId,
|
|
229
|
+
});
|
|
230
|
+
res.status(500).json({ error: 'Internal server error' });
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
// Auto-migrate database on startup (applies any pending Prisma migrations)
|
|
234
|
+
async function autoMigrate() {
|
|
235
|
+
const { execSync } = await import('child_process');
|
|
236
|
+
const dbUrl = process.env.DATABASE_URL;
|
|
237
|
+
try {
|
|
238
|
+
execSync('npx prisma migrate deploy', {
|
|
239
|
+
cwd: import.meta.dirname ? import.meta.dirname + '/..' : process.cwd(),
|
|
240
|
+
env: { ...process.env, DATABASE_URL: dbUrl },
|
|
241
|
+
stdio: 'pipe',
|
|
242
|
+
});
|
|
243
|
+
log.info('Database migrations applied');
|
|
244
|
+
} catch (err) {
|
|
245
|
+
log.warn({ err }, 'Database migration failed (may already be up to date)');
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Start server (preload defaults cache before listening)
|
|
250
|
+
let server: ReturnType<typeof app.listen>;
|
|
251
|
+
let socketServer: SocketServer | null = null;
|
|
252
|
+
|
|
253
|
+
async function startServer() {
|
|
254
|
+
// Apply pending migrations before anything else
|
|
255
|
+
await autoMigrate();
|
|
256
|
+
|
|
257
|
+
// Load system defaults into memory cache before handling any requests
|
|
258
|
+
await preloadCache().catch(err => {
|
|
259
|
+
log.warn({ err }, 'Failed to preload defaults cache (will use seed values)');
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
// Session crash recovery and log cleanup (before starting engine)
|
|
263
|
+
try {
|
|
264
|
+
const { recoverCrashedSessions, cleanupOldLogs } = await import('./lib/strategy/session-logger');
|
|
265
|
+
await recoverCrashedSessions().catch(err => log.warn({ err }, 'session crash recovery failed'));
|
|
266
|
+
await cleanupOldLogs().catch(err => log.warn({ err }, 'session log cleanup failed'));
|
|
267
|
+
} catch (err) {
|
|
268
|
+
log.warn({ err }, 'session logger import failed');
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
// Credential lifecycle retention: purge recently deleted credentials older than retention window.
|
|
272
|
+
try {
|
|
273
|
+
const { purgeDeletedCredentials } = await import('./lib/credentials');
|
|
274
|
+
const summary = purgeDeletedCredentials(30);
|
|
275
|
+
if (summary.purged > 0) {
|
|
276
|
+
log.info({ purged: summary.purged, scanned: summary.scanned }, 'Purged expired recently deleted credentials on startup');
|
|
277
|
+
}
|
|
278
|
+
if (summary.errors.length > 0) {
|
|
279
|
+
log.warn({ errors: summary.errors, scanned: summary.scanned }, 'Credential startup purge completed with errors');
|
|
280
|
+
}
|
|
281
|
+
} catch (err) {
|
|
282
|
+
log.warn({ err }, 'Credential startup purge failed');
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Auto-unlock vault if VAULT_PASSWORD env var is set
|
|
286
|
+
if (process.env.VAULT_PASSWORD) {
|
|
287
|
+
if (!hasColdWallet()) {
|
|
288
|
+
log.error('VAULT_PASSWORD is set but no vault exists. Run setup first.');
|
|
289
|
+
process.exit(1);
|
|
290
|
+
}
|
|
291
|
+
const ok = unlock(process.env.VAULT_PASSWORD);
|
|
292
|
+
if (!ok) {
|
|
293
|
+
log.error('VAULT_PASSWORD is incorrect. Exiting.');
|
|
294
|
+
process.exit(1);
|
|
295
|
+
}
|
|
296
|
+
delete process.env.VAULT_PASSWORD;
|
|
297
|
+
log.info('Vault auto-unlocked via VAULT_PASSWORD env var');
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Auto-unlock linked vaults when primary is already unlocked (e.g. via VAULT_PASSWORD).
|
|
301
|
+
// Independent vaults remain locked until explicitly unlocked with their own password.
|
|
302
|
+
const pid = getPrimaryVaultId();
|
|
303
|
+
if (pid && isVaultUnlocked(pid)) {
|
|
304
|
+
try {
|
|
305
|
+
const count = autoUnlockLinkedVaults();
|
|
306
|
+
if (count > 0) {
|
|
307
|
+
log.info({ count }, 'Auto-unlocked linked vaults on startup');
|
|
308
|
+
}
|
|
309
|
+
} catch (err) {
|
|
310
|
+
log.warn({ err }, 'Failed to auto-unlock linked vaults');
|
|
311
|
+
}
|
|
312
|
+
} else {
|
|
313
|
+
log.debug('Primary vault not unlocked yet — deferring linked vault auto-unlock');
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
server = app.listen(SERVER_PORT, '127.0.0.1', () => {
|
|
317
|
+
log.info({ port: SERVER_PORT, url: `http://127.0.0.1:${SERVER_PORT}` }, 'Aura Wallet server started');
|
|
318
|
+
|
|
319
|
+
// Start local Unix socket broker as part of normal server lifecycle.
|
|
320
|
+
socketServer = new SocketServer({ serverUrl: `http://127.0.0.1:${SERVER_PORT}` });
|
|
321
|
+
socketServer.start()
|
|
322
|
+
.then(() => {
|
|
323
|
+
if (!socketServer) return;
|
|
324
|
+
log.info({ socketPath: socketServer.getSocketPath() }, 'Local socket broker started');
|
|
325
|
+
})
|
|
326
|
+
.catch((err) => {
|
|
327
|
+
log.error({ err }, 'Failed to start local socket broker');
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
// Start approval adapter router (if configured in DB)
|
|
331
|
+
loadAdaptersFromDb().then(adapters => {
|
|
332
|
+
if (adapters.length > 0) {
|
|
333
|
+
const router = new ApprovalRouter(`http://127.0.0.1:${SERVER_PORT}`);
|
|
334
|
+
for (const adapter of adapters) {
|
|
335
|
+
router.registerAdapter(adapter);
|
|
336
|
+
}
|
|
337
|
+
setApprovalRouter(router);
|
|
338
|
+
router.start().catch(err =>
|
|
339
|
+
log.error({ err }, 'Failed to start approval router')
|
|
340
|
+
);
|
|
341
|
+
log.info({ count: adapters.length }, 'Approval adapters loaded');
|
|
342
|
+
}
|
|
343
|
+
}).catch(err => {
|
|
344
|
+
log.error({ err }, 'Failed to load adapters from DB');
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
// Daily summary generation (hourly + on boot)
|
|
348
|
+
import('./lib/strategy/session-logger').then(({ generateDailySummary }) => {
|
|
349
|
+
generateDailySummary().catch(() => {});
|
|
350
|
+
setInterval(() => generateDailySummary().catch(() => {}), 3_600_000);
|
|
351
|
+
}).catch(() => {});
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
startServer().catch(err => {
|
|
356
|
+
log.error({ err }, 'Failed to start server');
|
|
357
|
+
process.exit(1);
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
// Graceful shutdown
|
|
361
|
+
async function shutdown(signal: string) {
|
|
362
|
+
log.info({ signal }, 'Shutting down...');
|
|
363
|
+
try {
|
|
364
|
+
const { endAllActiveSessions } = await import('./lib/strategy/session-logger');
|
|
365
|
+
await endAllActiveSessions('completed').catch(() => {});
|
|
366
|
+
} catch {}
|
|
367
|
+
try {
|
|
368
|
+
if (socketServer) {
|
|
369
|
+
await socketServer.stop();
|
|
370
|
+
socketServer = null;
|
|
371
|
+
log.info('Local socket broker stopped');
|
|
372
|
+
}
|
|
373
|
+
} catch (err) {
|
|
374
|
+
log.error({ err }, 'Error stopping local socket broker');
|
|
375
|
+
}
|
|
376
|
+
try {
|
|
377
|
+
const router = getApprovalRouter();
|
|
378
|
+
if (router) await router.stop();
|
|
379
|
+
} catch (err) {
|
|
380
|
+
log.error({ err }, 'Error stopping approval router');
|
|
381
|
+
}
|
|
382
|
+
server.close(() => {
|
|
383
|
+
log.info('HTTP server closed');
|
|
384
|
+
process.exit(0);
|
|
385
|
+
});
|
|
386
|
+
// Force exit after 35s if graceful shutdown hangs
|
|
387
|
+
setTimeout(() => process.exit(1), 35_000);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
process.on('SIGTERM', () => shutdown('SIGTERM'));
|
|
391
|
+
process.on('SIGINT', () => shutdown('SIGINT'));
|
|
392
|
+
|
|
393
|
+
process.on('uncaughtException', async (err) => {
|
|
394
|
+
log.fatal({ err }, 'Uncaught exception');
|
|
395
|
+
try {
|
|
396
|
+
const { markAllSessionsCrashed } = await import('./lib/strategy/session-logger');
|
|
397
|
+
await markAllSessionsCrashed();
|
|
398
|
+
} catch {}
|
|
399
|
+
process.exit(1);
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
process.on('unhandledRejection', (reason) => {
|
|
403
|
+
log.error({ err: reason }, 'Unhandled rejection');
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
export default app;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adapter registry and factory.
|
|
3
|
+
*
|
|
4
|
+
* Maps adapter type names to constructors. Built-in types (webhook, telegram)
|
|
5
|
+
* are registered at import time. Third-party types can be registered via
|
|
6
|
+
* registerAdapterType().
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { ApprovalAdapter, AdapterFactory } from './types';
|
|
10
|
+
import { WebhookAdapter, type WebhookAdapterConfig } from './webhook';
|
|
11
|
+
import { TelegramAdapter, type TelegramAdapterConfig } from './telegram';
|
|
12
|
+
import { getErrorMessage } from '../error';
|
|
13
|
+
|
|
14
|
+
const registry = new Map<string, AdapterFactory>();
|
|
15
|
+
|
|
16
|
+
/** Register a custom adapter type */
|
|
17
|
+
export function registerAdapterType(type: string, factory: AdapterFactory): void {
|
|
18
|
+
registry.set(type, factory);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Internal config shape used by createAdapters and loadAdaptersFromDb */
|
|
22
|
+
interface AdapterEntry {
|
|
23
|
+
type: string;
|
|
24
|
+
enabled: boolean;
|
|
25
|
+
config: Record<string, unknown>;
|
|
26
|
+
chat?: { enabled?: boolean };
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Create adapter instances from structured config, skipping disabled entries */
|
|
30
|
+
export function createAdapters(entries: AdapterEntry[]): ApprovalAdapter[] {
|
|
31
|
+
const adapters: ApprovalAdapter[] = [];
|
|
32
|
+
|
|
33
|
+
for (const entry of entries) {
|
|
34
|
+
if (!entry.enabled) continue;
|
|
35
|
+
|
|
36
|
+
const factory = registry.get(entry.type);
|
|
37
|
+
if (!factory) {
|
|
38
|
+
console.warn(`[adapters] Unknown adapter type: ${entry.type}`);
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
const config = entry.chat ? { ...entry.config, chat: entry.chat } : entry.config;
|
|
44
|
+
adapters.push(factory(config));
|
|
45
|
+
} catch (err) {
|
|
46
|
+
const msg = getErrorMessage(err);
|
|
47
|
+
console.error(`[adapters] Failed to create ${entry.type} adapter:`, msg);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return adapters;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Load and create adapters from the database.
|
|
56
|
+
*
|
|
57
|
+
* Reads adapter settings from AppConfig.adapterConfig (enabled flags, non-secret config)
|
|
58
|
+
* and secrets from ApiKey (service = 'adapter:<type>'). Merges them to create instances.
|
|
59
|
+
*/
|
|
60
|
+
export async function loadAdaptersFromDb(): Promise<ApprovalAdapter[]> {
|
|
61
|
+
// Never load adapters during tests
|
|
62
|
+
if (process.env.NODE_ENV === 'test') return [];
|
|
63
|
+
|
|
64
|
+
// Lazy import to avoid circular deps
|
|
65
|
+
const { prisma } = await import('../db');
|
|
66
|
+
|
|
67
|
+
// 1. Read adapter config from AppConfig
|
|
68
|
+
const appConfig = await prisma.appConfig.findUnique({
|
|
69
|
+
where: { id: 'global' },
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
if (!appConfig?.adapterConfig) return [];
|
|
73
|
+
|
|
74
|
+
let parsed: { enabled?: boolean; chat?: { defaultApp?: string }; adapters?: AdapterEntry[] };
|
|
75
|
+
try {
|
|
76
|
+
parsed = JSON.parse(appConfig.adapterConfig);
|
|
77
|
+
} catch {
|
|
78
|
+
console.error('[adapters] Invalid adapterConfig JSON in AppConfig');
|
|
79
|
+
return [];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (!parsed.enabled || !parsed.adapters?.length) return [];
|
|
83
|
+
|
|
84
|
+
// 2. Read all adapter secrets from ApiKey table
|
|
85
|
+
const secretKeys = await prisma.apiKey.findMany({
|
|
86
|
+
where: { service: { startsWith: 'adapter:' }, isActive: true },
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// Build a lookup: { 'telegram': { botToken: '...' }, 'webhook': { secret: '...' } }
|
|
90
|
+
const secretsByType: Record<string, Record<string, string>> = {};
|
|
91
|
+
for (const key of secretKeys) {
|
|
92
|
+
const adapterType = key.service.replace('adapter:', '');
|
|
93
|
+
if (!secretsByType[adapterType]) secretsByType[adapterType] = {};
|
|
94
|
+
secretsByType[adapterType][key.name] = key.key;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// 3. Merge secrets into adapter configs and create instances
|
|
98
|
+
const entries: AdapterEntry[] = parsed.adapters.map((a) => ({
|
|
99
|
+
type: a.type,
|
|
100
|
+
enabled: a.enabled,
|
|
101
|
+
config: { ...a.config, ...(secretsByType[a.type] || {}) },
|
|
102
|
+
chat: a.chat,
|
|
103
|
+
}));
|
|
104
|
+
|
|
105
|
+
return createAdapters(entries);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Register built-in adapter types
|
|
109
|
+
registerAdapterType('webhook', (config) => new WebhookAdapter(config as WebhookAdapterConfig));
|
|
110
|
+
registerAdapterType('telegram', (config) => new TelegramAdapter(config as TelegramAdapterConfig));
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public exports for the approval adapter system.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export { ApprovalRouter } from './router';
|
|
6
|
+
export { createAdapters, registerAdapterType, loadAdaptersFromDb } from './factory';
|
|
7
|
+
export type {
|
|
8
|
+
ApprovalAdapter,
|
|
9
|
+
AdapterContext,
|
|
10
|
+
ActionNotification,
|
|
11
|
+
ActionResolution,
|
|
12
|
+
ResolveOptions,
|
|
13
|
+
ResolveResult,
|
|
14
|
+
AdapterFactory,
|
|
15
|
+
ChatMessage,
|
|
16
|
+
ChatReply,
|
|
17
|
+
} from './types';
|
|
18
|
+
export { WebhookAdapter, type WebhookAdapterConfig } from './webhook';
|
|
19
|
+
export { TelegramAdapter, type TelegramAdapterConfig } from './telegram';
|