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
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { ComponentType, lazy } from 'react';
|
|
2
|
+
import type { AppColor } from '@/components/apps/DraggableApp';
|
|
3
|
+
import type { LucideIcon } from 'lucide-react';
|
|
4
|
+
import {
|
|
5
|
+
Wallet,
|
|
6
|
+
Key,
|
|
7
|
+
ScrollText,
|
|
8
|
+
Send,
|
|
9
|
+
Globe,
|
|
10
|
+
Box,
|
|
11
|
+
Coins,
|
|
12
|
+
Sparkles,
|
|
13
|
+
ArrowUpDown,
|
|
14
|
+
} from 'lucide-react';
|
|
15
|
+
|
|
16
|
+
export interface AppDefinition {
|
|
17
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
18
|
+
component: ComponentType<any>; // Allow any props since different apps have different needs
|
|
19
|
+
icon: LucideIcon;
|
|
20
|
+
color: AppColor;
|
|
21
|
+
defaultSize: { width: number; height: number };
|
|
22
|
+
title: string;
|
|
23
|
+
resizable?: boolean;
|
|
24
|
+
/** Set to true if this app needs special props from page.tsx instead of just config */
|
|
25
|
+
requiresSpecialProps?: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* Singleton apps can only have one instance per workspace.
|
|
28
|
+
* Multi-instance apps (singleton: false) can be opened multiple times with different IDs.
|
|
29
|
+
* Default: true for built-in apps, false for iframe/installed
|
|
30
|
+
*/
|
|
31
|
+
singleton?: boolean;
|
|
32
|
+
/** Short description for the app store */
|
|
33
|
+
description?: string;
|
|
34
|
+
/** Category for filtering in the app store */
|
|
35
|
+
category?: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Cache for dynamically-created app definitions (installed:)
|
|
39
|
+
// Without this, each call to getAppDefinition creates a new lazy() component,
|
|
40
|
+
// causing React to remount the app on every render (e.g., during drag).
|
|
41
|
+
const _definitionCache = new Map<string, AppDefinition>();
|
|
42
|
+
|
|
43
|
+
// Built-in app types
|
|
44
|
+
export const APP_TYPES: Record<string, AppDefinition> = {
|
|
45
|
+
logs: {
|
|
46
|
+
component: lazy(() => import('@/components/apps/LogsApp')),
|
|
47
|
+
icon: ScrollText,
|
|
48
|
+
color: 'gray',
|
|
49
|
+
defaultSize: { width: 600, height: 300 },
|
|
50
|
+
title: 'EVENT LOGS',
|
|
51
|
+
singleton: true,
|
|
52
|
+
},
|
|
53
|
+
send: {
|
|
54
|
+
component: lazy(() => import('@/components/apps/SendApp')),
|
|
55
|
+
icon: Send,
|
|
56
|
+
color: 'teal',
|
|
57
|
+
defaultSize: { width: 320, height: 280 },
|
|
58
|
+
title: 'SEND',
|
|
59
|
+
singleton: true,
|
|
60
|
+
},
|
|
61
|
+
agentKeys: {
|
|
62
|
+
component: lazy(() => import('@/components/apps/AgentKeysApp')),
|
|
63
|
+
icon: Key,
|
|
64
|
+
color: 'orange',
|
|
65
|
+
defaultSize: { width: 340, height: 400 },
|
|
66
|
+
title: 'AGENT KEYS',
|
|
67
|
+
singleton: true,
|
|
68
|
+
},
|
|
69
|
+
token: {
|
|
70
|
+
component: lazy(() => import('@/components/apps/TokenApp')),
|
|
71
|
+
icon: Coins,
|
|
72
|
+
color: 'lime',
|
|
73
|
+
defaultSize: { width: 380, height: 480 },
|
|
74
|
+
title: 'TOKEN',
|
|
75
|
+
singleton: false,
|
|
76
|
+
resizable: true,
|
|
77
|
+
description: 'View token market data and price chart',
|
|
78
|
+
category: 'info',
|
|
79
|
+
},
|
|
80
|
+
setup: {
|
|
81
|
+
component: lazy(() => import('@/components/apps/SetupWizardApp')),
|
|
82
|
+
icon: Sparkles,
|
|
83
|
+
color: 'blue',
|
|
84
|
+
defaultSize: { width: 420, height: 520 },
|
|
85
|
+
title: 'GETTING STARTED',
|
|
86
|
+
singleton: true,
|
|
87
|
+
},
|
|
88
|
+
transactions: {
|
|
89
|
+
component: lazy(() => import('@/components/apps/TransactionsApp')),
|
|
90
|
+
icon: ArrowUpDown,
|
|
91
|
+
color: 'teal',
|
|
92
|
+
defaultSize: { width: 520, height: 400 },
|
|
93
|
+
title: 'TRANSACTIONS',
|
|
94
|
+
singleton: true,
|
|
95
|
+
resizable: true,
|
|
96
|
+
},
|
|
97
|
+
// Multi-instance app types (can open multiple with different IDs)
|
|
98
|
+
walletDetail: {
|
|
99
|
+
component: lazy(() => import('@/components/apps/WalletDetailApp')),
|
|
100
|
+
icon: Wallet,
|
|
101
|
+
color: 'orange',
|
|
102
|
+
defaultSize: { width: 320, height: 380 },
|
|
103
|
+
title: 'WALLET',
|
|
104
|
+
resizable: true,
|
|
105
|
+
requiresSpecialProps: true,
|
|
106
|
+
singleton: false, // Can have multiple wallet detail apps open
|
|
107
|
+
},
|
|
108
|
+
iframe: {
|
|
109
|
+
component: lazy(() => import('@/components/apps/IFrameApp')),
|
|
110
|
+
icon: Globe,
|
|
111
|
+
color: 'blue',
|
|
112
|
+
defaultSize: { width: 400, height: 300 },
|
|
113
|
+
title: 'IFRAME',
|
|
114
|
+
resizable: true,
|
|
115
|
+
singleton: false,
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Get app definition by type
|
|
121
|
+
* Supports:
|
|
122
|
+
* - Built-in types: 'wallets', 'logs', 'send', etc.
|
|
123
|
+
* - Installed (third-party) types: 'installed:app-id'
|
|
124
|
+
*/
|
|
125
|
+
export function getAppDefinition(appType: string): AppDefinition | null {
|
|
126
|
+
// Check if it's a built-in type
|
|
127
|
+
if (appType in APP_TYPES) {
|
|
128
|
+
return APP_TYPES[appType];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Check cache for installed: types
|
|
132
|
+
if (_definitionCache.has(appType)) {
|
|
133
|
+
return _definitionCache.get(appType)!;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Check if it's an installed (third-party) app
|
|
137
|
+
if (appType.startsWith('installed:')) {
|
|
138
|
+
const appId = appType.slice(10); // Remove 'installed:' prefix
|
|
139
|
+
const def: AppDefinition = {
|
|
140
|
+
component: lazy(() => import('@/components/apps/ThirdPartyApp')),
|
|
141
|
+
icon: Box,
|
|
142
|
+
color: 'gray',
|
|
143
|
+
defaultSize: { width: 320, height: 280 },
|
|
144
|
+
title: appId.toUpperCase(),
|
|
145
|
+
resizable: true,
|
|
146
|
+
singleton: false,
|
|
147
|
+
requiresSpecialProps: true,
|
|
148
|
+
category: 'installed',
|
|
149
|
+
};
|
|
150
|
+
_definitionCache.set(appType, def);
|
|
151
|
+
return def;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Check if a app type is singleton (only one instance per workspace)
|
|
159
|
+
*/
|
|
160
|
+
export function isSingletonApp(appType: string): boolean {
|
|
161
|
+
const def = getAppDefinition(appType);
|
|
162
|
+
// Default to true for unknown types (safer)
|
|
163
|
+
return def?.singleton ?? true;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Get all registered app types
|
|
168
|
+
*/
|
|
169
|
+
export function getRegisteredAppTypes(): string[] {
|
|
170
|
+
return Object.keys(APP_TYPES);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Check if a app type exists
|
|
175
|
+
*/
|
|
176
|
+
export function isValidAppType(appType: string): boolean {
|
|
177
|
+
return appType in APP_TYPES || appType.startsWith('installed:');
|
|
178
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* App SDK source code — injected into third-party app iframes.
|
|
3
|
+
* Provides the AuraApp API for communication with the host bridge.
|
|
4
|
+
*
|
|
5
|
+
* API:
|
|
6
|
+
* app.send(message) → Natural language message to agent (postMessage)
|
|
7
|
+
* app.fetch(url, options) → Proxy external HTTP request via server (direct fetch)
|
|
8
|
+
* app.on(channel, callback) → Subscribe to real-time data channels (postMessage)
|
|
9
|
+
* app.storage.get(key) → Read from persistent storage (direct fetch)
|
|
10
|
+
* app.storage.set(key, value) → Write to persistent storage (direct fetch)
|
|
11
|
+
* app.storage.delete(key) → Delete from persistent storage (direct fetch)
|
|
12
|
+
*
|
|
13
|
+
* Globals injected by host before this script runs:
|
|
14
|
+
* window.__AURA_TOKEN__ — Bearer token for authenticated API calls
|
|
15
|
+
* window.__AURA_API_BASE__ — Base URL for wallet API (e.g. http://127.0.0.1:4242)
|
|
16
|
+
* window.__AURA_APP_ID__ — App folder name (used for scoped storage)
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
export const APP_SDK_SOURCE = `
|
|
20
|
+
(function() {
|
|
21
|
+
var _callbacks = {};
|
|
22
|
+
var _subscriptions = {};
|
|
23
|
+
var _requestId = 0;
|
|
24
|
+
|
|
25
|
+
var TOKEN = window.__AURA_TOKEN__ || '';
|
|
26
|
+
var API_BASE = window.__AURA_API_BASE__ || 'http://127.0.0.1:4242';
|
|
27
|
+
var APP_ID = window.__AURA_APP_ID__ || '';
|
|
28
|
+
|
|
29
|
+
function generateId() {
|
|
30
|
+
return 'req_' + (++_requestId) + '_' + Date.now();
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function postToHost(msg) {
|
|
34
|
+
window.parent.postMessage(msg, '*');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function request(type, payload) {
|
|
38
|
+
return new Promise(function(resolve, reject) {
|
|
39
|
+
var id = generateId();
|
|
40
|
+
_callbacks[id] = { resolve: resolve, reject: reject };
|
|
41
|
+
postToHost({ type: type, id: id, payload: payload });
|
|
42
|
+
// Timeout after 30s
|
|
43
|
+
setTimeout(function() {
|
|
44
|
+
if (_callbacks[id]) {
|
|
45
|
+
delete _callbacks[id];
|
|
46
|
+
reject(new Error('Request timed out'));
|
|
47
|
+
}
|
|
48
|
+
}, 30000);
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Direct fetch to wallet API with Bearer token */
|
|
53
|
+
function apiFetch(method, path, body) {
|
|
54
|
+
var opts = {
|
|
55
|
+
method: method,
|
|
56
|
+
headers: { 'Content-Type': 'application/json' }
|
|
57
|
+
};
|
|
58
|
+
if (TOKEN) {
|
|
59
|
+
opts.headers['Authorization'] = 'Bearer ' + TOKEN;
|
|
60
|
+
}
|
|
61
|
+
if (body !== undefined) {
|
|
62
|
+
opts.body = JSON.stringify(body);
|
|
63
|
+
}
|
|
64
|
+
return fetch(API_BASE + path, opts).then(function(res) {
|
|
65
|
+
return res.json().then(function(data) {
|
|
66
|
+
if (!res.ok) throw new Error(data.error || 'API error ' + res.status);
|
|
67
|
+
return data;
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Listen for messages from host bridge
|
|
73
|
+
window.addEventListener('message', function(event) {
|
|
74
|
+
var msg = event.data;
|
|
75
|
+
if (!msg || !msg.type) return;
|
|
76
|
+
|
|
77
|
+
// Response to a request
|
|
78
|
+
if (msg.type === 'app:response' && msg.id && _callbacks[msg.id]) {
|
|
79
|
+
var cb = _callbacks[msg.id];
|
|
80
|
+
delete _callbacks[msg.id];
|
|
81
|
+
if (msg.error) {
|
|
82
|
+
cb.reject(new Error(msg.error));
|
|
83
|
+
} else {
|
|
84
|
+
cb.resolve(msg.data);
|
|
85
|
+
}
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Data push from subscribed channel
|
|
90
|
+
if (msg.type === 'app:data' && msg.channel && _subscriptions[msg.channel]) {
|
|
91
|
+
_subscriptions[msg.channel].forEach(function(fn) {
|
|
92
|
+
try { fn(msg.data); } catch(e) { console.error('Subscription callback error:', e); }
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
window.AuraApp = {
|
|
98
|
+
send: function(message) {
|
|
99
|
+
return apiFetch('POST', '/apps/' + APP_ID + '/message', { message: message })
|
|
100
|
+
.then(function(data) { return data.reply; });
|
|
101
|
+
},
|
|
102
|
+
|
|
103
|
+
on: function(channel, callback) {
|
|
104
|
+
if (!_subscriptions[channel]) {
|
|
105
|
+
_subscriptions[channel] = [];
|
|
106
|
+
postToHost({ type: 'app:subscribe', channel: channel });
|
|
107
|
+
}
|
|
108
|
+
_subscriptions[channel].push(callback);
|
|
109
|
+
// Return unsubscribe function
|
|
110
|
+
return function() {
|
|
111
|
+
var subs = _subscriptions[channel];
|
|
112
|
+
if (subs) {
|
|
113
|
+
var idx = subs.indexOf(callback);
|
|
114
|
+
if (idx !== -1) subs.splice(idx, 1);
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
},
|
|
118
|
+
|
|
119
|
+
fetch: function(url, options) {
|
|
120
|
+
return apiFetch('POST', '/apps/' + APP_ID + '/fetch', {
|
|
121
|
+
url: url,
|
|
122
|
+
method: (options && options.method) || 'GET',
|
|
123
|
+
headers: options && options.headers,
|
|
124
|
+
body: options && options.body
|
|
125
|
+
}).then(function(res) { return res.data; });
|
|
126
|
+
},
|
|
127
|
+
|
|
128
|
+
action: function(params) {
|
|
129
|
+
return apiFetch('POST', '/actions', params);
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
storage: {
|
|
133
|
+
get: function(key) {
|
|
134
|
+
return apiFetch('GET', '/apps/' + APP_ID + '/storage/' + encodeURIComponent(key))
|
|
135
|
+
.then(function(data) { return data.value; })
|
|
136
|
+
.catch(function(err) {
|
|
137
|
+
// 404 = key not found, return null
|
|
138
|
+
if (err.message && err.message.indexOf('not found') !== -1) return null;
|
|
139
|
+
throw err;
|
|
140
|
+
});
|
|
141
|
+
},
|
|
142
|
+
set: function(key, value) {
|
|
143
|
+
return apiFetch('PUT', '/apps/' + APP_ID + '/storage/' + encodeURIComponent(key), { value: value })
|
|
144
|
+
.then(function(data) { return data.value; });
|
|
145
|
+
},
|
|
146
|
+
delete: function(key) {
|
|
147
|
+
return apiFetch('DELETE', '/apps/' + APP_ID + '/storage/' + encodeURIComponent(key))
|
|
148
|
+
.then(function() { return true; })
|
|
149
|
+
.catch(function(err) {
|
|
150
|
+
if (err.message && err.message.indexOf('not found') !== -1) return false;
|
|
151
|
+
throw err;
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
})();
|
|
157
|
+
`;
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
export type UiDecision = 'ALLOW' | 'DENY' | 'RATE_LIMIT' | 'ERROR' | 'UNKNOWN';
|
|
2
|
+
export type UiReasonCode =
|
|
3
|
+
| 'SCOPE_DENY'
|
|
4
|
+
| 'TOKEN_REVOKED'
|
|
5
|
+
| 'TOKEN_EXPIRED'
|
|
6
|
+
| 'RATE_LIMITED'
|
|
7
|
+
| 'MISSING_KEY'
|
|
8
|
+
| 'NOT_FOUND'
|
|
9
|
+
| 'INTERNAL_ERROR'
|
|
10
|
+
| 'UNKNOWN';
|
|
11
|
+
|
|
12
|
+
export type AttributionConfidence = 'HIGH' | 'MEDIUM' | 'LOW';
|
|
13
|
+
|
|
14
|
+
export interface UiAuditEvent {
|
|
15
|
+
id: string;
|
|
16
|
+
timestamp: number;
|
|
17
|
+
tokenKey: string;
|
|
18
|
+
tokenHash?: string;
|
|
19
|
+
agentId?: string;
|
|
20
|
+
endpoint?: string;
|
|
21
|
+
credentialKey?: string;
|
|
22
|
+
sourceVersion: 'legacy-logs-v1' | 'task40-audit-v1';
|
|
23
|
+
decision: UiDecision;
|
|
24
|
+
reasonCode: UiReasonCode;
|
|
25
|
+
rawDecision?: string;
|
|
26
|
+
rawReasonCode?: string;
|
|
27
|
+
confidence: AttributionConfidence;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const REASON_MAP: Record<string, UiReasonCode> = {
|
|
31
|
+
CREDENTIAL_SCOPE_DENIED: 'SCOPE_DENY',
|
|
32
|
+
TOKEN_PERMISSION_DENIED: 'SCOPE_DENY',
|
|
33
|
+
TOKEN_REVOKED: 'TOKEN_REVOKED',
|
|
34
|
+
TOKEN_EXPIRED: 'TOKEN_EXPIRED',
|
|
35
|
+
RATE_LIMIT: 'RATE_LIMITED',
|
|
36
|
+
RATE_LIMITED: 'RATE_LIMITED',
|
|
37
|
+
CREDENTIAL_NOT_FOUND: 'NOT_FOUND',
|
|
38
|
+
CREDENTIAL_TOTP_NOT_CONFIGURED: 'NOT_FOUND',
|
|
39
|
+
TOKEN_AGENT_PUBKEY_MISSING: 'MISSING_KEY',
|
|
40
|
+
INTERNAL_ERROR: 'INTERNAL_ERROR',
|
|
41
|
+
ALLOW: 'UNKNOWN',
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
function normalizeDecision(raw?: unknown, allowed?: boolean, httpStatus?: number): UiDecision {
|
|
45
|
+
const v = typeof raw === 'string' ? raw.trim().toUpperCase() : '';
|
|
46
|
+
if (v === 'ALLOW') return 'ALLOW';
|
|
47
|
+
if (v === 'DENY') return 'DENY';
|
|
48
|
+
if (v === 'RATE_LIMIT') return 'RATE_LIMIT';
|
|
49
|
+
if (v === 'ERROR') return 'ERROR';
|
|
50
|
+
if (allowed === true) return 'ALLOW';
|
|
51
|
+
if (allowed === false && (httpStatus ?? 0) === 429) return 'RATE_LIMIT';
|
|
52
|
+
if (allowed === false) return 'DENY';
|
|
53
|
+
if ((httpStatus ?? 0) >= 500) return 'ERROR';
|
|
54
|
+
return 'UNKNOWN';
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function normalizeReasonCode(raw?: unknown): UiReasonCode {
|
|
58
|
+
const v = typeof raw === 'string' ? raw.trim().toUpperCase() : '';
|
|
59
|
+
return REASON_MAP[v] ?? 'UNKNOWN';
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function confidenceFromRow(input: { tokenHash?: string | null; agentId?: string | null }): AttributionConfidence {
|
|
63
|
+
if (input.tokenHash) return 'HIGH';
|
|
64
|
+
if (input.agentId) return 'MEDIUM';
|
|
65
|
+
return 'LOW';
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function fromTask40Row(row: Record<string, unknown>): UiAuditEvent {
|
|
69
|
+
const rawDecision = typeof row.decision === 'string' ? row.decision : undefined;
|
|
70
|
+
const rawReasonCode = typeof row.reasonCode === 'string' ? row.reasonCode : undefined;
|
|
71
|
+
const tokenHash = typeof row.tokenHash === 'string' ? row.tokenHash : undefined;
|
|
72
|
+
const agentId = typeof row.agentId === 'string' ? row.agentId : undefined;
|
|
73
|
+
const timestampValue = row.timestamp;
|
|
74
|
+
const timestamp = new Date(typeof timestampValue === 'string' || typeof timestampValue === 'number' ? timestampValue : Date.now()).getTime();
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
id: String(row.id ?? `${timestamp}-${tokenHash ?? agentId ?? 'unknown'}`),
|
|
78
|
+
timestamp,
|
|
79
|
+
tokenKey: tokenHash ?? agentId ?? 'unknown',
|
|
80
|
+
tokenHash,
|
|
81
|
+
agentId,
|
|
82
|
+
endpoint: typeof row.action === 'string' ? row.action : undefined,
|
|
83
|
+
credentialKey: typeof row.credentialId === 'string' ? row.credentialId : undefined,
|
|
84
|
+
sourceVersion: 'task40-audit-v1',
|
|
85
|
+
decision: normalizeDecision(rawDecision, row.allowed as boolean | undefined, row.httpStatus as number | undefined),
|
|
86
|
+
reasonCode: normalizeReasonCode(rawReasonCode),
|
|
87
|
+
rawDecision,
|
|
88
|
+
rawReasonCode,
|
|
89
|
+
confidence: confidenceFromRow({ tokenHash, agentId }),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function fromLegacyLog(row: Record<string, unknown>): UiAuditEvent {
|
|
94
|
+
const data = (typeof row.data === 'object' && row.data !== null ? row.data : {}) as Record<string, unknown>;
|
|
95
|
+
const metadata = (typeof data.metadata === 'object' && data.metadata !== null ? data.metadata : {}) as Record<string, unknown>;
|
|
96
|
+
const rawDecision = typeof data.result === 'string' ? data.result : (typeof data.decision === 'string' ? data.decision : undefined);
|
|
97
|
+
const rawReasonCode = typeof data.reasonCode === 'string' ? data.reasonCode : (typeof data.reason === 'string' ? data.reason : undefined);
|
|
98
|
+
const tokenHash = typeof data.tokenHash === 'string' ? data.tokenHash : undefined;
|
|
99
|
+
const agentId = typeof data.agentId === 'string' ? data.agentId : undefined;
|
|
100
|
+
const timestampValue = row.timestamp;
|
|
101
|
+
const timestamp = new Date(typeof timestampValue === 'string' || typeof timestampValue === 'number' ? timestampValue : Date.now()).getTime();
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
id: String(row.id ?? `${timestamp}-${tokenHash ?? agentId ?? 'unknown'}`),
|
|
105
|
+
timestamp,
|
|
106
|
+
tokenKey: tokenHash ?? agentId ?? 'unknown',
|
|
107
|
+
tokenHash,
|
|
108
|
+
agentId,
|
|
109
|
+
endpoint: typeof metadata.route === 'string' ? metadata.route : (typeof data.action === 'string' ? data.action : undefined),
|
|
110
|
+
credentialKey: typeof data.credentialId === 'string' ? data.credentialId : undefined,
|
|
111
|
+
sourceVersion: 'legacy-logs-v1',
|
|
112
|
+
decision: normalizeDecision(rawDecision, data.allowed as boolean | undefined, data.httpStatus as number | undefined),
|
|
113
|
+
reasonCode: normalizeReasonCode(rawReasonCode),
|
|
114
|
+
rawDecision,
|
|
115
|
+
rawReasonCode,
|
|
116
|
+
confidence: confidenceFromRow({ tokenHash, agentId }),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function dedupeKey(row: UiAuditEvent): string {
|
|
121
|
+
return `${row.timestamp}|${row.endpoint ?? ''}|${row.tokenKey}|${row.credentialKey ?? ''}|${row.decision}|${row.reasonCode}`;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function confidenceRank(confidence: AttributionConfidence): number {
|
|
125
|
+
if (confidence === 'HIGH') return 3;
|
|
126
|
+
if (confidence === 'MEDIUM') return 2;
|
|
127
|
+
return 1;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function dedupeAuditEvents(rows: UiAuditEvent[]): UiAuditEvent[] {
|
|
131
|
+
const byKey = new Map<string, UiAuditEvent>();
|
|
132
|
+
for (const row of rows) {
|
|
133
|
+
const key = row.id || dedupeKey(row);
|
|
134
|
+
const existing = byKey.get(key);
|
|
135
|
+
if (!existing) {
|
|
136
|
+
byKey.set(key, row);
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (confidenceRank(row.confidence) > confidenceRank(existing.confidence)) {
|
|
141
|
+
byKey.set(key, row);
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (row.sourceVersion === 'task40-audit-v1' && existing.sourceVersion === 'legacy-logs-v1') {
|
|
146
|
+
byKey.set(key, row);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return Array.from(byKey.values()).sort((a, b) => b.timestamp - a.timestamp);
|
|
151
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auth client for validating tokens from Next.js
|
|
3
|
+
* Calls the Express server's /auth/validate endpoint
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
const WALLET_SERVER_URL = process.env.WALLET_SERVER_URL || 'http://localhost:4242';
|
|
7
|
+
|
|
8
|
+
export interface TokenValidationResult {
|
|
9
|
+
valid: boolean;
|
|
10
|
+
isAdmin?: boolean;
|
|
11
|
+
tokenHash?: string;
|
|
12
|
+
payload?: {
|
|
13
|
+
agentId: string;
|
|
14
|
+
permissions: string[];
|
|
15
|
+
limits?: { fund?: number; send?: number; swap?: number };
|
|
16
|
+
walletAccess?: string[];
|
|
17
|
+
exp?: number;
|
|
18
|
+
};
|
|
19
|
+
error?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Validate a token by calling the Express server
|
|
24
|
+
* @param token - The raw token string to validate
|
|
25
|
+
* @returns Token validation result with payload if valid
|
|
26
|
+
*/
|
|
27
|
+
export async function validateToken(token: string): Promise<TokenValidationResult> {
|
|
28
|
+
try {
|
|
29
|
+
const response = await fetch(`${WALLET_SERVER_URL}/auth/validate`, {
|
|
30
|
+
method: 'POST',
|
|
31
|
+
headers: { 'Content-Type': 'application/json' },
|
|
32
|
+
body: JSON.stringify({ token }),
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
if (!response.ok) {
|
|
36
|
+
return { valid: false, error: `Server error: ${response.status}` };
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return await response.json();
|
|
40
|
+
} catch (error) {
|
|
41
|
+
return {
|
|
42
|
+
valid: false,
|
|
43
|
+
error: error instanceof Error ? error.message : 'Failed to validate token'
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Check if a token has a specific permission
|
|
50
|
+
*/
|
|
51
|
+
export function hasPermission(result: TokenValidationResult, permission: string): boolean {
|
|
52
|
+
if (!result.valid || !result.payload) return false;
|
|
53
|
+
if (result.isAdmin) return true;
|
|
54
|
+
return result.payload.permissions.includes(permission);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Check if a token has any of the required permissions
|
|
59
|
+
*/
|
|
60
|
+
export function hasAnyPermission(result: TokenValidationResult, permissions: string[]): boolean {
|
|
61
|
+
if (!result.valid || !result.payload) return false;
|
|
62
|
+
if (result.isAdmin) return true;
|
|
63
|
+
return permissions.some(p => result.payload!.permissions.includes(p));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Extract token from Authorization header
|
|
68
|
+
*/
|
|
69
|
+
export function extractBearerToken(authHeader: string | null | undefined): string | null {
|
|
70
|
+
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
return authHeader.slice(7);
|
|
74
|
+
}
|
|
75
|
+
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
|
|
4
|
+
const DATA_DIR = path.join(process.cwd(), 'data');
|
|
5
|
+
const CONFIG_PATH = path.join(DATA_DIR, 'config.json');
|
|
6
|
+
|
|
7
|
+
export interface ChainConfig {
|
|
8
|
+
rpc: string;
|
|
9
|
+
chainId: number;
|
|
10
|
+
explorer: string;
|
|
11
|
+
nativeCurrency: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface WalletConfig {
|
|
15
|
+
chains: Record<string, ChainConfig>;
|
|
16
|
+
defaultChain: string;
|
|
17
|
+
server: {
|
|
18
|
+
port: number;
|
|
19
|
+
host: string;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const DEFAULT_CONFIG: WalletConfig = {
|
|
24
|
+
chains: {
|
|
25
|
+
base: {
|
|
26
|
+
rpc: 'https://mainnet.base.org',
|
|
27
|
+
chainId: 8453,
|
|
28
|
+
explorer: 'https://basescan.org',
|
|
29
|
+
nativeCurrency: 'ETH'
|
|
30
|
+
},
|
|
31
|
+
ethereum: {
|
|
32
|
+
rpc: 'https://eth.llamarpc.com',
|
|
33
|
+
chainId: 1,
|
|
34
|
+
explorer: 'https://etherscan.io',
|
|
35
|
+
nativeCurrency: 'ETH'
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
defaultChain: 'base',
|
|
39
|
+
server: {
|
|
40
|
+
port: 4747,
|
|
41
|
+
host: '127.0.0.1'
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export function ensureDataDir(): void {
|
|
46
|
+
const dirs = [DATA_DIR, path.join(DATA_DIR, 'hot'), path.join(DATA_DIR, 'pending')];
|
|
47
|
+
dirs.forEach(dir => {
|
|
48
|
+
if (!fs.existsSync(dir)) {
|
|
49
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export function loadConfig(): WalletConfig {
|
|
55
|
+
ensureDataDir();
|
|
56
|
+
if (!fs.existsSync(CONFIG_PATH)) {
|
|
57
|
+
saveConfig(DEFAULT_CONFIG);
|
|
58
|
+
return DEFAULT_CONFIG;
|
|
59
|
+
}
|
|
60
|
+
const raw = fs.readFileSync(CONFIG_PATH, 'utf-8');
|
|
61
|
+
return { ...DEFAULT_CONFIG, ...JSON.parse(raw) };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function saveConfig(config: WalletConfig): void {
|
|
65
|
+
ensureDataDir();
|
|
66
|
+
fs.writeFileSync(CONFIG_PATH, JSON.stringify(config, null, 2));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export const DATA_PATHS = {
|
|
70
|
+
config: CONFIG_PATH,
|
|
71
|
+
wallets: DATA_DIR,
|
|
72
|
+
hotWallets: path.join(DATA_DIR, 'hot'),
|
|
73
|
+
pending: path.join(DATA_DIR, 'pending')
|
|
74
|
+
};
|