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,539 @@
|
|
|
1
|
+
import { Router, Request, Response } from 'express';
|
|
2
|
+
import { randomBytes } from 'crypto';
|
|
3
|
+
import { prisma } from '../lib/db';
|
|
4
|
+
import { events } from '../lib/events';
|
|
5
|
+
import { requireWalletAuth } from '../middleware/auth';
|
|
6
|
+
import { requireAdmin, isAdmin, requirePermission } from '../lib/permissions';
|
|
7
|
+
import { createToken, getTokenHash, type AgentTokenPayload } from '../lib/auth';
|
|
8
|
+
import { isValidAgentPubkey, normalizeAgentPubkey, encryptToAgentPubkey } from '../lib/credential-transport';
|
|
9
|
+
import { hashSecret } from '../lib/crypto';
|
|
10
|
+
import { isUnlocked } from '../lib/cold';
|
|
11
|
+
import { normalizeAddress } from '../lib/address';
|
|
12
|
+
import { generateVerifiedSummary } from '../lib/verified-summary';
|
|
13
|
+
import { listTokensFromDb, revokeToken } from '../lib/sessions';
|
|
14
|
+
import { createHumanActionNotification, createNotification } from '../lib/notifications';
|
|
15
|
+
import { getDefault } from '../lib/defaults';
|
|
16
|
+
import { logger } from '../lib/logger';
|
|
17
|
+
import { getErrorMessage } from '../lib/error';
|
|
18
|
+
import { buildApproveUrl } from '../lib/approval-link';
|
|
19
|
+
import { resolveAction } from '../lib/resolve-action';
|
|
20
|
+
import { AgentProfileError, resolveProfileToEffectivePolicy } from '../lib/agent-profiles';
|
|
21
|
+
import { buildPolicyPreviewV1, mapPreviewError } from '../lib/policy-preview';
|
|
22
|
+
|
|
23
|
+
const router = Router();
|
|
24
|
+
|
|
25
|
+
// ============================================================================
|
|
26
|
+
// INTERNAL ENDPOINTS — Used by the dashboard, strategy engine, and admin tools.
|
|
27
|
+
// External agents should use POST /auth for token requests (with optional
|
|
28
|
+
// `action` field for auto-execute on approval). These routes are NOT exposed
|
|
29
|
+
// in agent-facing documentation (SKILL.md, CLI.md, etc.).
|
|
30
|
+
// ============================================================================
|
|
31
|
+
|
|
32
|
+
// GET /actions/pending — List all pending human actions
|
|
33
|
+
router.get('/pending', requireWalletAuth, requirePermission('action:read'), async (_req: Request, res: Response) => {
|
|
34
|
+
try {
|
|
35
|
+
const actions = await prisma.humanAction.findMany({
|
|
36
|
+
where: {
|
|
37
|
+
status: 'pending',
|
|
38
|
+
NOT: { type: 'strategy:message' },
|
|
39
|
+
},
|
|
40
|
+
orderBy: { createdAt: 'desc' },
|
|
41
|
+
});
|
|
42
|
+
res.json({ success: true, actions });
|
|
43
|
+
} catch (error) {
|
|
44
|
+
const message = getErrorMessage(error);
|
|
45
|
+
res.status(500).json({ success: false, error: message });
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
// GET /actions/:id/summary — Public sanitized action details for the approval page
|
|
50
|
+
router.get('/:id/summary', async (req: Request<{ id: string }>, res: Response) => {
|
|
51
|
+
try {
|
|
52
|
+
const action = await prisma.humanAction.findUnique({ where: { id: req.params.id } });
|
|
53
|
+
if (!action) { res.status(404).json({ error: 'Action not found' }); return; }
|
|
54
|
+
|
|
55
|
+
const metadata = action.metadata ? JSON.parse(action.metadata) : {};
|
|
56
|
+
|
|
57
|
+
// Derive display fields from stored metadata
|
|
58
|
+
const summary: Record<string, unknown> = {
|
|
59
|
+
id: action.id,
|
|
60
|
+
type: action.type,
|
|
61
|
+
status: action.status,
|
|
62
|
+
createdAt: action.createdAt,
|
|
63
|
+
action: metadata.summary || `${action.type} request`,
|
|
64
|
+
profile: typeof metadata.profile === 'object' ? metadata.profile.displayName || metadata.profile.id : metadata.profile,
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
// Permissions → scope
|
|
68
|
+
if (Array.isArray(metadata.permissions) && metadata.permissions.length > 0) {
|
|
69
|
+
summary.scope = metadata.permissions;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Risk based on permissions
|
|
73
|
+
const perms = metadata.permissions || [];
|
|
74
|
+
const highRisk = perms.some((p: string) => ['admin:*', 'fund', 'send:hot', 'launch'].includes(p));
|
|
75
|
+
const medRisk = perms.some((p: string) => ['swap', 'trade:all', 'wallet:create:hot'].includes(p));
|
|
76
|
+
summary.risk = highRisk ? 'high' : medRisk ? 'medium' : 'low';
|
|
77
|
+
|
|
78
|
+
// Impact descriptions (skip zero-value limits)
|
|
79
|
+
const impact: string[] = [];
|
|
80
|
+
if (metadata.limit && metadata.limit > 0) impact.push(`Fund limit: ${metadata.limit} ETH`);
|
|
81
|
+
if (metadata.limits?.send && metadata.limits.send > 0) impact.push(`Send limit: ${metadata.limits.send} ETH`);
|
|
82
|
+
if (metadata.limits?.swap && metadata.limits.swap > 0) impact.push(`Swap limit: ${metadata.limits.swap} ETH`);
|
|
83
|
+
if (metadata.ttl) impact.push(`Token TTL: ${Math.round(metadata.ttl / 60)} minutes`);
|
|
84
|
+
if (impact.length > 0) summary.impact = impact;
|
|
85
|
+
|
|
86
|
+
// Never expose: secretHash, pubkey, token data
|
|
87
|
+
res.json({ success: true, ...summary });
|
|
88
|
+
} catch (error) {
|
|
89
|
+
const message = getErrorMessage(error);
|
|
90
|
+
res.status(500).json({ success: false, error: message });
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// POST /actions — Create a human action request (app proposes an action for approval)
|
|
95
|
+
// Requires Bearer token with action:create permission
|
|
96
|
+
router.post('/', requireWalletAuth, requirePermission('action:create'), async (req: Request, res: Response) => {
|
|
97
|
+
try {
|
|
98
|
+
const { summary, permissions, limits, walletAccess, ttl, type, metadata, notify, pubkey, credentialAccess } = req.body;
|
|
99
|
+
|
|
100
|
+
// Validate summary (required for all types)
|
|
101
|
+
if (!summary || typeof summary !== 'string' || summary.trim().length === 0) {
|
|
102
|
+
res.status(400).json({ success: false, error: 'summary is required and must be a non-empty string' });
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const MAX_SUMMARY_LENGTH = 500;
|
|
107
|
+
if (summary.length > MAX_SUMMARY_LENGTH) {
|
|
108
|
+
res.status(400).json({ success: false, error: `summary must be ${MAX_SUMMARY_LENGTH} characters or fewer` });
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const callerAgentId = req.auth!.token.agentId;
|
|
113
|
+
|
|
114
|
+
// === Notification-only branch: no permissions/limits needed ===
|
|
115
|
+
if (type === 'notify') {
|
|
116
|
+
const request = await prisma.humanAction.create({
|
|
117
|
+
data: {
|
|
118
|
+
type: 'notify',
|
|
119
|
+
fromTier: 'system',
|
|
120
|
+
toAddress: null,
|
|
121
|
+
amount: null,
|
|
122
|
+
chain: 'base',
|
|
123
|
+
status: 'acknowledged',
|
|
124
|
+
resolvedAt: new Date(),
|
|
125
|
+
metadata: JSON.stringify({ agentId: callerAgentId, summary, ...(metadata || {}) }),
|
|
126
|
+
},
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// Info notification (dismiss only, no approve/reject)
|
|
130
|
+
await createNotification({
|
|
131
|
+
type: 'info',
|
|
132
|
+
category: 'general',
|
|
133
|
+
title: 'Notification',
|
|
134
|
+
message: summary,
|
|
135
|
+
actions: [{ id: 'dismiss', label: 'DISMISS', type: 'secondary', action: 'dismiss' }],
|
|
136
|
+
metadata: { ...(metadata || {}), agentId: callerAgentId },
|
|
137
|
+
source: 'agent',
|
|
138
|
+
agentId: callerAgentId,
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// Emit to WebSocket for dashboard; adapters check type themselves
|
|
142
|
+
if (notify !== false) {
|
|
143
|
+
events.actionCreated({
|
|
144
|
+
id: request.id,
|
|
145
|
+
type: 'notify',
|
|
146
|
+
source: `agent:${callerAgentId}`,
|
|
147
|
+
summary,
|
|
148
|
+
expiresAt: null,
|
|
149
|
+
metadata: { agentId: callerAgentId, ...(metadata || {}) },
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
logger.actionCreated(callerAgentId, request.id, 'notify', summary);
|
|
154
|
+
|
|
155
|
+
res.json({ success: true, id: request.id });
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// === Standard approval flow: permissions required ===
|
|
160
|
+
|
|
161
|
+
// Validate permissions
|
|
162
|
+
if (!permissions || !Array.isArray(permissions) || permissions.length === 0) {
|
|
163
|
+
res.status(400).json({ success: false, error: 'permissions must be a non-empty array' });
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Block privilege escalation — cannot request admin:* or action:create
|
|
168
|
+
const blocked = permissions.filter((p: string) => p === 'admin:*' || p === 'action:create');
|
|
169
|
+
if (blocked.length > 0) {
|
|
170
|
+
res.status(400).json({ success: false, error: `Cannot request privileged permissions: ${blocked.join(', ')}` });
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
const requestedCredentialAccess = credentialAccess && typeof credentialAccess === 'object' && !Array.isArray(credentialAccess)
|
|
175
|
+
? credentialAccess as AgentTokenPayload['credentialAccess']
|
|
176
|
+
: undefined;
|
|
177
|
+
if (typeof pubkey !== 'string' || !pubkey.trim()) {
|
|
178
|
+
res.status(400).json({ success: false, error: 'pubkey is required' });
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
if (!isValidAgentPubkey(pubkey)) {
|
|
182
|
+
res.status(400).json({ success: false, error: 'pubkey must be a valid RSA public key (PEM or base64)' });
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
const normalizedPubkey = normalizeAgentPubkey(pubkey);
|
|
186
|
+
|
|
187
|
+
const defaultActionTtl = await getDefault<number>('ttl.action', 60);
|
|
188
|
+
const actionTtl = typeof ttl === 'number' && ttl > 0 ? ttl : defaultActionTtl;
|
|
189
|
+
|
|
190
|
+
// Generate secret for polling (same pattern as POST /auth)
|
|
191
|
+
const secret = randomBytes(32).toString('hex');
|
|
192
|
+
const secretHash = hashSecret(secret);
|
|
193
|
+
|
|
194
|
+
// Preserve pre-computed action from metadata if provided (for auto-execute on approval)
|
|
195
|
+
const precomputedAction = metadata?.action || undefined;
|
|
196
|
+
|
|
197
|
+
// Generate server-verified summary from actual action parameters
|
|
198
|
+
const verifiedSummary = generateVerifiedSummary({
|
|
199
|
+
agentId: callerAgentId,
|
|
200
|
+
summary,
|
|
201
|
+
permissions,
|
|
202
|
+
limits: limits || undefined,
|
|
203
|
+
walletAccess: walletAccess || undefined,
|
|
204
|
+
ttl: actionTtl,
|
|
205
|
+
action: precomputedAction,
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
const request = await prisma.humanAction.create({
|
|
209
|
+
data: {
|
|
210
|
+
type: 'action',
|
|
211
|
+
fromTier: 'system',
|
|
212
|
+
toAddress: null,
|
|
213
|
+
amount: null,
|
|
214
|
+
chain: 'base',
|
|
215
|
+
status: 'pending',
|
|
216
|
+
metadata: JSON.stringify({
|
|
217
|
+
agentId: callerAgentId,
|
|
218
|
+
permissions,
|
|
219
|
+
limits: limits || undefined,
|
|
220
|
+
walletAccess: walletAccess || undefined,
|
|
221
|
+
credentialAccess: requestedCredentialAccess,
|
|
222
|
+
pubkey: normalizedPubkey,
|
|
223
|
+
ttl: actionTtl,
|
|
224
|
+
secretHash,
|
|
225
|
+
summary,
|
|
226
|
+
action: precomputedAction,
|
|
227
|
+
verifiedSummary,
|
|
228
|
+
}),
|
|
229
|
+
},
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
// Create notification for human approval
|
|
233
|
+
await createHumanActionNotification(request);
|
|
234
|
+
|
|
235
|
+
// Emit WebSocket event
|
|
236
|
+
events.actionCreated({
|
|
237
|
+
id: request.id,
|
|
238
|
+
type: 'action',
|
|
239
|
+
source: `agent:${callerAgentId}`,
|
|
240
|
+
summary,
|
|
241
|
+
expiresAt: null,
|
|
242
|
+
metadata: { agentId: callerAgentId, permissions, limits, summary, verifiedSummary },
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
logger.actionCreated(callerAgentId, request.id, 'action', summary);
|
|
246
|
+
|
|
247
|
+
const dashboardBase = `http://localhost:${process.env.DASHBOARD_PORT || '4747'}`;
|
|
248
|
+
res.json({
|
|
249
|
+
success: true,
|
|
250
|
+
requestId: request.id,
|
|
251
|
+
secret,
|
|
252
|
+
approveUrl: buildApproveUrl(dashboardBase, request.id),
|
|
253
|
+
message: 'Action escalated — waiting for human approval',
|
|
254
|
+
});
|
|
255
|
+
} catch (error) {
|
|
256
|
+
const message = getErrorMessage(error);
|
|
257
|
+
res.status(500).json({ success: false, error: message });
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
// POST /actions/:id/resolve — Approve or reject a human action
|
|
262
|
+
router.post('/:id/resolve', requireWalletAuth, requirePermission('action:resolve'), async (req: Request<{ id: string }>, res: Response) => {
|
|
263
|
+
try {
|
|
264
|
+
const { approved, walletAccess, limits } = req.body;
|
|
265
|
+
const result = await resolveAction(req.params.id, approved, { walletAccess, limits });
|
|
266
|
+
res.status(result.statusCode).json(result.data);
|
|
267
|
+
} catch (error) {
|
|
268
|
+
const message = getErrorMessage(error);
|
|
269
|
+
res.status(500).json({ success: false, error: message });
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
// POST /actions/token/preview - Preview effective token policy without issuing token (requires admin)
|
|
274
|
+
router.post('/token/preview', requireWalletAuth, requireAdmin, async (req: Request, res: Response) => {
|
|
275
|
+
try {
|
|
276
|
+
const { profile, profileVersion, profileOverrides } = req.body;
|
|
277
|
+
|
|
278
|
+
if (typeof profile !== 'string' || profile.trim().length === 0) {
|
|
279
|
+
res.status(422).json({ version: 'v1', code: 'ERR_OVERRIDE_INVALID', error: 'profile is required' });
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
const previewInput = {
|
|
284
|
+
profileId: profile,
|
|
285
|
+
profileVersion: typeof profileVersion === 'string' ? profileVersion : undefined,
|
|
286
|
+
overrides: profileOverrides,
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
const resolved = resolveProfileToEffectivePolicy(previewInput);
|
|
290
|
+
const preview = buildPolicyPreviewV1(previewInput, resolved);
|
|
291
|
+
|
|
292
|
+
res.json(preview);
|
|
293
|
+
} catch (error) {
|
|
294
|
+
if (error instanceof AgentProfileError) {
|
|
295
|
+
const mapped = mapPreviewError(error.code);
|
|
296
|
+
res.status(mapped.status).json(mapped.error);
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
const mapped = mapPreviewError('ERR_RESOLUTION_FAILED');
|
|
300
|
+
res.status(mapped.status).json(mapped.error);
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
// POST /actions/token - Create signed token for agent (requires admin)
|
|
305
|
+
router.post('/token', requireWalletAuth, requireAdmin, async (req: Request, res: Response) => {
|
|
306
|
+
try {
|
|
307
|
+
const {
|
|
308
|
+
agentId,
|
|
309
|
+
limit,
|
|
310
|
+
permissions,
|
|
311
|
+
ttl,
|
|
312
|
+
limits, // Per-permission limits
|
|
313
|
+
walletAccess, // Wallet access grants
|
|
314
|
+
credentialAccess,
|
|
315
|
+
pubkey,
|
|
316
|
+
profile,
|
|
317
|
+
profileVersion,
|
|
318
|
+
profileOverrides,
|
|
319
|
+
} = req.body;
|
|
320
|
+
|
|
321
|
+
if (!agentId || typeof agentId !== 'string') {
|
|
322
|
+
res.status(400).json({ error: 'agentId is required' });
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Legacy limit or new limits.fund
|
|
327
|
+
const fundLimit = typeof limit === 'number' ? limit : (limits?.fund ?? 0);
|
|
328
|
+
if (fundLimit < 0) {
|
|
329
|
+
res.status(400).json({ error: 'limit must be a non-negative number (in ETH)' });
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// Wallet must be unlocked
|
|
334
|
+
if (!isUnlocked()) {
|
|
335
|
+
res.status(401).json({ error: 'Wallet is locked. Unlock first.' });
|
|
336
|
+
return;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
const defaultSendLimit = await getDefault<number>('limits.send', 0.1);
|
|
340
|
+
const defaultSwapLimit = await getDefault<number>('limits.swap', 0.1);
|
|
341
|
+
const defaultTtl = await getDefault<number>('ttl.agent', 3600);
|
|
342
|
+
|
|
343
|
+
const hasProfile = typeof profile === 'string' && profile.trim().length > 0;
|
|
344
|
+
const hasPermissions = permissions !== undefined;
|
|
345
|
+
if (hasProfile === hasPermissions) {
|
|
346
|
+
res.status(400).json({
|
|
347
|
+
error: 'Provide exactly one issuance mode: profile OR permissions.',
|
|
348
|
+
code: 'ISSUANCE_XOR_REQUIRED',
|
|
349
|
+
});
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
if (!hasProfile && (profileVersion !== undefined || profileOverrides !== undefined)) {
|
|
354
|
+
res.status(400).json({
|
|
355
|
+
error: 'profileVersion/profileOverrides require profile.',
|
|
356
|
+
code: 'PROFILE_FIELDS_WITHOUT_PROFILE',
|
|
357
|
+
});
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
if (hasPermissions && (!Array.isArray(permissions) || permissions.length === 0)) {
|
|
362
|
+
res.status(400).json({
|
|
363
|
+
error: 'permissions must be a non-empty array when using permissions mode.',
|
|
364
|
+
code: 'PERMISSIONS_REQUIRED',
|
|
365
|
+
});
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
const resolvedProfile = hasProfile
|
|
370
|
+
? resolveProfileToEffectivePolicy({
|
|
371
|
+
profileId: profile.trim(),
|
|
372
|
+
profileVersion: typeof profileVersion === 'string' ? profileVersion : undefined,
|
|
373
|
+
overrides: profileOverrides,
|
|
374
|
+
})
|
|
375
|
+
: null;
|
|
376
|
+
|
|
377
|
+
const validPermissions = resolvedProfile
|
|
378
|
+
? [...resolvedProfile.permissions]
|
|
379
|
+
: [...permissions];
|
|
380
|
+
|
|
381
|
+
if (typeof pubkey !== 'string' || !pubkey.trim()) {
|
|
382
|
+
res.status(400).json({ error: 'pubkey is required' });
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
if (!isValidAgentPubkey(pubkey)) {
|
|
386
|
+
res.status(400).json({ error: 'pubkey must be a valid RSA public key (PEM or base64)' });
|
|
387
|
+
return;
|
|
388
|
+
}
|
|
389
|
+
const normalizedPubkey = normalizeAgentPubkey(pubkey);
|
|
390
|
+
|
|
391
|
+
const ttlSeconds = resolvedProfile
|
|
392
|
+
? resolvedProfile.ttlSeconds
|
|
393
|
+
: (typeof ttl === 'number' ? ttl : defaultTtl);
|
|
394
|
+
|
|
395
|
+
// Normalize wallet access addresses
|
|
396
|
+
const normalizedWalletAccess = walletAccess && Array.isArray(walletAccess)
|
|
397
|
+
? walletAccess.map((addr: string) => normalizeAddress(addr))
|
|
398
|
+
: undefined;
|
|
399
|
+
|
|
400
|
+
// Build limits: per-token overrides > system defaults
|
|
401
|
+
const baseLimits = { fund: fundLimit, send: defaultSendLimit, swap: defaultSwapLimit };
|
|
402
|
+
const tokenLimits = limits ? { ...baseLimits, ...limits } : baseLimits;
|
|
403
|
+
|
|
404
|
+
const effectiveCredentialAccess = resolvedProfile
|
|
405
|
+
? resolvedProfile.credentialAccess
|
|
406
|
+
: credentialAccess;
|
|
407
|
+
|
|
408
|
+
const token = await createToken(agentId, fundLimit, validPermissions, ttlSeconds, {
|
|
409
|
+
limits: tokenLimits,
|
|
410
|
+
walletAccess: normalizedWalletAccess,
|
|
411
|
+
credentialAccess: effectiveCredentialAccess,
|
|
412
|
+
agentPubkey: normalizedPubkey,
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
const tokenHash = getTokenHash(token);
|
|
416
|
+
|
|
417
|
+
// Emit WebSocket event for direct token creation
|
|
418
|
+
events.tokenCreated({
|
|
419
|
+
tokenHash,
|
|
420
|
+
agentId,
|
|
421
|
+
limit: fundLimit,
|
|
422
|
+
permissions: validPermissions,
|
|
423
|
+
expiresAt: Date.now() + ttlSeconds * 1000,
|
|
424
|
+
});
|
|
425
|
+
|
|
426
|
+
if (resolvedProfile) {
|
|
427
|
+
events.custom('agent_profile:issued', {
|
|
428
|
+
eventSchemaVersion: 1,
|
|
429
|
+
eventType: 'agent_profile.issued',
|
|
430
|
+
profile: resolvedProfile.profile,
|
|
431
|
+
effectivePolicyHash: resolvedProfile.effectivePolicyHash,
|
|
432
|
+
overrideDelta: resolvedProfile.overrideDelta,
|
|
433
|
+
actor: 'admin',
|
|
434
|
+
agentId,
|
|
435
|
+
tokenHash,
|
|
436
|
+
timestamp: Date.now(),
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// Encrypt token to agent pubkey if provided (prevents model provider from seeing it)
|
|
441
|
+
const responseToken = normalizedPubkey
|
|
442
|
+
? { encryptedToken: encryptToAgentPubkey(token, normalizedPubkey) }
|
|
443
|
+
: { token };
|
|
444
|
+
|
|
445
|
+
res.json({
|
|
446
|
+
success: true,
|
|
447
|
+
...responseToken,
|
|
448
|
+
agentId,
|
|
449
|
+
limit: fundLimit,
|
|
450
|
+
limits: tokenLimits,
|
|
451
|
+
permissions: validPermissions,
|
|
452
|
+
walletAccess: normalizedWalletAccess,
|
|
453
|
+
credentialAccess: effectiveCredentialAccess,
|
|
454
|
+
profile: resolvedProfile ? resolvedProfile.profile : undefined,
|
|
455
|
+
effectivePolicyHash: resolvedProfile ? resolvedProfile.effectivePolicyHash : undefined,
|
|
456
|
+
overrideDelta: resolvedProfile ? resolvedProfile.overrideDelta : undefined,
|
|
457
|
+
warnings: resolvedProfile ? resolvedProfile.warnings : undefined,
|
|
458
|
+
hasPubkey: !!normalizedPubkey,
|
|
459
|
+
expiresIn: ttlSeconds
|
|
460
|
+
});
|
|
461
|
+
} catch (error) {
|
|
462
|
+
if (error instanceof AgentProfileError) {
|
|
463
|
+
res.status(400).json({ error: error.message, code: error.code });
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
const message = getErrorMessage(error);
|
|
467
|
+
res.status(400).json({ error: message });
|
|
468
|
+
}
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
// GET /actions/tokens - List all agent tokens (requires admin)
|
|
472
|
+
router.get('/tokens', requireWalletAuth, requireAdmin, async (_req: Request, res: Response) => {
|
|
473
|
+
try {
|
|
474
|
+
const tokens = await listTokensFromDb();
|
|
475
|
+
|
|
476
|
+
const active = tokens.filter(t => t.isActive && !t.isExpired && !t.isRevoked && t.remaining > 0);
|
|
477
|
+
const inactive = tokens.filter(t => !t.isActive && !t.isExpired && !t.isRevoked);
|
|
478
|
+
const expired = tokens.filter(t => t.isExpired);
|
|
479
|
+
const revoked = tokens.filter(t => t.isRevoked);
|
|
480
|
+
const depleted = tokens.filter(t => !t.isExpired && !t.isRevoked && t.remaining <= 0);
|
|
481
|
+
|
|
482
|
+
res.json({
|
|
483
|
+
success: true,
|
|
484
|
+
tokens: {
|
|
485
|
+
active,
|
|
486
|
+
inactive,
|
|
487
|
+
expired,
|
|
488
|
+
revoked,
|
|
489
|
+
depleted
|
|
490
|
+
},
|
|
491
|
+
total: tokens.length
|
|
492
|
+
});
|
|
493
|
+
} catch (error) {
|
|
494
|
+
const message = getErrorMessage(error);
|
|
495
|
+
res.status(400).json({ error: message });
|
|
496
|
+
}
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
// POST /actions/tokens/revoke - Revoke a token (admin or agent with own token)
|
|
500
|
+
router.post('/tokens/revoke', requireWalletAuth, async (req: Request, res: Response) => {
|
|
501
|
+
try {
|
|
502
|
+
const { tokenHash } = req.body;
|
|
503
|
+
const auth = req.auth!;
|
|
504
|
+
|
|
505
|
+
// Agent can only revoke their own token
|
|
506
|
+
if (!isAdmin(auth)) {
|
|
507
|
+
if (tokenHash && tokenHash !== auth.tokenHash) {
|
|
508
|
+
res.status(403).json({ error: 'Agents can only revoke their own token' });
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
const success = await revokeToken(auth.tokenHash);
|
|
512
|
+
if (success) {
|
|
513
|
+
logger.tokenRevoked(auth.tokenHash, auth.token.agentId);
|
|
514
|
+
}
|
|
515
|
+
res.json({ success, message: success ? 'Token revoked' : 'Token not found' });
|
|
516
|
+
return;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
// Admin revoking any token
|
|
520
|
+
if (!tokenHash || typeof tokenHash !== 'string') {
|
|
521
|
+
res.status(400).json({ error: 'tokenHash is required' });
|
|
522
|
+
return;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
const success = await revokeToken(tokenHash);
|
|
526
|
+
|
|
527
|
+
if (success) {
|
|
528
|
+
events.tokenRevoked({ tokenHash });
|
|
529
|
+
logger.tokenRevoked(tokenHash, 'admin');
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
res.json({ success, message: success ? 'Token revoked' : 'Token not found' });
|
|
533
|
+
} catch (error) {
|
|
534
|
+
const message = getErrorMessage(error);
|
|
535
|
+
res.status(400).json({ error: message });
|
|
536
|
+
}
|
|
537
|
+
});
|
|
538
|
+
|
|
539
|
+
export default router;
|