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,254 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared credential resolution logic for env.ts and shell-hook.ts
|
|
3
|
+
*
|
|
4
|
+
* Consolidates search → read → decrypt flow to prevent drift between
|
|
5
|
+
* the two consumers (audit finding #3).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { getErrorMessage } from '../../lib/error';
|
|
9
|
+
import { evaluateProjectScopeAccess, emitProjectScopeEvent } from '../../lib/project-scope';
|
|
10
|
+
|
|
11
|
+
// ── Types ──
|
|
12
|
+
|
|
13
|
+
export interface CredentialMeta {
|
|
14
|
+
id: string;
|
|
15
|
+
name: string;
|
|
16
|
+
type: string;
|
|
17
|
+
vaultId: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface DecryptedCredential {
|
|
21
|
+
id: string;
|
|
22
|
+
vaultId: string;
|
|
23
|
+
type: string;
|
|
24
|
+
fields: Array<{ key: string; value: string }>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface AuraMapping {
|
|
28
|
+
envVar: string;
|
|
29
|
+
vault: string | null;
|
|
30
|
+
credentialName: string;
|
|
31
|
+
field: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface ResolveResult {
|
|
35
|
+
resolved: Map<string, string>;
|
|
36
|
+
errors: string[];
|
|
37
|
+
missing: AuraMapping[];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// ── Env var name validation (audit finding #6) ──
|
|
41
|
+
|
|
42
|
+
const ENV_VAR_NAME_RE = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
43
|
+
|
|
44
|
+
export function isValidEnvVarName(name: string): boolean {
|
|
45
|
+
return ENV_VAR_NAME_RE.test(name);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function validateEnvVarName(name: string): void {
|
|
49
|
+
if (!isValidEnvVarName(name)) {
|
|
50
|
+
throw new Error(
|
|
51
|
+
`Invalid env var name '${name}': must match [A-Za-z_][A-Za-z0-9_]*`
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// ── Shell escaping (audit finding #1) ──
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Escape a value for safe use in shell export statements.
|
|
60
|
+
* Uses ANSI-C quoting ($'...') to safely handle newlines, tabs,
|
|
61
|
+
* single quotes, backslashes, and other control characters.
|
|
62
|
+
*/
|
|
63
|
+
export function escapeForShell(value: string): string {
|
|
64
|
+
// Use ANSI-C $'...' quoting which handles all special chars
|
|
65
|
+
const escaped = value
|
|
66
|
+
.replace(/\\/g, '\\\\')
|
|
67
|
+
.replace(/'/g, "\\'")
|
|
68
|
+
.replace(/\n/g, '\\n')
|
|
69
|
+
.replace(/\r/g, '\\r')
|
|
70
|
+
.replace(/\t/g, '\\t')
|
|
71
|
+
.replace(/\0/g, '\\0')
|
|
72
|
+
// Escape any other control characters
|
|
73
|
+
.replace(/[\x01-\x08\x0b\x0c\x0e-\x1f\x7f]/g, (ch) => {
|
|
74
|
+
return '\\x' + ch.charCodeAt(0).toString(16).padStart(2, '0');
|
|
75
|
+
});
|
|
76
|
+
return `$'${escaped}'`;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ── Credential search with exact-match preference (audit finding #4) ──
|
|
80
|
+
|
|
81
|
+
export async function searchCredential(
|
|
82
|
+
baseUrl: string,
|
|
83
|
+
token: string,
|
|
84
|
+
name: string,
|
|
85
|
+
): Promise<CredentialMeta | null> {
|
|
86
|
+
for (const param of [
|
|
87
|
+
`q=${encodeURIComponent(name)}`,
|
|
88
|
+
`tag=${encodeURIComponent(name)}`,
|
|
89
|
+
]) {
|
|
90
|
+
const res = await fetch(`${baseUrl}/credentials?${param}`, {
|
|
91
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
92
|
+
signal: AbortSignal.timeout(5000),
|
|
93
|
+
});
|
|
94
|
+
if (!res.ok) continue;
|
|
95
|
+
|
|
96
|
+
const data = (await res.json()) as { credentials: CredentialMeta[] };
|
|
97
|
+
const creds = data.credentials;
|
|
98
|
+
if (!creds || creds.length === 0) continue;
|
|
99
|
+
|
|
100
|
+
// Prefer exact name match (audit finding #4: ambiguous matching)
|
|
101
|
+
const exact = creds.find(
|
|
102
|
+
(c) => c.name.toLowerCase() === name.toLowerCase(),
|
|
103
|
+
);
|
|
104
|
+
if (exact) return exact;
|
|
105
|
+
|
|
106
|
+
// Warn if multiple non-exact matches
|
|
107
|
+
if (creds.length > 1) {
|
|
108
|
+
console.error(
|
|
109
|
+
`aura: warning: '${name}' matched ${creds.length} credentials, using first. ` +
|
|
110
|
+
`Matches: ${creds.map((c) => c.name).join(', ')}`,
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
return creds[0];
|
|
114
|
+
}
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// ── Credential read + decrypt ──
|
|
119
|
+
|
|
120
|
+
export async function readCredential(
|
|
121
|
+
baseUrl: string,
|
|
122
|
+
readToken: string,
|
|
123
|
+
credentialId: string,
|
|
124
|
+
decryptFn: (encrypted: string) => string,
|
|
125
|
+
): Promise<DecryptedCredential> {
|
|
126
|
+
const res = await fetch(`${baseUrl}/credentials/${credentialId}/read`, {
|
|
127
|
+
method: 'POST',
|
|
128
|
+
headers: { Authorization: `Bearer ${readToken}` },
|
|
129
|
+
signal: AbortSignal.timeout(5000),
|
|
130
|
+
});
|
|
131
|
+
if (!res.ok) {
|
|
132
|
+
const text = await res.text();
|
|
133
|
+
throw new Error(`Read failed (${res.status}): ${text}`);
|
|
134
|
+
}
|
|
135
|
+
const data = (await res.json()) as { encrypted: string };
|
|
136
|
+
const plaintext = decryptFn(data.encrypted);
|
|
137
|
+
return JSON.parse(plaintext);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// ── Resolve mappings to env vars ──
|
|
141
|
+
|
|
142
|
+
export async function resolveMappings(
|
|
143
|
+
mappings: AuraMapping[],
|
|
144
|
+
baseUrl: string,
|
|
145
|
+
token: string,
|
|
146
|
+
readToken: string,
|
|
147
|
+
decryptFn: (encrypted: string) => string,
|
|
148
|
+
): Promise<ResolveResult> {
|
|
149
|
+
const resolved = new Map<string, string>();
|
|
150
|
+
const errors: string[] = [];
|
|
151
|
+
const missing: AuraMapping[] = [];
|
|
152
|
+
|
|
153
|
+
const credentialCache = new Map<string, DecryptedCredential | null>();
|
|
154
|
+
const CONCURRENCY = 5;
|
|
155
|
+
|
|
156
|
+
let vaultNameById = new Map<string, string>();
|
|
157
|
+
try {
|
|
158
|
+
const vaultRes = await fetch(`${baseUrl}/setup/vaults`, { signal: AbortSignal.timeout(5000) });
|
|
159
|
+
if (vaultRes.ok) {
|
|
160
|
+
const vaultData = await vaultRes.json() as { vaults?: Array<{ id: string; name: string }> };
|
|
161
|
+
vaultNameById = new Map((vaultData.vaults || []).map((v) => [v.id, v.name]));
|
|
162
|
+
}
|
|
163
|
+
} catch {
|
|
164
|
+
// best effort only
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const uniqueTargets = new Map<string, AuraMapping>();
|
|
168
|
+
for (const mapping of mappings) {
|
|
169
|
+
const key = `${(mapping.vault || '').toLowerCase()}::${mapping.credentialName.toLowerCase()}`;
|
|
170
|
+
if (!uniqueTargets.has(key)) uniqueTargets.set(key, mapping);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const targetList = [...uniqueTargets.values()];
|
|
174
|
+
|
|
175
|
+
for (let i = 0; i < targetList.length; i += CONCURRENCY) {
|
|
176
|
+
const batch = targetList.slice(i, i + CONCURRENCY);
|
|
177
|
+
await Promise.all(
|
|
178
|
+
batch.map(async (mapping) => {
|
|
179
|
+
const cacheKey = `${(mapping.vault || '').toLowerCase()}::${mapping.credentialName.toLowerCase()}`;
|
|
180
|
+
const meta = await searchCredential(baseUrl, token, mapping.credentialName);
|
|
181
|
+
if (!meta) {
|
|
182
|
+
credentialCache.set(cacheKey, null);
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const decision = evaluateProjectScopeAccess({
|
|
187
|
+
surface: 'cli_env',
|
|
188
|
+
requested: { vaultName: mapping.vault, credentialName: mapping.credentialName },
|
|
189
|
+
candidates: [{ id: meta.id, name: meta.name, vaultName: vaultNameById.get(meta.vaultId) || null }],
|
|
190
|
+
actor: 'cli-env',
|
|
191
|
+
});
|
|
192
|
+
emitProjectScopeEvent({
|
|
193
|
+
actor: 'cli-env',
|
|
194
|
+
surface: 'cli_env',
|
|
195
|
+
requestedCredential: { vaultName: mapping.vault, credentialName: mapping.credentialName },
|
|
196
|
+
decision,
|
|
197
|
+
});
|
|
198
|
+
if (!decision.allowed) {
|
|
199
|
+
credentialCache.set(cacheKey, null);
|
|
200
|
+
errors.push(`credential '${mapping.credentialName}': ${decision.code}: ${decision.remediation}`);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
try {
|
|
205
|
+
const decrypted = await readCredential(
|
|
206
|
+
baseUrl,
|
|
207
|
+
readToken,
|
|
208
|
+
meta.id,
|
|
209
|
+
decryptFn,
|
|
210
|
+
);
|
|
211
|
+
credentialCache.set(cacheKey, decrypted);
|
|
212
|
+
} catch (err) {
|
|
213
|
+
credentialCache.set(cacheKey, null);
|
|
214
|
+
errors.push(`credential '${mapping.credentialName}': ${getErrorMessage(err)}`);
|
|
215
|
+
}
|
|
216
|
+
}),
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
for (const mapping of mappings) {
|
|
221
|
+
const cacheKey = `${(mapping.vault || '').toLowerCase()}::${mapping.credentialName.toLowerCase()}`;
|
|
222
|
+
const cred = credentialCache.get(cacheKey);
|
|
223
|
+
if (!cred) {
|
|
224
|
+
if (
|
|
225
|
+
!errors.some((e) =>
|
|
226
|
+
e.startsWith(`credential '${mapping.credentialName}'`),
|
|
227
|
+
)
|
|
228
|
+
) {
|
|
229
|
+
errors.push(
|
|
230
|
+
`${mapping.envVar}: credential '${mapping.credentialName}' not found`,
|
|
231
|
+
);
|
|
232
|
+
} else {
|
|
233
|
+
errors.push(
|
|
234
|
+
`${mapping.envVar}: credential '${mapping.credentialName}' failed to resolve`,
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
missing.push(mapping);
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
const field = cred.fields.find(
|
|
241
|
+
(f) => f.key.toLowerCase() === mapping.field.toLowerCase(),
|
|
242
|
+
);
|
|
243
|
+
if (!field) {
|
|
244
|
+
const available = cred.fields.map((f) => f.key).join(', ');
|
|
245
|
+
errors.push(
|
|
246
|
+
`${mapping.envVar}: field '${mapping.field}' not found in '${mapping.credentialName}' (available: ${available})`,
|
|
247
|
+
);
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
resolved.set(mapping.envVar, field.value);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
return { resolved, errors, missing };
|
|
254
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared dotenv → vault migration logic.
|
|
3
|
+
* Used by both `aura init --from-dotenv` and `aura env init`.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import * as fs from 'fs';
|
|
7
|
+
import * as path from 'path';
|
|
8
|
+
import { parseDotenv, groupByPrefix, noGrouping, generateAuraFile, type CredentialGroup } from './dotenv-parser';
|
|
9
|
+
import { createCredentialViaApi, getPrimaryVaultId } from './credential-create';
|
|
10
|
+
|
|
11
|
+
export interface MigrateOptions {
|
|
12
|
+
token: string;
|
|
13
|
+
envPath: string;
|
|
14
|
+
noGroup?: boolean;
|
|
15
|
+
dryRun?: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface MigrateResult {
|
|
19
|
+
groups: CredentialGroup[];
|
|
20
|
+
created: number;
|
|
21
|
+
failed: number;
|
|
22
|
+
auraPath: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Migrate a .env file into the vault and generate a .aura file.
|
|
27
|
+
* Returns summary of what was done.
|
|
28
|
+
*/
|
|
29
|
+
export async function migrateDotenv(opts: MigrateOptions): Promise<MigrateResult> {
|
|
30
|
+
const { token, envPath, noGroup, dryRun } = opts;
|
|
31
|
+
|
|
32
|
+
if (!fs.existsSync(envPath)) {
|
|
33
|
+
throw new Error(`No .env file found at ${envPath}`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const auraPath = path.join(path.dirname(envPath), '.aura');
|
|
37
|
+
const shouldWriteAura = !fs.existsSync(auraPath);
|
|
38
|
+
|
|
39
|
+
if (!shouldWriteAura && !dryRun) {
|
|
40
|
+
console.log(`\n Note: existing .aura file found at ${auraPath} — skipping overwrite.`);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const envContent = fs.readFileSync(envPath, 'utf-8');
|
|
44
|
+
const vars = parseDotenv(envContent);
|
|
45
|
+
|
|
46
|
+
if (vars.size === 0) {
|
|
47
|
+
throw new Error('No variables found in .env file.');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const groups = noGroup ? noGrouping(vars) : groupByPrefix(vars);
|
|
51
|
+
|
|
52
|
+
if (dryRun) {
|
|
53
|
+
console.log(`\n .env → .aura migration plan (${vars.size} variables → ${groups.length} credentials)\n`);
|
|
54
|
+
for (const group of groups) {
|
|
55
|
+
console.log(` 📦 ${group.name} (${group.fields.length} field${group.fields.length > 1 ? 's' : ''})`);
|
|
56
|
+
for (const field of group.fields) {
|
|
57
|
+
const preview = field.value.length > 20 ? field.value.substring(0, 20) + '...' : field.value;
|
|
58
|
+
console.log(` ${field.envVar} → ${group.name}/${field.key} (${preview})`);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
console.log(`\n Run without --dry-run to execute.\n`);
|
|
62
|
+
return { groups, created: 0, failed: 0, auraPath };
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const vaultId = await getPrimaryVaultId(token);
|
|
66
|
+
|
|
67
|
+
console.log(`\n Migrating ${vars.size} variables → ${groups.length} credentials\n`);
|
|
68
|
+
|
|
69
|
+
let created = 0;
|
|
70
|
+
let failed = 0;
|
|
71
|
+
|
|
72
|
+
for (const group of groups) {
|
|
73
|
+
const result = await createCredentialViaApi({
|
|
74
|
+
token,
|
|
75
|
+
vaultId,
|
|
76
|
+
name: group.name,
|
|
77
|
+
fields: group.fields.map(f => ({ key: f.key, value: f.value })),
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
if (result.success) {
|
|
81
|
+
console.log(` ✓ Created credential: ${group.name} (${group.fields.length} field${group.fields.length > 1 ? 's' : ''})`);
|
|
82
|
+
created++;
|
|
83
|
+
} else if (result.error?.includes('already exists') || result.error?.includes('duplicate')) {
|
|
84
|
+
console.log(` ⚠ Skipped credential: ${group.name} (already exists)`);
|
|
85
|
+
} else {
|
|
86
|
+
console.error(` ✗ Failed to create ${group.name}: ${result.error}`);
|
|
87
|
+
failed++;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Generate .aura file when needed
|
|
92
|
+
if (shouldWriteAura) {
|
|
93
|
+
const auraContent = generateAuraFile(groups);
|
|
94
|
+
fs.writeFileSync(auraPath, auraContent, 'utf-8');
|
|
95
|
+
console.log(`\n ✓ Generated .aura file (${groups.length} credential${groups.length > 1 ? 's' : ''})`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Add .env to .gitignore
|
|
99
|
+
const gitignorePath = path.join(path.dirname(envPath), '.gitignore');
|
|
100
|
+
if (fs.existsSync(gitignorePath)) {
|
|
101
|
+
const gitignore = fs.readFileSync(gitignorePath, 'utf-8');
|
|
102
|
+
if (!gitignore.includes('.env')) {
|
|
103
|
+
fs.appendFileSync(gitignorePath, '\n.env\n');
|
|
104
|
+
console.log(' ✓ Added .env to .gitignore');
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
console.log(`\n Summary: ${created} created, ${failed} failed`);
|
|
109
|
+
if (created > 0) {
|
|
110
|
+
console.log(' Your .env variables are now in the vault.');
|
|
111
|
+
console.log(' Use `aura env -- <cmd>` to run commands with vault-injected env vars.');
|
|
112
|
+
console.log(' You can safely delete your .env file.\n');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return { groups, created, failed, auraPath };
|
|
116
|
+
}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* .env file parser and credential grouping for `aura env init`
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { isValidEnvVarName } from './credential-resolve';
|
|
6
|
+
|
|
7
|
+
export interface CredentialGroup {
|
|
8
|
+
name: string;
|
|
9
|
+
fields: Array<{ key: string; value: string; envVar: string }>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Parse a .env file into key-value pairs.
|
|
14
|
+
* Handles: comments, blank lines, `export` prefix, single/double quotes, escaped chars.
|
|
15
|
+
* Does NOT handle: multiline values, variable expansion.
|
|
16
|
+
*/
|
|
17
|
+
export function parseDotenv(content: string): Map<string, string> {
|
|
18
|
+
const result = new Map<string, string>();
|
|
19
|
+
|
|
20
|
+
for (const rawLine of content.split('\n')) {
|
|
21
|
+
const line = rawLine.trim();
|
|
22
|
+
|
|
23
|
+
// Skip empty lines and comments
|
|
24
|
+
if (!line || line.startsWith('#')) continue;
|
|
25
|
+
|
|
26
|
+
// Strip optional `export ` prefix
|
|
27
|
+
const stripped = line.startsWith('export ') ? line.slice(7).trim() : line;
|
|
28
|
+
|
|
29
|
+
const eqIdx = stripped.indexOf('=');
|
|
30
|
+
if (eqIdx === -1) continue;
|
|
31
|
+
|
|
32
|
+
const key = stripped.substring(0, eqIdx).trim();
|
|
33
|
+
if (!key) continue;
|
|
34
|
+
|
|
35
|
+
// Validate env var name (audit finding #6)
|
|
36
|
+
if (!isValidEnvVarName(key)) {
|
|
37
|
+
console.error(`warning: skipping invalid env var name '${key}'`);
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
let value = stripped.substring(eqIdx + 1).trim();
|
|
42
|
+
|
|
43
|
+
// Handle quoted values
|
|
44
|
+
if ((value.startsWith('"') && value.endsWith('"')) ||
|
|
45
|
+
(value.startsWith("'") && value.endsWith("'"))) {
|
|
46
|
+
const quote = value[0];
|
|
47
|
+
value = value.slice(1, -1);
|
|
48
|
+
if (quote === '"') {
|
|
49
|
+
// Unescape double-quoted values
|
|
50
|
+
value = value
|
|
51
|
+
.replace(/\\n/g, '\n')
|
|
52
|
+
.replace(/\\r/g, '\r')
|
|
53
|
+
.replace(/\\t/g, '\t')
|
|
54
|
+
.replace(/\\"/g, '"')
|
|
55
|
+
.replace(/\\\\/g, '\\');
|
|
56
|
+
}
|
|
57
|
+
// Single-quoted: literal, no escaping
|
|
58
|
+
} else {
|
|
59
|
+
// Unquoted: strip inline comment
|
|
60
|
+
const commentIdx = value.indexOf(' #');
|
|
61
|
+
if (commentIdx !== -1) {
|
|
62
|
+
value = value.substring(0, commentIdx).trim();
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
result.set(key, value);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return result;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Group env vars by common prefix for credential creation.
|
|
74
|
+
*
|
|
75
|
+
* Rules:
|
|
76
|
+
* - Split var name by `_`, use first segment as group prefix
|
|
77
|
+
* - Groups with 1 member: credential name = full lowercased var name, field = "value"
|
|
78
|
+
* - Groups with 2+ members: credential name = prefix, field = rest lowercased
|
|
79
|
+
*/
|
|
80
|
+
export function groupByPrefix(vars: Map<string, string>): CredentialGroup[] {
|
|
81
|
+
const prefixGroups = new Map<string, Array<{ key: string; value: string; envVar: string }>>();
|
|
82
|
+
|
|
83
|
+
for (const [envVar, value] of vars) {
|
|
84
|
+
const underscoreIdx = envVar.indexOf('_');
|
|
85
|
+
const prefix = underscoreIdx === -1
|
|
86
|
+
? envVar.toLowerCase()
|
|
87
|
+
: envVar.substring(0, underscoreIdx).toLowerCase();
|
|
88
|
+
|
|
89
|
+
if (!prefixGroups.has(prefix)) {
|
|
90
|
+
prefixGroups.set(prefix, []);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const fieldName = underscoreIdx === -1
|
|
94
|
+
? 'value'
|
|
95
|
+
: envVar.substring(underscoreIdx + 1).toLowerCase();
|
|
96
|
+
|
|
97
|
+
prefixGroups.get(prefix)!.push({ key: fieldName, value, envVar });
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const groups: CredentialGroup[] = [];
|
|
101
|
+
|
|
102
|
+
for (const [prefix, fields] of prefixGroups) {
|
|
103
|
+
if (fields.length === 1 && fields[0].key !== 'value') {
|
|
104
|
+
groups.push({
|
|
105
|
+
name: fields[0].envVar.toLowerCase(),
|
|
106
|
+
fields: [{ key: 'value', value: fields[0].value, envVar: fields[0].envVar }],
|
|
107
|
+
});
|
|
108
|
+
} else {
|
|
109
|
+
groups.push({ name: prefix, fields });
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return groups;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* No grouping — one credential per env var, field always "value".
|
|
118
|
+
*/
|
|
119
|
+
export function noGrouping(vars: Map<string, string>): CredentialGroup[] {
|
|
120
|
+
const groups: CredentialGroup[] = [];
|
|
121
|
+
for (const [envVar, value] of vars) {
|
|
122
|
+
groups.push({
|
|
123
|
+
name: envVar.toLowerCase(),
|
|
124
|
+
fields: [{ key: 'value', value, envVar }],
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
return groups;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Generate .aura file content from credential groups.
|
|
132
|
+
*/
|
|
133
|
+
export function generateAuraFile(groups: CredentialGroup[]): string {
|
|
134
|
+
const lines = ['# Generated by aura env init'];
|
|
135
|
+
|
|
136
|
+
for (const group of groups) {
|
|
137
|
+
for (const field of group.fields) {
|
|
138
|
+
const ref = field.key === 'value'
|
|
139
|
+
? `${group.name}/value`
|
|
140
|
+
: `${group.name}/${field.key}`;
|
|
141
|
+
lines.push(`${field.envVar}=${ref}`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return lines.join('\n') + '\n';
|
|
146
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HTTP helpers for CLI commands
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const DEFAULT_SERVER_URL = 'http://localhost:4242';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Get the wallet server URL from env or default
|
|
9
|
+
*/
|
|
10
|
+
export function serverUrl(): string {
|
|
11
|
+
return process.env.WALLET_SERVER_URL || DEFAULT_SERVER_URL;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Fetch JSON from the wallet server
|
|
16
|
+
*/
|
|
17
|
+
export async function fetchJson<T = unknown>(
|
|
18
|
+
path: string,
|
|
19
|
+
opts: { method?: string; body?: unknown; token?: string } = {}
|
|
20
|
+
): Promise<T> {
|
|
21
|
+
const url = `${serverUrl()}${path}`;
|
|
22
|
+
const headers: Record<string, string> = { 'Content-Type': 'application/json' };
|
|
23
|
+
if (opts.token) {
|
|
24
|
+
headers['Authorization'] = `Bearer ${opts.token}`;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const response = await fetch(url, {
|
|
28
|
+
method: opts.method || (opts.body ? 'POST' : 'GET'),
|
|
29
|
+
headers,
|
|
30
|
+
body: opts.body ? JSON.stringify(opts.body) : undefined,
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const data = await response.json() as T & { error?: string };
|
|
34
|
+
|
|
35
|
+
if (!response.ok) {
|
|
36
|
+
throw new Error((data as { error?: string }).error || `HTTP ${response.status}`);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return data;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Fetch the server's RSA public key for password encryption
|
|
44
|
+
*/
|
|
45
|
+
export async function fetchPublicKey(): Promise<string> {
|
|
46
|
+
const data = await fetchJson<{ publicKey: string }>('/auth/connect');
|
|
47
|
+
return data.publicKey;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Check if the wallet server is running
|
|
52
|
+
*/
|
|
53
|
+
export async function isServerRunning(): Promise<boolean> {
|
|
54
|
+
try {
|
|
55
|
+
await fetchJson('/health');
|
|
56
|
+
return true;
|
|
57
|
+
} catch {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Poll /health until the server is up, or timeout
|
|
64
|
+
*/
|
|
65
|
+
export async function waitForServer(timeoutMs: number = 15000): Promise<void> {
|
|
66
|
+
const start = Date.now();
|
|
67
|
+
while (Date.now() - start < timeoutMs) {
|
|
68
|
+
if (await isServerRunning()) return;
|
|
69
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
70
|
+
}
|
|
71
|
+
throw new Error(`Server did not start within ${timeoutMs / 1000}s`);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Setup status from GET /setup
|
|
76
|
+
*/
|
|
77
|
+
export interface SetupStatus {
|
|
78
|
+
hasWallet: boolean;
|
|
79
|
+
unlocked: boolean;
|
|
80
|
+
address: string | null;
|
|
81
|
+
adapters?: { telegram: boolean; webhook: boolean };
|
|
82
|
+
apiKeys?: { alchemy: boolean; anthropic: boolean };
|
|
83
|
+
defaultChain?: string;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Fetch setup status from the wallet server
|
|
88
|
+
*/
|
|
89
|
+
export async function fetchSetupStatus(): Promise<SetupStatus> {
|
|
90
|
+
return fetchJson<SetupStatus>('/setup');
|
|
91
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Filesystem + Prisma setup steps for init command
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as path from 'path';
|
|
6
|
+
import * as fs from 'fs';
|
|
7
|
+
import * as os from 'os';
|
|
8
|
+
import { execSync } from 'child_process';
|
|
9
|
+
import { findProjectRoot } from './process';
|
|
10
|
+
|
|
11
|
+
function getDataDir(): string {
|
|
12
|
+
return process.env.WALLET_DATA_DIR || path.join(os.homedir(), '.aurawallet');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Create the ~/.aurawallet/ directory structure
|
|
17
|
+
*/
|
|
18
|
+
export function ensureDirectories(): void {
|
|
19
|
+
const dataDir = getDataDir();
|
|
20
|
+
const dirs = [
|
|
21
|
+
dataDir,
|
|
22
|
+
path.join(dataDir, 'hot'),
|
|
23
|
+
path.join(dataDir, 'pending'),
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
for (const dir of dirs) {
|
|
27
|
+
if (!fs.existsSync(dir)) {
|
|
28
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Root-level tmp/backups directories are no longer created here.
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Run Prisma migrations against ~/.aurawallet/aurawallet.db
|
|
37
|
+
*/
|
|
38
|
+
export function runMigrations(root?: string): void {
|
|
39
|
+
const projectRoot = root || findProjectRoot();
|
|
40
|
+
const dbUrl = process.env.DATABASE_URL || `file:${getDataDir()}/aurawallet.db`;
|
|
41
|
+
const env = { ...process.env, DATABASE_URL: dbUrl };
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
// Try deploy first (production-style, no prompts)
|
|
45
|
+
execSync('npx prisma migrate deploy', {
|
|
46
|
+
cwd: projectRoot,
|
|
47
|
+
env,
|
|
48
|
+
stdio: 'pipe',
|
|
49
|
+
});
|
|
50
|
+
} catch {
|
|
51
|
+
// Fallback to dev migration (creates migration if needed)
|
|
52
|
+
execSync('npx prisma migrate dev --name init', {
|
|
53
|
+
cwd: projectRoot,
|
|
54
|
+
env,
|
|
55
|
+
stdio: 'pipe',
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Generate the Prisma client
|
|
62
|
+
*/
|
|
63
|
+
export function generatePrismaClient(root?: string): void {
|
|
64
|
+
const projectRoot = root || findProjectRoot();
|
|
65
|
+
execSync('npx prisma generate', {
|
|
66
|
+
cwd: projectRoot,
|
|
67
|
+
stdio: 'pipe',
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Check if a primary vault file already exists
|
|
73
|
+
*/
|
|
74
|
+
export function hasVault(): boolean {
|
|
75
|
+
return fs.existsSync(path.join(getDataDir(), 'vault-primary.json'));
|
|
76
|
+
}
|