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,328 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extracted resolve logic from `POST /actions/:id/resolve`.
|
|
3
|
+
*
|
|
4
|
+
* Called by both the HTTP route handler and the ApprovalRouter (direct in-process).
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { prisma } from './db';
|
|
8
|
+
import { events, emitWalletEvent } from './events';
|
|
9
|
+
import { createToken, getTokenHash, escrowToken, type AgentTokenPayload } from './auth';
|
|
10
|
+
import { isValidAgentPubkey, normalizeAgentPubkey } from './credential-transport';
|
|
11
|
+
import { isUnlocked, getColdWalletAddress } from './cold';
|
|
12
|
+
import { normalizeAddress } from './address';
|
|
13
|
+
import { getDefault } from './defaults';
|
|
14
|
+
import { logger } from './logger';
|
|
15
|
+
import { getErrorMessage } from './error';
|
|
16
|
+
import { autoExecuteAction } from './auto-execute';
|
|
17
|
+
|
|
18
|
+
export interface ResolveActionOptions {
|
|
19
|
+
walletAccess?: string[];
|
|
20
|
+
limits?: Record<string, number>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface ResolveActionResult {
|
|
24
|
+
success: boolean;
|
|
25
|
+
statusCode: 200 | 400 | 401 | 404;
|
|
26
|
+
data: Record<string, unknown>;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export async function resolveAction(
|
|
30
|
+
actionId: string,
|
|
31
|
+
approved: boolean,
|
|
32
|
+
opts?: ResolveActionOptions,
|
|
33
|
+
): Promise<ResolveActionResult> {
|
|
34
|
+
const id = actionId;
|
|
35
|
+
const walletAccess = opts?.walletAccess;
|
|
36
|
+
const overrideLimits = opts?.limits;
|
|
37
|
+
|
|
38
|
+
if (typeof approved !== 'boolean') {
|
|
39
|
+
return { success: false, statusCode: 400, data: { success: false, error: 'approved (boolean) is required' } };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const request = await prisma.humanAction.findUnique({ where: { id } });
|
|
43
|
+
if (!request || request.status !== 'pending') {
|
|
44
|
+
return { success: false, statusCode: 404, data: { success: false, error: 'Action not found or already resolved' } };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (request.type === 'strategy:message') {
|
|
48
|
+
return { success: false, statusCode: 400, data: { success: false, error: 'Internal message jobs are not manually resolvable' } };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Handle rejection for all types
|
|
52
|
+
if (!approved) {
|
|
53
|
+
await prisma.humanAction.update({
|
|
54
|
+
where: { id },
|
|
55
|
+
data: { status: 'rejected', resolvedAt: new Date() },
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
events.actionResolved({ id, type: request.type, approved: false, resolvedBy: 'dashboard' });
|
|
59
|
+
logger.actionResolved(id, request.type, false, 'dashboard');
|
|
60
|
+
|
|
61
|
+
// Notify app of rejection via app:emit
|
|
62
|
+
if (request.type === 'action') {
|
|
63
|
+
let meta: { agentId?: string } = {};
|
|
64
|
+
try { meta = JSON.parse(request.metadata || '{}'); } catch {}
|
|
65
|
+
emitWalletEvent('app:emit', {
|
|
66
|
+
strategyId: (meta.agentId || '').replace(/^app:/, ''),
|
|
67
|
+
channel: 'action:resolved',
|
|
68
|
+
data: { requestId: id, approved: false },
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return { success: true, statusCode: 200, data: { success: true, approved: false } };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// === APPROVAL ===
|
|
76
|
+
|
|
77
|
+
// Strategy approvals
|
|
78
|
+
if (request.type === 'strategy:approve') {
|
|
79
|
+
await prisma.humanAction.update({
|
|
80
|
+
where: { id },
|
|
81
|
+
data: { status: 'approved', resolvedAt: new Date() },
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
events.actionResolved({ id, type: request.type, approved: true, resolvedBy: 'dashboard' });
|
|
85
|
+
return { success: true, statusCode: 200, data: { success: true, approved: true } };
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Auth / agent_access / action approvals — generate token
|
|
89
|
+
if (request.type === 'auth' || request.type === 'agent_access' || request.type === 'action') {
|
|
90
|
+
if (!isUnlocked()) {
|
|
91
|
+
return { success: false, statusCode: 401, data: { success: false, error: 'Wallet is locked. Unlock first.' } };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
let metadata: {
|
|
95
|
+
agentId?: string;
|
|
96
|
+
limit?: number;
|
|
97
|
+
permissions?: string[];
|
|
98
|
+
ttl?: number;
|
|
99
|
+
secretHash?: string;
|
|
100
|
+
limits?: { fund?: number; send?: number; swap?: number };
|
|
101
|
+
walletAccess?: string[];
|
|
102
|
+
credentialAccess?: AgentTokenPayload['credentialAccess'];
|
|
103
|
+
pubkey?: string;
|
|
104
|
+
strategyId?: string;
|
|
105
|
+
summary?: string;
|
|
106
|
+
action?: { endpoint?: string; method?: string; body?: Record<string, unknown> };
|
|
107
|
+
} = {};
|
|
108
|
+
if (request.metadata) {
|
|
109
|
+
try { metadata = JSON.parse(request.metadata); } catch { /* ignore */ }
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const agentId = metadata.agentId || `agent-${id.slice(0, 8)}`;
|
|
113
|
+
const defaultFundLimit = await getDefault<number>('limits.fund', 0);
|
|
114
|
+
const defaultSendLimit = await getDefault<number>('limits.send', 0.1);
|
|
115
|
+
const defaultSwapLimit = await getDefault<number>('limits.swap', 0.1);
|
|
116
|
+
const defaultPermissions = await getDefault<string[]>('permissions.default', ['wallet:create:hot', 'send:hot', 'swap', 'fund', 'action:create']);
|
|
117
|
+
const defaultTtl = await getDefault<number>('ttl.agent', 3600);
|
|
118
|
+
const limit = overrideLimits?.fund ?? metadata.limit ?? defaultFundLimit;
|
|
119
|
+
const permissions = metadata.permissions || defaultPermissions;
|
|
120
|
+
const ttl = metadata.ttl || defaultTtl;
|
|
121
|
+
let normalizedPubkey = metadata.pubkey;
|
|
122
|
+
if (normalizedPubkey) {
|
|
123
|
+
if (!isValidAgentPubkey(normalizedPubkey)) {
|
|
124
|
+
return { success: false, statusCode: 400, data: { success: false, error: 'Stored pubkey is invalid for token issuance' } };
|
|
125
|
+
}
|
|
126
|
+
normalizedPubkey = normalizeAgentPubkey(normalizedPubkey);
|
|
127
|
+
}
|
|
128
|
+
if (!normalizedPubkey) {
|
|
129
|
+
return { success: false, statusCode: 400, data: { success: false, error: 'pubkey is required when approving token issuance' } };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const finalWalletAccess = walletAccess
|
|
133
|
+
? walletAccess.map((addr: string) => normalizeAddress(addr))
|
|
134
|
+
: metadata.walletAccess;
|
|
135
|
+
|
|
136
|
+
// Build limits: per-token overrides > request metadata > system defaults
|
|
137
|
+
const baseLimits = { fund: limit, send: defaultSendLimit, swap: defaultSwapLimit };
|
|
138
|
+
const finalLimits = overrideLimits
|
|
139
|
+
? { ...baseLimits, ...overrideLimits }
|
|
140
|
+
: metadata.limits
|
|
141
|
+
? { ...baseLimits, ...metadata.limits }
|
|
142
|
+
: baseLimits;
|
|
143
|
+
|
|
144
|
+
const token = await createToken(agentId, limit, permissions, ttl, {
|
|
145
|
+
limits: finalLimits,
|
|
146
|
+
walletAccess: finalWalletAccess,
|
|
147
|
+
credentialAccess: metadata.credentialAccess,
|
|
148
|
+
agentPubkey: normalizedPubkey,
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
// Escrow the raw token in memory — never store it in the DB
|
|
152
|
+
escrowToken(id, token);
|
|
153
|
+
|
|
154
|
+
// Update request status with tokenHash (not raw token) for audit/display
|
|
155
|
+
await prisma.humanAction.update({
|
|
156
|
+
where: { id },
|
|
157
|
+
data: {
|
|
158
|
+
status: 'approved',
|
|
159
|
+
resolvedAt: new Date(),
|
|
160
|
+
metadata: JSON.stringify({
|
|
161
|
+
...metadata,
|
|
162
|
+
tokenHash: getTokenHash(token),
|
|
163
|
+
limits: finalLimits,
|
|
164
|
+
walletAccess: finalWalletAccess,
|
|
165
|
+
pubkey: normalizedPubkey,
|
|
166
|
+
credentialAccess: metadata.credentialAccess,
|
|
167
|
+
}),
|
|
168
|
+
},
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
// Log the approval
|
|
172
|
+
await prisma.log.create({
|
|
173
|
+
data: {
|
|
174
|
+
walletAddress: getColdWalletAddress() || 'system',
|
|
175
|
+
title: 'Agent Access Approved',
|
|
176
|
+
description: `Generated token for ${agentId} with ${limit} ETH limit`,
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
events.tokenCreated({
|
|
181
|
+
tokenHash: getTokenHash(token),
|
|
182
|
+
agentId,
|
|
183
|
+
limit,
|
|
184
|
+
permissions,
|
|
185
|
+
expiresAt: Date.now() + ttl * 1000,
|
|
186
|
+
});
|
|
187
|
+
events.actionResolved({ id, type: request.type, approved: true, resolvedBy: 'dashboard' });
|
|
188
|
+
logger.actionResolved(id, request.type, true, 'dashboard');
|
|
189
|
+
|
|
190
|
+
// Notify app of approval via app:emit (always, even with auto-execute)
|
|
191
|
+
if (request.type === 'action') {
|
|
192
|
+
emitWalletEvent('app:emit', {
|
|
193
|
+
strategyId: (metadata.agentId || '').replace(/^app:/, ''),
|
|
194
|
+
channel: 'action:resolved',
|
|
195
|
+
data: { requestId: id, approved: true },
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Auto-execute pre-computed action if present in metadata
|
|
200
|
+
if (metadata.action && (request.type === 'action' || request.type === 'auth')) {
|
|
201
|
+
const action = metadata.action as { endpoint?: string; method?: string; body?: Record<string, unknown> };
|
|
202
|
+
if (action.endpoint && action.method) {
|
|
203
|
+
await autoExecuteAction(
|
|
204
|
+
{ endpoint: action.endpoint, method: action.method, body: action.body },
|
|
205
|
+
{ requestId: id, agentId: metadata.agentId || '', summary: metadata.summary, token },
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return {
|
|
211
|
+
success: true,
|
|
212
|
+
statusCode: 200,
|
|
213
|
+
data: {
|
|
214
|
+
success: true,
|
|
215
|
+
token,
|
|
216
|
+
agentId,
|
|
217
|
+
limit,
|
|
218
|
+
limits: finalLimits,
|
|
219
|
+
permissions,
|
|
220
|
+
walletAccess: finalWalletAccess,
|
|
221
|
+
expiresIn: ttl,
|
|
222
|
+
},
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Permission update approvals — generate token with updated permissions
|
|
227
|
+
if (request.type === 'permission_update') {
|
|
228
|
+
if (!isUnlocked()) {
|
|
229
|
+
return { success: false, statusCode: 401, data: { success: false, error: 'Wallet is locked. Unlock first.' } };
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
let metadata: {
|
|
233
|
+
agentId?: string;
|
|
234
|
+
tokenHash?: string;
|
|
235
|
+
requestedPermissions?: string[];
|
|
236
|
+
requestedWalletAccess?: string[];
|
|
237
|
+
requestedLimits?: { fund?: number; send?: number; swap?: number };
|
|
238
|
+
requestedPubkey?: string;
|
|
239
|
+
secretHash?: string;
|
|
240
|
+
} = {};
|
|
241
|
+
if (request.metadata) {
|
|
242
|
+
try { metadata = JSON.parse(request.metadata); } catch { /* ignore */ }
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const agentId = metadata.agentId || `agent-${id.slice(0, 8)}`;
|
|
246
|
+
const newPermissions = overrideLimits?.permissions ?? metadata.requestedPermissions ?? [];
|
|
247
|
+
const newWalletAccess = walletAccess
|
|
248
|
+
? walletAccess.map((addr: string) => normalizeAddress(addr))
|
|
249
|
+
: metadata.requestedWalletAccess;
|
|
250
|
+
const newLimits = overrideLimits || metadata.requestedLimits;
|
|
251
|
+
let normalizedPubkey = metadata.requestedPubkey;
|
|
252
|
+
if (normalizedPubkey) {
|
|
253
|
+
if (!isValidAgentPubkey(normalizedPubkey)) {
|
|
254
|
+
return { success: false, statusCode: 400, data: { success: false, error: 'requestedPubkey is invalid' } };
|
|
255
|
+
}
|
|
256
|
+
normalizedPubkey = normalizeAgentPubkey(normalizedPubkey);
|
|
257
|
+
}
|
|
258
|
+
if (!normalizedPubkey) {
|
|
259
|
+
return { success: false, statusCode: 400, data: { success: false, error: 'requestedPubkey is required for token issuance' } };
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const ttl = await getDefault<number>('ttl.agent', 3600);
|
|
263
|
+
const token = await createToken(agentId, newLimits?.fund ?? 0, newPermissions, ttl, {
|
|
264
|
+
limits: newLimits,
|
|
265
|
+
walletAccess: newWalletAccess,
|
|
266
|
+
agentPubkey: normalizedPubkey,
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
// Escrow the raw token in memory — never store it in the DB
|
|
270
|
+
escrowToken(id, token);
|
|
271
|
+
|
|
272
|
+
await prisma.humanAction.update({
|
|
273
|
+
where: { id },
|
|
274
|
+
data: {
|
|
275
|
+
status: 'approved',
|
|
276
|
+
resolvedAt: new Date(),
|
|
277
|
+
metadata: JSON.stringify({
|
|
278
|
+
...metadata,
|
|
279
|
+
tokenHash: getTokenHash(token),
|
|
280
|
+
approvedPermissions: newPermissions,
|
|
281
|
+
approvedWalletAccess: newWalletAccess,
|
|
282
|
+
approvedLimits: newLimits,
|
|
283
|
+
requestedPubkey: normalizedPubkey,
|
|
284
|
+
}),
|
|
285
|
+
},
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
await prisma.log.create({
|
|
289
|
+
data: {
|
|
290
|
+
walletAddress: getColdWalletAddress() || 'system',
|
|
291
|
+
title: 'Permission Update Approved',
|
|
292
|
+
description: `Updated permissions for ${agentId}`,
|
|
293
|
+
},
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
events.tokenCreated({
|
|
297
|
+
tokenHash: getTokenHash(token),
|
|
298
|
+
agentId,
|
|
299
|
+
limit: newLimits?.fund ?? 0,
|
|
300
|
+
permissions: newPermissions,
|
|
301
|
+
expiresAt: Date.now() + ttl * 1000,
|
|
302
|
+
});
|
|
303
|
+
events.actionResolved({ id, type: request.type, approved: true, resolvedBy: 'dashboard' });
|
|
304
|
+
|
|
305
|
+
return {
|
|
306
|
+
success: true,
|
|
307
|
+
statusCode: 200,
|
|
308
|
+
data: {
|
|
309
|
+
success: true,
|
|
310
|
+
token,
|
|
311
|
+
agentId,
|
|
312
|
+
permissions: newPermissions,
|
|
313
|
+
walletAccess: newWalletAccess,
|
|
314
|
+
limits: newLimits,
|
|
315
|
+
},
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// For other types (fund_transfer, etc.), update DB and emit event
|
|
320
|
+
await prisma.humanAction.update({
|
|
321
|
+
where: { id },
|
|
322
|
+
data: { status: approved ? 'approved' : 'rejected', resolvedAt: new Date() },
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
events.actionResolved({ id, type: request.type, approved, resolvedBy: 'dashboard' });
|
|
326
|
+
|
|
327
|
+
return { success: true, statusCode: 200, data: { success: true, approved } };
|
|
328
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { ethers } from 'ethers';
|
|
2
|
+
import { getRpcUrl } from './config';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Resolve an ENS name (.eth) to an Ethereum address.
|
|
6
|
+
* Uses ethers built-in provider.resolveName() which handles ENS natively.
|
|
7
|
+
* ENS resolution always uses Ethereum mainnet (ENS is deployed on L1).
|
|
8
|
+
*/
|
|
9
|
+
export async function resolveName(name: string): Promise<{ address: string; name: string }> {
|
|
10
|
+
if (!name || typeof name !== 'string') {
|
|
11
|
+
throw new Error('Name is required');
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Only support .eth names for now (.sol is out of scope)
|
|
15
|
+
if (!name.endsWith('.eth')) {
|
|
16
|
+
throw new Error(`Unsupported name format: ${name}. Only .eth names are supported.`);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// ENS lives on Ethereum mainnet — always resolve against L1
|
|
20
|
+
const rpcUrl = await getRpcUrl('ethereum');
|
|
21
|
+
const provider = new ethers.JsonRpcProvider(rpcUrl);
|
|
22
|
+
|
|
23
|
+
const address = await provider.resolveName(name);
|
|
24
|
+
if (!address) {
|
|
25
|
+
throw new Error(`Could not resolve: ${name}`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return { address, name };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Check if a string looks like an ENS name (contains a dot).
|
|
33
|
+
*/
|
|
34
|
+
export function looksLikeName(value: string): boolean {
|
|
35
|
+
return value.includes('.') && !value.startsWith('0x') && !value.match(/^[1-9A-HJ-NP-Za-km-z]{32,44}$/);
|
|
36
|
+
}
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
import { spawn } from 'child_process';
|
|
2
|
+
import {
|
|
3
|
+
canonicalizeCredentialFieldKey,
|
|
4
|
+
getCredentialPrimaryFieldKey,
|
|
5
|
+
} from '../../shared/credential-field-schema';
|
|
6
|
+
|
|
7
|
+
export type SecretGistAccessMode = 'anyone' | 'password';
|
|
8
|
+
export type SecretGistErrorCode = 'GH_MISSING' | 'GH_AUTH_REQUIRED' | 'GH_CREATE_FAILED';
|
|
9
|
+
|
|
10
|
+
export interface SecretGistField {
|
|
11
|
+
key: string;
|
|
12
|
+
value: string;
|
|
13
|
+
sensitive?: boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export class SecretGistError extends Error {
|
|
17
|
+
code: SecretGistErrorCode;
|
|
18
|
+
remediation: string;
|
|
19
|
+
detail?: string;
|
|
20
|
+
|
|
21
|
+
constructor(
|
|
22
|
+
code: SecretGistErrorCode,
|
|
23
|
+
message: string,
|
|
24
|
+
remediation: string,
|
|
25
|
+
detail?: string,
|
|
26
|
+
) {
|
|
27
|
+
super(message);
|
|
28
|
+
this.name = 'SecretGistError';
|
|
29
|
+
this.code = code;
|
|
30
|
+
this.remediation = remediation;
|
|
31
|
+
this.detail = detail;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface SecretGistInput {
|
|
36
|
+
credentialId: string;
|
|
37
|
+
credentialName: string;
|
|
38
|
+
credentialType?: string;
|
|
39
|
+
shareUrl: string;
|
|
40
|
+
accessMode: SecretGistAccessMode;
|
|
41
|
+
oneTimeOnly: boolean;
|
|
42
|
+
expiresAfter: string;
|
|
43
|
+
fields?: SecretGistField[];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface SecretGistDraft {
|
|
47
|
+
title: string;
|
|
48
|
+
filename: string;
|
|
49
|
+
marker: string;
|
|
50
|
+
identifier: string;
|
|
51
|
+
content: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface SecretGistResult extends SecretGistDraft {
|
|
55
|
+
url: string;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
interface GhCommandResult {
|
|
59
|
+
code: number;
|
|
60
|
+
stdout: string;
|
|
61
|
+
stderr: string;
|
|
62
|
+
spawnError: NodeJS.ErrnoException | null;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function normalizeInlineText(raw: string): string {
|
|
66
|
+
return raw.replace(/\s+/g, ' ').trim();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function normalizeFieldValue(raw: string): string {
|
|
70
|
+
return raw.replace(/\r?\n/g, '\\n').trim();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function normalizeLookupKey(type: string | undefined, key: string): string {
|
|
74
|
+
const trimmed = key.trim();
|
|
75
|
+
if (!trimmed) return '';
|
|
76
|
+
if (!type) return trimmed.toLowerCase();
|
|
77
|
+
return canonicalizeCredentialFieldKey(type, trimmed).toLowerCase();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function formatFieldLabel(raw: string): string {
|
|
81
|
+
const trimmed = normalizeInlineText(raw);
|
|
82
|
+
if (!trimmed) return 'FIELD';
|
|
83
|
+
return trimmed.replace(/\s+/g, '_').toUpperCase();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function resolvePrimaryField(fields: SecretGistField[], type?: string): { index: number; value: string } {
|
|
87
|
+
if (fields.length === 0) return { index: -1, value: '(none)' };
|
|
88
|
+
|
|
89
|
+
if (type) {
|
|
90
|
+
const mappedKey = normalizeLookupKey(type, getCredentialPrimaryFieldKey(type));
|
|
91
|
+
if (mappedKey) {
|
|
92
|
+
const mappedSensitiveIndex = fields.findIndex((field) =>
|
|
93
|
+
normalizeLookupKey(type, field.key) === mappedKey && field.sensitive !== false);
|
|
94
|
+
if (mappedSensitiveIndex >= 0) {
|
|
95
|
+
return { index: mappedSensitiveIndex, value: fields[mappedSensitiveIndex].value };
|
|
96
|
+
}
|
|
97
|
+
const mappedIndex = fields.findIndex((field) => normalizeLookupKey(type, field.key) === mappedKey);
|
|
98
|
+
if (mappedIndex >= 0) {
|
|
99
|
+
return { index: mappedIndex, value: fields[mappedIndex].value };
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const valueKeyIndex = fields.findIndex((field) => normalizeLookupKey(type, field.key) === 'value');
|
|
105
|
+
if (valueKeyIndex >= 0) {
|
|
106
|
+
return { index: valueKeyIndex, value: fields[valueKeyIndex].value };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const firstSensitiveIndex = fields.findIndex((field) => field.sensitive !== false);
|
|
110
|
+
if (firstSensitiveIndex >= 0) {
|
|
111
|
+
return { index: firstSensitiveIndex, value: fields[firstSensitiveIndex].value };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return { index: 0, value: fields[0].value };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function sanitizeFilenameSlug(raw: string): string {
|
|
118
|
+
return raw
|
|
119
|
+
.toLowerCase()
|
|
120
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
121
|
+
.replace(/^-+|-+$/g, '')
|
|
122
|
+
.slice(0, 48);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function extractFirstUrl(raw: string): string | null {
|
|
126
|
+
const match = raw.match(/https?:\/\/[^\s]+/i);
|
|
127
|
+
if (!match) return null;
|
|
128
|
+
return match[0].replace(/[)\],.]+$/, '');
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function looksLikeAuthFailure(raw: string): boolean {
|
|
132
|
+
const lower = raw.toLowerCase();
|
|
133
|
+
return lower.includes('gh auth login')
|
|
134
|
+
|| lower.includes('not logged in')
|
|
135
|
+
|| lower.includes('authenticate')
|
|
136
|
+
|| lower.includes('authentication');
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function missingGhError(): SecretGistError {
|
|
140
|
+
return new SecretGistError(
|
|
141
|
+
'GH_MISSING',
|
|
142
|
+
'GitHub CLI (`gh`) is not installed. Secret gist sharing requires `gh`.',
|
|
143
|
+
'Install GitHub CLI from https://cli.github.com/ and run `gh auth login`, then retry.',
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function unauthenticatedGhError(detail?: string): SecretGistError {
|
|
148
|
+
return new SecretGistError(
|
|
149
|
+
'GH_AUTH_REQUIRED',
|
|
150
|
+
'GitHub CLI is not authenticated for gist creation.',
|
|
151
|
+
'Run `gh auth login` (or `gh auth status`) and retry the share command.',
|
|
152
|
+
detail,
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function gistCreateError(detail?: string): SecretGistError {
|
|
157
|
+
return new SecretGistError(
|
|
158
|
+
'GH_CREATE_FAILED',
|
|
159
|
+
'Failed to create secret gist via GitHub CLI.',
|
|
160
|
+
'Verify `gh auth status` succeeds and retry `gh gist create` (without `--public`).',
|
|
161
|
+
detail,
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
function runGh(args: string[], stdinText?: string): Promise<GhCommandResult> {
|
|
166
|
+
return new Promise((resolve) => {
|
|
167
|
+
let stdout = '';
|
|
168
|
+
let stderr = '';
|
|
169
|
+
let settled = false;
|
|
170
|
+
|
|
171
|
+
const finish = (result: GhCommandResult) => {
|
|
172
|
+
if (settled) return;
|
|
173
|
+
settled = true;
|
|
174
|
+
resolve(result);
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
const child = spawn('gh', args, {
|
|
178
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
child.stdout.on('data', (chunk: Buffer | string) => {
|
|
182
|
+
stdout += chunk.toString();
|
|
183
|
+
});
|
|
184
|
+
child.stderr.on('data', (chunk: Buffer | string) => {
|
|
185
|
+
stderr += chunk.toString();
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
child.on('error', (error: NodeJS.ErrnoException) => {
|
|
189
|
+
finish({
|
|
190
|
+
code: 127,
|
|
191
|
+
stdout,
|
|
192
|
+
stderr,
|
|
193
|
+
spawnError: error,
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
child.on('close', (code) => {
|
|
198
|
+
finish({
|
|
199
|
+
code: typeof code === 'number' ? code : 1,
|
|
200
|
+
stdout,
|
|
201
|
+
stderr,
|
|
202
|
+
spawnError: null,
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
if (stdinText) {
|
|
207
|
+
child.stdin.write(stdinText);
|
|
208
|
+
}
|
|
209
|
+
child.stdin.end();
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function isMissingGh(result: GhCommandResult): boolean {
|
|
214
|
+
return result.spawnError?.code === 'ENOENT';
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export function buildSecretGistDraft(input: SecretGistInput): SecretGistDraft {
|
|
218
|
+
const normalizedName = normalizeInlineText(input.credentialName) || 'credential';
|
|
219
|
+
const filenameSlug = sanitizeFilenameSlug(normalizedName) || 'credential';
|
|
220
|
+
const title = 'AURAMAXX.SH';
|
|
221
|
+
const filename = `auramaxx-sh-${filenameSlug}.txt`;
|
|
222
|
+
const marker = '';
|
|
223
|
+
const identifier = '';
|
|
224
|
+
|
|
225
|
+
const normalizedFields = (input.fields || [])
|
|
226
|
+
.map((field) => ({
|
|
227
|
+
key: normalizeInlineText(field.key || 'field'),
|
|
228
|
+
value: normalizeFieldValue(String(field.value || '')),
|
|
229
|
+
sensitive: field.sensitive,
|
|
230
|
+
}))
|
|
231
|
+
.filter((field) => field.key.length > 0 && field.value.length > 0);
|
|
232
|
+
|
|
233
|
+
const primary = resolvePrimaryField(normalizedFields, input.credentialType);
|
|
234
|
+
const otherFields = normalizedFields.filter((_, index) => index !== primary.index);
|
|
235
|
+
|
|
236
|
+
const lines = [
|
|
237
|
+
'------------------------------',
|
|
238
|
+
'AURAMAXX.SH',
|
|
239
|
+
'------------------------------',
|
|
240
|
+
`NAME: ${normalizedName}`,
|
|
241
|
+
`VALUE: ${normalizeFieldValue(primary.value) || '(none)'}`,
|
|
242
|
+
'',
|
|
243
|
+
];
|
|
244
|
+
|
|
245
|
+
for (const field of otherFields) {
|
|
246
|
+
lines.push(`${formatFieldLabel(field.key)}: ${field.value}`);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return {
|
|
250
|
+
title,
|
|
251
|
+
filename,
|
|
252
|
+
marker,
|
|
253
|
+
identifier,
|
|
254
|
+
content: `${lines.join('\n')}\n`,
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
export async function createSecretGist(input: SecretGistInput): Promise<SecretGistResult> {
|
|
259
|
+
const authCheck = await runGh(['auth', 'status']);
|
|
260
|
+
if (isMissingGh(authCheck)) {
|
|
261
|
+
throw missingGhError();
|
|
262
|
+
}
|
|
263
|
+
if (authCheck.code !== 0) {
|
|
264
|
+
const detail = normalizeInlineText(`${authCheck.stderr}\n${authCheck.stdout}`.trim());
|
|
265
|
+
throw unauthenticatedGhError(detail || undefined);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
const draft = buildSecretGistDraft(input);
|
|
269
|
+
const createResult = await runGh(
|
|
270
|
+
// `gh gist create` defaults to secret/private. Newer gh versions do not support `--private`.
|
|
271
|
+
['gist', 'create', '--filename', draft.filename, '--desc', draft.title, '-'],
|
|
272
|
+
draft.content,
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
if (isMissingGh(createResult)) {
|
|
276
|
+
throw missingGhError();
|
|
277
|
+
}
|
|
278
|
+
if (createResult.code !== 0) {
|
|
279
|
+
const detail = normalizeInlineText(`${createResult.stderr}\n${createResult.stdout}`.trim());
|
|
280
|
+
if (looksLikeAuthFailure(detail)) {
|
|
281
|
+
throw unauthenticatedGhError(detail || undefined);
|
|
282
|
+
}
|
|
283
|
+
throw gistCreateError(detail || undefined);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const combinedOutput = `${createResult.stdout}\n${createResult.stderr}`;
|
|
287
|
+
const url = extractFirstUrl(combinedOutput);
|
|
288
|
+
if (!url) {
|
|
289
|
+
throw gistCreateError('gh gist create succeeded but no gist URL was returned.');
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return {
|
|
293
|
+
...draft,
|
|
294
|
+
url,
|
|
295
|
+
};
|
|
296
|
+
}
|