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,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* auramaxx wallet — opinionated wallet API wrappers (incremental parity)
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
bootstrapViaAuthRequest,
|
|
7
|
+
bootstrapViaSocket,
|
|
8
|
+
generateEphemeralKeypair,
|
|
9
|
+
type ProfileIssuanceSelection,
|
|
10
|
+
} from '../../lib/credential-transport';
|
|
11
|
+
import { getErrorMessage } from '../../lib/error';
|
|
12
|
+
import { serverUrl } from '../lib/http';
|
|
13
|
+
import { printHelp } from '../lib/theme';
|
|
14
|
+
|
|
15
|
+
type WalletCommand =
|
|
16
|
+
| 'status'
|
|
17
|
+
| 'assets'
|
|
18
|
+
| 'transactions'
|
|
19
|
+
| 'swap'
|
|
20
|
+
| 'send'
|
|
21
|
+
| 'fund'
|
|
22
|
+
| 'launch';
|
|
23
|
+
|
|
24
|
+
interface WalletCliArgs {
|
|
25
|
+
command: WalletCommand;
|
|
26
|
+
bodyRaw?: string;
|
|
27
|
+
noAuth: boolean;
|
|
28
|
+
yes: boolean;
|
|
29
|
+
profile?: string;
|
|
30
|
+
profileVersion?: string;
|
|
31
|
+
profileOverridesRaw?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function showHelp(): void {
|
|
35
|
+
printHelp('WALLET', 'npx auramaxx wallet <command> [options]', [], [
|
|
36
|
+
'Commands:',
|
|
37
|
+
' status GET /wallet',
|
|
38
|
+
' assets GET /wallet/assets',
|
|
39
|
+
' transactions GET /wallet/transactions',
|
|
40
|
+
' swap --body <json> POST /swap',
|
|
41
|
+
' send --body <json> POST /send',
|
|
42
|
+
' fund --body <json> POST /fund',
|
|
43
|
+
' launch --body <json> POST /launch',
|
|
44
|
+
'',
|
|
45
|
+
'Options:',
|
|
46
|
+
' --body <json> JSON request body (required for mutating commands)',
|
|
47
|
+
' --no-auth Do not attach bearer auth token',
|
|
48
|
+
' --yes Confirm mutating wallet actions (swap/send/fund/launch)',
|
|
49
|
+
' --profile <p> Profile for /auth fallback when socket auto-approve is blocked',
|
|
50
|
+
' --profile-version <v> Profile version for /auth fallback',
|
|
51
|
+
' --profile-overrides <j> Tighten-only profile override JSON for /auth fallback',
|
|
52
|
+
]);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function parseArgs(argv: string[]): WalletCliArgs | null {
|
|
56
|
+
const args = [...argv];
|
|
57
|
+
if (args.length === 0 || args.includes('--help') || args.includes('-h')) return null;
|
|
58
|
+
|
|
59
|
+
const command = args.shift() as WalletCommand | undefined;
|
|
60
|
+
if (!command || !['status', 'assets', 'transactions', 'swap', 'send', 'fund', 'launch'].includes(command)) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const getValue = (flag: string): string | undefined => {
|
|
65
|
+
const idx = args.indexOf(flag);
|
|
66
|
+
return idx >= 0 ? args[idx + 1] : undefined;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
command,
|
|
71
|
+
bodyRaw: getValue('--body'),
|
|
72
|
+
noAuth: args.includes('--no-auth'),
|
|
73
|
+
yes: args.includes('--yes'),
|
|
74
|
+
profile: getValue('--profile'),
|
|
75
|
+
profileVersion: getValue('--profile-version'),
|
|
76
|
+
profileOverridesRaw: getValue('--profile-overrides'),
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function resolveRoute(command: WalletCommand): { method: 'GET' | 'POST'; route: string } {
|
|
81
|
+
switch (command) {
|
|
82
|
+
case 'status':
|
|
83
|
+
return { method: 'GET', route: '/wallet' };
|
|
84
|
+
case 'assets':
|
|
85
|
+
return { method: 'GET', route: '/wallet/assets' };
|
|
86
|
+
case 'transactions':
|
|
87
|
+
return { method: 'GET', route: '/wallet/transactions' };
|
|
88
|
+
case 'swap':
|
|
89
|
+
return { method: 'POST', route: '/swap' };
|
|
90
|
+
case 'send':
|
|
91
|
+
return { method: 'POST', route: '/send' };
|
|
92
|
+
case 'fund':
|
|
93
|
+
return { method: 'POST', route: '/fund' };
|
|
94
|
+
case 'launch':
|
|
95
|
+
return { method: 'POST', route: '/launch' };
|
|
96
|
+
default:
|
|
97
|
+
return { method: 'GET', route: '/wallet' };
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function parseProfileOverrides(raw?: string): ProfileIssuanceSelection['profileOverrides'] | undefined {
|
|
102
|
+
if (!raw) return undefined;
|
|
103
|
+
const parsed = JSON.parse(raw) as unknown;
|
|
104
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
105
|
+
throw new Error('--profile-overrides must be a JSON object');
|
|
106
|
+
}
|
|
107
|
+
return parsed as ProfileIssuanceSelection['profileOverrides'];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function parseBody(raw?: string): unknown | undefined {
|
|
111
|
+
if (!raw) return undefined;
|
|
112
|
+
return JSON.parse(raw) as unknown;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
async function resolveAuthToken(selection: ProfileIssuanceSelection): Promise<string> {
|
|
116
|
+
const envToken = process.env.AURA_TOKEN;
|
|
117
|
+
if (envToken) return envToken;
|
|
118
|
+
|
|
119
|
+
const keypair = generateEphemeralKeypair();
|
|
120
|
+
|
|
121
|
+
if (selection.profile) {
|
|
122
|
+
const result = await bootstrapViaAuthRequest(serverUrl(), 'cli-wallet', keypair, {
|
|
123
|
+
...selection,
|
|
124
|
+
noWait: true,
|
|
125
|
+
onStatus: (message) => console.error(message),
|
|
126
|
+
});
|
|
127
|
+
if (result.approveUrl) {
|
|
128
|
+
console.error(`Approve at: ${result.approveUrl}`);
|
|
129
|
+
}
|
|
130
|
+
console.error(`After approval, re-run with: AURA_TOKEN=<token> npx auramaxx wallet ...`);
|
|
131
|
+
console.error(`Or use: npx auramaxx auth request --profile ${selection.profile} --raw-token`);
|
|
132
|
+
process.exit(1);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
try {
|
|
136
|
+
return await bootstrapViaSocket('cli-wallet', keypair);
|
|
137
|
+
} catch (socketErr) {
|
|
138
|
+
return bootstrapViaAuthRequest(serverUrl(), 'cli-wallet', keypair, {
|
|
139
|
+
...selection,
|
|
140
|
+
onStatus: (message) => console.error(message),
|
|
141
|
+
}).catch((authErr) => {
|
|
142
|
+
throw new Error(`${getErrorMessage(socketErr)}\n${getErrorMessage(authErr)}`);
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
async function main(): Promise<void> {
|
|
148
|
+
const parsed = parseArgs(process.argv.slice(2));
|
|
149
|
+
if (!parsed) {
|
|
150
|
+
showHelp();
|
|
151
|
+
process.exit(0);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const { method, route } = resolveRoute(parsed.command);
|
|
155
|
+
const body = parseBody(parsed.bodyRaw);
|
|
156
|
+
if (method === 'POST' && body === undefined) {
|
|
157
|
+
throw new Error(`'${parsed.command}' requires --body <json>`);
|
|
158
|
+
}
|
|
159
|
+
if (method === 'POST' && !parsed.yes) {
|
|
160
|
+
throw new Error(`'${parsed.command}' is mutating and requires --yes`);
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const profileOverrides = parseProfileOverrides(parsed.profileOverridesRaw);
|
|
164
|
+
const authSelection: ProfileIssuanceSelection = {
|
|
165
|
+
...(parsed.profile ? { profile: parsed.profile } : {}),
|
|
166
|
+
...(parsed.profileVersion ? { profileVersion: parsed.profileVersion } : {}),
|
|
167
|
+
...(profileOverrides ? { profileOverrides } : {}),
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
const token = parsed.noAuth ? null : await resolveAuthToken(authSelection);
|
|
171
|
+
const headers: Record<string, string> = {};
|
|
172
|
+
if (body !== undefined) headers['Content-Type'] = 'application/json';
|
|
173
|
+
if (token) headers['Authorization'] = `Bearer ${token}`;
|
|
174
|
+
|
|
175
|
+
const res = await fetch(`${serverUrl()}${route}`, {
|
|
176
|
+
method,
|
|
177
|
+
headers,
|
|
178
|
+
body: body !== undefined ? JSON.stringify(body) : undefined,
|
|
179
|
+
signal: AbortSignal.timeout(15_000),
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
const text = await res.text();
|
|
183
|
+
let payload: unknown = text;
|
|
184
|
+
try {
|
|
185
|
+
payload = text ? JSON.parse(text) as unknown : {};
|
|
186
|
+
} catch {
|
|
187
|
+
payload = text;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (!res.ok) {
|
|
191
|
+
if (typeof payload === 'string') console.error(`HTTP ${res.status}: ${payload}`);
|
|
192
|
+
else console.error(`HTTP ${res.status}: ${JSON.stringify(payload, null, 2)}`);
|
|
193
|
+
process.exit(1);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (typeof payload === 'string') {
|
|
197
|
+
console.log(payload);
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (import.meta.url === `file://${process.argv[1]}`) {
|
|
205
|
+
main().catch((error) => {
|
|
206
|
+
console.error(`wallet command failed: ${getErrorMessage(error)}`);
|
|
207
|
+
process.exit(1);
|
|
208
|
+
});
|
|
209
|
+
}
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Aura Wallet CLI - Headless mode for programmatic wallet control
|
|
4
|
+
*
|
|
5
|
+
* Security Design:
|
|
6
|
+
* - Admin token stored in-process memory only (never touches disk)
|
|
7
|
+
* - Main wallet server hosts Unix socket IPC for local agent connections
|
|
8
|
+
* - Crash = re-auth required (aligns with server restart security model)
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* npx tsx server/cli/index.ts # Interactive password prompt
|
|
12
|
+
* npx tsx server/cli/index.ts --password-stdin # Read password from stdin (CI/automation)
|
|
13
|
+
* npx tsx server/cli/index.ts --auto-approve # Auto-approve all requests (DANGEROUS)
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import * as readline from 'readline';
|
|
17
|
+
import { encryptPassword, generateAgentKeypair } from './transport-client';
|
|
18
|
+
import { ApprovalManager } from './approval';
|
|
19
|
+
import * as fs from 'fs';
|
|
20
|
+
import { getErrorMessage } from '../lib/error';
|
|
21
|
+
import { printBanner, printBox, printStatus } from './lib/theme';
|
|
22
|
+
import { resolveAuraSocketPath } from '../lib/socket-path';
|
|
23
|
+
import { promptPassword } from './lib/prompt';
|
|
24
|
+
|
|
25
|
+
const SERVER_URL = process.env.WALLET_SERVER_URL || 'http://localhost:4242';
|
|
26
|
+
const LOCK_FILE = `/tmp/aura-cli-${process.getuid?.() ?? 'unknown'}.lock`;
|
|
27
|
+
|
|
28
|
+
// In-process memory only - never persisted
|
|
29
|
+
let adminToken: string | null = null;
|
|
30
|
+
let coldWalletAddress: string | null = null;
|
|
31
|
+
|
|
32
|
+
// Parse command line arguments
|
|
33
|
+
const args = process.argv.slice(2);
|
|
34
|
+
const passwordStdin = args.includes('--password-stdin');
|
|
35
|
+
const autoApprove = args.includes('--auto-approve');
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Check if another CLI instance is running
|
|
39
|
+
*/
|
|
40
|
+
function checkLockFile(): boolean {
|
|
41
|
+
try {
|
|
42
|
+
if (fs.existsSync(LOCK_FILE)) {
|
|
43
|
+
const pid = parseInt(fs.readFileSync(LOCK_FILE, 'utf8'), 10);
|
|
44
|
+
// Check if process is still running
|
|
45
|
+
try {
|
|
46
|
+
process.kill(pid, 0); // Signal 0 tests if process exists
|
|
47
|
+
return true; // Process exists, lock is valid
|
|
48
|
+
} catch {
|
|
49
|
+
// Process doesn't exist, stale lock file
|
|
50
|
+
fs.unlinkSync(LOCK_FILE);
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return false;
|
|
55
|
+
} catch {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Create lock file with our PID
|
|
62
|
+
*/
|
|
63
|
+
function createLockFile(): void {
|
|
64
|
+
fs.writeFileSync(LOCK_FILE, process.pid.toString(), { mode: 0o600 });
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Remove lock file on exit
|
|
69
|
+
*/
|
|
70
|
+
function removeLockFile(): void {
|
|
71
|
+
try {
|
|
72
|
+
if (fs.existsSync(LOCK_FILE)) {
|
|
73
|
+
const pid = parseInt(fs.readFileSync(LOCK_FILE, 'utf8'), 10);
|
|
74
|
+
if (pid === process.pid) {
|
|
75
|
+
fs.unlinkSync(LOCK_FILE);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
} catch {
|
|
79
|
+
// Ignore cleanup errors
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Fetch the server's RSA public key for password encryption
|
|
85
|
+
*/
|
|
86
|
+
async function fetchPublicKey(): Promise<string> {
|
|
87
|
+
const response = await fetch(`${SERVER_URL}/auth/connect`);
|
|
88
|
+
if (!response.ok) {
|
|
89
|
+
throw new Error(`Failed to connect to wallet server: ${response.status}`);
|
|
90
|
+
}
|
|
91
|
+
const data = await response.json() as { publicKey: string };
|
|
92
|
+
return data.publicKey;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Unlock the wallet with encrypted password
|
|
97
|
+
*/
|
|
98
|
+
async function unlockWallet(encryptedPassword: string, pubkey: string): Promise<{ token: string; address: string }> {
|
|
99
|
+
const response = await fetch(`${SERVER_URL}/unlock`, {
|
|
100
|
+
method: 'POST',
|
|
101
|
+
headers: { 'Content-Type': 'application/json' },
|
|
102
|
+
body: JSON.stringify({ encrypted: encryptedPassword, pubkey })
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
const data = await response.json() as { success?: boolean; token?: string; address?: string; error?: string };
|
|
106
|
+
|
|
107
|
+
if (!response.ok || !data.success) {
|
|
108
|
+
throw new Error(data.error || 'Failed to unlock wallet');
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return { token: data.token!, address: data.address! };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Read password from stdin (for automation)
|
|
116
|
+
*/
|
|
117
|
+
async function readPasswordFromStdin(): Promise<string> {
|
|
118
|
+
return new Promise((resolve, reject) => {
|
|
119
|
+
let data = '';
|
|
120
|
+
|
|
121
|
+
process.stdin.setEncoding('utf8');
|
|
122
|
+
process.stdin.on('data', (chunk) => {
|
|
123
|
+
data += chunk;
|
|
124
|
+
});
|
|
125
|
+
process.stdin.on('end', () => {
|
|
126
|
+
const password = data.trim();
|
|
127
|
+
if (!password) {
|
|
128
|
+
reject(new Error('No password provided on stdin'));
|
|
129
|
+
} else {
|
|
130
|
+
resolve(password);
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
process.stdin.on('error', reject);
|
|
134
|
+
|
|
135
|
+
// Set a timeout for stdin read
|
|
136
|
+
setTimeout(() => {
|
|
137
|
+
if (!data) {
|
|
138
|
+
reject(new Error('Timeout waiting for password on stdin'));
|
|
139
|
+
}
|
|
140
|
+
}, 5000);
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Get the admin token (for use by other modules)
|
|
146
|
+
*/
|
|
147
|
+
export function getAdminToken(): string | null {
|
|
148
|
+
return adminToken;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Get the cold wallet address
|
|
153
|
+
*/
|
|
154
|
+
export function getColdWalletAddress(): string | null {
|
|
155
|
+
return coldWalletAddress;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Get the server URL
|
|
160
|
+
*/
|
|
161
|
+
export function getServerUrl(): string {
|
|
162
|
+
return SERVER_URL;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Main entry point
|
|
167
|
+
*/
|
|
168
|
+
async function main() {
|
|
169
|
+
printBanner('CLI MODE');
|
|
170
|
+
|
|
171
|
+
// Check for existing instance
|
|
172
|
+
if (checkLockFile()) {
|
|
173
|
+
console.error('ERROR: Another CLI instance is already running.');
|
|
174
|
+
console.error('If this is incorrect, remove the lock file:', LOCK_FILE);
|
|
175
|
+
process.exit(1);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Auto-approve warning
|
|
179
|
+
if (autoApprove) {
|
|
180
|
+
console.log('⚠️ WARNING: --auto-approve is enabled!');
|
|
181
|
+
console.log(' All agent requests will be automatically approved.');
|
|
182
|
+
console.log(' This is DANGEROUS in production environments.\n');
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
try {
|
|
186
|
+
// 1. Fetch server's public key
|
|
187
|
+
console.log('Connecting to wallet server...');
|
|
188
|
+
const publicKey = await fetchPublicKey();
|
|
189
|
+
console.log('Connected. Server RSA key received.\n');
|
|
190
|
+
|
|
191
|
+
// 2. Get password
|
|
192
|
+
let password: string;
|
|
193
|
+
if (passwordStdin) {
|
|
194
|
+
console.log('Reading password from stdin...');
|
|
195
|
+
password = await readPasswordFromStdin();
|
|
196
|
+
} else {
|
|
197
|
+
password = await promptPassword();
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// 3. Encrypt and unlock
|
|
201
|
+
console.log('Unlocking wallet...');
|
|
202
|
+
const encryptedPassword = encryptPassword(password, publicKey);
|
|
203
|
+
const { publicKey: agentPubkey } = generateAgentKeypair();
|
|
204
|
+
const { token, address } = await unlockWallet(encryptedPassword, agentPubkey);
|
|
205
|
+
|
|
206
|
+
// Clear password from memory
|
|
207
|
+
password = '';
|
|
208
|
+
|
|
209
|
+
// Store token in memory only
|
|
210
|
+
adminToken = token;
|
|
211
|
+
coldWalletAddress = address;
|
|
212
|
+
|
|
213
|
+
console.log(`\n✓ Wallet unlocked successfully`);
|
|
214
|
+
console.log(` Address: ${address}`);
|
|
215
|
+
console.log(` Token: ${token.substring(0, 20)}...`);
|
|
216
|
+
|
|
217
|
+
// Create lock file
|
|
218
|
+
createLockFile();
|
|
219
|
+
|
|
220
|
+
// 4. Start approval manager (WebSocket listener)
|
|
221
|
+
console.log('\nStarting approval listener...');
|
|
222
|
+
const approvalManager = new ApprovalManager({
|
|
223
|
+
serverUrl: SERVER_URL,
|
|
224
|
+
getToken: () => adminToken,
|
|
225
|
+
autoApprove,
|
|
226
|
+
headless: passwordStdin // Skip terminal interface when using stdin
|
|
227
|
+
});
|
|
228
|
+
await approvalManager.start();
|
|
229
|
+
|
|
230
|
+
// 5. Socket broker now runs in the main wallet server process.
|
|
231
|
+
const socketPath = resolveAuraSocketPath({
|
|
232
|
+
serverUrl: SERVER_URL,
|
|
233
|
+
serverPort: process.env.WALLET_SERVER_PORT,
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
console.log('');
|
|
237
|
+
printBox([
|
|
238
|
+
'CLI Ready — Listening for agent requests',
|
|
239
|
+
'',
|
|
240
|
+
`Socket: ${socketPath}`,
|
|
241
|
+
'Press Ctrl+C to exit',
|
|
242
|
+
]);
|
|
243
|
+
|
|
244
|
+
// Handle graceful shutdown
|
|
245
|
+
const shutdown = async () => {
|
|
246
|
+
console.log('\n\nShutting down...');
|
|
247
|
+
approvalManager.stop();
|
|
248
|
+
removeLockFile();
|
|
249
|
+
|
|
250
|
+
// Clear sensitive data
|
|
251
|
+
adminToken = null;
|
|
252
|
+
coldWalletAddress = null;
|
|
253
|
+
|
|
254
|
+
console.log('Goodbye.');
|
|
255
|
+
process.exit(0);
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
process.on('SIGINT', shutdown);
|
|
259
|
+
process.on('SIGTERM', shutdown);
|
|
260
|
+
process.on('exit', removeLockFile);
|
|
261
|
+
|
|
262
|
+
// Keep process alive
|
|
263
|
+
await new Promise(() => {
|
|
264
|
+
// Never resolves - keeps CLI running
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
} catch (error) {
|
|
268
|
+
const message = getErrorMessage(error);
|
|
269
|
+
console.error(`\nERROR: ${message}`);
|
|
270
|
+
removeLockFile();
|
|
271
|
+
process.exit(1);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Run main
|
|
276
|
+
main().catch((error) => {
|
|
277
|
+
console.error('Fatal error:', error);
|
|
278
|
+
removeLockFile();
|
|
279
|
+
process.exit(1);
|
|
280
|
+
});
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { getErrorMessage } from '../../lib/error';
|
|
2
|
+
|
|
3
|
+
export type AuthDecisionStatus = 'pending' | 'approved' | 'rejected';
|
|
4
|
+
|
|
5
|
+
export interface AuthDecisionResponse {
|
|
6
|
+
success?: boolean;
|
|
7
|
+
status?: AuthDecisionStatus;
|
|
8
|
+
encryptedToken?: string;
|
|
9
|
+
error?: string;
|
|
10
|
+
[key: string]: unknown;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface AuthDecisionFetchResult {
|
|
14
|
+
httpStatus: number;
|
|
15
|
+
payload: AuthDecisionResponse;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface AuthDecisionPollOptions {
|
|
19
|
+
timeoutMs?: number;
|
|
20
|
+
intervalMs?: number;
|
|
21
|
+
signal?: AbortSignal;
|
|
22
|
+
onPending?: (info: { attempt: number; elapsedMs: number }) => void;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async function sleep(ms: number): Promise<void> {
|
|
26
|
+
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export async function fetchAuthDecisionOnce(
|
|
30
|
+
baseUrl: string,
|
|
31
|
+
requestId: string,
|
|
32
|
+
secret: string,
|
|
33
|
+
): Promise<AuthDecisionFetchResult> {
|
|
34
|
+
const res = await fetch(
|
|
35
|
+
`${baseUrl}/auth/${encodeURIComponent(requestId)}?secret=${encodeURIComponent(secret)}`,
|
|
36
|
+
{ signal: AbortSignal.timeout(10_000) },
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
const payload = await res.json().catch(() => ({})) as AuthDecisionResponse;
|
|
40
|
+
return { httpStatus: res.status, payload };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function waitForAuthDecision(
|
|
44
|
+
baseUrl: string,
|
|
45
|
+
requestId: string,
|
|
46
|
+
secret: string,
|
|
47
|
+
options: AuthDecisionPollOptions = {},
|
|
48
|
+
): Promise<{ response: AuthDecisionResponse; attempts: number; elapsedMs: number }> {
|
|
49
|
+
const timeoutMs = options.timeoutMs ?? 120_000;
|
|
50
|
+
const intervalMs = options.intervalMs ?? 3_000;
|
|
51
|
+
const startedAt = Date.now();
|
|
52
|
+
let attempts = 0;
|
|
53
|
+
|
|
54
|
+
while (Date.now() - startedAt <= timeoutMs) {
|
|
55
|
+
if (options.signal?.aborted) {
|
|
56
|
+
throw new Error('Polling aborted');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
attempts += 1;
|
|
60
|
+
let result: AuthDecisionFetchResult;
|
|
61
|
+
try {
|
|
62
|
+
result = await fetchAuthDecisionOnce(baseUrl, requestId, secret);
|
|
63
|
+
} catch (error) {
|
|
64
|
+
const message = getErrorMessage(error);
|
|
65
|
+
throw new Error(`Auth polling request failed: ${message}`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const { httpStatus, payload } = result;
|
|
69
|
+
|
|
70
|
+
if (httpStatus === 200 && payload.success) {
|
|
71
|
+
if (payload.status === 'approved' || payload.status === 'rejected') {
|
|
72
|
+
return {
|
|
73
|
+
response: payload,
|
|
74
|
+
attempts,
|
|
75
|
+
elapsedMs: Date.now() - startedAt,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (payload.status === 'pending') {
|
|
80
|
+
options.onPending?.({ attempt: attempts, elapsedMs: Date.now() - startedAt });
|
|
81
|
+
}
|
|
82
|
+
} else if (httpStatus === 410) {
|
|
83
|
+
throw new Error('Token already claimed or expired (HTTP 410).');
|
|
84
|
+
} else if (httpStatus === 403) {
|
|
85
|
+
throw new Error(payload.error || 'Invalid secret for auth polling (HTTP 403).');
|
|
86
|
+
} else if (httpStatus >= 400 && httpStatus < 500 && httpStatus !== 404) {
|
|
87
|
+
throw new Error(payload.error || `Auth polling failed (HTTP ${httpStatus}).`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
await sleep(intervalMs);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
throw new Error(`Timed out waiting for approval after ${Math.round(timeoutMs / 1000)}s.`);
|
|
94
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* .aura file parser — shared between env.ts and init.ts
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as fs from 'fs';
|
|
6
|
+
import {
|
|
7
|
+
type AuraMapping,
|
|
8
|
+
validateEnvVarName,
|
|
9
|
+
} from './credential-resolve';
|
|
10
|
+
|
|
11
|
+
export { type AuraMapping } from './credential-resolve';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Parse a .aura file into an array of env-var → credential mappings.
|
|
15
|
+
*/
|
|
16
|
+
export function parseAuraFile(filePath: string): AuraMapping[] {
|
|
17
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
18
|
+
const mappings: AuraMapping[] = [];
|
|
19
|
+
|
|
20
|
+
for (const rawLine of content.split('\n')) {
|
|
21
|
+
const line = rawLine.trim();
|
|
22
|
+
if (!line || line.startsWith('#')) continue;
|
|
23
|
+
|
|
24
|
+
const eqIdx = line.indexOf('=');
|
|
25
|
+
if (eqIdx === -1) {
|
|
26
|
+
throw new Error(`Invalid line in .aura (missing '='): ${line}`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const envVar = line.substring(0, eqIdx).trim();
|
|
30
|
+
const ref = line.substring(eqIdx + 1).trim();
|
|
31
|
+
|
|
32
|
+
if (!envVar || !ref) {
|
|
33
|
+
throw new Error(`Invalid line in .aura: ${line}`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Validate env var name (audit finding #6)
|
|
37
|
+
validateEnvVarName(envVar);
|
|
38
|
+
|
|
39
|
+
let vault: string | null = null;
|
|
40
|
+
let credentialName: string;
|
|
41
|
+
let field: string;
|
|
42
|
+
|
|
43
|
+
if (ref.startsWith('@')) {
|
|
44
|
+
const parts = ref.substring(1).split('/');
|
|
45
|
+
if (parts.length < 3) {
|
|
46
|
+
throw new Error(`Invalid vault reference (expected @vault/credential/field): ${ref}`);
|
|
47
|
+
}
|
|
48
|
+
vault = parts[0];
|
|
49
|
+
credentialName = parts[1];
|
|
50
|
+
field = parts.slice(2).join('/');
|
|
51
|
+
} else {
|
|
52
|
+
const parts = ref.split('/');
|
|
53
|
+
if (parts.length < 2) {
|
|
54
|
+
throw new Error(`Invalid reference (expected credential/field): ${ref}`);
|
|
55
|
+
}
|
|
56
|
+
credentialName = parts[0];
|
|
57
|
+
field = parts.slice(1).join('/');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
mappings.push({ envVar, vault, credentialName, field });
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return mappings;
|
|
64
|
+
}
|