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,570 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unix Socket IPC Server
|
|
3
|
+
*
|
|
4
|
+
* Provides a secure Unix socket for agent connections with:
|
|
5
|
+
* - Socket at /tmp/aura-cli-{uid}.sock with mode 0600 (owner only)
|
|
6
|
+
* - Auto-approve for same-UID processes (socket permission = peer credential)
|
|
7
|
+
* - Encrypted token delivery (server-issued RSA-OAEP/AES-256-GCM envelope)
|
|
8
|
+
*
|
|
9
|
+
* Protocol (JSON over newline-delimited messages):
|
|
10
|
+
*
|
|
11
|
+
* Agent -> CLI:
|
|
12
|
+
* { "type": "auth", "agentId": "my-agent", "pubkey": "...", "autoApprove": true }
|
|
13
|
+
* { "type": "auth", "agentId": "my-agent", "pubkey": "...", "limit": 0.1, "permissions": [...] }
|
|
14
|
+
* { "type": "ping" }
|
|
15
|
+
*
|
|
16
|
+
* CLI -> Agent:
|
|
17
|
+
* { "type": "auth_pending", "requestId": "..." }
|
|
18
|
+
* { "type": "auth_approved", "encryptedToken": "...", "agentId": "...", ... }
|
|
19
|
+
* { "type": "auth_rejected", "requestId": "..." }
|
|
20
|
+
* { "type": "pong" }
|
|
21
|
+
* { "type": "error", "message": "..." }
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import * as net from 'net';
|
|
25
|
+
import * as fs from 'fs';
|
|
26
|
+
import { getDefaultSync } from '../lib/defaults';
|
|
27
|
+
import { getErrorMessage } from '../lib/error';
|
|
28
|
+
import { isValidAgentPubkey, normalizeAgentPubkey, encryptToAgentPubkey } from '../lib/credential-transport';
|
|
29
|
+
import { createToken } from '../lib/auth';
|
|
30
|
+
import { isUnlocked } from '../lib/cold';
|
|
31
|
+
import { AgentProfileError, resolveProfileToEffectivePolicy } from '../lib/agent-profiles';
|
|
32
|
+
|
|
33
|
+
interface SocketServerOptions {
|
|
34
|
+
serverUrl: string;
|
|
35
|
+
getToken?: () => string | null;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface AgentAuthRequest {
|
|
39
|
+
type: 'auth';
|
|
40
|
+
agentId: string;
|
|
41
|
+
autoApprove?: boolean;
|
|
42
|
+
limit?: number;
|
|
43
|
+
permissions?: string[];
|
|
44
|
+
ttl?: number;
|
|
45
|
+
limits?: { fund?: number; send?: number; swap?: number };
|
|
46
|
+
walletAccess?: string[];
|
|
47
|
+
credentialAccess?: { read?: string[]; write?: string[]; excludeFields?: string[]; ttl?: number; maxReads?: number };
|
|
48
|
+
profile?: string;
|
|
49
|
+
profileVersion?: string;
|
|
50
|
+
profileOverrides?: {
|
|
51
|
+
ttlSeconds?: number;
|
|
52
|
+
maxReads?: number;
|
|
53
|
+
readScopes?: string[];
|
|
54
|
+
writeScopes?: string[];
|
|
55
|
+
excludeFields?: string[];
|
|
56
|
+
};
|
|
57
|
+
pubkey?: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
interface PendingAgentAuth {
|
|
61
|
+
socket: net.Socket;
|
|
62
|
+
requestId: string;
|
|
63
|
+
secret: string;
|
|
64
|
+
agentId: string;
|
|
65
|
+
pubkey?: string;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Rate limiting for auto-approve: track tokens per socket
|
|
69
|
+
const autoApproveRates = new WeakMap<net.Socket, { count: number; resetAt: number }>();
|
|
70
|
+
const AUTO_APPROVE_MAX = 10;
|
|
71
|
+
const AUTO_APPROVE_WINDOW_MS = 60_000;
|
|
72
|
+
|
|
73
|
+
function checkAutoApproveRate(socket: net.Socket): boolean {
|
|
74
|
+
const now = Date.now();
|
|
75
|
+
let entry = autoApproveRates.get(socket);
|
|
76
|
+
if (!entry || now > entry.resetAt) {
|
|
77
|
+
entry = { count: 0, resetAt: now + AUTO_APPROVE_WINDOW_MS };
|
|
78
|
+
autoApproveRates.set(socket, entry);
|
|
79
|
+
}
|
|
80
|
+
if (entry.count >= AUTO_APPROVE_MAX) return false;
|
|
81
|
+
entry.count++;
|
|
82
|
+
return true;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export class SocketServer {
|
|
86
|
+
private server: net.Server | null = null;
|
|
87
|
+
private socketPath: string;
|
|
88
|
+
private options: SocketServerOptions;
|
|
89
|
+
private pendingAuths: Map<string, PendingAgentAuth> = new Map();
|
|
90
|
+
private pollIntervals: Map<string, NodeJS.Timeout> = new Map();
|
|
91
|
+
|
|
92
|
+
constructor(options: SocketServerOptions) {
|
|
93
|
+
this.options = options;
|
|
94
|
+
const uid = process.getuid?.() ?? 'unknown';
|
|
95
|
+
this.socketPath = `/tmp/aura-cli-${uid}.sock`;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
getSocketPath(): string {
|
|
99
|
+
return this.socketPath;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async start(): Promise<void> {
|
|
103
|
+
if (fs.existsSync(this.socketPath)) {
|
|
104
|
+
fs.unlinkSync(this.socketPath);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return new Promise((resolve, reject) => {
|
|
108
|
+
this.server = net.createServer((socket) => {
|
|
109
|
+
this.handleConnection(socket);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
this.server.on('error', (err) => {
|
|
113
|
+
console.error('Socket server error:', err.message);
|
|
114
|
+
reject(err);
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
this.server.listen(this.socketPath, () => {
|
|
118
|
+
fs.chmodSync(this.socketPath, 0o600);
|
|
119
|
+
console.log(`Unix socket listening at ${this.socketPath}`);
|
|
120
|
+
resolve();
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async stop(): Promise<void> {
|
|
126
|
+
this.pollIntervals.forEach((interval) => {
|
|
127
|
+
clearInterval(interval);
|
|
128
|
+
});
|
|
129
|
+
this.pollIntervals.clear();
|
|
130
|
+
|
|
131
|
+
return new Promise((resolve) => {
|
|
132
|
+
if (this.server) {
|
|
133
|
+
this.server.close(() => {
|
|
134
|
+
if (fs.existsSync(this.socketPath)) {
|
|
135
|
+
try { fs.unlinkSync(this.socketPath); } catch { /* ignore */ }
|
|
136
|
+
}
|
|
137
|
+
resolve();
|
|
138
|
+
});
|
|
139
|
+
} else {
|
|
140
|
+
resolve();
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
private handleConnection(socket: net.Socket): void {
|
|
146
|
+
const MAX_BUFFER_SIZE = 64 * 1024; // 64KB
|
|
147
|
+
let buffer = '';
|
|
148
|
+
|
|
149
|
+
socket.on('data', (data) => {
|
|
150
|
+
buffer += data.toString();
|
|
151
|
+
|
|
152
|
+
if (buffer.length > MAX_BUFFER_SIZE) {
|
|
153
|
+
console.error('[socket] Buffer overflow — disconnecting client');
|
|
154
|
+
this.send(socket, { type: 'error', message: 'Message too large' });
|
|
155
|
+
socket.destroy();
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
let newlineIndex;
|
|
160
|
+
while ((newlineIndex = buffer.indexOf('\n')) !== -1) {
|
|
161
|
+
const line = buffer.substring(0, newlineIndex);
|
|
162
|
+
buffer = buffer.substring(newlineIndex + 1);
|
|
163
|
+
|
|
164
|
+
if (line.trim()) {
|
|
165
|
+
this.handleMessage(socket, line.trim());
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
socket.on('error', (err) => {
|
|
171
|
+
console.error(`[socket] Connection error: ${err.message} (code=${(err as NodeJS.ErrnoException).code})`);
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
socket.on('close', () => {
|
|
175
|
+
const toDelete: string[] = [];
|
|
176
|
+
this.pendingAuths.forEach((pending, requestId) => {
|
|
177
|
+
if (pending.socket === socket) {
|
|
178
|
+
const interval = this.pollIntervals.get(requestId);
|
|
179
|
+
if (interval) {
|
|
180
|
+
clearInterval(interval);
|
|
181
|
+
this.pollIntervals.delete(requestId);
|
|
182
|
+
}
|
|
183
|
+
toDelete.push(requestId);
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
toDelete.forEach(id => this.pendingAuths.delete(id));
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
private handleMessage(socket: net.Socket, message: string): void {
|
|
191
|
+
try {
|
|
192
|
+
const msg = JSON.parse(message);
|
|
193
|
+
|
|
194
|
+
switch (msg.type) {
|
|
195
|
+
case 'ping':
|
|
196
|
+
this.send(socket, { type: 'pong' });
|
|
197
|
+
break;
|
|
198
|
+
|
|
199
|
+
case 'auth':
|
|
200
|
+
this.handleAuthRequest(socket, msg as AgentAuthRequest);
|
|
201
|
+
break;
|
|
202
|
+
|
|
203
|
+
default:
|
|
204
|
+
this.send(socket, { type: 'error', message: `Unknown message type: ${msg.type}` });
|
|
205
|
+
}
|
|
206
|
+
} catch {
|
|
207
|
+
this.send(socket, { type: 'error', message: 'Invalid JSON message' });
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Handle auth request — auto-approve path or standard approval flow
|
|
213
|
+
*/
|
|
214
|
+
private async handleAuthRequest(socket: net.Socket, request: AgentAuthRequest): Promise<void> {
|
|
215
|
+
try {
|
|
216
|
+
if (typeof request.pubkey !== 'string' || !request.pubkey.trim()) {
|
|
217
|
+
this.send(socket, { type: 'error', message: 'pubkey is required for auth requests' });
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
if (!isValidAgentPubkey(request.pubkey)) {
|
|
222
|
+
this.send(socket, { type: 'error', message: 'Invalid RSA public key' });
|
|
223
|
+
return;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const normalizedPubkey = normalizeAgentPubkey(request.pubkey);
|
|
227
|
+
|
|
228
|
+
// ── Auto-approve path ──
|
|
229
|
+
if (request.autoApprove) {
|
|
230
|
+
const autoApproveEnabled = getDefaultSync<boolean>('trust.localAutoApprove', true);
|
|
231
|
+
if (!autoApproveEnabled) {
|
|
232
|
+
this.send(socket, { type: 'error', message: 'Auto-approve is disabled. Use standard approval flow.' });
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Rate limit
|
|
237
|
+
if (!checkAutoApproveRate(socket)) {
|
|
238
|
+
this.send(socket, { type: 'error', message: 'Rate limit exceeded for auto-approve (max 10/minute)' });
|
|
239
|
+
return;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// Resolve local auto-approve policy defaults
|
|
243
|
+
const limits = getDefaultSync<Record<string, number>>('trust.localLimits', { fund: 0, send: 0, swap: 0 });
|
|
244
|
+
const localProfile = String(getDefaultSync<string>('trust.localProfile', 'dev') || '').trim();
|
|
245
|
+
const localProfileVersion = String(getDefaultSync<string>('trust.localProfileVersion', 'v1') || '').trim();
|
|
246
|
+
const localProfileOverrides = getDefaultSync<AgentAuthRequest['profileOverrides'] | null>('trust.localProfileOverrides', null);
|
|
247
|
+
const agentId = request.agentId || 'local-agent';
|
|
248
|
+
if (!localProfile) {
|
|
249
|
+
this.send(socket, { type: 'error', message: 'Local profile is not configured for socket issuance.' });
|
|
250
|
+
return;
|
|
251
|
+
}
|
|
252
|
+
if (localProfile === 'strict') {
|
|
253
|
+
this.send(socket, { type: 'error', message: 'Strict profile requires manual approval (auto-approve disabled).' });
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
const hasTokenProvider = typeof this.options.getToken === 'function';
|
|
258
|
+
const adminToken = hasTokenProvider ? this.options.getToken() : null;
|
|
259
|
+
|
|
260
|
+
// CLI daemon mode: require unlocked admin token callback.
|
|
261
|
+
if (hasTokenProvider && !adminToken) {
|
|
262
|
+
this.send(socket, { type: 'error', message: 'CLI daemon is locked. Unlock before socket auto-approve.' });
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// When an admin token callback exists, issue via HTTP route.
|
|
267
|
+
if (adminToken) {
|
|
268
|
+
const issueBody: Record<string, unknown> = {
|
|
269
|
+
agentId,
|
|
270
|
+
limits,
|
|
271
|
+
pubkey: normalizedPubkey,
|
|
272
|
+
profile: localProfile,
|
|
273
|
+
profileVersion: localProfileVersion || 'v1',
|
|
274
|
+
};
|
|
275
|
+
if (localProfileOverrides) {
|
|
276
|
+
issueBody.profileOverrides = localProfileOverrides;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
const issueResponse = await fetch(`${this.options.serverUrl}/actions/token`, {
|
|
280
|
+
method: 'POST',
|
|
281
|
+
headers: {
|
|
282
|
+
'Authorization': `Bearer ${adminToken}`,
|
|
283
|
+
'Content-Type': 'application/json',
|
|
284
|
+
},
|
|
285
|
+
body: JSON.stringify(issueBody),
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
let issueData: {
|
|
289
|
+
success?: boolean;
|
|
290
|
+
error?: string;
|
|
291
|
+
code?: string;
|
|
292
|
+
encryptedToken?: string;
|
|
293
|
+
permissions?: string[];
|
|
294
|
+
limits?: { fund?: number; send?: number; swap?: number };
|
|
295
|
+
profile?: { id: string; version: string; displayName?: string; rationale?: string };
|
|
296
|
+
effectivePolicyHash?: string;
|
|
297
|
+
overrideDelta?: string[];
|
|
298
|
+
warnings?: string[];
|
|
299
|
+
expiresIn?: number;
|
|
300
|
+
} = {};
|
|
301
|
+
try {
|
|
302
|
+
issueData = await issueResponse.json();
|
|
303
|
+
} catch {
|
|
304
|
+
issueData = {};
|
|
305
|
+
}
|
|
306
|
+
if (!issueResponse.ok || !issueData.success || !issueData.encryptedToken) {
|
|
307
|
+
const message = issueData.error
|
|
308
|
+
? (issueData.code ? `${issueData.error} (${issueData.code})` : issueData.error)
|
|
309
|
+
: `Auto-approve token issue failed (${issueResponse.status})`;
|
|
310
|
+
this.send(socket, { type: 'error', message });
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
this.send(socket, {
|
|
315
|
+
type: 'auth_approved',
|
|
316
|
+
encryptedToken: issueData.encryptedToken,
|
|
317
|
+
agentId,
|
|
318
|
+
permissions: issueData.permissions || [],
|
|
319
|
+
limits: issueData.limits || limits,
|
|
320
|
+
ttl: issueData.expiresIn,
|
|
321
|
+
profile: issueData.profile,
|
|
322
|
+
effectivePolicyHash: issueData.effectivePolicyHash,
|
|
323
|
+
overrideDelta: issueData.overrideDelta,
|
|
324
|
+
warnings: issueData.warnings,
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
console.log(`[socket] Auto-approved token for ${agentId} (ttl=${issueData.expiresIn ?? 0}s)`);
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Server-runtime mode: issue token in-process so settings changes apply immediately.
|
|
332
|
+
if (!isUnlocked()) {
|
|
333
|
+
this.send(socket, { type: 'error', message: 'Wallet is locked. Unlock first.' });
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
const fundLimit = typeof limits.fund === 'number' ? limits.fund : 0;
|
|
338
|
+
const defaultSendLimit = getDefaultSync<number>('limits.send', 0.1);
|
|
339
|
+
const defaultSwapLimit = getDefaultSync<number>('limits.swap', 0.1);
|
|
340
|
+
|
|
341
|
+
const resolvedProfile = resolveProfileToEffectivePolicy({
|
|
342
|
+
profileId: localProfile,
|
|
343
|
+
profileVersion: localProfileVersion || 'v1',
|
|
344
|
+
overrides: localProfileOverrides ?? undefined,
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
const effectivePermissions = [...resolvedProfile.permissions];
|
|
348
|
+
const ttlSeconds = resolvedProfile.ttlSeconds;
|
|
349
|
+
const tokenLimits = {
|
|
350
|
+
fund: fundLimit,
|
|
351
|
+
send: defaultSendLimit,
|
|
352
|
+
swap: defaultSwapLimit,
|
|
353
|
+
...limits,
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
const token = await createToken(agentId, fundLimit, effectivePermissions, ttlSeconds, {
|
|
357
|
+
limits: tokenLimits,
|
|
358
|
+
credentialAccess: resolvedProfile.credentialAccess,
|
|
359
|
+
agentPubkey: normalizedPubkey,
|
|
360
|
+
});
|
|
361
|
+
const encryptedToken = encryptToAgentPubkey(token, normalizedPubkey);
|
|
362
|
+
|
|
363
|
+
this.send(socket, {
|
|
364
|
+
type: 'auth_approved',
|
|
365
|
+
encryptedToken,
|
|
366
|
+
agentId,
|
|
367
|
+
permissions: effectivePermissions,
|
|
368
|
+
limits: tokenLimits,
|
|
369
|
+
ttl: ttlSeconds,
|
|
370
|
+
profile: resolvedProfile.profile,
|
|
371
|
+
effectivePolicyHash: resolvedProfile.effectivePolicyHash,
|
|
372
|
+
overrideDelta: resolvedProfile.overrideDelta,
|
|
373
|
+
warnings: resolvedProfile.warnings,
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
console.log(`[socket] Auto-approved token for ${agentId} (ttl=${ttlSeconds}s; in-process issuance)`);
|
|
377
|
+
return;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// ── Standard approval flow (proxy to HTTP) ──
|
|
381
|
+
if (request.permissions !== undefined || request.ttl !== undefined || request.credentialAccess !== undefined) {
|
|
382
|
+
this.send(socket, {
|
|
383
|
+
type: 'error',
|
|
384
|
+
message: 'Raw permission issuance is disabled. Use profile + optional profileOverrides.',
|
|
385
|
+
});
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
const requestProfile = (
|
|
389
|
+
typeof request.profile === 'string' && request.profile.trim().length > 0
|
|
390
|
+
? request.profile.trim()
|
|
391
|
+
: String(getDefaultSync<string>('trust.localProfile', 'dev') || '').trim()
|
|
392
|
+
);
|
|
393
|
+
const requestProfileVersion = (
|
|
394
|
+
typeof request.profileVersion === 'string' && request.profileVersion.trim().length > 0
|
|
395
|
+
? request.profileVersion.trim()
|
|
396
|
+
: String(getDefaultSync<string>('trust.localProfileVersion', 'v1') || '').trim()
|
|
397
|
+
);
|
|
398
|
+
if (!requestProfile) {
|
|
399
|
+
this.send(socket, { type: 'error', message: 'profile is required for auth requests' });
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const response = await fetch(`${this.options.serverUrl}/auth`, {
|
|
404
|
+
method: 'POST',
|
|
405
|
+
headers: { 'Content-Type': 'application/json' },
|
|
406
|
+
body: JSON.stringify({
|
|
407
|
+
agentId: request.agentId,
|
|
408
|
+
limit: request.limit ?? getDefaultSync<number>('limits.fund', 0.1),
|
|
409
|
+
limits: request.limits,
|
|
410
|
+
walletAccess: request.walletAccess,
|
|
411
|
+
profile: requestProfile,
|
|
412
|
+
profileVersion: requestProfileVersion || 'v1',
|
|
413
|
+
profileOverrides: request.profileOverrides,
|
|
414
|
+
pubkey: request.pubkey,
|
|
415
|
+
})
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
const data = await response.json() as {
|
|
419
|
+
success?: boolean;
|
|
420
|
+
requestId?: string;
|
|
421
|
+
secret?: string;
|
|
422
|
+
error?: string;
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
if (!response.ok || !data.success) {
|
|
426
|
+
this.send(socket, {
|
|
427
|
+
type: 'error',
|
|
428
|
+
message: data.error || 'Failed to create auth request'
|
|
429
|
+
});
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// Store pending auth info (with pubkey for encrypted delivery)
|
|
434
|
+
const pending: PendingAgentAuth = {
|
|
435
|
+
socket,
|
|
436
|
+
requestId: data.requestId!,
|
|
437
|
+
secret: data.secret!,
|
|
438
|
+
agentId: request.agentId,
|
|
439
|
+
pubkey: normalizedPubkey,
|
|
440
|
+
};
|
|
441
|
+
this.pendingAuths.set(data.requestId!, pending);
|
|
442
|
+
|
|
443
|
+
this.send(socket, {
|
|
444
|
+
type: 'auth_pending',
|
|
445
|
+
requestId: data.requestId,
|
|
446
|
+
message: 'Waiting for human approval'
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
this.startPolling(data.requestId!, data.secret!);
|
|
450
|
+
|
|
451
|
+
} catch (error) {
|
|
452
|
+
if (error instanceof AgentProfileError) {
|
|
453
|
+
const suffix = error.code ? ` (${error.code})` : '';
|
|
454
|
+
this.send(socket, { type: 'error', message: `${error.message}${suffix}` });
|
|
455
|
+
return;
|
|
456
|
+
}
|
|
457
|
+
const message = getErrorMessage(error);
|
|
458
|
+
this.send(socket, { type: 'error', message });
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* Start polling for request approval
|
|
464
|
+
*/
|
|
465
|
+
private startPolling(requestId: string, secret: string): void {
|
|
466
|
+
const MAX_POLL_DURATION_MS = 10 * 60 * 1000; // 10 minutes
|
|
467
|
+
const pollStart = Date.now();
|
|
468
|
+
|
|
469
|
+
const interval = setInterval(async () => {
|
|
470
|
+
// Timeout: stop polling after 10 minutes
|
|
471
|
+
if (Date.now() - pollStart > MAX_POLL_DURATION_MS) {
|
|
472
|
+
const pending = this.pendingAuths.get(requestId);
|
|
473
|
+
if (pending) {
|
|
474
|
+
this.send(pending.socket, { type: 'error', message: 'Auth request timed out (10 minutes)' });
|
|
475
|
+
this.pendingAuths.delete(requestId);
|
|
476
|
+
}
|
|
477
|
+
clearInterval(interval);
|
|
478
|
+
this.pollIntervals.delete(requestId);
|
|
479
|
+
console.error(`[socket] Poll timeout for request ${requestId}`);
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
try {
|
|
483
|
+
const response = await fetch(
|
|
484
|
+
`${this.options.serverUrl}/auth/${requestId}?secret=${encodeURIComponent(secret)}`
|
|
485
|
+
);
|
|
486
|
+
|
|
487
|
+
const data = await response.json() as {
|
|
488
|
+
success?: boolean;
|
|
489
|
+
status?: string;
|
|
490
|
+
token?: string;
|
|
491
|
+
encryptedToken?: string;
|
|
492
|
+
agentId?: string;
|
|
493
|
+
limit?: number;
|
|
494
|
+
limits?: { fund?: number; send?: number; swap?: number };
|
|
495
|
+
permissions?: string[];
|
|
496
|
+
walletAccess?: string[];
|
|
497
|
+
profile?: { id: string; version: string; displayName?: string; rationale?: string };
|
|
498
|
+
effectivePolicyHash?: string;
|
|
499
|
+
overrideDelta?: string[];
|
|
500
|
+
warnings?: string[];
|
|
501
|
+
error?: string;
|
|
502
|
+
};
|
|
503
|
+
|
|
504
|
+
if (!response.ok || !data.success) {
|
|
505
|
+
return; // Keep polling
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
const pending = this.pendingAuths.get(requestId);
|
|
509
|
+
if (!pending) {
|
|
510
|
+
clearInterval(interval);
|
|
511
|
+
this.pollIntervals.delete(requestId);
|
|
512
|
+
return;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
if (data.status === 'approved') {
|
|
516
|
+
if (!data.encryptedToken) {
|
|
517
|
+
this.send(pending.socket, {
|
|
518
|
+
type: 'error',
|
|
519
|
+
message: 'Encrypted token unavailable for auth approval',
|
|
520
|
+
});
|
|
521
|
+
clearInterval(interval);
|
|
522
|
+
this.pollIntervals.delete(requestId);
|
|
523
|
+
this.pendingAuths.delete(requestId);
|
|
524
|
+
return;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
this.send(pending.socket, {
|
|
528
|
+
type: 'auth_approved',
|
|
529
|
+
encryptedToken: data.encryptedToken,
|
|
530
|
+
agentId: data.agentId,
|
|
531
|
+
limit: data.limit,
|
|
532
|
+
limits: data.limits,
|
|
533
|
+
permissions: data.permissions,
|
|
534
|
+
walletAccess: data.walletAccess,
|
|
535
|
+
profile: data.profile,
|
|
536
|
+
effectivePolicyHash: data.effectivePolicyHash,
|
|
537
|
+
overrideDelta: data.overrideDelta,
|
|
538
|
+
warnings: data.warnings,
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
clearInterval(interval);
|
|
542
|
+
this.pollIntervals.delete(requestId);
|
|
543
|
+
this.pendingAuths.delete(requestId);
|
|
544
|
+
} else if (data.status === 'rejected') {
|
|
545
|
+
this.send(pending.socket, {
|
|
546
|
+
type: 'auth_rejected',
|
|
547
|
+
requestId,
|
|
548
|
+
message: 'Request was rejected'
|
|
549
|
+
});
|
|
550
|
+
|
|
551
|
+
clearInterval(interval);
|
|
552
|
+
this.pollIntervals.delete(requestId);
|
|
553
|
+
this.pendingAuths.delete(requestId);
|
|
554
|
+
}
|
|
555
|
+
} catch (err) {
|
|
556
|
+
console.error(`[socket] Poll error for ${requestId}: ${getErrorMessage(err)}`);
|
|
557
|
+
}
|
|
558
|
+
}, 2000);
|
|
559
|
+
|
|
560
|
+
this.pollIntervals.set(requestId, interval);
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
private send(socket: net.Socket, message: object): void {
|
|
564
|
+
try {
|
|
565
|
+
socket.write(JSON.stringify(message) + '\n');
|
|
566
|
+
} catch {
|
|
567
|
+
// Socket may be closed
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transport Client - RSA encryption for CLI password transport
|
|
3
|
+
*
|
|
4
|
+
* Mirrors the server's transport.ts but provides the encryption side
|
|
5
|
+
* (server has decryption). Uses RSA-OAEP with SHA-256 for semantic security.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { publicEncrypt, constants, generateKeyPairSync } from 'crypto';
|
|
9
|
+
|
|
10
|
+
export interface AgentKeypair {
|
|
11
|
+
publicKey: string;
|
|
12
|
+
privateKey: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Encrypt a password using the server's RSA public key
|
|
17
|
+
* @param password Plaintext password
|
|
18
|
+
* @param publicKeyPem Server's RSA public key in PEM format
|
|
19
|
+
* @returns Base64-encoded RSA-OAEP encrypted password
|
|
20
|
+
*/
|
|
21
|
+
export function encryptPassword(password: string, publicKeyPem: string): string {
|
|
22
|
+
const buffer = Buffer.from(password, 'utf8');
|
|
23
|
+
const encrypted = publicEncrypt(
|
|
24
|
+
{
|
|
25
|
+
key: publicKeyPem,
|
|
26
|
+
padding: constants.RSA_PKCS1_OAEP_PADDING,
|
|
27
|
+
oaepHash: 'sha256'
|
|
28
|
+
},
|
|
29
|
+
buffer
|
|
30
|
+
);
|
|
31
|
+
return encrypted.toString('base64');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Generate an RSA-OAEP keypair for agent token mint requests.
|
|
36
|
+
*
|
|
37
|
+
* The public key is sent as `pubkey` when minting tokens. The private key can
|
|
38
|
+
* be retained by the caller runtime if credential decryption is needed.
|
|
39
|
+
*/
|
|
40
|
+
export function generateAgentKeypair(): AgentKeypair {
|
|
41
|
+
const pair = generateKeyPairSync('rsa', {
|
|
42
|
+
modulusLength: 2048,
|
|
43
|
+
publicKeyEncoding: { type: 'spki', format: 'pem' },
|
|
44
|
+
privateKeyEncoding: { type: 'pkcs8', format: 'pem' },
|
|
45
|
+
});
|
|
46
|
+
return {
|
|
47
|
+
publicKey: pair.publicKey,
|
|
48
|
+
privateKey: pair.privateKey,
|
|
49
|
+
};
|
|
50
|
+
}
|