@wraps.dev/cli 1.5.2 → 1.5.3
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/dist/cli.js +25 -6
- package/dist/cli.js.map +1 -1
- package/dist/lambda/event-processor/.bundled +1 -1
- package/package.json +1 -1
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../node_modules/.pnpm/tsup@8.5.0_jiti@2.6.1_postcss@8.5.6_tsx@4.20.6_typescript@5.9.3/node_modules/tsup/assets/esm_shims.js","../src/utils/shared/ci-detection.ts","../src/telemetry/config.ts","../package.json","../src/telemetry/client.ts","../src/telemetry/events.ts","../src/utils/shared/errors.ts","../src/utils/shared/aws.ts","../src/utils/email/route53.ts","../src/infrastructure/resources/acm.ts","../src/infrastructure/resources/cloudfront.ts","../src/infrastructure/resources/mail-manager.ts","../src/utils/email/costs.ts","../src/utils/email/presets.ts","../src/utils/shared/prompts.ts","../src/utils/shared/assume-role.ts","../src/utils/archive.ts","../src/console/services/email-archive.ts","../src/console/services/dynamodb-metrics.ts","../src/cli.ts","../src/commands/dashboard/update-role.ts","../src/utils/shared/metadata.ts","../src/utils/shared/fs.ts","../src/utils/shared/output.ts","../src/commands/email/config.ts","../src/infrastructure/email-stack.ts","../src/infrastructure/resources/dynamodb.ts","../src/infrastructure/resources/eventbridge.ts","../src/infrastructure/resources/iam.ts","../src/infrastructure/resources/lambda.ts","../src/infrastructure/resources/ses.ts","../src/infrastructure/resources/sqs.ts","../src/infrastructure/vercel-oidc.ts","../src/utils/shared/pulumi.ts","../src/commands/email/connect.ts","../src/utils/shared/scanner.ts","../src/commands/email/destroy.ts","../src/utils/route53.ts","../src/commands/email/domains.ts","../src/commands/email/init.ts","../src/commands/email/restore.ts","../src/commands/email/status.ts","../src/commands/email/upgrade.ts","../src/commands/shared/dashboard.ts","../src/console/server.ts","../src/console/middleware/auth.ts","../src/console/middleware/error.ts","../src/console/routes/domains.ts","../src/console/services/ses-service.ts","../src/console/routes/emails.ts","../src/console/services/email-logs.ts","../src/console/routes/metrics.ts","../src/console/services/aws-metrics.ts","../src/console/routes/settings.ts","../src/console/services/settings-service.ts","../src/console/routes/user.ts","../src/commands/shared/destroy.ts","../src/commands/shared/status.ts","../src/commands/telemetry.ts","../src/utils/shared/completion.ts"],"sourcesContent":["// Shim globals in esm bundle\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nconst getFilename = () => fileURLToPath(import.meta.url)\nconst getDirname = () => path.dirname(getFilename())\n\nexport const __dirname = /* @__PURE__ */ getDirname()\nexport const __filename = /* @__PURE__ */ getFilename()\n","/**\n * CI environment detection utilities\n * @module utils/shared/ci-detection\n */\n\n/**\n * Detect if running in CI environment\n *\n * Checks for common CI environment variables to determine\n * if the CLI is running in a continuous integration environment.\n *\n * @returns {boolean} True if running in CI, false otherwise\n *\n * @example\n * ```typescript\n * if (isCI()) {\n * console.log('Running in CI environment');\n * }\n * ```\n */\nexport function isCI(): boolean {\n // Universal CI indicator\n if (process.env.CI === \"true\" || process.env.CI === \"1\") {\n return true;\n }\n\n // Common CI providers\n const ciEnvVars = [\n \"GITHUB_ACTIONS\", // GitHub Actions\n \"GITLAB_CI\", // GitLab CI\n \"CIRCLECI\", // CircleCI\n \"TRAVIS\", // Travis CI\n \"JENKINS_URL\", // Jenkins\n \"BUILDKITE\", // Buildkite\n \"DRONE\", // Drone\n \"SEMAPHORE\", // Semaphore\n \"TEAMCITY_VERSION\", // TeamCity\n \"TF_BUILD\", // Azure Pipelines\n \"CODEBUILD_BUILD_ID\", // AWS CodeBuild\n \"NETLIFY\", // Netlify\n \"VERCEL\", // Vercel\n \"HEROKU_TEST_RUN_ID\", // Heroku CI\n \"BUDDY\", // Buddy\n \"BITBUCKET_BUILD_NUMBER\", // Bitbucket Pipelines\n ];\n\n return ciEnvVars.some((envVar) => process.env[envVar] !== undefined);\n}\n","/**\n * Telemetry configuration management\n * @module telemetry/config\n */\n\nimport Conf from \"conf\";\nimport { v4 as uuidv4 } from \"uuid\";\nimport type { TelemetryConfig } from \"./types.js\";\n\nconst CONFIG_DEFAULTS: TelemetryConfig = {\n enabled: true,\n anonymousId: uuidv4(),\n notificationShown: false,\n};\n\n/**\n * Manages telemetry configuration stored locally\n *\n * Configuration is stored in platform-specific locations:\n * - macOS: ~/Library/Preferences/wraps/telemetry.json\n * - Linux: ~/.config/wraps/telemetry.json\n * - Windows: %APPDATA%\\wraps\\Config\\telemetry.json\n *\n * @example\n * ```typescript\n * const config = new TelemetryConfigManager();\n *\n * if (config.isEnabled()) {\n * console.log('Telemetry is enabled');\n * }\n *\n * config.setEnabled(false);\n * ```\n */\nexport class TelemetryConfigManager {\n private readonly config: Conf<TelemetryConfig>;\n\n constructor() {\n this.config = new Conf<TelemetryConfig>({\n projectName: \"wraps\",\n configName: \"telemetry\",\n defaults: CONFIG_DEFAULTS,\n });\n }\n\n /**\n * Check if telemetry is enabled\n */\n isEnabled(): boolean {\n return this.config.get(\"enabled\");\n }\n\n /**\n * Enable or disable telemetry\n */\n setEnabled(enabled: boolean): void {\n this.config.set(\"enabled\", enabled);\n }\n\n /**\n * Get the anonymous user ID\n */\n getAnonymousId(): string {\n return this.config.get(\"anonymousId\");\n }\n\n /**\n * Check if the first-run notification has been shown\n */\n hasShownNotification(): boolean {\n return this.config.get(\"notificationShown\");\n }\n\n /**\n * Mark the first-run notification as shown\n */\n markNotificationShown(): void {\n this.config.set(\"notificationShown\", true);\n }\n\n /**\n * Get the full path to the configuration file\n */\n getConfigPath(): string {\n return this.config.path;\n }\n\n /**\n * Reset configuration to defaults\n */\n reset(): void {\n this.config.clear();\n // Set new defaults with fresh UUID\n this.config.set({\n ...CONFIG_DEFAULTS,\n anonymousId: uuidv4(),\n });\n }\n}\n","{\n \"name\": \"@wraps.dev/cli\",\n \"version\": \"1.5.2\",\n \"description\": \"CLI for deploying Wraps email infrastructure to your AWS account\",\n \"type\": \"module\",\n \"main\": \"./dist/cli.js\",\n \"bin\": {\n \"wraps\": \"./dist/cli.js\"\n },\n \"files\": [\n \"dist\",\n \"README.md\",\n \"LICENSE\"\n ],\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/wraps-team/wraps.git\",\n \"directory\": \"packages/cli\"\n },\n \"homepage\": \"https://wraps.dev\",\n \"bugs\": {\n \"url\": \"https://github.com/wraps-team/wraps/issues\"\n },\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"scripts\": {\n \"dev\": \"tsup --watch\",\n \"build\": \"pnpm build:console && pnpm build:lambda && tsup\",\n \"build:lambda\": \"tsx scripts/build-lambda.ts\",\n \"build:console\": \"pnpm --filter @wraps/console build\",\n \"test\": \"vitest run\",\n \"test:watch\": \"vitest --watch\",\n \"test:ui\": \"vitest --ui\",\n \"test:coverage\": \"vitest run --coverage\",\n \"typecheck\": \"tsc --noEmit\",\n \"lint\": \"eslint src\",\n \"prepublishOnly\": \"pnpm build\"\n },\n \"keywords\": [\n \"aws\",\n \"ses\",\n \"email\",\n \"infrastructure\",\n \"cli\"\n ],\n \"author\": \"Wraps\",\n \"license\": \"MIT\",\n \"dependencies\": {\n \"@aws-sdk/client-acm\": \"3.933.0\",\n \"@aws-sdk/client-cloudformation\": \"^3.490.0\",\n \"@aws-sdk/client-cloudfront\": \"3.933.0\",\n \"@aws-sdk/client-cloudwatch\": \"^3.490.0\",\n \"@aws-sdk/client-dynamodb\": \"^3.490.0\",\n \"@aws-sdk/client-iam\": \"3.932.0\",\n \"@aws-sdk/client-lambda\": \"3.925.0\",\n \"@aws-sdk/client-mailmanager\": \"3.925.0\",\n \"@aws-sdk/client-route-53\": \"3.925.0\",\n \"@aws-sdk/client-ses\": \"^3.490.0\",\n \"@aws-sdk/client-sesv2\": \"3.925.0\",\n \"@aws-sdk/client-sns\": \"^3.490.0\",\n \"@aws-sdk/client-sts\": \"^3.490.0\",\n \"@aws-sdk/util-dynamodb\": \"3.927.0\",\n \"@clack/prompts\": \"^0.11.0\",\n \"@pulumi/aws\": \"^7.11.1\",\n \"@pulumi/pulumi\": \"^3.207.0\",\n \"args\": \"^5.0.3\",\n \"conf\": \"^13.0.1\",\n \"cosmiconfig\": \"^9.0.0\",\n \"esbuild\": \"^0.25.12\",\n \"express\": \"^4.21.2\",\n \"get-port\": \"^7.1.0\",\n \"http-terminator\": \"^3.2.0\",\n \"isomorphic-dompurify\": \"2.32.0\",\n \"mailparser\": \"3.9.0\",\n \"open\": \"^10.1.0\",\n \"picocolors\": \"^1.1.1\",\n \"tabtab\": \"^3.0.2\",\n \"uuid\": \"^11.0.3\"\n },\n \"devDependencies\": {\n \"@types/args\": \"5.0.4\",\n \"@types/express\": \"^5.0.0\",\n \"@types/mailparser\": \"3.4.6\",\n \"@types/node\": \"^20.11.0\",\n \"@types/uuid\": \"^10.0.0\",\n \"@vitest/coverage-v8\": \"4.0.7\",\n \"aws-sdk-client-mock\": \"4.1.0\",\n \"aws-sdk-client-mock-vitest\": \"7.0.1\",\n \"eslint\": \"^8.56.0\",\n \"tsup\": \"^8.0.1\",\n \"tsx\": \"4.20.6\",\n \"typescript\": \"catalog:\",\n \"vitest\": \"^4.0.7\"\n },\n \"engines\": {\n \"node\": \">=20.0.0\"\n }\n}\n","/**\n * Telemetry client for Wraps CLI\n * @module telemetry/client\n */\n\nimport { isCI } from \"../utils/shared/ci-detection.js\";\nimport { TelemetryConfigManager } from \"./config.js\";\nimport type {\n TelemetryClientOptions,\n TelemetryEvent,\n TelemetryRequest,\n} from \"./types.js\";\n\nconst DEFAULT_ENDPOINT = \"https://wraps.dev/api/telemetry\";\nconst DEFAULT_TIMEOUT = 2000; // 2 seconds\n\n/**\n * Main telemetry client for tracking CLI usage\n *\n * Features:\n * - Non-blocking event tracking with automatic batching\n * - Respects DO_NOT_TRACK and WRAPS_TELEMETRY_DISABLED environment variables\n * - Auto-disabled in CI environments\n * - 2-second timeout with silent failure\n * - Debug mode for development\n *\n * @example\n * ```typescript\n * const client = getTelemetryClient();\n *\n * client.track('command:init', {\n * service: 'email',\n * success: true\n * });\n *\n * await client.shutdown(); // Flush remaining events\n * ```\n */\nexport class TelemetryClient {\n private readonly config: TelemetryConfigManager;\n private readonly endpoint: string;\n private readonly timeout: number;\n private readonly debug: boolean;\n private enabled: boolean;\n private eventQueue: TelemetryEvent[] = [];\n private flushTimer?: NodeJS.Timeout;\n\n constructor(options: TelemetryClientOptions = {}) {\n this.config = new TelemetryConfigManager();\n this.endpoint = options.endpoint || DEFAULT_ENDPOINT;\n this.timeout = options.timeout || DEFAULT_TIMEOUT;\n this.debug = options.debug || process.env.WRAPS_TELEMETRY_DEBUG === \"1\";\n\n // Check if telemetry should be enabled\n this.enabled = this.shouldBeEnabled();\n }\n\n /**\n * Determine if telemetry should be enabled based on environment and config\n */\n private shouldBeEnabled(): boolean {\n // Check DO_NOT_TRACK first (universal standard)\n if (process.env.DO_NOT_TRACK === \"1\") {\n return false;\n }\n\n // Check Wraps-specific env var\n if (process.env.WRAPS_TELEMETRY_DISABLED === \"1\") {\n return false;\n }\n\n // Check CI environment\n if (isCI()) {\n return false;\n }\n\n // Check config file\n if (!this.config.isEnabled()) {\n return false;\n }\n\n return true;\n }\n\n /**\n * Track an event\n *\n * @param event - Event name in format \"category:action\" (e.g., \"command:init\")\n * @param properties - Additional event properties (no PII)\n */\n track(event: string, properties?: Record<string, unknown>): void {\n const telemetryEvent: TelemetryEvent = {\n event,\n properties: {\n ...properties,\n cli_version: this.getCLIVersion(),\n os: process.platform,\n node_version: process.version,\n ci: isCI(),\n },\n anonymousId: this.config.getAnonymousId(),\n timestamp: new Date().toISOString(),\n };\n\n // Debug mode: show what would be sent (even if disabled)\n if (this.debug) {\n console.log(\n \"[Telemetry Debug] Event:\",\n JSON.stringify(telemetryEvent, null, 2)\n );\n return;\n }\n\n // Check if telemetry is enabled (after debug check)\n if (!this.enabled) {\n return;\n }\n\n // Add to queue\n this.eventQueue.push(telemetryEvent);\n\n // Flush after short delay (batch events from same command)\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n }\n this.flushTimer = setTimeout(() => this.flush(), 100);\n }\n\n /**\n * Flush queued events to server\n */\n private async flush(): Promise<void> {\n if (this.eventQueue.length === 0) {\n return;\n }\n\n const eventsToSend = [...this.eventQueue];\n this.eventQueue = [];\n\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n const requestBody: TelemetryRequest = {\n events: eventsToSend,\n batch: true,\n };\n\n await fetch(this.endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(requestBody),\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n } catch (error) {\n // Silent failure - never break user's workflow\n if (this.debug) {\n console.error(\"[Telemetry Debug] Failed to send events:\", error);\n }\n }\n }\n\n /**\n * Flush and wait for all events to be sent\n * Should be called before CLI exits\n */\n async shutdown(): Promise<void> {\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n }\n await this.flush();\n }\n\n /**\n * Enable telemetry\n * Note: Won't override environment variable opt-outs (DO_NOT_TRACK, CI, etc.)\n */\n enable(): void {\n this.config.setEnabled(true);\n\n // Check if environment variables prevent telemetry\n if (\n process.env.DO_NOT_TRACK === \"1\" ||\n process.env.DO_NOT_TRACK === \"true\"\n ) {\n this.enabled = false;\n return;\n }\n\n if (process.env.WRAPS_TELEMETRY_DISABLED === \"1\") {\n this.enabled = false;\n return;\n }\n\n if (isCI()) {\n this.enabled = false;\n return;\n }\n\n // No env restrictions, enable telemetry\n this.enabled = true;\n }\n\n /**\n * Disable telemetry\n */\n disable(): void {\n this.config.setEnabled(false);\n this.enabled = false;\n this.eventQueue = [];\n }\n\n /**\n * Check if telemetry is enabled\n */\n isEnabled(): boolean {\n return this.enabled;\n }\n\n /**\n * Get config file path\n */\n getConfigPath(): string {\n return this.config.getConfigPath();\n }\n\n /**\n * Show first-run notification\n */\n shouldShowNotification(): boolean {\n return this.enabled && !this.config.hasShownNotification();\n }\n\n /**\n * Mark notification as shown\n */\n markNotificationShown(): void {\n this.config.markNotificationShown();\n }\n\n /**\n * Get CLI version from package.json\n */\n private getCLIVersion(): string {\n try {\n // Dynamic import would be better but requires async\n // Using require for now since this is CommonJS compatible\n const packageJson = require(\"../../package.json\");\n return packageJson.version;\n } catch {\n return \"unknown\";\n }\n }\n}\n\n// Singleton instance\nlet telemetryInstance: TelemetryClient | null = null;\n\n/**\n * Get the singleton telemetry client instance\n *\n * @example\n * ```typescript\n * const client = getTelemetryClient();\n * client.track('command:init', { success: true });\n * ```\n */\nexport function getTelemetryClient(): TelemetryClient {\n if (!telemetryInstance) {\n telemetryInstance = new TelemetryClient();\n }\n return telemetryInstance;\n}\n","/**\n * Event tracking helpers for Wraps CLI\n * @module telemetry/events\n */\n\nimport { getTelemetryClient } from \"./client.js\";\n\n/**\n * Track CLI command execution\n *\n * @param command - Command name (e.g., \"email:init\", \"status\")\n * @param metadata - Additional metadata about command execution\n *\n * @example\n * ```typescript\n * trackCommand('email:init', {\n * success: true,\n * duration_ms: 1500,\n * preset: 'production'\n * });\n * ```\n */\nexport function trackCommand(\n command: string,\n metadata?: {\n success?: boolean;\n duration_ms?: number;\n preset?: string;\n provider?: string;\n service?: string;\n [key: string]: unknown;\n }\n): void {\n const client = getTelemetryClient();\n\n // Sanitize metadata to ensure no PII\n const sanitized = metadata ? { ...metadata } : {};\n\n // Remove any potentially sensitive fields\n sanitized.domain = undefined;\n sanitized.accountId = undefined;\n sanitized.email = undefined;\n\n client.track(`command:${command}`, sanitized);\n}\n\n/**\n * Track service initialization\n *\n * @param service - Service type (email, sms, etc.)\n * @param success - Whether initialization succeeded\n * @param metadata - Additional metadata\n *\n * @example\n * ```typescript\n * trackServiceInit('email', true, {\n * preset: 'production',\n * provider: 'vercel'\n * });\n * ```\n */\nexport function trackServiceInit(\n service: string,\n success: boolean,\n metadata?: {\n preset?: string;\n provider?: string;\n features?: string[];\n duration_ms?: number;\n [key: string]: unknown;\n }\n): void {\n const client = getTelemetryClient();\n\n client.track(\"service:init\", {\n service,\n success,\n ...metadata,\n });\n}\n\n/**\n * Track service deployment\n *\n * @param service - Service type (email, sms, etc.)\n * @param metadata - Deployment metadata\n *\n * @example\n * ```typescript\n * trackServiceDeployed('email', {\n * duration_ms: 45000,\n * features: ['tracking', 'history']\n * });\n * ```\n */\nexport function trackServiceDeployed(\n service: string,\n metadata?: {\n duration_ms?: number;\n features?: string[];\n preset?: string;\n [key: string]: unknown;\n }\n): void {\n const client = getTelemetryClient();\n\n client.track(\"service:deployed\", {\n service,\n ...metadata,\n });\n}\n\n/**\n * Track dashboard/console start\n *\n * @param mode - Console mode (local or hosted)\n * @param metadata - Additional metadata\n *\n * @example\n * ```typescript\n * trackConsoleStart('local', { port: 3100 });\n * ```\n */\nexport function trackConsoleStart(\n mode: \"local\" | \"hosted\",\n metadata?: Record<string, unknown>\n): void {\n const client = getTelemetryClient();\n\n client.track(\"console:started\", {\n mode,\n ...metadata,\n });\n}\n\n/**\n * Track console stop\n *\n * @param metadata - Stop metadata (e.g., duration)\n *\n * @example\n * ```typescript\n * trackConsoleStop({ duration_s: 300 });\n * ```\n */\nexport function trackConsoleStop(metadata?: {\n duration_s?: number;\n [key: string]: unknown;\n}): void {\n const client = getTelemetryClient();\n\n client.track(\"console:stopped\", metadata || {});\n}\n\n/**\n * Track error occurrence\n *\n * IMPORTANT: Never include error messages, only error codes\n * Error messages may contain sensitive information\n *\n * @param errorCode - Error code (e.g., \"AWS_CREDENTIALS_INVALID\")\n * @param command - Command where error occurred\n * @param metadata - Additional metadata\n *\n * @example\n * ```typescript\n * trackError('AWS_CREDENTIALS_INVALID', 'email:init', {\n * step: 'credential_validation'\n * });\n * ```\n */\nexport function trackError(\n errorCode: string,\n command: string,\n metadata?: Record<string, unknown>\n): void {\n const client = getTelemetryClient();\n\n client.track(\"error:occurred\", {\n error_code: errorCode,\n command,\n ...metadata,\n });\n}\n\n/**\n * Track feature usage\n *\n * @param feature - Feature name\n * @param metadata - Feature metadata\n *\n * @example\n * ```typescript\n * trackFeature('domain_verified', {\n * dns_provider: 'route53',\n * auto_detected: true\n * });\n * ```\n */\nexport function trackFeature(\n feature: string,\n metadata?: Record<string, unknown>\n): void {\n const client = getTelemetryClient();\n\n client.track(`feature:${feature}`, metadata || {});\n}\n\n/**\n * Track service upgrade\n *\n * @param service - Service type\n * @param metadata - Upgrade metadata\n *\n * @example\n * ```typescript\n * trackServiceUpgrade('email', {\n * from_preset: 'starter',\n * to_preset: 'production',\n * added_features: ['history', 'dedicated_ip']\n * });\n * ```\n */\nexport function trackServiceUpgrade(\n service: string,\n metadata?: {\n from_preset?: string;\n to_preset?: string;\n added_features?: string[];\n [key: string]: unknown;\n }\n): void {\n const client = getTelemetryClient();\n\n client.track(\"service:upgraded\", {\n service,\n ...metadata,\n });\n}\n\n/**\n * Track service removal\n *\n * @param service - Service type\n * @param metadata - Removal metadata\n *\n * @example\n * ```typescript\n * trackServiceRemoved('email', { reason: 'user_initiated' });\n * ```\n */\nexport function trackServiceRemoved(\n service: string,\n metadata?: Record<string, unknown>\n): void {\n const client = getTelemetryClient();\n\n client.track(\"service:removed\", {\n service,\n ...metadata,\n });\n}\n","import * as clack from \"@clack/prompts\";\nimport pc from \"picocolors\";\nimport { trackError } from \"../../telemetry/events.js\";\n\n/**\n * Custom error class for Wraps CLI errors\n */\nexport class WrapsError extends Error {\n constructor(\n message: string,\n public code: string,\n public suggestion?: string,\n public docsUrl?: string\n ) {\n super(message);\n this.name = \"WrapsError\";\n }\n}\n\n/**\n * Global error handler for CLI errors\n * Formats and displays errors with suggestions and docs\n */\nexport function handleCLIError(error: unknown): never {\n console.error(\"\"); // Blank line\n\n if (error instanceof WrapsError) {\n // Track error (code only, never message)\n trackError(error.code, \"unknown\");\n\n clack.log.error(error.message);\n\n if (error.suggestion) {\n console.log(`\\n${pc.yellow(\"Suggestion:\")}`);\n console.log(` ${pc.white(error.suggestion)}\\n`);\n }\n\n if (error.docsUrl) {\n console.log(`${pc.dim(\"Documentation:\")}`);\n console.log(` ${pc.blue(error.docsUrl)}\\n`);\n }\n\n process.exit(1);\n }\n\n // Unknown error\n trackError(\"UNKNOWN_ERROR\", \"unknown\");\n\n clack.log.error(\"An unexpected error occurred\");\n console.error(error);\n console.log(`\\n${pc.dim(\"If this persists, please report at:\")}`);\n console.log(` ${pc.blue(\"https://github.com/wraps-team/wraps/issues\")}\\n`);\n process.exit(1);\n}\n\n/**\n * Common error factory functions\n */\nexport const errors = {\n noAWSCredentials: () =>\n new WrapsError(\n \"AWS credentials not found\",\n \"NO_AWS_CREDENTIALS\",\n \"Run: aws configure\\nOr set AWS_PROFILE environment variable\",\n \"https://wraps.dev/docs/setup/aws-credentials\"\n ),\n\n stackExists: (stackName: string) =>\n new WrapsError(\n `Stack \"${stackName}\" already exists`,\n \"STACK_EXISTS\",\n `To update: wraps email upgrade\\nTo remove: wraps destroy --stack ${stackName}`,\n \"https://wraps.dev/docs/cli/upgrade\"\n ),\n\n invalidRegion: (region: string) =>\n new WrapsError(\n `Invalid AWS region: ${region}`,\n \"INVALID_REGION\",\n \"Use a valid AWS region like: us-east-1, eu-west-1, ap-southeast-1\",\n \"https://docs.aws.amazon.com/general/latest/gr/rande.html\"\n ),\n\n pulumiError: (message: string) =>\n new WrapsError(\n `Infrastructure deployment failed: ${message}`,\n \"PULUMI_ERROR\",\n \"Check your AWS permissions and try again\",\n \"https://wraps.dev/docs/troubleshooting\"\n ),\n\n noStack: () =>\n new WrapsError(\n \"No Wraps infrastructure found in this AWS account\",\n \"NO_STACK\",\n \"Run: wraps email init\\nTo deploy new infrastructure\",\n \"https://wraps.dev/docs/cli/init\"\n ),\n\n pulumiNotInstalled: () =>\n new WrapsError(\n \"Pulumi CLI is not installed\",\n \"PULUMI_NOT_INSTALLED\",\n \"Install Pulumi:\\n macOS: brew install pulumi/tap/pulumi\\n Linux: curl -fsSL https://get.pulumi.com | sh\\n Windows: choco install pulumi\\n\\nOr download from: https://www.pulumi.com/docs/install/\",\n \"https://www.pulumi.com/docs/install/\"\n ),\n\n stackLocked: () =>\n new WrapsError(\n \"The Pulumi stack is locked from a previous run\",\n \"STACK_LOCKED\",\n \"This happens when a previous deployment was interrupted.\\n\\nTo unlock, run:\\n rm -rf ~/.wraps/pulumi/.pulumi/locks\\n\\nThen try your command again.\",\n \"https://wraps.dev/docs/troubleshooting\"\n ),\n};\n","import { ACMClient, DescribeCertificateCommand } from \"@aws-sdk/client-acm\";\nimport {\n GetIdentityVerificationAttributesCommand,\n ListIdentitiesCommand,\n SESClient,\n} from \"@aws-sdk/client-ses\";\nimport { GetCallerIdentityCommand, STSClient } from \"@aws-sdk/client-sts\";\nimport { errors } from \"./errors.js\";\n\n/**\n * AWS identity information\n */\nexport type AWSIdentity = {\n accountId: string;\n userId: string;\n arn: string;\n};\n\n/**\n * Validate AWS credentials by calling STS GetCallerIdentity\n */\nexport async function validateAWSCredentials(): Promise<AWSIdentity> {\n const sts = new STSClient({ region: \"us-east-1\" });\n\n try {\n const identity = await sts.send(new GetCallerIdentityCommand({}));\n return {\n accountId: identity.Account!,\n userId: identity.UserId!,\n arn: identity.Arn!,\n };\n } catch (_error) {\n throw errors.noAWSCredentials();\n }\n}\n\n/**\n * Check if a region is valid\n */\nexport async function checkRegion(region: string): Promise<boolean> {\n // List of valid AWS regions (as of 2025)\n const validRegions = [\n \"us-east-1\",\n \"us-east-2\",\n \"us-west-1\",\n \"us-west-2\",\n \"af-south-1\",\n \"ap-east-1\",\n \"ap-south-1\",\n \"ap-northeast-1\",\n \"ap-northeast-2\",\n \"ap-northeast-3\",\n \"ap-southeast-1\",\n \"ap-southeast-2\",\n \"ap-southeast-3\",\n \"ca-central-1\",\n \"eu-central-1\",\n \"eu-west-1\",\n \"eu-west-2\",\n \"eu-west-3\",\n \"eu-south-1\",\n \"eu-north-1\",\n \"me-south-1\",\n \"sa-east-1\",\n ];\n\n return validRegions.includes(region);\n}\n\n/**\n * Get AWS region from environment or config\n */\nexport async function getAWSRegion(): Promise<string> {\n // Try to detect region from various sources\n if (process.env.AWS_REGION) {\n return process.env.AWS_REGION;\n }\n if (process.env.AWS_DEFAULT_REGION) {\n return process.env.AWS_DEFAULT_REGION;\n }\n\n // Default fallback\n return \"us-east-1\";\n}\n\n/**\n * SES domain identity\n */\nexport type SESDomain = {\n domain: string;\n verified: boolean;\n};\n\n/**\n * List all SES identities (domains) in the account\n */\nexport async function listSESDomains(region: string): Promise<SESDomain[]> {\n const ses = new SESClient({ region });\n\n try {\n // Get all identities\n const identitiesResponse = await ses.send(\n new ListIdentitiesCommand({\n IdentityType: \"Domain\",\n })\n );\n\n const identities = identitiesResponse.Identities || [];\n\n if (identities.length === 0) {\n return [];\n }\n\n // Get verification attributes\n const attributesResponse = await ses.send(\n new GetIdentityVerificationAttributesCommand({\n Identities: identities,\n })\n );\n\n const attributes = attributesResponse.VerificationAttributes || {};\n\n // Map to SESDomain objects\n return identities.map((domain) => ({\n domain,\n verified: attributes[domain]?.VerificationStatus === \"Success\",\n }));\n } catch (error) {\n console.error(\"Error listing SES domains:\", error);\n return [];\n }\n}\n\n/**\n * Check if SES is in sandbox mode\n */\nexport async function isSESSandbox(region: string): Promise<boolean> {\n const ses = new SESClient({ region });\n\n try {\n // In sandbox mode, you can only send to verified addresses\n // This is a heuristic - we check if there are any identities\n await ses.send(\n new ListIdentitiesCommand({\n MaxItems: 1,\n })\n );\n\n // If we can call this API, SES is enabled\n // The actual sandbox check requires checking send quota\n // For now, we'll return false (not sandbox) if the API works\n return false;\n } catch (error: any) {\n // If we get an error about SES not being enabled, return true\n if (error.name === \"InvalidParameterValue\") {\n return true;\n }\n throw error;\n }\n}\n\n/**\n * ACM certificate status\n */\nexport type ACMCertificateStatus = {\n status: string;\n domainName: string;\n validationRecords: Array<{\n name: string;\n type: string;\n value: string;\n }>;\n};\n\n/**\n * Check ACM certificate validation status\n * Note: ACM certificates for CloudFront must be in us-east-1\n */\nexport async function getACMCertificateStatus(\n certificateArn: string\n): Promise<ACMCertificateStatus | null> {\n const acm = new ACMClient({ region: \"us-east-1\" });\n\n try {\n const response = await acm.send(\n new DescribeCertificateCommand({\n CertificateArn: certificateArn,\n })\n );\n\n const certificate = response.Certificate;\n if (!certificate) {\n return null;\n }\n\n // Extract validation records\n const validationRecords =\n certificate.DomainValidationOptions?.map((option) => ({\n name: option.ResourceRecord?.Name || \"\",\n type: option.ResourceRecord?.Type || \"\",\n value: option.ResourceRecord?.Value || \"\",\n })) || [];\n\n return {\n status: certificate.Status || \"UNKNOWN\",\n domainName: certificate.DomainName || \"\",\n validationRecords,\n };\n } catch (error) {\n console.error(\"Error getting ACM certificate status:\", error);\n return null;\n }\n}\n","import {\n type Change,\n ChangeResourceRecordSetsCommand,\n ListHostedZonesByNameCommand,\n Route53Client,\n} from \"@aws-sdk/client-route-53\";\n\n/**\n * Find Route53 hosted zone for a domain\n *\n * This function searches for a hosted zone that matches the given domain.\n * It will try the exact domain first, then fall back to parent domains.\n * For example, if given \"track.example.com\", it will check:\n * 1. track.example.com\n * 2. example.com\n */\nexport async function findHostedZone(\n domain: string,\n region: string\n): Promise<{ id: string; name: string } | null> {\n const client = new Route53Client({ region });\n\n // Try exact domain first\n try {\n const response = await client.send(\n new ListHostedZonesByNameCommand({\n DNSName: domain,\n MaxItems: 1,\n })\n );\n\n const zone = response.HostedZones?.[0];\n if (zone && zone.Name === `${domain}.` && zone.Id) {\n return {\n id: zone.Id.replace(\"/hostedzone/\", \"\"),\n name: zone.Name,\n };\n }\n } catch (_error) {\n // Continue to try parent domains\n }\n\n // Try parent domains (e.g., track.example.com -> example.com)\n const parts = domain.split(\".\");\n if (parts.length > 2) {\n const parentDomain = parts.slice(1).join(\".\");\n return findHostedZone(parentDomain, region);\n }\n\n return null;\n}\n\n/**\n * Create DNS records in Route53\n */\nexport async function createDNSRecords(\n hostedZoneId: string,\n domain: string,\n dkimTokens: string[],\n region: string,\n customTrackingDomain?: string,\n mailFromDomain?: string,\n cloudFrontDomain?: string\n): Promise<void> {\n const client = new Route53Client({ region });\n\n const changes: Change[] = [];\n\n // DKIM CNAME records\n for (const token of dkimTokens) {\n changes.push({\n Action: \"UPSERT\",\n ResourceRecordSet: {\n Name: `${token}._domainkey.${domain}`,\n Type: \"CNAME\",\n TTL: 1800,\n ResourceRecords: [{ Value: `${token}.dkim.amazonses.com` }],\n },\n });\n }\n\n // SPF TXT record\n changes.push({\n Action: \"UPSERT\",\n ResourceRecordSet: {\n Name: domain,\n Type: \"TXT\",\n TTL: 1800,\n ResourceRecords: [{ Value: '\"v=spf1 include:amazonses.com ~all\"' }],\n },\n });\n\n // DMARC TXT record\n // If MAIL FROM domain is provided, use it for the reporting address (rua)\n // Otherwise, use the main domain\n const dmarcReportEmail = mailFromDomain\n ? `postmaster@${mailFromDomain}`\n : `postmaster@${domain}`;\n\n changes.push({\n Action: \"UPSERT\",\n ResourceRecordSet: {\n Name: `_dmarc.${domain}`,\n Type: \"TXT\",\n TTL: 1800,\n ResourceRecords: [\n { Value: `\"v=DMARC1; p=quarantine; rua=mailto:${dmarcReportEmail}\"` },\n ],\n },\n });\n\n // Custom tracking domain CNAME (if provided)\n // This allows SES to rewrite links for open/click tracking using your custom domain\n if (customTrackingDomain) {\n // If CloudFront domain is provided, use it (HTTPS tracking)\n // Otherwise, use direct SES tracking endpoint (HTTP tracking)\n const targetDomain = cloudFrontDomain || `r.${region}.awstrack.me`;\n\n changes.push({\n Action: \"UPSERT\",\n ResourceRecordSet: {\n Name: customTrackingDomain,\n Type: \"CNAME\",\n TTL: 1800,\n ResourceRecords: [{ Value: targetDomain }],\n },\n });\n }\n\n // MAIL FROM domain records (if provided)\n // These records enable DMARC alignment by using a custom subdomain for the envelope sender\n if (mailFromDomain) {\n // MX record pointing to SES feedback server\n changes.push({\n Action: \"UPSERT\",\n ResourceRecordSet: {\n Name: mailFromDomain,\n Type: \"MX\",\n TTL: 1800,\n ResourceRecords: [\n { Value: `10 feedback-smtp.${region}.amazonses.com` },\n ],\n },\n });\n\n // SPF record for MAIL FROM domain\n changes.push({\n Action: \"UPSERT\",\n ResourceRecordSet: {\n Name: mailFromDomain,\n Type: \"TXT\",\n TTL: 1800,\n ResourceRecords: [{ Value: '\"v=spf1 include:amazonses.com ~all\"' }],\n },\n });\n }\n\n await client.send(\n new ChangeResourceRecordSetsCommand({\n HostedZoneId: hostedZoneId,\n ChangeBatch: {\n Changes: changes,\n },\n })\n );\n}\n","import * as aws from \"@pulumi/aws\";\nimport type * as pulumi from \"@pulumi/pulumi\";\n\n/**\n * ACM certificate configuration\n */\nexport type ACMCertificateConfig = {\n domain: string;\n hostedZoneId?: string; // Optional Route53 hosted zone for automatic DNS validation\n};\n\n/**\n * ACM certificate output\n */\nexport type ACMCertificateResources = {\n certificate: aws.acm.Certificate;\n certificateValidation?: aws.acm.CertificateValidation;\n validationRecords: pulumi.Output<\n Array<{\n name: string;\n type: string;\n value: string;\n }>\n >;\n};\n\n/**\n * Create ACM certificate for custom tracking domain\n *\n * IMPORTANT: CloudFront requires ACM certificates to be created in us-east-1 region.\n * This function creates the certificate in us-east-1 regardless of the SES region.\n *\n * If a Route53 hosted zone ID is provided, DNS validation records will be created\n * automatically and we'll wait for validation. Otherwise, validation records are\n * returned for manual creation.\n */\nexport async function createACMCertificate(\n config: ACMCertificateConfig\n): Promise<ACMCertificateResources> {\n const usEast1Provider = new aws.Provider(\"acm-us-east-1\", {\n region: \"us-east-1\",\n });\n\n // Create ACM certificate in us-east-1 (required for CloudFront)\n const certificate = new aws.acm.Certificate(\n \"wraps-email-tracking-cert\",\n {\n domainName: config.domain,\n validationMethod: \"DNS\",\n tags: {\n ManagedBy: \"wraps-cli\",\n Description: \"SSL certificate for Wraps email tracking domain\",\n },\n },\n {\n provider: usEast1Provider,\n }\n );\n\n // Extract validation records\n const validationRecords = certificate.domainValidationOptions.apply(\n (options) =>\n options.map((option) => ({\n name: option.resourceRecordName,\n type: option.resourceRecordType,\n value: option.resourceRecordValue,\n }))\n );\n\n // If Route53 hosted zone is provided, create validation records automatically\n let certificateValidation: aws.acm.CertificateValidation | undefined;\n\n if (config.hostedZoneId) {\n // Create validation record in Route53\n const validationRecord = new aws.route53.Record(\n \"wraps-email-tracking-cert-validation\",\n {\n zoneId: config.hostedZoneId,\n name: certificate.domainValidationOptions[0].resourceRecordName,\n type: certificate.domainValidationOptions[0].resourceRecordType,\n records: [certificate.domainValidationOptions[0].resourceRecordValue],\n ttl: 60,\n }\n );\n\n // Wait for certificate validation to complete\n certificateValidation = new aws.acm.CertificateValidation(\n \"wraps-email-tracking-cert-validation-waiter\",\n {\n certificateArn: certificate.arn,\n validationRecordFqdns: [validationRecord.fqdn],\n },\n {\n provider: usEast1Provider,\n }\n );\n }\n // For manual DNS: certificateValidation is undefined\n // User must add DNS records manually and run upgrade again\n\n return {\n certificate,\n certificateValidation,\n validationRecords,\n };\n}\n","import * as aws from \"@pulumi/aws\";\nimport type * as pulumi from \"@pulumi/pulumi\";\n\n/**\n * CloudFront resources configuration\n */\nexport type CloudFrontTrackingConfig = {\n customTrackingDomain: string;\n region: string; // SES region for the origin\n certificateArn: pulumi.Output<string>;\n hostedZoneId?: string; // Optional Route53 hosted zone for automatic DNS record creation\n};\n\n/**\n * Find existing CloudFront distribution by alias (CNAME)\n */\nasync function findDistributionByAlias(alias: string): Promise<string | null> {\n try {\n const { CloudFrontClient, ListDistributionsCommand } = await import(\n \"@aws-sdk/client-cloudfront\"\n );\n const cloudfront = new CloudFrontClient({ region: \"us-east-1\" }); // CloudFront is global but API is in us-east-1\n\n const response = await cloudfront.send(new ListDistributionsCommand({}));\n\n // Find distribution with matching alias\n const distribution = response.DistributionList?.Items?.find((dist) =>\n dist.Aliases?.Items?.includes(alias)\n );\n\n return distribution?.Id || null;\n } catch (error) {\n console.error(\"Error finding CloudFront distribution:\", error);\n return null;\n }\n}\n\n/**\n * CloudFront resources output\n */\nexport type CloudFrontResources = {\n distribution: aws.cloudfront.Distribution;\n domainName: pulumi.Output<string>;\n webAcl: aws.wafv2.WebAcl;\n};\n\n/**\n * Create WAF Web ACL with rate limiting for CloudFront\n *\n * This creates a WAFv2 Web ACL with rate limiting protection\n * to prevent abuse of tracking endpoints.\n */\nasync function createWAFWebACL(): Promise<aws.wafv2.WebAcl> {\n // WAF for CloudFront must be created in us-east-1\n const usEast1Provider = new aws.Provider(\"waf-us-east-1\", {\n region: \"us-east-1\",\n });\n\n const webAcl = new aws.wafv2.WebAcl(\n \"wraps-email-tracking-waf\",\n {\n scope: \"CLOUDFRONT\", // WAF for CloudFront must use CLOUDFRONT scope\n description: \"Rate limiting protection for Wraps email tracking\",\n\n defaultAction: {\n allow: {}, // Allow by default\n },\n\n rules: [\n {\n name: \"RateLimitRule\",\n priority: 1,\n action: {\n block: {}, // Block requests exceeding rate limit\n },\n statement: {\n rateBasedStatement: {\n limit: 2000, // 2000 requests per 5 minutes per IP\n aggregateKeyType: \"IP\",\n },\n },\n visibilityConfig: {\n sampledRequestsEnabled: true,\n cloudwatchMetricsEnabled: true,\n metricName: \"RateLimitRule\",\n },\n },\n ],\n\n visibilityConfig: {\n sampledRequestsEnabled: true,\n cloudwatchMetricsEnabled: true,\n metricName: \"wraps-email-tracking-waf\",\n },\n\n tags: {\n ManagedBy: \"wraps-cli\",\n Description: \"WAF for Wraps email tracking with rate limiting\",\n },\n },\n {\n provider: usEast1Provider,\n }\n );\n\n return webAcl;\n}\n\n/**\n * Create CloudFront distribution for HTTPS tracking domain\n *\n * This creates a CloudFront distribution that sits in front of AWS SES's tracking endpoint\n * (r.{region}.awstrack.me) and provides HTTPS support with a custom domain and SSL certificate.\n * Also creates a WAF Web ACL with rate limiting for security.\n */\nexport async function createCloudFrontTracking(\n config: CloudFrontTrackingConfig\n): Promise<CloudFrontResources> {\n const sesTrackingOrigin = `r.${config.region}.awstrack.me`;\n\n // Create WAF Web ACL with rate limiting protection\n const webAcl = await createWAFWebACL();\n\n // Check if CloudFront distribution already exists with this alias\n const existingDistributionId = await findDistributionByAlias(\n config.customTrackingDomain\n );\n\n // CloudFront distribution configuration\n const distributionConfig = {\n enabled: true,\n comment: \"Wraps email tracking with HTTPS support\",\n aliases: [config.customTrackingDomain],\n\n // Attach WAF Web ACL for rate limiting protection\n webAclId: webAcl.arn,\n\n // Origin: SES tracking endpoint\n origins: [\n {\n domainName: sesTrackingOrigin,\n originId: \"ses-tracking\",\n customOriginConfig: {\n httpPort: 80,\n httpsPort: 443,\n originProtocolPolicy: \"http-only\", // SES tracking endpoint is HTTP\n originSslProtocols: [\"TLSv1.2\"],\n },\n },\n ],\n\n // Default cache behavior\n defaultCacheBehavior: {\n targetOriginId: \"ses-tracking\",\n viewerProtocolPolicy: \"redirect-to-https\", // Force HTTPS\n allowedMethods: [\"GET\", \"HEAD\", \"OPTIONS\"],\n cachedMethods: [\"GET\", \"HEAD\"],\n\n // Forward all query strings and headers (tracking links use query params)\n forwardedValues: {\n queryString: true,\n cookies: {\n forward: \"all\",\n },\n headers: [\"*\"], // Forward all headers to preserve tracking functionality\n },\n\n // Minimal caching for tracking redirects\n minTtl: 0,\n defaultTtl: 0,\n maxTtl: 31_536_000,\n\n compress: true,\n },\n\n // Price class (use only North America & Europe for cost optimization)\n priceClass: \"PriceClass_100\",\n\n // Restrictions (none)\n restrictions: {\n geoRestriction: {\n restrictionType: \"none\",\n },\n },\n\n // SSL certificate from ACM\n viewerCertificate: {\n acmCertificateArn: config.certificateArn,\n sslSupportMethod: \"sni-only\",\n minimumProtocolVersion: \"TLSv1.2_2021\",\n },\n\n tags: {\n ManagedBy: \"wraps-cli\",\n Description: \"Wraps email tracking CloudFront distribution\",\n },\n };\n\n const distribution = existingDistributionId\n ? new aws.cloudfront.Distribution(\n \"wraps-email-tracking-cdn\",\n distributionConfig,\n {\n import: existingDistributionId, // Import existing distribution\n }\n )\n : new aws.cloudfront.Distribution(\n \"wraps-email-tracking-cdn\",\n distributionConfig\n );\n\n // Note: DNS CNAME record for custom tracking domain is created separately\n // via Route53 SDK in route53.ts (using UPSERT for idempotency)\n // This prevents Pulumi resource conflicts on subsequent deployments\n\n return {\n distribution,\n domainName: distribution.domainName,\n webAcl,\n };\n}\n","import {\n CreateArchiveCommand,\n GetArchiveCommand,\n ListArchivesCommand,\n MailManagerClient,\n type RetentionPeriod,\n} from \"@aws-sdk/client-mailmanager\";\nimport {\n PutConfigurationSetArchivingOptionsCommand,\n SESv2Client,\n} from \"@aws-sdk/client-sesv2\";\nimport type * as pulumi from \"@pulumi/pulumi\";\nimport type { ArchiveRetention } from \"../../types/index.js\";\n\n/**\n * Mail Manager archive configuration\n */\nexport type MailManagerArchiveConfig = {\n name: string;\n retention: ArchiveRetention;\n configSetName: pulumi.Output<string>;\n region?: string;\n kmsKeyArn?: string; // Optional: provide existing KMS key, otherwise AWS-managed key is used\n};\n\n/**\n * Mail Manager archive resources\n */\nexport type MailManagerArchiveResources = {\n archiveId: string;\n archiveArn: string;\n kmsKeyArn?: string;\n};\n\n/**\n * Convert our retention types to AWS SDK RetentionPeriod enum\n */\nfunction retentionToAWSPeriod(retention: ArchiveRetention): RetentionPeriod {\n switch (retention) {\n case \"3months\":\n return \"THREE_MONTHS\";\n case \"6months\":\n return \"SIX_MONTHS\";\n case \"9months\":\n return \"NINE_MONTHS\";\n case \"1year\":\n return \"ONE_YEAR\";\n case \"18months\":\n return \"EIGHTEEN_MONTHS\";\n case \"2years\":\n return \"TWO_YEARS\";\n case \"30months\":\n return \"THIRTY_MONTHS\";\n case \"3years\":\n return \"THREE_YEARS\";\n case \"4years\":\n return \"FOUR_YEARS\";\n case \"5years\":\n return \"FIVE_YEARS\";\n case \"6years\":\n return \"SIX_YEARS\";\n case \"7years\":\n return \"SEVEN_YEARS\";\n case \"8years\":\n return \"EIGHT_YEARS\";\n case \"9years\":\n return \"NINE_YEARS\";\n case \"10years\":\n return \"TEN_YEARS\";\n case \"permanent\":\n return \"PERMANENT\";\n default:\n return \"THREE_MONTHS\";\n }\n}\n\n/**\n * Create Mail Manager archive for storing full email content\n *\n * This creates:\n * 1. Mail Manager Archive - stores RFC 822/MIME formatted emails\n * 2. Links archive to SES Configuration Set\n *\n * Uses AWS SDK directly since Pulumi doesn't support Mail Manager yet.\n *\n * Cost: $2/GB ingestion + $0.19/GB/month storage\n * See: https://docs.aws.amazon.com/ses/latest/dg/eb-archiving.html\n *\n * Note: KMS encryption is optional. If not provided, AWS-managed encryption is used.\n */\nexport async function createMailManagerArchive(\n config: MailManagerArchiveConfig\n): Promise<MailManagerArchiveResources> {\n const region = config.region || process.env.AWS_REGION || \"us-east-1\";\n const archiveName = `wraps-${config.name}-archive`;\n\n // Initialize clients\n const mailManagerClient = new MailManagerClient({ region });\n const sesClient = new SESv2Client({ region });\n\n const kmsKeyArn = config.kmsKeyArn;\n\n // If no KMS key provided, create one for encryption\n // Note: User can also opt to not provide one and AWS will use service-managed keys\n if (!kmsKeyArn) {\n // For now, we'll let AWS use service-managed keys\n // In the future, we could create a customer-managed key here if needed:\n //\n // const kmsClient = new KMSClient({ region });\n // const createKeyResult = await kmsClient.send(\n // new CreateKeyCommand({\n // Description: `KMS key for Wraps email archive (${archiveName})`,\n // Tags: [\n // { TagKey: \"ManagedBy\", TagValue: \"wraps-cli\" },\n // { TagKey: \"Name\", TagValue: `wraps-${config.name}-archive-key` },\n // ],\n // })\n // );\n // kmsKeyArn = createKeyResult.KeyMetadata?.Arn;\n }\n\n // 1. Create Mail Manager Archive (or get existing)\n const awsRetention = retentionToAWSPeriod(config.retention);\n\n let archiveId: string | undefined;\n let archiveArn: string | undefined;\n\n // Check if archive already exists\n try {\n const listCommand = new ListArchivesCommand({});\n const listResult = await mailManagerClient.send(listCommand);\n\n const existingArchive = listResult.Archives?.find(\n (archive: { ArchiveName?: string; ArchiveId?: string }) =>\n archive.ArchiveName === archiveName\n );\n\n if (existingArchive?.ArchiveId) {\n // Archive exists, use it\n console.log(`Using existing Mail Manager archive: ${archiveName}`);\n archiveId = existingArchive.ArchiveId;\n\n // Get full archive details to get ARN\n const getCommand = new GetArchiveCommand({ ArchiveId: archiveId });\n const getResult = await mailManagerClient.send(getCommand);\n archiveArn = getResult.ArchiveArn;\n }\n } catch (error) {\n console.log(\"Error checking for existing archive:\", error);\n // Continue to create new archive\n }\n\n // Create archive if it doesn't exist\n if (!archiveId) {\n try {\n const createArchiveCommand = new CreateArchiveCommand({\n ArchiveName: archiveName,\n Retention: {\n RetentionPeriod: awsRetention,\n },\n ...(kmsKeyArn && { KmsKeyArn: kmsKeyArn }),\n Tags: [\n { Key: \"ManagedBy\", Value: \"wraps-cli\" },\n { Key: \"Name\", Value: archiveName },\n { Key: \"Retention\", Value: config.retention },\n ],\n });\n\n const archiveResult = await mailManagerClient.send(createArchiveCommand);\n archiveId = archiveResult.ArchiveId;\n\n if (!archiveId) {\n throw new Error(\n \"Failed to create Mail Manager Archive: No ArchiveId returned\"\n );\n }\n\n console.log(`Created new Mail Manager archive: ${archiveName}`);\n } catch (error) {\n // If it's a ConflictException, the archive was created between our check and now\n if (\n error instanceof Error &&\n error.name === \"ConflictException\" &&\n error.message.includes(\"Archive already exists\")\n ) {\n console.log(\n \"Archive was created concurrently, fetching existing archive...\"\n );\n\n // List again and find it\n const listCommand = new ListArchivesCommand({});\n const listResult = await mailManagerClient.send(listCommand);\n const existingArchive = listResult.Archives?.find(\n (archive: { ArchiveName?: string; ArchiveId?: string }) =>\n archive.ArchiveName === archiveName\n );\n\n if (!existingArchive?.ArchiveId) {\n throw new Error(\n `Archive exists but couldn't find it: ${archiveName}`\n );\n }\n\n archiveId = existingArchive.ArchiveId;\n\n // Get full archive details\n const getCommand = new GetArchiveCommand({ ArchiveId: archiveId });\n const getResult = await mailManagerClient.send(getCommand);\n archiveArn = getResult.ArchiveArn;\n } else {\n throw error;\n }\n }\n }\n\n // Construct the ARN if we don't have it yet\n if (!archiveArn) {\n // ARN format: arn:aws:ses:region:account-id:mailmanager-archive/archive-id\n const identity = await import(\"@aws-sdk/client-sts\").then((m) =>\n new m.STSClient({ region }).send(new m.GetCallerIdentityCommand({}))\n );\n const accountId = identity.Account;\n archiveArn = `arn:aws:ses:${region}:${accountId}:mailmanager-archive/${archiveId}`;\n }\n\n // 2. Link archive to SES Configuration Set\n // We need to wait for the configSetName to resolve from Pulumi Output\n const configSetName = await new Promise<string>((resolve) => {\n config.configSetName.apply((name) => {\n resolve(name);\n });\n });\n\n const putArchivingOptionsCommand =\n new PutConfigurationSetArchivingOptionsCommand({\n ConfigurationSetName: configSetName,\n ArchiveArn: archiveArn,\n });\n\n await sesClient.send(putArchivingOptionsCommand);\n\n if (!(archiveId && archiveArn)) {\n throw new Error(\"Failed to get archive ID or ARN\");\n }\n\n return {\n archiveId,\n archiveArn,\n kmsKeyArn,\n };\n}\n","import type {\n ArchiveRetention,\n FeatureCost,\n FeatureCostBreakdown,\n WrapsEmailConfig,\n} from \"../../types/index.js\";\n\n/**\n * AWS pricing constants (as of 2025)\n * All costs in USD (US East N. Virginia region)\n * Source: aws.amazon.com pricing pages verified January 2025\n */\nconst AWS_PRICING = {\n // SES pricing\n SES_PER_EMAIL: 0.0001, // $0.10 per 1,000 emails (outbound)\n SES_ATTACHMENT_PER_GB: 0.12, // $0.12 per GB of attachments\n\n // DynamoDB pricing (on-demand, Standard table class)\n DYNAMODB_WRITE_PER_MILLION: 1.25, // $1.25 per million write request units (US East Ohio)\n DYNAMODB_READ_PER_MILLION: 0.25, // $0.25 per million read request units (US East Ohio)\n DYNAMODB_STORAGE_PER_GB: 0.25, // $0.25 per GB-month\n\n // Lambda pricing (x86)\n LAMBDA_REQUESTS_PER_MILLION: 0.2, // $0.20 per 1M requests\n LAMBDA_COMPUTE_PER_GB_SECOND: 0.000_016_666_7, // $0.0000166667 per GB-second\n\n // SQS pricing (Standard queues)\n SQS_REQUESTS_PER_MILLION: 0.5, // $0.50 per million requests (after free tier)\n\n // EventBridge pricing\n EVENTBRIDGE_EVENTS_PER_MILLION: 1.0, // $1.00 per million custom events published\n\n // Dedicated IP\n DEDICATED_IP_PER_MONTH: 24.95, // $24.95 per dedicated IP per month\n\n // CloudWatch pricing\n CLOUDWATCH_LOGS_PER_GB: 0.5, // $0.50 per GB ingested\n CLOUDWATCH_LOGS_STORAGE_PER_GB: 0.03, // $0.03 per GB-month\n\n // SES Mail Manager Archiving\n MAIL_MANAGER_INGESTION_PER_GB: 2.0, // $2.00 per GB ingested\n MAIL_MANAGER_STORAGE_PER_GB: 0.19, // $0.19 per GB-month\n} as const;\n\n/**\n * AWS Free tier limits (monthly, always-free or first 12 months)\n * Note: Some limits are permanently free, others only for first 12 months\n */\nconst FREE_TIER = {\n // SES: 3,000 emails/month for first 12 months (new AWS accounts only)\n // After 12 months or for existing accounts: NO free tier\n SES_EMAILS: 0, // Conservative: assume no free tier (most users are past 12 months)\n\n // Lambda: Permanently free tier\n LAMBDA_REQUESTS: 1_000_000, // 1M requests per month (always free)\n LAMBDA_COMPUTE_GB_SECONDS: 400_000, // 400,000 GB-seconds per month (always free)\n\n // DynamoDB: Permanently free tier\n DYNAMODB_WRITES: 0, // No free tier for writes in on-demand mode\n DYNAMODB_READS: 0, // No free tier for reads in on-demand mode\n DYNAMODB_STORAGE_GB: 25, // 25 GB storage per month (always free)\n\n // SQS: Permanently free tier\n SQS_REQUESTS: 1_000_000, // 1M requests per month (always free)\n\n // CloudWatch: Permanently free tier\n CLOUDWATCH_LOGS_GB: 5, // 5 GB ingestion per month (always free)\n} as const;\n\n/**\n * Estimate storage size in GB based on retention period and email volume\n * Note: Each email generates multiple events (SEND, DELIVERY, OPEN, CLICK, etc.)\n *\n * This calculates STEADY-STATE storage (after retention period fills up).\n * Initial months will be cheaper as storage builds up gradually.\n *\n * Example for 90-day retention with 10k emails/month:\n * - Month 1: ~0.015 GB stored\n * - Month 2: ~0.031 GB stored\n * - Month 3: ~0.046 GB stored (steady state reached)\n * - Month 4+: ~0.046 GB stored (old data deleted, new data added)\n */\nfunction estimateStorageSize(\n emailsPerMonth: number,\n retention: ArchiveRetention,\n numEventTypes = 8\n): number {\n // Average email event record size: ~2 KB (including metadata)\n const avgRecordSizeKB = 2;\n\n // Calculate total months of retention\n const retentionMonths = {\n \"7days\": 0.25,\n \"30days\": 1,\n \"90days\": 3,\n \"3months\": 3,\n \"6months\": 6,\n \"9months\": 9,\n \"1year\": 12,\n \"18months\": 18,\n \"2years\": 24,\n \"30months\": 30,\n \"3years\": 36,\n \"4years\": 48,\n \"5years\": 60,\n \"6years\": 72,\n \"7years\": 84,\n \"8years\": 96,\n \"9years\": 108,\n \"10years\": 120,\n indefinite: 120,\n permanent: 120,\n }[retention];\n\n // Total steady-state storage = emails/month * event types * months * record size\n // This represents storage after retention period fills up\n const totalKB =\n emailsPerMonth * numEventTypes * (retentionMonths ?? 12) * avgRecordSizeKB;\n return totalKB / 1024 / 1024; // Convert to GB\n}\n\n/**\n * Estimate email archive storage size in GB (for full email content)\n * Different from event storage - stores complete RFC 822/MIME emails\n *\n * This calculates STEADY-STATE storage (after retention period fills up).\n */\nfunction estimateArchiveStorageSize(\n emailsPerMonth: number,\n retention: ArchiveRetention\n): number {\n // Average full email size: ~50 KB (HTML, headers, small attachments)\n const avgEmailSizeKB = 50;\n\n // Calculate total months of retention\n const retentionMonths = {\n \"7days\": 0.25,\n \"30days\": 1,\n \"90days\": 3,\n \"3months\": 3,\n \"6months\": 6,\n \"9months\": 9,\n \"1year\": 12,\n \"18months\": 18,\n \"2years\": 24,\n \"30months\": 30,\n \"3years\": 36,\n \"4years\": 48,\n \"5years\": 60,\n \"6years\": 72,\n \"7years\": 84,\n \"8years\": 96,\n \"9years\": 108,\n \"10years\": 120,\n indefinite: 120,\n permanent: 120,\n }[retention];\n\n // Total steady-state storage = emails/month * months * email size\n const totalKB = emailsPerMonth * (retentionMonths ?? 12) * avgEmailSizeKB;\n return totalKB / 1024 / 1024; // Convert to GB\n}\n\n/**\n * Calculate cost for event tracking feature\n * Architecture: SES → EventBridge → SQS → Lambda → DynamoDB\n */\nfunction calculateEventTrackingCost(\n config: WrapsEmailConfig,\n emailsPerMonth: number\n): FeatureCost | undefined {\n if (!config.eventTracking?.enabled) {\n return;\n }\n\n let monthlyCost = 0;\n const components: string[] = [];\n\n // Calculate number of events based on event types tracked\n const numEventTypes = config.eventTracking.events?.length || 8; // Default to 8 common events\n const totalEvents = emailsPerMonth * numEventTypes; // Each email can trigger multiple events\n\n // EventBridge custom events (SES → EventBridge)\n if (config.eventTracking.eventBridge) {\n const eventCost =\n (totalEvents / 1_000_000) * AWS_PRICING.EVENTBRIDGE_EVENTS_PER_MILLION;\n monthlyCost += eventCost;\n components.push(\"EventBridge\");\n }\n\n // SQS queue costs (EventBridge → SQS)\n // Each event: 1 send to SQS + 1 receive by Lambda + 1 delete = 3 requests\n const sqsRequests = totalEvents * 3;\n const sqsCost =\n (Math.max(0, sqsRequests - FREE_TIER.SQS_REQUESTS) / 1_000_000) *\n AWS_PRICING.SQS_REQUESTS_PER_MILLION;\n monthlyCost += sqsCost;\n components.push(\"SQS\");\n\n // Lambda processing costs (SQS → Lambda → DynamoDB)\n const lambdaInvocations = totalEvents; // One invocation per event\n const lambdaRequestCost =\n (Math.max(0, lambdaInvocations - FREE_TIER.LAMBDA_REQUESTS) / 1_000_000) *\n AWS_PRICING.LAMBDA_REQUESTS_PER_MILLION;\n\n // Compute cost: 512MB memory, 100ms average execution\n const memoryGB = 0.5; // 512MB = 0.5GB\n const avgDurationSeconds = 0.1; // 100ms\n const computeGBSeconds = lambdaInvocations * memoryGB * avgDurationSeconds;\n const lambdaComputeCost =\n Math.max(0, computeGBSeconds - FREE_TIER.LAMBDA_COMPUTE_GB_SECONDS) *\n AWS_PRICING.LAMBDA_COMPUTE_PER_GB_SECOND;\n\n monthlyCost += lambdaRequestCost + lambdaComputeCost;\n components.push(\"Lambda\");\n\n return {\n monthly: monthlyCost,\n description: `Event processing (${numEventTypes} event types: ${components.join(\" → \")})`,\n };\n}\n\n/**\n * Calculate cost for DynamoDB email history storage\n */\nfunction calculateDynamoDBCost(\n config: WrapsEmailConfig,\n emailsPerMonth: number\n): FeatureCost | undefined {\n if (!config.eventTracking?.dynamoDBHistory) {\n return;\n }\n\n const retention = config.eventTracking.archiveRetention || \"90days\";\n const numEventTypes = config.eventTracking.events?.length || 8;\n\n // Write costs: one write per event (each email generates multiple events)\n const totalEvents = emailsPerMonth * numEventTypes;\n const writeCost =\n (Math.max(0, totalEvents - FREE_TIER.DYNAMODB_WRITES) / 1_000_000) *\n AWS_PRICING.DYNAMODB_WRITE_PER_MILLION;\n\n // Storage costs\n const storageGB = estimateStorageSize(\n emailsPerMonth,\n retention,\n numEventTypes\n );\n const storageCost =\n Math.max(0, storageGB - FREE_TIER.DYNAMODB_STORAGE_GB) *\n AWS_PRICING.DYNAMODB_STORAGE_PER_GB;\n\n return {\n monthly: writeCost + storageCost,\n description: `Email history (${retention}, ~${storageGB.toFixed(2)} GB at steady-state, ${numEventTypes} event types)`,\n };\n}\n\n/**\n * Calculate cost for tracking features (opens/clicks)\n */\nfunction calculateTrackingCost(\n config: WrapsEmailConfig\n): FeatureCost | undefined {\n if (!config.tracking?.enabled) {\n return;\n }\n\n // Tracking is free - it's built into SES\n // Custom redirect domain DNS records are managed where user already manages DNS\n return {\n monthly: 0,\n description: \"Open/click tracking (no additional cost)\",\n };\n}\n\n/**\n * Calculate cost for reputation metrics\n */\nfunction calculateReputationMetricsCost(\n config: WrapsEmailConfig\n): FeatureCost | undefined {\n if (!config.reputationMetrics) {\n return;\n }\n\n // Reputation metrics are free in CloudWatch\n return {\n monthly: 0,\n description: \"Reputation metrics in CloudWatch (no additional cost)\",\n };\n}\n\n/**\n * Calculate cost for dedicated IP\n */\nfunction calculateDedicatedIpCost(\n config: WrapsEmailConfig\n): FeatureCost | undefined {\n if (!config.dedicatedIp) {\n return;\n }\n\n return {\n monthly: AWS_PRICING.DEDICATED_IP_PER_MONTH,\n description: \"Dedicated IP address (requires 100k+ emails/day for warmup)\",\n };\n}\n\n/**\n * Calculate cost for email archiving (full email content storage)\n * Architecture: SES → Mail Manager Archive (S3-backed)\n */\nfunction calculateEmailArchivingCost(\n config: WrapsEmailConfig,\n emailsPerMonth: number\n): FeatureCost | undefined {\n if (!config.emailArchiving?.enabled) {\n return;\n }\n\n const retention = config.emailArchiving.retention;\n const storageGB = estimateArchiveStorageSize(emailsPerMonth, retention);\n\n // Ingestion cost: one-time per email (charged monthly as new emails arrive)\n const monthlyDataGB = (emailsPerMonth * 50) / 1024 / 1024; // 50 KB average email\n const ingestionCost =\n monthlyDataGB * AWS_PRICING.MAIL_MANAGER_INGESTION_PER_GB;\n\n // Storage cost: steady-state storage after retention period fills\n const storageCost = storageGB * AWS_PRICING.MAIL_MANAGER_STORAGE_PER_GB;\n\n return {\n monthly: ingestionCost + storageCost,\n description: `Email archiving (${retention}, ~${storageGB.toFixed(2)} GB at steady-state)`,\n };\n}\n\n/**\n * Calculate total infrastructure costs\n *\n * @param config Email configuration\n * @param emailsPerMonth Estimated monthly email volume\n * @returns Detailed cost breakdown\n */\nexport function calculateCosts(\n config: WrapsEmailConfig,\n emailsPerMonth = 10_000\n): FeatureCostBreakdown {\n const tracking = calculateTrackingCost(config);\n const reputationMetrics = calculateReputationMetricsCost(config);\n const eventTracking = calculateEventTrackingCost(config, emailsPerMonth);\n const dynamoDBHistory = calculateDynamoDBCost(config, emailsPerMonth);\n const emailArchiving = calculateEmailArchivingCost(config, emailsPerMonth);\n const dedicatedIp = calculateDedicatedIpCost(config);\n\n // Calculate SES base costs (always present)\n const sesEmailCost =\n Math.max(0, emailsPerMonth - FREE_TIER.SES_EMAILS) *\n AWS_PRICING.SES_PER_EMAIL;\n\n // Sum all costs\n const totalMonthlyCost =\n sesEmailCost +\n (tracking?.monthly || 0) +\n (reputationMetrics?.monthly || 0) +\n (eventTracking?.monthly || 0) +\n (dynamoDBHistory?.monthly || 0) +\n (emailArchiving?.monthly || 0) +\n (dedicatedIp?.monthly || 0);\n\n return {\n tracking,\n reputationMetrics,\n eventTracking,\n dynamoDBHistory,\n emailArchiving,\n dedicatedIp,\n total: {\n monthly: totalMonthlyCost,\n perEmail: AWS_PRICING.SES_PER_EMAIL,\n description: `Total estimated cost for ${emailsPerMonth.toLocaleString()} emails/month`,\n },\n };\n}\n\n/**\n * Format cost for display\n */\nexport function formatCost(cost: number): string {\n if (cost === 0) {\n return \"Free\";\n }\n if (cost < 0.01) {\n return \"< $0.01\";\n }\n return `$${cost.toFixed(2)}`;\n}\n\n/**\n * Get cost estimate summary for display\n */\nexport function getCostSummary(\n config: WrapsEmailConfig,\n emailsPerMonth = 10_000\n): string {\n const costs = calculateCosts(config, emailsPerMonth);\n const lines: string[] = [];\n\n lines.push(\n `Estimated cost for ${emailsPerMonth.toLocaleString()} emails/month: ${formatCost(costs.total.monthly)}/mo`\n );\n lines.push(\n ` (${formatCost((costs.total.perEmail ?? 0) * 1000)}/1k emails + infrastructure)`\n );\n\n if (costs.tracking) {\n lines.push(\n ` - ${costs.tracking.description}: ${formatCost(costs.tracking.monthly)}`\n );\n }\n if (costs.reputationMetrics) {\n lines.push(\n ` - ${costs.reputationMetrics.description}: ${formatCost(costs.reputationMetrics.monthly)}`\n );\n }\n if (costs.eventTracking) {\n lines.push(\n ` - ${costs.eventTracking.description}: ${formatCost(costs.eventTracking.monthly)}`\n );\n }\n if (costs.dynamoDBHistory) {\n lines.push(\n ` - ${costs.dynamoDBHistory.description}: ${formatCost(costs.dynamoDBHistory.monthly)}`\n );\n }\n if (costs.emailArchiving) {\n lines.push(\n ` - ${costs.emailArchiving.description}: ${formatCost(costs.emailArchiving.monthly)}`\n );\n }\n if (costs.dedicatedIp) {\n lines.push(\n ` - ${costs.dedicatedIp.description}: ${formatCost(costs.dedicatedIp.monthly)}`\n );\n }\n\n return lines.join(\"\\n\");\n}\n","import type { ConfigPreset, WrapsEmailConfig } from \"../../types/index.js\";\nimport { calculateCosts, formatCost } from \"./costs.js\";\n\n/**\n * Preset configurations with recommended settings for different use cases\n */\n\n/**\n * Starter preset - minimal features for low-volume senders\n * Perfect for: Side projects, MVPs, development/staging\n * Volume: Up to 10k emails/month\n * Cost: ~$1-2/month (without archiving)\n */\nexport const STARTER_PRESET: WrapsEmailConfig = {\n tracking: {\n enabled: true,\n opens: true,\n clicks: true,\n },\n tlsRequired: true,\n reputationMetrics: false,\n suppressionList: {\n enabled: true,\n reasons: [\"BOUNCE\", \"COMPLAINT\"],\n },\n eventTracking: {\n enabled: false,\n },\n // Email archiving disabled by default\n emailArchiving: {\n enabled: false,\n retention: \"30days\",\n },\n sendingEnabled: true,\n};\n\n/**\n * Production preset - recommended for most production applications\n * Perfect for: SaaS apps, B2B products, moderate volume\n * Volume: 10k-500k emails/month\n * Cost: ~$10-50/month (scales with volume, add ~$5-15/mo for archiving)\n */\nexport const PRODUCTION_PRESET: WrapsEmailConfig = {\n tracking: {\n enabled: true,\n opens: true,\n clicks: true,\n },\n tlsRequired: true,\n reputationMetrics: true,\n suppressionList: {\n enabled: true,\n reasons: [\"BOUNCE\", \"COMPLAINT\"],\n },\n eventTracking: {\n enabled: true,\n eventBridge: true,\n events: [\n \"SEND\",\n \"DELIVERY\",\n \"OPEN\",\n \"CLICK\",\n \"BOUNCE\",\n \"COMPLAINT\",\n \"REJECT\",\n \"RENDERING_FAILURE\",\n ],\n dynamoDBHistory: true,\n archiveRetention: \"90days\",\n },\n // Email archiving with 90-day retention\n emailArchiving: {\n enabled: false, // User can opt-in\n retention: \"90days\",\n },\n sendingEnabled: true,\n};\n\n/**\n * Enterprise preset - full features for high-volume senders\n * Perfect for: Large platforms, high-volume transactional email\n * Volume: 500k+ emails/month\n * Cost: ~$100-200/month (includes $24.95 dedicated IP, add ~$50+/mo for archiving)\n */\nexport const ENTERPRISE_PRESET: WrapsEmailConfig = {\n tracking: {\n enabled: true,\n opens: true,\n clicks: true,\n },\n tlsRequired: true,\n reputationMetrics: true,\n suppressionList: {\n enabled: true,\n reasons: [\"BOUNCE\", \"COMPLAINT\"],\n },\n eventTracking: {\n enabled: true,\n eventBridge: true,\n events: [\n \"SEND\",\n \"DELIVERY\",\n \"OPEN\",\n \"CLICK\",\n \"BOUNCE\",\n \"COMPLAINT\",\n \"REJECT\",\n \"RENDERING_FAILURE\",\n \"DELIVERY_DELAY\",\n \"SUBSCRIPTION\",\n ],\n dynamoDBHistory: true,\n archiveRetention: \"1year\",\n },\n // Email archiving with 1-year retention\n emailArchiving: {\n enabled: false, // User can opt-in\n retention: \"1year\",\n },\n dedicatedIp: true,\n sendingEnabled: true,\n};\n\n/**\n * Get preset configuration by name\n */\nexport function getPreset(preset: ConfigPreset): WrapsEmailConfig | null {\n switch (preset) {\n case \"starter\":\n return STARTER_PRESET;\n case \"production\":\n return PRODUCTION_PRESET;\n case \"enterprise\":\n return ENTERPRISE_PRESET;\n case \"custom\":\n return null; // User will configure manually\n }\n}\n\n/**\n * Preset metadata for display\n */\nexport type PresetInfo = {\n name: string;\n description: string;\n recommended: string;\n volume: string;\n estimatedCost: string;\n features: string[];\n};\n\n/**\n * Get preset information for display\n */\nexport function getPresetInfo(preset: ConfigPreset): PresetInfo {\n const config = getPreset(preset);\n\n if (preset === \"custom\" || !config) {\n return {\n name: \"Custom\",\n description: \"Configure each feature individually\",\n recommended: \"Advanced users who need specific configuration\",\n volume: \"Any volume\",\n estimatedCost: \"Varies\",\n features: [\"Full control over all features\"],\n };\n }\n\n const costs = calculateCosts(\n config,\n preset === \"starter\"\n ? 10_000\n : preset === \"production\"\n ? 100_000\n : 1_000_000\n );\n\n const baseInfo = {\n starter: {\n name: \"Starter\",\n description: \"Minimal features for low-volume senders\",\n recommended: \"Side projects, MVPs, development/staging\",\n volume: \"Up to 10k emails/month\",\n features: [\n \"Open & click tracking\",\n \"TLS encryption required\",\n \"Automatic bounce/complaint suppression\",\n \"Optional: Email archiving (full content storage)\",\n ],\n },\n production: {\n name: \"Production\",\n description: \"Recommended for most production applications\",\n recommended: \"SaaS apps, B2B products, moderate volume (RECOMMENDED)\",\n volume: \"10k-500k emails/month\",\n features: [\n \"Everything in Starter\",\n \"Reputation tracking\",\n \"Real-time event tracking (EventBridge)\",\n \"90-day email history storage\",\n \"Optional: Email archiving with rendered viewer\",\n \"Complete event visibility\",\n ],\n },\n enterprise: {\n name: \"Enterprise\",\n description: \"Full features for high-volume senders\",\n recommended: \"Large platforms, high-volume transactional email\",\n volume: \"500k+ emails/month\",\n features: [\n \"Everything in Production\",\n \"Dedicated IP address\",\n \"1-year email history\",\n \"Optional: 1-year+ email archiving\",\n \"All event types tracked\",\n \"Priority support eligibility\",\n ],\n },\n }[preset];\n\n return {\n ...baseInfo,\n estimatedCost: formatCost(costs.total.monthly),\n } as PresetInfo;\n}\n\n/**\n * Get all preset options for CLI prompts\n */\nexport function getAllPresetInfo(): PresetInfo[] {\n return [\n getPresetInfo(\"starter\"),\n getPresetInfo(\"production\"),\n getPresetInfo(\"enterprise\"),\n getPresetInfo(\"custom\"),\n ];\n}\n\n/**\n * Compare two configurations to determine upgrade path\n */\nexport function getUpgradePath(\n current: WrapsEmailConfig,\n target: WrapsEmailConfig\n): string[] {\n const changes: string[] = [];\n\n // Check tracking changes\n if (!current.tracking?.enabled && target.tracking?.enabled) {\n changes.push(\"Enable email tracking (opens & clicks)\");\n }\n\n // Check reputation metrics\n if (!current.reputationMetrics && target.reputationMetrics) {\n changes.push(\"Enable reputation metrics\");\n }\n\n // Check event tracking\n if (!current.eventTracking?.enabled && target.eventTracking?.enabled) {\n changes.push(\"Enable real-time event tracking\");\n }\n\n // Check DynamoDB history\n if (\n !current.eventTracking?.dynamoDBHistory &&\n target.eventTracking?.dynamoDBHistory\n ) {\n changes.push(\"Enable email history storage\");\n }\n\n // Check retention upgrade\n if (\n current.eventTracking?.archiveRetention !==\n target.eventTracking?.archiveRetention &&\n target.eventTracking?.archiveRetention\n ) {\n changes.push(\n `Upgrade retention: ${current.eventTracking?.archiveRetention || \"none\"} → ${target.eventTracking.archiveRetention}`\n );\n }\n\n // Check dedicated IP\n if (!current.dedicatedIp && target.dedicatedIp) {\n changes.push(\"Add dedicated IP address\");\n }\n\n return changes;\n}\n\n/**\n * Validate configuration for common issues\n */\nexport function validateConfig(config: WrapsEmailConfig): string[] {\n const warnings: string[] = [];\n\n // Warn about dedicated IP without high volume\n if (config.dedicatedIp) {\n warnings.push(\n \"⚠️ Dedicated IPs require 100k+ emails/day for proper warmup. Consider starting with shared IPs.\"\n );\n }\n\n // Warn about event tracking without storage\n if (config.eventTracking?.enabled && !config.eventTracking?.dynamoDBHistory) {\n warnings.push(\n \"💡 Event tracking is enabled but history storage is disabled. Events will only be available in real-time.\"\n );\n }\n\n // Warn about long retention without need\n if (config.eventTracking?.archiveRetention === \"indefinite\") {\n warnings.push(\n \"⚠️ Indefinite retention can become expensive. Consider 90-day or 1-year retention.\"\n );\n }\n\n return warnings;\n}\n","import * as clack from \"@clack/prompts\";\nimport pc from \"picocolors\";\nimport type { ArchiveRetention } from \"../../types/index.js\";\n\n/**\n * Hosting provider type\n */\nexport type Provider = \"vercel\" | \"aws\" | \"railway\" | \"other\";\n\n/**\n * Prompt for hosting provider\n */\nexport async function promptProvider(): Promise<Provider> {\n const provider = await clack.select({\n message: \"Where is your app hosted?\",\n options: [\n {\n value: \"aws\",\n label: \"AWS (Lambda/ECS/EC2)\",\n hint: \"Uses IAM roles automatically\",\n },\n {\n value: \"vercel\",\n label: \"Vercel\",\n hint: \"Uses OIDC (no AWS credentials needed)\",\n },\n {\n value: \"railway\",\n label: \"Railway\",\n hint: \"Requires AWS credentials\",\n },\n {\n value: \"other\",\n label: \"Other\",\n hint: \"Will use AWS access keys\",\n },\n ],\n });\n\n if (clack.isCancel(provider)) {\n clack.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n return provider as Provider;\n}\n\n/**\n * Prompt for AWS region\n */\nexport async function promptRegion(defaultRegion: string): Promise<string> {\n const region = await clack.select({\n message: \"Select AWS region:\",\n options: [\n { value: \"us-east-1\", label: \"US East (N. Virginia)\", hint: \"us-east-1\" },\n { value: \"us-east-2\", label: \"US East (Ohio)\", hint: \"us-east-2\" },\n {\n value: \"us-west-1\",\n label: \"US West (N. California)\",\n hint: \"us-west-1\",\n },\n { value: \"us-west-2\", label: \"US West (Oregon)\", hint: \"us-west-2\" },\n { value: \"af-south-1\", label: \"Africa (Cape Town)\", hint: \"af-south-1\" },\n {\n value: \"ap-east-1\",\n label: \"Asia Pacific (Hong Kong)\",\n hint: \"ap-east-1\",\n },\n {\n value: \"ap-south-1\",\n label: \"Asia Pacific (Mumbai)\",\n hint: \"ap-south-1\",\n },\n {\n value: \"ap-northeast-1\",\n label: \"Asia Pacific (Tokyo)\",\n hint: \"ap-northeast-1\",\n },\n {\n value: \"ap-northeast-2\",\n label: \"Asia Pacific (Seoul)\",\n hint: \"ap-northeast-2\",\n },\n {\n value: \"ap-northeast-3\",\n label: \"Asia Pacific (Osaka)\",\n hint: \"ap-northeast-3\",\n },\n {\n value: \"ap-southeast-1\",\n label: \"Asia Pacific (Singapore)\",\n hint: \"ap-southeast-1\",\n },\n {\n value: \"ap-southeast-2\",\n label: \"Asia Pacific (Sydney)\",\n hint: \"ap-southeast-2\",\n },\n {\n value: \"ap-southeast-3\",\n label: \"Asia Pacific (Jakarta)\",\n hint: \"ap-southeast-3\",\n },\n {\n value: \"ca-central-1\",\n label: \"Canada (Central)\",\n hint: \"ca-central-1\",\n },\n {\n value: \"eu-central-1\",\n label: \"Europe (Frankfurt)\",\n hint: \"eu-central-1\",\n },\n { value: \"eu-west-1\", label: \"Europe (Ireland)\", hint: \"eu-west-1\" },\n { value: \"eu-west-2\", label: \"Europe (London)\", hint: \"eu-west-2\" },\n { value: \"eu-west-3\", label: \"Europe (Paris)\", hint: \"eu-west-3\" },\n { value: \"eu-south-1\", label: \"Europe (Milan)\", hint: \"eu-south-1\" },\n { value: \"eu-north-1\", label: \"Europe (Stockholm)\", hint: \"eu-north-1\" },\n {\n value: \"me-south-1\",\n label: \"Middle East (Bahrain)\",\n hint: \"me-south-1\",\n },\n {\n value: \"sa-east-1\",\n label: \"South America (São Paulo)\",\n hint: \"sa-east-1\",\n },\n ],\n initialValue: defaultRegion || \"us-east-1\",\n });\n\n if (clack.isCancel(region)) {\n clack.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n return region as string;\n}\n\n/**\n * Prompt for domain to verify (optional)\n */\nexport async function promptDomain(): Promise<string> {\n const domain = await clack.text({\n message: \"Domain to verify (optional):\",\n placeholder: \"myapp.com\",\n validate: (value) => {\n if (!value) {\n return; // Optional\n }\n if (!value.includes(\".\")) {\n return \"Please enter a valid domain (e.g., myapp.com)\";\n }\n },\n });\n\n if (clack.isCancel(domain)) {\n clack.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n return domain || \"\";\n}\n\n/**\n * Vercel configuration\n */\nexport type VercelConfig = {\n teamSlug: string;\n projectName: string;\n};\n\n/**\n * Prompt for Vercel configuration\n */\nexport async function promptVercelConfig(): Promise<VercelConfig> {\n const config = await clack.group(\n {\n teamSlug: () =>\n clack.text({\n message: \"Vercel team slug:\",\n placeholder: \"my-team\",\n validate: (value) => {\n if (!value) {\n return \"Team slug is required for Vercel integration\";\n }\n },\n }),\n projectName: () =>\n clack.text({\n message: \"Vercel project name:\",\n placeholder: \"my-project\",\n validate: (value) => {\n if (!value) {\n return \"Project name is required\";\n }\n },\n }),\n },\n {\n onCancel: () => {\n clack.cancel(\"Operation cancelled.\");\n process.exit(0);\n },\n }\n );\n\n return config as VercelConfig;\n}\n\n/**\n * Prompt for integration level\n */\nexport async function promptIntegrationLevel(): Promise<\n \"dashboard-only\" | \"enhanced\"\n> {\n const level = await clack.select({\n message: \"Integration level:\",\n options: [\n {\n value: \"enhanced\",\n label: \"Enhanced (full email tracking)\",\n hint: \"Creates SES config, DynamoDB, Lambda functions\",\n },\n {\n value: \"dashboard-only\",\n label: \"Dashboard-only (read-only)\",\n hint: \"Only creates IAM role for dashboard access\",\n },\n ],\n });\n\n if (clack.isCancel(level)) {\n clack.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n return level as \"dashboard-only\" | \"enhanced\";\n}\n\n/**\n * Confirm deployment\n */\nexport async function confirmDeploy(): Promise<boolean> {\n const confirmed = await clack.confirm({\n message: \"Deploy infrastructure to your AWS account?\",\n initialValue: true,\n });\n\n if (clack.isCancel(confirmed)) {\n clack.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n return confirmed;\n}\n\n/**\n * Feature definition for multi-select\n */\nexport type FeatureOption = {\n value: string;\n label: string;\n hint: string;\n};\n\n/**\n * Get available features\n */\nexport function getAvailableFeatures(): FeatureOption[] {\n return [\n {\n value: \"configSet\",\n label: \"Configuration Set\",\n hint: \"Track opens, clicks, bounces, and complaints\",\n },\n {\n value: \"bounceHandling\",\n label: \"Bounce Handling\",\n hint: \"Automatically process bounce notifications\",\n },\n {\n value: \"complaintHandling\",\n label: \"Complaint Handling\",\n hint: \"Automatically process spam complaints\",\n },\n {\n value: \"emailHistory\",\n label: \"Email History\",\n hint: \"Store sent emails in DynamoDB (90-day retention)\",\n },\n {\n value: \"eventProcessor\",\n label: \"Event Processor\",\n hint: \"Advanced analytics and webhook forwarding\",\n },\n {\n value: \"dashboardAccess\",\n label: \"Dashboard Access\",\n hint: \"Read-only IAM role for web dashboard\",\n },\n ];\n}\n\n/**\n * Prompt for feature selection (multi-select)\n */\nexport async function promptFeatureSelection(\n preselected?: string[]\n): Promise<string[]> {\n const features = getAvailableFeatures();\n\n const selected = await clack.multiselect({\n message: \"Select features to deploy:\",\n options: features,\n initialValues: preselected || [\n \"configSet\",\n \"bounceHandling\",\n \"complaintHandling\",\n \"dashboardAccess\",\n ],\n required: true,\n });\n\n if (clack.isCancel(selected)) {\n clack.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n return selected as string[];\n}\n\n/**\n * Conflict resolution action\n */\nexport type ConflictAction = \"deploy-alongside\" | \"replace\" | \"skip\";\n\n/**\n * Prompt for conflict resolution\n */\nexport async function promptConflictResolution(\n resourceType: string,\n existingResourceName: string\n): Promise<ConflictAction> {\n const action = await clack.select({\n message: `Found existing ${resourceType}: ${pc.cyan(existingResourceName)}. How should we handle this?`,\n options: [\n {\n value: \"deploy-alongside\",\n label: \"Deploy alongside (no changes)\",\n hint: \"Create our resources without modifying yours\",\n },\n {\n value: \"replace\",\n label: \"Replace with Wraps version\",\n hint: \"Save original for restore, use ours\",\n },\n {\n value: \"skip\",\n label: \"Skip this feature\",\n hint: \"Keep your setup, skip Wraps deployment\",\n },\n ],\n });\n\n if (clack.isCancel(action)) {\n clack.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n return action as ConflictAction;\n}\n\n/**\n * Prompt to select identities to track\n */\nexport async function promptSelectIdentities(\n identities: Array<{ name: string; verified: boolean }>\n): Promise<string[]> {\n const selected = await clack.multiselect({\n message: \"Select identities to connect with Wraps:\",\n options: identities.map((id) => ({\n value: id.name,\n label: id.name,\n hint: id.verified ? \"Verified\" : \"Pending verification\",\n })),\n required: false,\n });\n\n if (clack.isCancel(selected)) {\n clack.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n return selected as string[];\n}\n\n/**\n * Confirm connection deployment\n */\nexport async function confirmConnect(): Promise<boolean> {\n const confirmed = await clack.confirm({\n message: \"Connect to existing AWS infrastructure?\",\n initialValue: true,\n });\n\n if (clack.isCancel(confirmed)) {\n clack.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n return confirmed;\n}\n\n/**\n * Prompt for configuration preset\n */\nexport async function promptConfigPreset(): Promise<\n \"starter\" | \"production\" | \"enterprise\" | \"custom\"\n> {\n const { getAllPresetInfo } = await import(\"../email/presets.js\");\n const presets = getAllPresetInfo();\n\n const preset = await clack.select({\n message: \"Choose a configuration preset:\",\n options: presets.map((p: any) => ({\n value: p.name.toLowerCase() as\n | \"starter\"\n | \"production\"\n | \"enterprise\"\n | \"custom\",\n label: `${p.name} - ${p.description}`,\n hint: `${p.volume} | Est. ${p.estimatedCost}/mo`,\n })),\n });\n\n if (clack.isCancel(preset)) {\n clack.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n return preset as \"starter\" | \"production\" | \"enterprise\" | \"custom\";\n}\n\n/**\n * Prompt for estimated monthly email volume\n */\nexport async function promptEstimatedVolume(): Promise<number> {\n const volume = await clack.select({\n message: \"Estimated monthly email volume:\",\n options: [\n { value: 1000, label: \"< 1k emails/month\", hint: \"Hobby/Development\" },\n { value: 10_000, label: \"1k-10k emails/month\", hint: \"Side Project\" },\n {\n value: 50_000,\n label: \"10k-100k emails/month\",\n hint: \"Growing Startup\",\n },\n {\n value: 250_000,\n label: \"100k-500k emails/month\",\n hint: \"Production SaaS\",\n },\n { value: 1_000_000, label: \"500k+ emails/month\", hint: \"High Volume\" },\n ],\n });\n\n if (clack.isCancel(volume)) {\n clack.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n return volume as number;\n}\n\n/**\n * Prompt for custom configuration\n */\n/**\n * Prompt for email archiving configuration (for presets)\n */\nexport async function promptEmailArchiving(): Promise<{\n enabled: boolean;\n retention: ArchiveRetention;\n}> {\n const enabled = await clack.confirm({\n message:\n \"Enable email archiving? (Store full email content with HTML for viewing in dashboard)\",\n initialValue: false,\n });\n\n if (clack.isCancel(enabled)) {\n clack.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n if (!enabled) {\n return { enabled: false, retention: \"90days\" };\n }\n\n const retention = await clack.select({\n message: \"Email archive retention period:\",\n options: [\n { value: \"7days\", label: \"7 days\", hint: \"~$1-2/mo for 10k emails\" },\n { value: \"30days\", label: \"30 days\", hint: \"~$2-4/mo for 10k emails\" },\n {\n value: \"90days\",\n label: \"90 days (recommended)\",\n hint: \"~$5-10/mo for 10k emails\",\n },\n {\n value: \"6months\",\n label: \"6 months\",\n hint: \"~$15-25/mo for 10k emails\",\n },\n { value: \"1year\", label: \"1 year\", hint: \"~$25-40/mo for 10k emails\" },\n {\n value: \"18months\",\n label: \"18 months\",\n hint: \"~$35-60/mo for 10k emails\",\n },\n ],\n initialValue: \"90days\",\n });\n\n if (clack.isCancel(retention)) {\n clack.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n clack.log.info(\n pc.dim(\n \"Archiving stores full RFC 822 emails with HTML, attachments, and headers\"\n )\n );\n clack.log.info(\n pc.dim(\"Cost: $2/GB ingestion + $0.19/GB/month storage (~50KB per email)\")\n );\n\n return {\n enabled: true,\n retention: retention as ArchiveRetention,\n };\n}\n\nexport async function promptCustomConfig(existingConfig?: any): Promise<any> {\n clack.log.info(\"Custom configuration builder\");\n clack.log.info(\"Configure each feature individually\");\n\n // Reputation tracking (first, as it's recommended)\n const reputationMetrics = await clack.confirm({\n message: \"Enable reputation tracking (recommended)?\",\n initialValue: existingConfig?.reputationMetrics ?? true,\n });\n\n if (clack.isCancel(reputationMetrics)) {\n clack.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n // Tracking\n const trackingEnabled = await clack.confirm({\n message: \"Enable open & click tracking?\",\n initialValue: existingConfig?.tracking?.enabled ?? true,\n });\n\n if (clack.isCancel(trackingEnabled)) {\n clack.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n // Event tracking (combined - EventBridge + DynamoDB)\n const eventTrackingEnabled = await clack.confirm({\n message: \"Store email events in DynamoDB?\",\n initialValue: existingConfig?.eventTracking?.enabled ?? true,\n });\n\n if (clack.isCancel(eventTrackingEnabled)) {\n clack.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n let archiveRetention: string | symbol = \"90days\";\n\n if (eventTrackingEnabled) {\n archiveRetention = await clack.select({\n message: \"Event history retention period:\",\n options: [\n { value: \"7days\", label: \"7 days\", hint: \"Minimal storage cost\" },\n { value: \"30days\", label: \"30 days\", hint: \"Development/testing\" },\n {\n value: \"90days\",\n label: \"90 days (recommended)\",\n hint: \"Standard retention\",\n },\n { value: \"1year\", label: \"1 year\", hint: \"Compliance requirements\" },\n {\n value: \"indefinite\",\n label: \"Indefinite\",\n hint: \"Higher storage cost\",\n },\n ],\n initialValue:\n existingConfig?.eventTracking?.archiveRetention || \"90days\",\n });\n\n if (clack.isCancel(archiveRetention)) {\n clack.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n }\n\n // Security\n const tlsRequired = await clack.confirm({\n message: \"Require TLS encryption for all emails?\",\n initialValue: existingConfig?.tlsRequired ?? true,\n });\n\n if (clack.isCancel(tlsRequired)) {\n clack.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n // Custom MAIL FROM domain (for DMARC alignment)\n const customMailFrom = await clack.confirm({\n message: \"Configure custom MAIL FROM domain? (improves DMARC alignment)\",\n initialValue: existingConfig?.mailFromDomain !== undefined,\n });\n\n if (clack.isCancel(customMailFrom)) {\n clack.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n let mailFromSubdomain: string | symbol = \"mail\";\n\n if (customMailFrom) {\n mailFromSubdomain = await clack.text({\n message: \"MAIL FROM subdomain:\",\n placeholder: \"mail\",\n initialValue:\n existingConfig?.mailFromDomain?.split(\".\")[0] || \"mail\",\n validate: (value) => {\n if (!value || value.trim() === \"\") {\n return \"Subdomain is required\";\n }\n if (!/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/i.test(value)) {\n return \"Invalid subdomain format\";\n }\n return undefined;\n },\n });\n\n if (clack.isCancel(mailFromSubdomain)) {\n clack.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n clack.log.info(\n pc.dim(\n `MAIL FROM will be set to ${mailFromSubdomain}.yourdomain.com`\n )\n );\n }\n\n // Dedicated IP\n const dedicatedIp = await clack.confirm({\n message: \"Request dedicated IP address? (requires 100k+ emails/day)\",\n initialValue: existingConfig?.dedicatedIp ?? false,\n });\n\n if (clack.isCancel(dedicatedIp)) {\n clack.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n // Email Archiving\n const emailArchivingEnabled = await clack.confirm({\n message:\n \"Enable email archiving? (Store full email content with HTML for viewing)\",\n initialValue: existingConfig?.emailArchiving?.enabled ?? false,\n });\n\n if (clack.isCancel(emailArchivingEnabled)) {\n clack.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n let emailArchiveRetention: string | symbol = \"90days\";\n\n if (emailArchivingEnabled) {\n emailArchiveRetention = await clack.select({\n message: \"Email archive retention period:\",\n options: [\n { value: \"7days\", label: \"7 days\", hint: \"~$1-2/mo for 10k emails\" },\n { value: \"30days\", label: \"30 days\", hint: \"~$2-4/mo for 10k emails\" },\n {\n value: \"90days\",\n label: \"90 days (recommended)\",\n hint: \"~$5-10/mo for 10k emails\",\n },\n {\n value: \"6months\",\n label: \"6 months\",\n hint: \"~$15-25/mo for 10k emails\",\n },\n { value: \"1year\", label: \"1 year\", hint: \"~$25-40/mo for 10k emails\" },\n {\n value: \"18months\",\n label: \"18 months\",\n hint: \"~$35-60/mo for 10k emails\",\n },\n ],\n initialValue: existingConfig?.emailArchiving?.retention || \"90days\",\n });\n\n if (clack.isCancel(emailArchiveRetention)) {\n clack.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n clack.log.info(\n pc.dim(\n \"Note: Archiving stores full RFC 822 emails with HTML, attachments, and headers\"\n )\n );\n clack.log.info(\n pc.dim(\"Cost: $2/GB ingestion + $0.19/GB/month storage (~50KB per email)\")\n );\n }\n\n return {\n tracking: trackingEnabled\n ? {\n enabled: true,\n opens: true,\n clicks: true,\n }\n : { enabled: false },\n tlsRequired,\n reputationMetrics,\n mailFromSubdomain: customMailFrom\n ? typeof mailFromSubdomain === \"string\"\n ? mailFromSubdomain\n : \"mail\"\n : undefined,\n suppressionList: {\n enabled: true,\n reasons: [\"BOUNCE\", \"COMPLAINT\"],\n },\n eventTracking: eventTrackingEnabled\n ? {\n enabled: true,\n eventBridge: true,\n events: [\n \"SEND\",\n \"DELIVERY\",\n \"OPEN\",\n \"CLICK\",\n \"BOUNCE\",\n \"COMPLAINT\",\n \"REJECT\",\n \"RENDERING_FAILURE\",\n ],\n dynamoDBHistory: true,\n archiveRetention:\n typeof archiveRetention === \"string\" ? archiveRetention : \"90days\",\n }\n : { enabled: false },\n emailArchiving: emailArchivingEnabled\n ? {\n enabled: true,\n retention:\n typeof emailArchiveRetention === \"string\"\n ? emailArchiveRetention\n : \"90days\",\n }\n : { enabled: false, retention: \"90days\" },\n dedicatedIp,\n sendingEnabled: true,\n };\n}\n","import { AssumeRoleCommand, STSClient } from \"@aws-sdk/client-sts\";\n\n/**\n * AWS credential identity type\n */\nexport type AwsCredentialIdentity = {\n accessKeyId: string;\n secretAccessKey: string;\n sessionToken?: string;\n expiration?: Date;\n};\n\n/**\n * Assume IAM role and return temporary credentials\n */\nexport async function assumeRole(\n roleArn: string,\n region: string,\n sessionName = \"wraps-console\"\n): Promise<AwsCredentialIdentity> {\n const sts = new STSClient({ region });\n\n const response = await sts.send(\n new AssumeRoleCommand({\n RoleArn: roleArn,\n RoleSessionName: sessionName,\n DurationSeconds: 3600, // 1 hour\n })\n );\n\n if (!response.Credentials) {\n throw new Error(\"Failed to assume role: No credentials returned\");\n }\n\n return {\n accessKeyId: response.Credentials.AccessKeyId!,\n secretAccessKey: response.Credentials.SecretAccessKey!,\n sessionToken: response.Credentials.SessionToken!,\n expiration: response.Credentials.Expiration,\n };\n}\n","import {\n GetArchiveMessageCommand,\n type GetArchiveMessageCommandOutput,\n GetArchiveSearchResultsCommand,\n MailManagerClient,\n StartArchiveSearchCommand,\n} from \"@aws-sdk/client-mailmanager\";\nimport DOMPurify from \"isomorphic-dompurify\";\nimport { type ParsedMail, simpleParser } from \"mailparser\";\n\n/**\n * Parsed email from archive\n */\nexport type ParsedEmail = {\n messageId: string;\n from: string;\n to: string;\n subject: string;\n html?: string;\n text?: string;\n attachments: Array<{\n filename?: string;\n contentType: string;\n size: number;\n }>;\n headers: Record<string, string | string[] | undefined>;\n timestamp: Date;\n metadata?: {\n senderIp?: string;\n tlsProtocol?: string;\n tlsCipherSuite?: string;\n senderHostname?: string;\n };\n};\n\n/**\n * Extract archive ID from ARN\n * ARN format: arn:aws:ses:region:account-id:mailmanager-archive/archive-id\n * Returns just the archive-id part\n */\nfunction extractArchiveId(archiveArnOrId: string): string {\n if (archiveArnOrId.startsWith(\"arn:\")) {\n // Extract ID from ARN\n const parts = archiveArnOrId.split(\"/\");\n return parts.at(-1) as string;\n }\n // Already just an ID\n return archiveArnOrId;\n}\n\n/**\n * Search criteria for finding archived email\n */\nexport type ArchiveSearchCriteria = {\n from?: string;\n to?: string;\n subject?: string;\n timestamp?: Date;\n};\n\n/**\n * Get an archived email using search criteria\n *\n * This function performs a two-step process:\n * 1. Search the archive using FROM/TO/SUBJECT to find the ArchivedMessageId\n * 2. Retrieve the actual email content using the ArchivedMessageId\n *\n * Note: MailManager Archive doesn't support searching by SES Message-ID directly.\n * We search using FROM, TO, and SUBJECT which we get from DynamoDB events.\n *\n * @param archiveArnOrId Archive ARN or ID (will extract ID if ARN provided)\n * @param searchCriteria Email search criteria (from, to, subject, timestamp)\n * @param region AWS region\n * @returns Parsed email with full content\n */\nexport async function getArchivedEmail(\n archiveArnOrId: string,\n searchCriteria: ArchiveSearchCriteria,\n region: string\n): Promise<ParsedEmail> {\n const client = new MailManagerClient({ region });\n\n // Extract archive ID from ARN if needed\n const archiveId = extractArchiveId(archiveArnOrId);\n\n // Step 1: Search for the message to get the ArchivedMessageId\n // MailManager doesn't support MESSAGE_ID search, so we use FROM/TO/SUBJECT\n // Use timestamp to narrow the search window (±1 day)\n const searchTime = searchCriteria.timestamp || new Date();\n const dayBefore = new Date(searchTime.getTime() - 24 * 60 * 60 * 1000);\n const dayAfter = new Date(searchTime.getTime() + 24 * 60 * 60 * 1000);\n\n // Build search filters\n const filters: any[] = [];\n\n if (searchCriteria.from) {\n filters.push({\n StringExpression: {\n Evaluate: {\n Attribute: \"FROM\",\n },\n Operator: \"CONTAINS\",\n Values: [searchCriteria.from],\n },\n });\n }\n\n if (searchCriteria.to) {\n filters.push({\n StringExpression: {\n Evaluate: {\n Attribute: \"TO\",\n },\n Operator: \"CONTAINS\",\n Values: [searchCriteria.to],\n },\n });\n }\n\n if (searchCriteria.subject) {\n filters.push({\n StringExpression: {\n Evaluate: {\n Attribute: \"SUBJECT\",\n },\n Operator: \"CONTAINS\",\n Values: [searchCriteria.subject],\n },\n });\n }\n\n // If no filters provided, throw error\n if (filters.length === 0) {\n throw new Error(\n \"At least one search criterion (from, to, or subject) is required\"\n );\n }\n\n const searchCommand = new StartArchiveSearchCommand({\n ArchiveId: archiveId,\n FromTimestamp: dayBefore,\n ToTimestamp: dayAfter,\n Filters: {\n Include: filters,\n },\n MaxResults: 10, // Get a few results in case there are multiple matches\n });\n\n const searchResponse = await client.send(searchCommand);\n const searchId = searchResponse.SearchId;\n\n if (!searchId) {\n throw new Error(\"Failed to start archive search\");\n }\n\n // Step 2: Poll for search results (searches are async)\n let archivedMessageId: string | undefined;\n let attempts = 0;\n const maxAttempts = 20; // Increased max attempts\n const pollInterval = 1000; // 1 second between polls\n\n // Wait a bit before first poll to let search start\n await new Promise((resolve) => setTimeout(resolve, 1000));\n\n while (attempts < maxAttempts) {\n try {\n const resultsCommand = new GetArchiveSearchResultsCommand({\n SearchId: searchId,\n });\n\n const resultsResponse = await client.send(resultsCommand);\n\n if (resultsResponse.Rows && resultsResponse.Rows.length > 0) {\n // Found the message!\n archivedMessageId = resultsResponse.Rows[0].ArchivedMessageId;\n break;\n }\n\n // No results yet, but search completed - email not found\n if (resultsResponse.Rows && resultsResponse.Rows.length === 0) {\n // Search completed but no results\n break;\n }\n } catch (error: unknown) {\n // If search is still in progress, continue polling\n if (\n error instanceof Error &&\n error.name === \"ConflictException\" &&\n error.message.includes(\"still in progress\")\n ) {\n console.log(`Search still in progress, attempt ${attempts + 1}...`);\n } else {\n // Other errors should be thrown\n throw error;\n }\n }\n\n // Wait before next poll\n await new Promise((resolve) => setTimeout(resolve, pollInterval));\n attempts++;\n }\n\n if (!archivedMessageId) {\n throw new Error(\n \"Email not found in archive with the provided search criteria. It may have been sent before archiving was enabled.\"\n );\n }\n\n // Step 3: Get the actual archived message using the ArchivedMessageId\n const command = new GetArchiveMessageCommand({\n ArchivedMessageId: archivedMessageId,\n });\n\n const response: GetArchiveMessageCommandOutput = await client.send(command);\n\n if (!response.MessageDownloadLink) {\n throw new Error(\"No download link available for archived message\");\n }\n\n // Download raw email from presigned S3 URL\n const emailResponse = await fetch(response.MessageDownloadLink);\n if (!emailResponse.ok) {\n throw new Error(`Failed to download email: ${emailResponse.statusText}`);\n }\n\n const emailRaw = await emailResponse.text();\n\n // Parse RFC 822/MIME message\n const parsed: ParsedMail = await simpleParser(emailRaw);\n\n // Extract attachment metadata (don't include full content to save memory)\n const attachments =\n parsed.attachments?.map((att) => ({\n filename: att.filename,\n contentType: att.contentType,\n size: att.size,\n })) || [];\n\n // Convert headers Map to plain object\n const headers: Record<string, string | string[] | undefined> = {};\n if (parsed.headers) {\n for (const [key, value] of parsed.headers) {\n // Convert header values to string/string[]\n if (value instanceof Date) {\n headers[key] = value.toISOString();\n } else if (typeof value === \"string\") {\n headers[key] = value;\n } else if (\n Array.isArray(value) &&\n value.every((v) => typeof v === \"string\")\n ) {\n headers[key] = value as string[];\n } else {\n // Convert complex header types (AddressObject, StructuredHeader, etc.) to string\n headers[key] = JSON.stringify(value);\n }\n }\n }\n\n // Extract from/to as text\n const getAddressText = (\n addr: ParsedMail[\"from\"] | ParsedMail[\"to\"]\n ): string => {\n if (!addr) {\n return \"\";\n }\n if (Array.isArray(addr)) {\n return addr.map((a) => a.text).join(\", \");\n }\n return addr.text || \"\";\n };\n\n return {\n messageId: parsed.messageId || headers[\"message-id\"]?.toString() || \"\",\n from: getAddressText(parsed.from),\n to: getAddressText(parsed.to),\n subject: parsed.subject || \"\",\n html: parsed.html || undefined,\n text: parsed.text || undefined,\n attachments,\n headers,\n timestamp: parsed.date || new Date(),\n // Note: MessageMetadata is not available in GetArchiveMessageCommandOutput\n // These fields would need to be retrieved separately if needed\n metadata: {},\n };\n}\n\n/**\n * Search archived emails\n *\n * TODO: Update this to use the correct Mail Manager Archive Search API:\n * - StartArchiveSearchCommand to initiate search\n * - GetArchiveSearchResultsCommand to retrieve results\n * - Implement polling logic for async search completion\n *\n * @param archiveId Archive ARN or ID\n * @param params Search parameters\n * @param region AWS region\n * @returns Search results\n */\nexport async function searchArchivedEmails(\n archiveId: string,\n params: {\n from?: string;\n to?: string;\n subject?: string;\n startDate?: Date;\n endDate?: Date;\n maxResults?: number;\n },\n region: string\n): Promise<never> {\n // TODO: Implement proper search using StartArchiveSearchCommand\n // and GetArchiveSearchResultsCommand\n // Suppress unused variable warnings\n void archiveId;\n void params;\n void region;\n\n throw new Error(\n \"Archive search is not yet implemented. This requires:\\n\" +\n \"1. Start search with StartArchiveSearchCommand\\n\" +\n \"2. Poll for completion\\n\" +\n \"3. Get results with GetArchiveSearchResultsCommand\"\n );\n}\n\n/**\n * Sanitize HTML for safe rendering using DOMPurify\n * Removes potentially dangerous elements and attributes\n *\n * Uses isomorphic-dompurify which provides secure HTML sanitization\n * that works in both Node.js and browser environments.\n *\n * @param html HTML content to sanitize\n * @returns Sanitized HTML safe for rendering\n */\nexport function sanitizeEmailHtml(html: string): string {\n // DOMPurify provides comprehensive XSS protection including:\n // - Script tag removal (including nested bypass attacks like <scr<script>ipt>)\n // - Event handler removal (onclick, onload, etc.)\n // - Protocol sanitization (javascript:, data:text/html, etc.)\n // - Object/embed/iframe removal\n // - Style-based XSS protection\n return DOMPurify.sanitize(html, {\n // Allow common safe HTML elements for email rendering\n ALLOWED_TAGS: [\n \"p\",\n \"div\",\n \"span\",\n \"a\",\n \"img\",\n \"br\",\n \"strong\",\n \"em\",\n \"b\",\n \"i\",\n \"u\",\n \"h1\",\n \"h2\",\n \"h3\",\n \"h4\",\n \"h5\",\n \"h6\",\n \"ul\",\n \"ol\",\n \"li\",\n \"table\",\n \"thead\",\n \"tbody\",\n \"tr\",\n \"td\",\n \"th\",\n \"blockquote\",\n \"pre\",\n \"code\",\n ],\n ALLOWED_ATTR: [\"href\", \"src\", \"alt\", \"title\", \"class\", \"style\", \"target\"],\n // Keep safe URIs only (removes javascript:, data:text/html, etc.)\n ALLOW_DATA_ATTR: false,\n });\n}\n","import {\n type ArchiveSearchCriteria,\n getArchivedEmail,\n} from \"../../utils/archive.js\";\n\n/**\n * Archived email with full content\n */\nexport type ArchivedEmail = {\n messageId: string;\n from: string;\n to: string;\n subject: string;\n html?: string;\n text?: string;\n attachments: Array<{\n filename?: string;\n contentType: string;\n size: number;\n }>;\n headers: Record<string, string | string[] | undefined>;\n timestamp: Date;\n metadata?: {\n senderIp?: string;\n tlsProtocol?: string;\n tlsCipherSuite?: string;\n senderHostname?: string;\n };\n};\n\ntype FetchArchivedEmailOptions = {\n region: string;\n archiveArn: string;\n from?: string;\n to?: string;\n subject?: string;\n timestamp?: Date;\n};\n\n/**\n * Fetch archived email by message ID from AWS SES Mail Manager\n *\n * This function searches the archive using email metadata (from, to, subject)\n * to find the archived message, since MailManager doesn't support direct\n * search by SES Message-ID.\n *\n * @param messageId Email message ID (for logging/correlation only)\n * @param options Configuration options including search criteria\n * @returns Archived email with full content, or null if not found\n */\nexport async function fetchArchivedEmail(\n messageId: string,\n options: FetchArchivedEmailOptions\n): Promise<ArchivedEmail | null> {\n const { region, archiveArn, from, to, subject, timestamp } = options;\n\n try {\n console.log(\"Fetching archived email:\", {\n messageId,\n archiveArn,\n region,\n });\n\n // Build search criteria from email metadata\n const searchCriteria: ArchiveSearchCriteria = {\n from,\n to,\n subject,\n timestamp,\n };\n\n // Call the archive utility to get the email\n const email = await getArchivedEmail(archiveArn, searchCriteria, region);\n\n console.log(\"Archived email fetched successfully:\", {\n messageId: email.messageId,\n hasHtml: !!email.html,\n hasText: !!email.text,\n attachmentCount: email.attachments.length,\n });\n\n // Return the email data\n return email;\n } catch (error: unknown) {\n // If the email is not found, return null instead of throwing\n if (\n error instanceof Error &&\n (error.message.includes(\"not found\") ||\n error.message.includes(\"ResourceNotFoundException\"))\n ) {\n console.log(\"Archived email not found:\", messageId);\n return null;\n }\n\n // For other errors, log and rethrow\n console.error(\"Error fetching archived email:\", error);\n throw error;\n }\n}\n","import { DynamoDBClient, ScanCommand } from \"@aws-sdk/client-dynamodb\";\nimport { unmarshall } from \"@aws-sdk/util-dynamodb\";\n\nexport type DynamoDBMetrics = {\n opens: Array<{ timestamp: number; value: number }>;\n clicks: Array<{ timestamp: number; value: number }>;\n};\n\n/**\n * Fetch Open and Click metrics from DynamoDB\n */\nexport async function fetchDynamoDBMetrics(\n region: string,\n tableName: string,\n timeRange: { start: Date; end: Date }\n): Promise<DynamoDBMetrics> {\n const dynamodb = new DynamoDBClient({ region });\n\n try {\n const startTime = timeRange.start.getTime();\n const endTime = timeRange.end.getTime();\n\n // Scan the table for events in the time range\n // Note: In production, consider using accountId GSI for better performance\n const response = await dynamodb.send(\n new ScanCommand({\n TableName: tableName,\n FilterExpression:\n \"sentAt BETWEEN :startTime AND :endTime AND (eventType = :open OR eventType = :click)\",\n ExpressionAttributeValues: {\n \":startTime\": { N: startTime.toString() },\n \":endTime\": { N: endTime.toString() },\n \":open\": { S: \"Open\" },\n \":click\": { S: \"Click\" },\n },\n })\n );\n\n const items = (response.Items || []).map((item) => unmarshall(item));\n\n // Group events by 5-minute buckets (to match CloudWatch period)\n const period = 5 * 60 * 1000; // 5 minutes in milliseconds\n const openBuckets = new Map<number, number>();\n const clickBuckets = new Map<number, number>();\n\n for (const item of items) {\n const timestamp = Number(item.sentAt);\n const bucket = Math.floor(timestamp / period) * period;\n const eventType = item.eventType;\n\n if (eventType === \"Open\") {\n openBuckets.set(bucket, (openBuckets.get(bucket) || 0) + 1);\n } else if (eventType === \"Click\") {\n clickBuckets.set(bucket, (clickBuckets.get(bucket) || 0) + 1);\n }\n }\n\n // Convert to array format\n const opens = Array.from(openBuckets.entries()).map(\n ([timestamp, value]) => ({\n timestamp,\n value,\n })\n );\n\n const clicks = Array.from(clickBuckets.entries()).map(\n ([timestamp, value]) => ({\n timestamp,\n value,\n })\n );\n\n return {\n opens: opens.sort((a, b) => a.timestamp - b.timestamp),\n clicks: clicks.sort((a, b) => a.timestamp - b.timestamp),\n };\n } catch (error) {\n console.error(\"Error fetching DynamoDB metrics:\", error);\n // Return empty arrays on error instead of throwing\n return {\n opens: [],\n clicks: [],\n };\n }\n}\n","#!/usr/bin/env node\nimport { readFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport * as clack from \"@clack/prompts\";\nimport args from \"args\";\nimport pc from \"picocolors\";\n// Dashboard commands\nimport { updateRole } from \"./commands/dashboard/update-role.js\";\nimport { config } from \"./commands/email/config.js\";\n// Email commands\nimport { connect } from \"./commands/email/connect.js\";\nimport { emailDestroy } from \"./commands/email/destroy.js\";\nimport {\n addDomain,\n getDkim,\n listDomains,\n removeDomain,\n verifyDomain,\n} from \"./commands/email/domains.js\";\nimport { init } from \"./commands/email/init.js\";\nimport { restore } from \"./commands/email/restore.js\";\nimport { emailStatus } from \"./commands/email/status.js\";\nimport { upgrade } from \"./commands/email/upgrade.js\";\n// Shared commands\nimport { dashboard } from \"./commands/shared/dashboard.js\";\nimport { destroy } from \"./commands/shared/destroy.js\";\nimport { status } from \"./commands/shared/status.js\";\n// Telemetry commands\nimport {\n telemetryDisable,\n telemetryEnable,\n telemetryStatus,\n} from \"./commands/telemetry.js\";\nimport { getTelemetryClient } from \"./telemetry/client.js\";\nimport { trackCommand } from \"./telemetry/events.js\";\nimport {\n printCompletionScript,\n setupTabCompletion,\n} from \"./utils/shared/completion.js\";\nimport { handleCLIError } from \"./utils/shared/errors.js\";\n\n// Get package version\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\nconst packageJson = JSON.parse(\n readFileSync(join(__dirname, \"../package.json\"), \"utf-8\")\n);\nconst VERSION = packageJson.version;\n\n// Setup tab completion\nsetupTabCompletion();\n\n// Function to show version\nfunction showVersion() {\n console.log(`wraps v${VERSION}`);\n process.exit(0);\n}\n\n// Function to show help\nfunction showHelp() {\n clack.intro(pc.bold(`WRAPS CLI v${VERSION}`));\n console.log(\"Deploy AWS infrastructure to your account\\n\");\n console.log(\"Usage: wraps [service] <command> [options]\\n\");\n console.log(\"Services:\");\n console.log(` ${pc.cyan(\"email\")} Email infrastructure (AWS SES)\\n`);\n console.log(\"Email Commands:\");\n console.log(\n ` ${pc.cyan(\"email init\")} Deploy new email infrastructure`\n );\n console.log(\n ` ${pc.cyan(\"email connect\")} Connect to existing AWS SES`\n );\n console.log(` ${pc.cyan(\"email status\")} Show email infrastructure details`);\n console.log(` ${pc.cyan(\"email verify\")} Verify domain DNS records`);\n console.log(\n ` ${pc.cyan(\"email sync\")} Apply CLI updates to infrastructure`\n );\n console.log(` ${pc.cyan(\"email upgrade\")} Add features`);\n console.log(\n ` ${pc.cyan(\"email restore\")} Restore original configuration`\n );\n console.log(\n ` ${pc.cyan(\"email destroy\")} Remove email infrastructure`\n );\n console.log(` ${pc.cyan(\"email domains add\")} Add a domain to SES`);\n console.log(` ${pc.cyan(\"email domains list\")} List all domains`);\n console.log(` ${pc.cyan(\"email domains remove\")} Remove a domain\\n`);\n console.log(\"Console & Dashboard:\");\n console.log(` ${pc.cyan(\"console\")} Start local web console`);\n console.log(\n ` ${pc.cyan(\"dashboard update-role\")} Update hosted dashboard IAM permissions\\n`\n );\n console.log(\"Global Commands:\");\n console.log(` ${pc.cyan(\"status\")} Show overview of all services`);\n console.log(` ${pc.cyan(\"destroy\")} Remove deployed infrastructure`);\n console.log(` ${pc.cyan(\"completion\")} Generate shell completion script`);\n console.log(\n ` ${pc.cyan(\"telemetry\")} Manage anonymous telemetry settings\\n`\n );\n console.log(\"Options:\");\n console.log(\n ` ${pc.dim(\"-p, --provider\")} Hosting provider (vercel, aws, railway, other)`\n );\n console.log(` ${pc.dim(\"-r, --region\")} AWS region`);\n console.log(` ${pc.dim(\"-d, --domain\")} Domain name`);\n console.log(` ${pc.dim(\"--account\")} AWS account ID or alias`);\n console.log(` ${pc.dim(\"--preset\")} Configuration preset`);\n console.log(` ${pc.dim(\"-y, --yes\")} Skip confirmation prompts`);\n console.log(` ${pc.dim(\"-f, --force\")} Force destructive operations`);\n console.log(\n ` ${pc.dim(\"--preview\")} Preview changes without deploying`\n );\n console.log(` ${pc.dim(\"-v, --version\")} Show version number\\n`);\n console.log(\n `Run ${pc.cyan(\"wraps <service> <command> --help\")} for more information.\\n`\n );\n process.exit(0);\n}\n\n// Check for version before args parses\nif (process.argv.includes(\"--version\") || process.argv.includes(\"-v\")) {\n showVersion();\n}\n\n// Check for help before args parses (to override args' built-in help)\nif (process.argv.includes(\"--help\") || process.argv.includes(\"-h\")) {\n showHelp();\n}\n\n// Configure args\nargs.options([\n {\n name: [\"p\", \"provider\"],\n description: \"Hosting provider (vercel, aws, railway, other)\",\n defaultValue: undefined,\n },\n {\n name: [\"r\", \"region\"],\n description: \"AWS region\",\n defaultValue: undefined,\n },\n {\n name: [\"d\", \"domain\"],\n description: \"Domain name\",\n defaultValue: undefined,\n },\n {\n name: \"account\",\n description: \"AWS account ID or alias\",\n defaultValue: undefined,\n },\n {\n name: \"preset\",\n description:\n \"Configuration preset (starter, production, enterprise, custom)\",\n defaultValue: undefined,\n },\n {\n name: [\"y\", \"yes\"],\n description: \"Skip confirmation prompts (non-destructive operations)\",\n defaultValue: false,\n },\n {\n name: [\"f\", \"force\"],\n description:\n \"Force operation without confirmation (destructive operations)\",\n defaultValue: false,\n },\n {\n name: \"port\",\n description: \"Port for dashboard server\",\n defaultValue: undefined,\n },\n {\n name: \"noOpen\",\n description: \"Don't open browser automatically\",\n defaultValue: false,\n },\n {\n name: \"preview\",\n description: \"Preview changes without deploying\",\n defaultValue: false,\n },\n]);\n\n// Get command and flags\nconst flags = args.parse(process.argv);\nconst [primaryCommand, subCommand] = args.sub;\n\n// If no command provided, show interactive service selection\nif (!primaryCommand) {\n async function selectService() {\n clack.intro(pc.bold(`WRAPS CLI v${VERSION}`));\n console.log(\"Welcome! Let's get started deploying your email infrastructure.\\n\");\n\n // Ask what action they want to take\n const action = await clack.select({\n message: \"What would you like to do?\",\n options: [\n {\n value: \"init\",\n label: \"Deploy new infrastructure\",\n hint: \"Create new AWS SES infrastructure\",\n },\n {\n value: \"connect\",\n label: \"Connect existing infrastructure\",\n hint: \"Connect to existing AWS SES setup\",\n },\n ],\n });\n\n if (clack.isCancel(action)) {\n clack.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n // Run the appropriate command\n if (action === \"init\") {\n await init({\n provider: flags.provider,\n region: flags.region,\n domain: flags.domain,\n preset: flags.preset,\n yes: flags.yes,\n preview: flags.preview,\n });\n } else {\n await connect({\n provider: flags.provider,\n region: flags.region,\n yes: flags.yes,\n preview: flags.preview,\n });\n }\n }\n\n selectService().catch(handleCLIError);\n // Early exit - don't run the main run() function\n process.exit(0);\n}\n\n// Route to appropriate command\nasync function run() {\n const startTime = Date.now();\n const telemetry = getTelemetryClient();\n\n // Show first-run telemetry notification\n if (telemetry.shouldShowNotification()) {\n console.log();\n clack.log.info(pc.bold(\"Anonymous Telemetry\"));\n console.log(\n ` Wraps collects ${pc.cyan(\"anonymous usage data\")} to improve the CLI.`\n );\n console.log(\n ` We ${pc.bold(\"never\")} collect: domains, AWS credentials, email content, or PII.`\n );\n console.log(\n ` We ${pc.bold(\"only\")} collect: command names, success/failure, CLI version, OS.`\n );\n console.log();\n console.log(` Opt-out anytime: ${pc.cyan(\"wraps telemetry disable\")}`);\n console.log(` Or set: ${pc.cyan(\"WRAPS_TELEMETRY_DISABLED=1\")}`);\n console.log(` Learn more: ${pc.cyan(\"https://wraps.dev/docs/telemetry\")}`);\n console.log();\n\n telemetry.markNotificationShown();\n }\n\n try {\n // Handle service-specific subcommands (e.g., wraps email init)\n if (primaryCommand === \"email\" && subCommand) {\n switch (subCommand) {\n case \"init\":\n await init({\n provider: flags.provider,\n region: flags.region,\n domain: flags.domain,\n preset: flags.preset,\n yes: flags.yes,\n preview: flags.preview,\n });\n break;\n\n case \"connect\":\n await connect({\n provider: flags.provider,\n region: flags.region,\n yes: flags.yes,\n preview: flags.preview,\n });\n break;\n\n case \"config\":\n case \"sync\":\n await config({\n region: flags.region,\n yes: flags.yes,\n preview: flags.preview,\n });\n break;\n\n case \"upgrade\":\n await upgrade({\n region: flags.region,\n yes: flags.yes,\n preview: flags.preview,\n });\n break;\n\n case \"restore\":\n await restore({\n region: flags.region,\n force: flags.force,\n preview: flags.preview,\n });\n break;\n\n case \"status\":\n await emailStatus({\n account: flags.account,\n });\n break;\n\n case \"verify\": {\n if (!flags.domain) {\n clack.log.error(\"--domain flag is required\");\n console.log(\n `\\nUsage: ${pc.cyan(\"wraps email verify --domain yourapp.com\")}\\n`\n );\n process.exit(1);\n }\n await verifyDomain({ domain: flags.domain });\n break;\n }\n\n case \"domains\": {\n // Handle domains subcommands\n const domainsSubCommand = args.sub[2];\n\n switch (domainsSubCommand) {\n case \"add\": {\n if (!flags.domain) {\n clack.log.error(\"--domain flag is required\");\n console.log(\n `\\nUsage: ${pc.cyan(\"wraps email domains add --domain yourapp.com\")}\\n`\n );\n process.exit(1);\n }\n await addDomain({ domain: flags.domain });\n break;\n }\n\n case \"list\":\n await listDomains();\n break;\n\n case \"verify\": {\n if (!flags.domain) {\n clack.log.error(\"--domain flag is required\");\n console.log(\n `\\nUsage: ${pc.cyan(\"wraps email domains verify --domain yourapp.com\")}\\n`\n );\n process.exit(1);\n }\n await verifyDomain({ domain: flags.domain });\n break;\n }\n\n case \"get-dkim\": {\n if (!flags.domain) {\n clack.log.error(\"--domain flag is required\");\n console.log(\n `\\nUsage: ${pc.cyan(\"wraps email domains get-dkim --domain yourapp.com\")}\\n`\n );\n process.exit(1);\n }\n await getDkim({ domain: flags.domain });\n break;\n }\n\n case \"remove\": {\n if (!flags.domain) {\n clack.log.error(\"--domain flag is required\");\n console.log(\n `\\nUsage: ${pc.cyan(\"wraps email domains remove --domain yourapp.com --force\")}\\n`\n );\n process.exit(1);\n }\n await removeDomain({\n domain: flags.domain,\n force: flags.force,\n });\n break;\n }\n\n default:\n clack.log.error(\n `Unknown domains command: ${domainsSubCommand || \"(none)\"}`\n );\n console.log(\n `\\nAvailable commands: ${pc.cyan(\"add\")}, ${pc.cyan(\"list\")}, ${pc.cyan(\"verify\")}, ${pc.cyan(\"get-dkim\")}, ${pc.cyan(\"remove\")}\\n`\n );\n process.exit(1);\n }\n break;\n }\n\n case \"destroy\":\n await emailDestroy({\n force: flags.force,\n preview: flags.preview,\n });\n break;\n\n default:\n clack.log.error(`Unknown email command: ${subCommand}`);\n console.log(\n `\\nRun ${pc.cyan(\"wraps --help\")} for available commands.\\n`\n );\n process.exit(1);\n }\n // Track email commands (they return early, so track here)\n const emailDuration = Date.now() - startTime;\n const emailCommandName = `email:${subCommand}`;\n trackCommand(emailCommandName, {\n success: true,\n duration_ms: emailDuration,\n service: \"email\",\n });\n return;\n }\n\n // Handle Dashboard subcommands\n if (primaryCommand === \"dashboard\" && subCommand) {\n switch (subCommand) {\n case \"update-role\":\n await updateRole({\n region: flags.region,\n force: flags.force,\n });\n break;\n\n default:\n clack.log.error(`Unknown dashboard command: ${subCommand}`);\n console.log(`\\nAvailable commands: ${pc.cyan(\"update-role\")}\\n`);\n console.log(`Run ${pc.cyan(\"wraps --help\")} for more information.\\n`);\n process.exit(1);\n }\n // Track dashboard commands (they return early, so track here)\n const dashboardDuration = Date.now() - startTime;\n const dashboardCommandName = `dashboard:${subCommand}`;\n trackCommand(dashboardCommandName, {\n success: true,\n duration_ms: dashboardDuration,\n });\n return;\n }\n\n // Handle global commands\n switch (primaryCommand) {\n // Global commands (work across all services)\n case \"status\":\n await status({\n account: flags.account,\n });\n break;\n\n case \"console\":\n await dashboard({\n port: flags.port,\n noOpen: flags.noOpen,\n });\n break;\n\n case \"dashboard\":\n // Deprecated: 'wraps dashboard' without subcommand redirects to 'wraps console'\n if (!subCommand) {\n clack.log.warn(\n `'wraps dashboard' is deprecated. Use ${pc.cyan(\"wraps console\")} instead.`\n );\n await dashboard({\n port: flags.port,\n noOpen: flags.noOpen,\n });\n }\n break;\n\n case \"destroy\":\n await destroy({\n force: flags.force,\n preview: flags.preview,\n });\n break;\n\n case \"completion\":\n printCompletionScript();\n break;\n\n case \"telemetry\": {\n // Handle telemetry subcommands\n switch (subCommand) {\n case \"enable\":\n await telemetryEnable();\n break;\n\n case \"disable\":\n await telemetryDisable();\n break;\n\n case \"status\":\n case undefined:\n await telemetryStatus();\n break;\n\n default:\n clack.log.error(`Unknown telemetry command: ${subCommand}`);\n console.log(\n `\\nAvailable commands: ${pc.cyan(\"enable\")}, ${pc.cyan(\"disable\")}, ${pc.cyan(\"status\")}\\n`\n );\n process.exit(1);\n }\n break;\n }\n\n // Show help for service without subcommand\n case \"email\":\n console.log(\n `\\nPlease specify a command for ${primaryCommand} service.\\n`\n );\n showHelp();\n break;\n\n default:\n clack.log.error(`Unknown command: ${primaryCommand}`);\n console.log(\n `\\nRun ${pc.cyan(\"wraps --help\")} for available commands.\\n`\n );\n process.exit(1);\n }\n // Track successful command execution\n const duration = Date.now() - startTime;\n const commandName = subCommand\n ? `${primaryCommand}:${subCommand}`\n : primaryCommand;\n\n trackCommand(commandName, {\n success: true,\n duration_ms: duration,\n });\n } catch (error) {\n // Track failed command execution\n const duration = Date.now() - startTime;\n const commandName = subCommand\n ? `${primaryCommand}:${subCommand}`\n : primaryCommand;\n\n trackCommand(commandName, {\n success: false,\n duration_ms: duration,\n });\n\n handleCLIError(error);\n } finally {\n // Ensure telemetry events are sent before exit\n await telemetry.shutdown();\n }\n}\n\nrun();\n","import { GetRoleCommand, IAMClient } from \"@aws-sdk/client-iam\";\nimport { confirm, intro, isCancel, log, outro } from \"@clack/prompts\";\nimport pc from \"picocolors\";\nimport type { UpdateRoleOptions } from \"../../types/index.js\";\nimport {\n getAWSRegion,\n validateAWSCredentials,\n} from \"../../utils/shared/aws.js\";\nimport { loadConnectionMetadata } from \"../../utils/shared/metadata.js\";\nimport { DeploymentProgress } from \"../../utils/shared/output.js\";\n\n/**\n * Update hosted dashboard access role command\n *\n * Updates the wraps-console-access-role IAM role with the latest permissions\n * needed for feature detection in the hosted dashboard app (e.g., dynamodb:DescribeTable).\n *\n * This role is created when you connect AWS accounts through the hosted dashboard.\n * This command updates its permissions to match your current infrastructure setup.\n *\n * This command:\n * - Only updates the role if it exists (does not create it)\n * - Updates inline policies to match current feature requirements\n * - Preserves the trust policy (AssumeRole configuration)\n */\nexport async function updateRole(options: UpdateRoleOptions): Promise<void> {\n intro(pc.bold(\"Update Hosted Dashboard Access Role\"));\n\n const progress = new DeploymentProgress();\n\n // 1. Validate AWS credentials\n const identity = await progress.execute(\n \"Validating AWS credentials\",\n async () => validateAWSCredentials()\n );\n\n // 2. Get region\n const region = options.region || (await getAWSRegion());\n\n // 3. Load metadata to check if deployment exists\n const metadata = await loadConnectionMetadata(identity.accountId, region);\n if (!metadata) {\n progress.stop();\n log.error(\n `No Wraps deployment found for account ${pc.cyan(identity.accountId)} in region ${pc.cyan(region)}`\n );\n console.log(\n `\\nRun ${pc.cyan(\"wraps email init\")} to deploy infrastructure first.\\n`\n );\n process.exit(1);\n }\n\n // 4. Check if wraps-console-access-role exists\n const roleName = \"wraps-console-access-role\";\n const iam = new IAMClient({ region: \"us-east-1\" }); // IAM is global\n\n let roleExists = false;\n try {\n await iam.send(new GetRoleCommand({ RoleName: roleName }));\n roleExists = true;\n } catch (error) {\n if (\n error &&\n typeof error === \"object\" &&\n \"name\" in error &&\n error.name !== \"NoSuchEntity\"\n ) {\n throw error;\n }\n }\n\n if (!roleExists) {\n progress.stop();\n log.warn(`IAM role ${pc.cyan(roleName)} does not exist`);\n console.log(\n \"\\nThis role is created when you connect AWS accounts through the hosted dashboard.\"\n );\n console.log(\n \"If you haven't connected an AWS account to the hosted dashboard yet, there's nothing to update.\\n\"\n );\n process.exit(0);\n }\n\n progress.info(`Found IAM role: ${pc.cyan(roleName)}`);\n\n // 5. Confirm update (unless --force)\n if (!options.force) {\n progress.stop();\n const shouldContinue = await confirm({\n message: `Update IAM role ${pc.cyan(roleName)} with latest permissions?`,\n initialValue: true,\n });\n\n if (isCancel(shouldContinue) || !shouldContinue) {\n outro(\"Update cancelled\");\n process.exit(0);\n }\n }\n\n // 6. Build updated policy\n const emailConfig = metadata.services.email?.config;\n const policy = buildConsolePolicyDocument(emailConfig);\n\n // Extract config values for display\n const sendingEnabled =\n !emailConfig ||\n (emailConfig.sendingEnabled as boolean | undefined) !== false;\n const eventTracking = emailConfig?.eventTracking as\n | Record<string, unknown>\n | undefined;\n const emailArchiving = emailConfig?.emailArchiving as\n | Record<string, unknown>\n | undefined;\n\n // 7. Update role policy\n await progress.execute(\"Updating IAM role permissions\", async () => {\n const { PutRolePolicyCommand } = await import(\"@aws-sdk/client-iam\");\n\n await iam.send(\n new PutRolePolicyCommand({\n RoleName: roleName,\n PolicyName: \"wraps-console-access-policy\",\n PolicyDocument: JSON.stringify(policy, null, 2),\n })\n );\n });\n\n progress.stop();\n\n // Success\n outro(pc.green(\"✓ Hosted dashboard access role updated successfully\"));\n\n console.log(`\\n${pc.bold(\"Updated Permissions:\")}`);\n console.log(\n ` ${pc.green(\"✓\")} SES metrics and identity verification (always enabled)`\n );\n console.log(` ${pc.green(\"✓\")} SES template management (always enabled)`);\n\n if (sendingEnabled) {\n console.log(` ${pc.green(\"✓\")} Email sending via SES`);\n }\n\n if (eventTracking?.dynamoDBHistory) {\n console.log(\n ` ${pc.green(\"✓\")} DynamoDB read access (including DescribeTable)`\n );\n }\n\n if (eventTracking?.enabled) {\n console.log(` ${pc.green(\"✓\")} EventBridge and SQS access`);\n }\n\n if (emailArchiving?.enabled) {\n console.log(` ${pc.green(\"✓\")} Mail Manager Archive access`);\n }\n\n console.log(\n `\\n${pc.dim(\"The hosted dashboard will now have updated permissions for feature detection.\")}\\n`\n );\n}\n\n/**\n * Build IAM policy document for hosted dashboard access role\n *\n * This mirrors the permissions from the main wraps-email-role but is used\n * for the hosted dashboard app (not for SDK sending or local console).\n */\ntype PolicyStatement = {\n Effect: string;\n Action: string[];\n Resource: string | string[];\n};\n\ntype PolicyDocument = {\n Version: string;\n Statement: PolicyStatement[];\n};\n\nfunction buildConsolePolicyDocument(\n emailConfig: Record<string, unknown> | undefined\n): PolicyDocument {\n const statements: PolicyStatement[] = [];\n\n // Always allow reading SES metrics for dashboard\n statements.push({\n Effect: \"Allow\",\n Action: [\n \"ses:GetSendStatistics\",\n \"ses:ListIdentities\",\n \"ses:GetIdentityVerificationAttributes\",\n \"cloudwatch:GetMetricData\",\n \"cloudwatch:GetMetricStatistics\",\n ],\n Resource: \"*\",\n });\n\n // Always allow SES template management (for publishing email templates)\n statements.push({\n Effect: \"Allow\",\n Action: [\n \"ses:GetTemplate\",\n \"ses:ListTemplates\",\n \"ses:CreateTemplate\",\n \"ses:UpdateTemplate\",\n \"ses:DeleteTemplate\",\n \"ses:TestRenderTemplate\",\n ],\n Resource: \"*\",\n });\n\n // Allow sending if enabled\n const sendingEnabled = !emailConfig || emailConfig.sendingEnabled !== false;\n if (sendingEnabled) {\n statements.push({\n Effect: \"Allow\",\n Action: [\n \"ses:SendEmail\",\n \"ses:SendRawEmail\",\n \"ses:SendTemplatedEmail\",\n \"ses:SendBulkTemplatedEmail\",\n ],\n Resource: \"*\",\n });\n }\n\n // Allow DynamoDB access if history storage enabled\n const eventTracking = emailConfig?.eventTracking as\n | Record<string, unknown>\n | undefined;\n if (eventTracking?.dynamoDBHistory) {\n statements.push({\n Effect: \"Allow\",\n Action: [\n \"dynamodb:PutItem\",\n \"dynamodb:GetItem\",\n \"dynamodb:Query\",\n \"dynamodb:Scan\",\n \"dynamodb:BatchGetItem\",\n \"dynamodb:DescribeTable\",\n ],\n Resource: [\n \"arn:aws:dynamodb:*:*:table/wraps-email-*\",\n \"arn:aws:dynamodb:*:*:table/wraps-email-*/index/*\",\n ],\n });\n }\n\n // Allow EventBridge access if event tracking enabled\n if (eventTracking?.enabled) {\n statements.push({\n Effect: \"Allow\",\n Action: [\"events:PutEvents\", \"events:DescribeEventBus\"],\n Resource: \"arn:aws:events:*:*:event-bus/wraps-email-*\",\n });\n }\n\n // Allow SQS access if event tracking enabled\n if (eventTracking?.enabled) {\n statements.push({\n Effect: \"Allow\",\n Action: [\n \"sqs:SendMessage\",\n \"sqs:ReceiveMessage\",\n \"sqs:DeleteMessage\",\n \"sqs:GetQueueAttributes\",\n ],\n Resource: \"arn:aws:sqs:*:*:wraps-email-*\",\n });\n }\n\n // Allow Mail Manager Archive access if email archiving enabled\n const emailArchiving = emailConfig?.emailArchiving as\n | Record<string, unknown>\n | undefined;\n if (emailArchiving?.enabled) {\n statements.push({\n Effect: \"Allow\",\n Action: [\n \"ses:StartArchiveSearch\",\n \"ses:GetArchiveSearchResults\",\n \"ses:GetArchiveMessage\",\n \"ses:GetArchiveMessageContent\",\n \"ses:GetArchive\",\n \"ses:ListArchives\",\n \"ses:StartArchiveExport\",\n \"ses:GetArchiveExport\",\n ],\n Resource: \"arn:aws:ses:*:*:mailmanager-archive/*\",\n });\n }\n\n return {\n Version: \"2012-10-17\",\n Statement: statements,\n };\n}\n","import { existsSync } from \"node:fs\";\nimport { readFile, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type {\n EmailConfigPreset,\n Provider,\n ServiceType,\n SMSConfigPreset,\n WrapsEmailConfig,\n WrapsSMSConfig,\n} from \"../../types/index.js\";\nimport { ensureWrapsDir, getWrapsDir } from \"./fs.js\";\n\n/**\n * Service-specific configuration with metadata\n */\nexport type ServiceConfig<TConfig, TPreset> = {\n preset?: TPreset;\n config: TConfig;\n pulumiStackName?: string;\n deployedAt: string;\n};\n\n/**\n * Connection metadata - supports multiple services per AWS account/region\n */\nexport type ConnectionMetadata = {\n version: string; // Metadata format version (e.g., \"1.0.0\")\n accountId: string;\n region: string;\n provider: Provider;\n timestamp: string; // Last updated timestamp\n vercel?: {\n teamSlug: string;\n projectName: string;\n };\n\n // Service-specific configurations\n services: {\n email?: ServiceConfig<WrapsEmailConfig, EmailConfigPreset>;\n sms?: ServiceConfig<WrapsSMSConfig, SMSConfigPreset>;\n };\n};\n\n/**\n * Legacy connection metadata (for backwards compatibility)\n * @deprecated Use ConnectionMetadata instead\n */\nexport type LegacyConnectionMetadata = {\n accountId: string;\n region: string;\n provider: Provider;\n timestamp: string;\n preset?: EmailConfigPreset;\n emailConfig: WrapsEmailConfig;\n vercel?: {\n teamSlug: string;\n projectName: string;\n };\n pulumiStackName?: string;\n};\n\n/**\n * Get the connections directory\n */\nfunction getConnectionsDir(): string {\n return join(getWrapsDir(), \"connections\");\n}\n\n/**\n * Get metadata file path for an account and region\n */\nfunction getMetadataPath(accountId: string, region: string): string {\n return join(getConnectionsDir(), `${accountId}-${region}.json`);\n}\n\n/**\n * Ensure the connections directory exists\n */\nasync function ensureConnectionsDir(): Promise<void> {\n await ensureWrapsDir();\n const connectionsDir = getConnectionsDir();\n if (!existsSync(connectionsDir)) {\n const { mkdir } = await import(\"node:fs/promises\");\n await mkdir(connectionsDir, { recursive: true });\n }\n}\n\n/**\n * Migrate legacy metadata to new multi-service format\n */\nfunction migrateLegacyMetadata(\n legacy: LegacyConnectionMetadata\n): ConnectionMetadata {\n return {\n version: \"1.0.0\",\n accountId: legacy.accountId,\n region: legacy.region,\n provider: legacy.provider,\n timestamp: legacy.timestamp,\n vercel: legacy.vercel,\n services: {\n email: {\n preset: legacy.preset,\n config: legacy.emailConfig,\n pulumiStackName: legacy.pulumiStackName,\n deployedAt: legacy.timestamp,\n },\n },\n };\n}\n\n/**\n * Check if metadata is in legacy format\n */\nfunction isLegacyMetadata(data: any): data is LegacyConnectionMetadata {\n return (\n \"emailConfig\" in data &&\n !(\"services\" in data) &&\n typeof data.emailConfig === \"object\"\n );\n}\n\n/**\n * Load connection metadata from disk\n * Automatically migrates legacy format to new multi-service format\n */\nexport async function loadConnectionMetadata(\n accountId: string,\n region: string\n): Promise<ConnectionMetadata | null> {\n const metadataPath = getMetadataPath(accountId, region);\n\n if (!existsSync(metadataPath)) {\n return null;\n }\n\n try {\n const content = await readFile(metadataPath, \"utf-8\");\n const data = JSON.parse(content);\n\n // Migrate legacy format if needed\n if (isLegacyMetadata(data)) {\n const migrated = migrateLegacyMetadata(data);\n // Save migrated version\n await saveConnectionMetadata(migrated);\n return migrated;\n }\n\n // Add version if missing (for backwards compatibility with early multi-service format)\n if (!data.version) {\n data.version = \"1.0.0\";\n await saveConnectionMetadata(data);\n }\n\n return data as ConnectionMetadata;\n } catch (error: any) {\n console.error(\"Error loading connection metadata:\", error.message);\n return null;\n }\n}\n\n/**\n * Save connection metadata to disk\n */\nexport async function saveConnectionMetadata(\n metadata: ConnectionMetadata\n): Promise<void> {\n await ensureConnectionsDir();\n const metadataPath = getMetadataPath(metadata.accountId, metadata.region);\n\n try {\n const content = JSON.stringify(metadata, null, 2);\n await writeFile(metadataPath, content, \"utf-8\");\n } catch (error: any) {\n console.error(\"Error saving connection metadata:\", error.message);\n throw error;\n }\n}\n\n/**\n * Delete connection metadata\n */\nexport async function deleteConnectionMetadata(\n accountId: string,\n region: string\n): Promise<void> {\n const metadataPath = getMetadataPath(accountId, region);\n\n if (existsSync(metadataPath)) {\n const { unlink } = await import(\"node:fs/promises\");\n await unlink(metadataPath);\n }\n}\n\n/**\n * List all connections\n */\nexport async function listConnections(): Promise<ConnectionMetadata[]> {\n const connectionsDir = getConnectionsDir();\n\n if (!existsSync(connectionsDir)) {\n return [];\n }\n\n try {\n const { readdir } = await import(\"node:fs/promises\");\n const files = await readdir(connectionsDir);\n const connections: ConnectionMetadata[] = [];\n\n for (const file of files) {\n if (file.endsWith(\".json\")) {\n const content = await readFile(join(connectionsDir, file), \"utf-8\");\n try {\n const metadata = JSON.parse(content) as ConnectionMetadata;\n connections.push(metadata);\n } catch (error) {\n console.error(`Error parsing ${file}:`, error);\n }\n }\n }\n\n return connections;\n } catch (error: any) {\n console.error(\"Error listing connections:\", error.message);\n return [];\n }\n}\n\n/**\n * Check if a connection exists\n */\nexport async function connectionExists(\n accountId: string,\n region: string\n): Promise<boolean> {\n const metadataPath = getMetadataPath(accountId, region);\n return existsSync(metadataPath);\n}\n\n/**\n * Create initial connection metadata\n * @deprecated Use addServiceToConnection instead\n */\nexport function createConnectionMetadata(\n accountId: string,\n region: string,\n provider: Provider,\n emailConfig: WrapsEmailConfig,\n preset?: EmailConfigPreset\n): ConnectionMetadata {\n return {\n version: \"1.0.0\",\n accountId,\n region,\n provider,\n timestamp: new Date().toISOString(),\n services: {\n email: {\n preset,\n config: emailConfig,\n deployedAt: new Date().toISOString(),\n },\n },\n };\n}\n\n/**\n * Apply config updates to existing config while preserving user-customized fields.\n *\n * This function starts with the existing config and applies updates,\n * while ensuring user-customized fields are never lost:\n * - domain (sending identity)\n * - mailFromDomain (custom MAIL FROM subdomain)\n * - tracking.customRedirectDomain (custom tracking domain)\n * - tracking.httpsEnabled (HTTPS tracking via CloudFront)\n */\nexport function applyConfigUpdates(\n existingConfig: WrapsEmailConfig,\n updates: Partial<WrapsEmailConfig>\n): WrapsEmailConfig {\n // Start with existing config (ensures all required fields are present)\n const result = { ...existingConfig };\n\n // Apply each update, with special handling for nested objects\n for (const [key, value] of Object.entries(updates)) {\n if (value === undefined) {\n continue;\n }\n\n if (key === \"tracking\" && typeof value === \"object\") {\n // Merge tracking updates while preserving user-customized fields\n const trackingUpdate = value as NonNullable<WrapsEmailConfig[\"tracking\"]>;\n result.tracking = {\n ...result.tracking,\n ...trackingUpdate,\n // Always preserve these if they exist in original\n customRedirectDomain:\n result.tracking?.customRedirectDomain ||\n trackingUpdate.customRedirectDomain,\n httpsEnabled:\n result.tracking?.httpsEnabled ?? trackingUpdate.httpsEnabled,\n };\n } else if (key === \"eventTracking\" && typeof value === \"object\") {\n // Deep merge eventTracking\n result.eventTracking = {\n ...result.eventTracking,\n ...(value as NonNullable<WrapsEmailConfig[\"eventTracking\"]>),\n } as NonNullable<WrapsEmailConfig[\"eventTracking\"]>;\n } else if (key === \"suppressionList\" && typeof value === \"object\") {\n // Deep merge suppressionList\n result.suppressionList = {\n ...result.suppressionList,\n ...(value as NonNullable<WrapsEmailConfig[\"suppressionList\"]>),\n } as NonNullable<WrapsEmailConfig[\"suppressionList\"]>;\n } else if (key === \"emailArchiving\" && typeof value === \"object\") {\n // Deep merge emailArchiving\n result.emailArchiving = {\n ...result.emailArchiving,\n ...(value as NonNullable<WrapsEmailConfig[\"emailArchiving\"]>),\n } as NonNullable<WrapsEmailConfig[\"emailArchiving\"]>;\n } else {\n // Direct assignment for primitives and other objects\n result[key as keyof WrapsEmailConfig] = value as any;\n }\n }\n\n return result;\n}\n\n/**\n * Update email configuration in metadata\n * @deprecated Use updateServiceConfig instead\n */\nexport function updateEmailConfig(\n metadata: ConnectionMetadata,\n emailConfig: Partial<WrapsEmailConfig>\n): void {\n if (!metadata.services.email) {\n throw new Error(\"Email service not configured in metadata\");\n }\n\n // Apply updates while preserving user-customized fields\n metadata.services.email.config = applyConfigUpdates(\n metadata.services.email.config,\n emailConfig\n );\n\n metadata.timestamp = new Date().toISOString();\n}\n\n/**\n * Add a service to an existing connection or create new connection metadata\n */\nexport function addServiceToConnection(\n accountId: string,\n region: string,\n provider: Provider,\n service: ServiceType,\n config: WrapsEmailConfig | WrapsSMSConfig,\n preset?: EmailConfigPreset | SMSConfigPreset,\n existingMetadata?: ConnectionMetadata\n): ConnectionMetadata {\n const timestamp = new Date().toISOString();\n\n if (existingMetadata) {\n // Add service to existing connection\n if (service === \"email\") {\n existingMetadata.services.email = {\n preset: preset as EmailConfigPreset,\n config: config as WrapsEmailConfig,\n deployedAt: timestamp,\n };\n } else if (service === \"sms\") {\n existingMetadata.services.sms = {\n preset: preset as SMSConfigPreset,\n config: config as WrapsSMSConfig,\n deployedAt: timestamp,\n };\n }\n existingMetadata.timestamp = timestamp;\n return existingMetadata;\n }\n\n // Create new connection metadata\n const metadata: ConnectionMetadata = {\n version: \"1.0.0\",\n accountId,\n region,\n provider,\n timestamp,\n services: {},\n };\n\n if (service === \"email\") {\n metadata.services.email = {\n preset: preset as EmailConfigPreset,\n config: config as WrapsEmailConfig,\n deployedAt: timestamp,\n };\n } else if (service === \"sms\") {\n metadata.services.sms = {\n preset: preset as SMSConfigPreset,\n config: config as WrapsSMSConfig,\n deployedAt: timestamp,\n };\n }\n\n return metadata;\n}\n\n/**\n * Update service configuration in metadata\n */\nexport function updateServiceConfig<T extends ServiceType>(\n metadata: ConnectionMetadata,\n service: T,\n config: T extends \"email\"\n ? Partial<WrapsEmailConfig>\n : T extends \"sms\"\n ? Partial<WrapsSMSConfig>\n : never\n): void {\n if (service === \"email\" && metadata.services.email) {\n metadata.services.email.config = {\n ...metadata.services.email.config,\n ...(config as Partial<WrapsEmailConfig>),\n };\n } else if (service === \"sms\" && metadata.services.sms) {\n metadata.services.sms.config = {\n ...metadata.services.sms.config,\n ...(config as Partial<WrapsSMSConfig>),\n };\n } else {\n throw new Error(`${service} service not configured in metadata`);\n }\n\n metadata.timestamp = new Date().toISOString();\n}\n\n/**\n * Remove a service from connection metadata\n */\nexport function removeServiceFromConnection(\n metadata: ConnectionMetadata,\n service: ServiceType\n): void {\n if (service === \"email\") {\n const { email, ...rest } = metadata.services;\n metadata.services = rest;\n } else if (service === \"sms\") {\n const { sms, ...rest } = metadata.services;\n metadata.services = rest;\n }\n metadata.timestamp = new Date().toISOString();\n}\n\n/**\n * Check if a service is configured in metadata\n */\nexport function hasService(\n metadata: ConnectionMetadata,\n service: ServiceType\n): boolean {\n if (service === \"email\") {\n return metadata.services.email !== undefined;\n }\n if (service === \"sms\") {\n return metadata.services.sms !== undefined;\n }\n return false;\n}\n\n/**\n * Get list of configured services in metadata\n */\nexport function getConfiguredServices(\n metadata: ConnectionMetadata\n): ServiceType[] {\n const services: ServiceType[] = [];\n if (metadata.services.email) {\n services.push(\"email\");\n }\n if (metadata.services.sms) {\n services.push(\"sms\");\n }\n return services;\n}\n","import { existsSync } from \"node:fs\";\nimport { mkdir } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\n/**\n * Get the Wraps configuration directory\n */\nexport function getWrapsDir(): string {\n return join(homedir(), \".wraps\");\n}\n\n/**\n * Get the Pulumi workspace directory\n */\nexport function getPulumiWorkDir(): string {\n return join(getWrapsDir(), \"pulumi\");\n}\n\n/**\n * Ensure the Wraps configuration directory exists\n */\nexport async function ensureWrapsDir(): Promise<void> {\n const wrapsDir = getWrapsDir();\n if (!existsSync(wrapsDir)) {\n await mkdir(wrapsDir, { recursive: true });\n }\n}\n\n/**\n * Ensure the Pulumi workspace directory exists and configure local backend\n */\nexport async function ensurePulumiWorkDir(): Promise<void> {\n await ensureWrapsDir();\n const pulumiDir = getPulumiWorkDir();\n if (!existsSync(pulumiDir)) {\n await mkdir(pulumiDir, { recursive: true });\n }\n\n // Set Pulumi to use local backend (file-based state)\n // This avoids needing to login to Pulumi Cloud\n process.env.PULUMI_BACKEND_URL = `file://${pulumiDir}`;\n process.env.PULUMI_CONFIG_PASSPHRASE = \"\"; // Empty passphrase for simplicity\n}\n","import * as clack from \"@clack/prompts\";\nimport pc from \"picocolors\";\n\n/**\n * Deployment progress tracker with spinners using clack\n */\nexport class DeploymentProgress {\n private currentSpinner: ReturnType<typeof clack.spinner> | null = null;\n\n /**\n * Start a spinner with a message\n */\n start(message: string) {\n this.currentSpinner = clack.spinner();\n this.currentSpinner.start(message);\n }\n\n /**\n * Mark current step as succeeded\n */\n succeed(message: string) {\n if (this.currentSpinner) {\n this.currentSpinner.stop(message);\n }\n clack.log.success(message);\n }\n\n /**\n * Mark current step as failed\n */\n fail(message: string) {\n if (this.currentSpinner) {\n this.currentSpinner.stop(message);\n }\n clack.log.error(message);\n }\n\n /**\n * Show info message\n */\n info(message: string) {\n clack.log.info(message);\n }\n\n /**\n * Show step message\n */\n step(message: string) {\n clack.log.step(message);\n }\n\n /**\n * Execute a step with automatic spinner handling\n */\n async execute<T>(message: string, fn: () => Promise<T>): Promise<T> {\n this.start(message);\n try {\n const result = await fn();\n this.succeed(message);\n return result;\n } catch (error) {\n this.fail(message);\n throw error;\n }\n }\n\n /**\n * Stop the spinner\n */\n stop(message?: string) {\n if (this.currentSpinner) {\n this.currentSpinner.stop(message || \"\");\n }\n }\n}\n\n/**\n * DNS record type\n */\nexport type DNSRecord = {\n name: string;\n type: string;\n value: string;\n};\n\n/**\n * Success output configuration\n */\nexport type SuccessOutputs = {\n roleArn: string;\n configSetName?: string;\n region: string;\n dnsRecords?: DNSRecord[];\n trackingDomainDnsRecords?: DNSRecord[];\n acmValidationRecords?: DNSRecord[];\n tableName?: string;\n dnsAutoCreated?: boolean;\n domain?: string;\n customTrackingDomain?: string;\n httpsTrackingEnabled?: boolean;\n mailFromDomain?: string;\n};\n\n/**\n * Display success message with infrastructure outputs\n */\nexport function displaySuccess(outputs: SuccessOutputs) {\n const lines = [\n \"\",\n pc.bold(\"Role ARN:\"),\n ` ${pc.cyan(outputs.roleArn)}`,\n \"\",\n `${pc.bold(\"Region:\")} ${pc.cyan(outputs.region)}`,\n ];\n\n if (outputs.configSetName) {\n lines.push(`${pc.bold(\"Config Set:\")} ${pc.cyan(outputs.configSetName)}`);\n }\n\n if (outputs.tableName) {\n lines.push(`${pc.bold(\"DynamoDB Table:\")} ${pc.cyan(outputs.tableName)}`);\n }\n\n lines.push(\n \"\",\n pc.bold(\"Next steps:\"),\n ` 1. Install SDK: ${pc.yellow(\"npm install @wraps/sdk\")}`,\n ` 2. View dashboard: ${pc.blue(\"https://app.wraps.dev\")}`,\n \"\"\n );\n\n clack.outro(pc.green(\"Email infrastructure deployed successfully!\"));\n console.log(lines.join(\"\\n\"));\n\n // Show DNS auto-creation message\n if (outputs.dnsAutoCreated && outputs.domain) {\n clack.note(\n `DNS records (DKIM, SPF, DMARC) were automatically created in Route53 for ${pc.cyan(\n outputs.domain\n )}.\\n\\nVerification should complete within a few minutes.`,\n pc.green(\"✓ DNS Auto-Configured\")\n );\n }\n\n if (outputs.dnsRecords && outputs.dnsRecords.length > 0) {\n // Extract domain from first DKIM record\n const domain = outputs.dnsRecords[0]?.name.split(\"._domainkey.\")[1];\n\n const dnsLines = [\n pc.bold(\"DKIM Records (CNAME):\"),\n ...outputs.dnsRecords.map(\n (record) =>\n ` ${pc.cyan(record.name)} ${pc.dim(record.type)} \"${record.value}\"`\n ),\n ];\n\n if (domain) {\n // Use MAIL FROM domain for DMARC rua if configured, otherwise use main domain\n const dmarcRuaDomain = outputs.mailFromDomain || domain;\n dnsLines.push(\n \"\",\n pc.bold(\"SPF Record (TXT):\"),\n ` ${pc.cyan(domain)} ${pc.dim(\"TXT\")} \"v=spf1 include:amazonses.com ~all\"`,\n pc.dim(\" Note: If you have an existing SPF record, add 'include:amazonses.com' to it\"),\n \"\",\n pc.bold(\"DMARC Record (TXT):\"),\n ` ${pc.cyan(`_dmarc.${domain}`)} ${pc.dim(\"TXT\")} \"v=DMARC1; p=quarantine; rua=mailto:postmaster@${dmarcRuaDomain}\"`\n );\n\n // Add MAIL FROM domain DNS records if configured\n if (outputs.mailFromDomain) {\n dnsLines.push(\n \"\",\n pc.bold(\"MAIL FROM Domain Records (for DMARC alignment):\"),\n ` ${pc.cyan(outputs.mailFromDomain)} ${pc.dim(\"MX\")} \"10 feedback-smtp.${outputs.region}.amazonses.com\"`,\n ` ${pc.cyan(outputs.mailFromDomain)} ${pc.dim(\"TXT\")} \"v=spf1 include:amazonses.com ~all\"`\n );\n }\n }\n\n clack.note(dnsLines.join(\"\\n\"), \"DNS Records to add:\");\n }\n\n // Show ACM certificate validation records if HTTPS tracking is enabled\n if (outputs.acmValidationRecords && outputs.acmValidationRecords.length > 0) {\n const acmDnsLines = [\n pc.bold(\"SSL Certificate Validation (ACM):\"),\n ...outputs.acmValidationRecords.map(\n (record) =>\n ` ${pc.cyan(record.name)} ${pc.dim(record.type)} \"${record.value}\"`\n ),\n \"\",\n pc.dim(\n \"Note: These records are required to validate your SSL certificate.\"\n ),\n pc.dim(\n \"CloudFront will be enabled automatically after certificate validation.\"\n ),\n ];\n\n clack.note(\n acmDnsLines.join(\"\\n\"),\n \"SSL Certificate Validation DNS Records:\"\n );\n }\n\n // Show tracking domain DNS records if custom tracking domain is configured\n if (\n outputs.trackingDomainDnsRecords &&\n outputs.trackingDomainDnsRecords.length > 0\n ) {\n const trackingProtocol = outputs.httpsTrackingEnabled ? \"HTTPS\" : \"HTTP\";\n const trackingDnsLines = [\n pc.bold(`Custom Tracking Domain - ${trackingProtocol} Redirect CNAME:`),\n ...outputs.trackingDomainDnsRecords.map(\n (record) =>\n ` ${pc.cyan(record.name)} ${pc.dim(record.type)} \"${record.value}\"`\n ),\n \"\",\n pc.dim(\n \"Note: This CNAME allows SES to rewrite links in your emails to use\"\n ),\n pc.dim(\"your custom domain for open and click tracking.\"),\n ];\n\n if (outputs.httpsTrackingEnabled) {\n trackingDnsLines.push(\n \"\",\n pc.dim(\"HTTPS tracking is enabled via CloudFront with SSL certificate.\")\n );\n }\n\n clack.note(\n trackingDnsLines.join(\"\\n\"),\n \"Custom Tracking Domain DNS Records:\"\n );\n\n if (outputs.customTrackingDomain) {\n console.log(\n `\\n${pc.dim(\"Run:\")} ${pc.yellow(`wraps email verify --domain ${outputs.customTrackingDomain}`)} ${pc.dim(\n \"(after DNS propagates)\"\n )}\\n`\n );\n }\n }\n\n // Show tracking domain separately if we only have tracking domain (no other DNS records)\n // ONLY for HTTP tracking - HTTPS tracking DNS records are shown after CloudFront is created\n if (\n outputs.customTrackingDomain &&\n !outputs.httpsTrackingEnabled && // Only show for HTTP tracking\n !outputs.dnsAutoCreated &&\n (!outputs.dnsRecords || outputs.dnsRecords.length === 0) &&\n (!outputs.trackingDomainDnsRecords ||\n outputs.trackingDomainDnsRecords.length === 0)\n ) {\n const trackingLines = [\n pc.bold(\"Tracking Domain (CNAME):\"),\n ` ${pc.cyan(outputs.customTrackingDomain)} ${pc.dim(\"CNAME\")} \"r.${outputs.region}.awstrack.me\"`,\n \"\",\n pc.dim(\n \"Note: This CNAME allows SES to rewrite links in your emails to use\"\n ),\n pc.dim(\"your custom domain for open and click tracking.\"),\n ];\n\n clack.note(trackingLines.join(\"\\n\"), \"DNS Record to add:\");\n }\n}\n\n/**\n * Status output configuration\n */\nexport type StatusOutputs = {\n integrationLevel: \"dashboard-only\" | \"enhanced\";\n region: string;\n domains: Array<{\n domain: string;\n status: \"verified\" | \"pending\" | \"failed\";\n dkimTokens?: string[];\n mailFromDomain?: string;\n mailFromStatus?: string;\n }>;\n resources: {\n roleArn?: string;\n configSetName?: string;\n tableName?: string;\n lambdaFunctions?: number;\n snsTopics?: number;\n archiveArn?: string;\n archivingEnabled?: boolean;\n archiveRetention?: string;\n };\n tracking?: {\n customTrackingDomain?: string;\n httpsEnabled?: boolean;\n cloudFrontDomain?: string;\n };\n};\n\n/**\n * Display status information\n */\nexport function displayStatus(status: StatusOutputs) {\n clack.intro(pc.bold(\"Wraps Email Infrastructure\"));\n\n const infoLines = [\n `${pc.bold(\"Integration:\")} ${pc.cyan(status.integrationLevel)}`,\n `${pc.bold(\"Region:\")} ${pc.cyan(status.region)}`,\n ];\n\n if (status.domains.length > 0) {\n const domainStrings = status.domains.map((d) => {\n const statusIcon =\n d.status === \"verified\" ? \"✓\" : d.status === \"pending\" ? \"⏱\" : \"✗\";\n const statusColor =\n d.status === \"verified\"\n ? pc.green\n : d.status === \"pending\"\n ? pc.yellow\n : pc.red;\n\n let domainLine = ` ${d.domain} ${statusColor(`${statusIcon} ${d.status}`)}`;\n\n // Add MAIL FROM domain info if configured\n if (d.mailFromDomain) {\n const mailFromStatusIcon = d.mailFromStatus === \"SUCCESS\" ? \"✓\" : \"⏱\";\n const mailFromColor =\n d.mailFromStatus === \"SUCCESS\" ? pc.green : pc.yellow;\n domainLine += `\\n ${pc.dim(\"MAIL FROM:\")} ${d.mailFromDomain} ${mailFromColor(mailFromStatusIcon)}`;\n }\n\n return domainLine;\n });\n infoLines.push(`${pc.bold(\"Domains:\")}\\n${domainStrings.join(\"\\n\")}`);\n }\n\n clack.note(infoLines.join(\"\\n\"), \"Configuration\");\n\n // Features\n const featureLines = [];\n featureLines.push(` ${pc.green(\"✓\")} Email Sending ${pc.dim(\"(via SES)\")}`);\n\n if (status.resources.tableName) {\n featureLines.push(\n ` ${pc.green(\"✓\")} Email Tracking ${pc.dim(\"(DynamoDB logs)\")}`\n );\n } else {\n featureLines.push(\n ` ${pc.dim(\"○\")} Email Tracking ${pc.dim(\"(run 'wraps email upgrade' to enable)\")}`\n );\n }\n\n if (\n status.resources.lambdaFunctions &&\n status.resources.lambdaFunctions > 0\n ) {\n featureLines.push(\n ` ${pc.green(\"✓\")} Bounce/Complaint Handling ${pc.dim(\"(automated)\")}`\n );\n } else {\n featureLines.push(\n ` ${pc.dim(\"○\")} Bounce/Complaint Handling ${pc.dim(\"(run 'wraps email upgrade' to enable)\")}`\n );\n }\n\n // Email Archiving\n if (status.resources.archivingEnabled) {\n const retentionLabel =\n {\n \"7days\": \"7 days\",\n \"30days\": \"30 days\",\n \"90days\": \"90 days\",\n \"6months\": \"6 months\",\n \"1year\": \"1 year\",\n \"18months\": \"18 months\",\n }[status.resources.archiveRetention || \"90days\"] || \"90 days\";\n featureLines.push(\n ` ${pc.green(\"✓\")} Email Archiving ${pc.dim(`(${retentionLabel} retention)`)}`\n );\n } else {\n featureLines.push(\n ` ${pc.dim(\"○\")} Email Archiving ${pc.dim(\"(run 'wraps email upgrade' to enable)\")}`\n );\n }\n\n // Custom Tracking Domain\n if (status.tracking?.customTrackingDomain) {\n const protocol = status.tracking.httpsEnabled ? \"HTTPS\" : \"HTTP\";\n const cloudFrontStatus = status.tracking.httpsEnabled\n ? status.tracking.cloudFrontDomain\n ? pc.green(\"✓ Active\")\n : pc.yellow(\"⏱ Pending\")\n : \"\";\n const trackingLabel = status.tracking.httpsEnabled\n ? `${protocol} tracking ${cloudFrontStatus}`\n : `${protocol} tracking`;\n featureLines.push(\n ` ${pc.green(\"✓\")} Custom Tracking Domain ${pc.dim(`(${trackingLabel})`)}`\n );\n featureLines.push(` ${pc.cyan(status.tracking.customTrackingDomain)}`);\n } else {\n featureLines.push(\n ` ${pc.dim(\"○\")} Custom Tracking Domain ${pc.dim(\"(run 'wraps email upgrade' to enable)\")}`\n );\n }\n\n featureLines.push(\n ` ${pc.green(\"✓\")} Console Dashboard ${pc.dim(\"(run 'wraps console')\")}`\n );\n\n clack.note(featureLines.join(\"\\n\"), \"Features\");\n\n // Resources\n const resourceLines = [];\n\n if (status.resources.roleArn) {\n resourceLines.push(\n ` ${pc.green(\"✓\")} IAM Role: ${pc.cyan(status.resources.roleArn)}`\n );\n }\n\n if (status.resources.configSetName) {\n resourceLines.push(\n ` ${pc.green(\"✓\")} Configuration Set: ${pc.cyan(status.resources.configSetName)}`\n );\n }\n\n if (status.resources.tableName) {\n resourceLines.push(\n ` ${pc.green(\"✓\")} DynamoDB Table: ${pc.cyan(status.resources.tableName)}`\n );\n }\n\n if (status.resources.lambdaFunctions) {\n resourceLines.push(\n ` ${pc.green(\"✓\")} Lambda Functions: ${pc.cyan(\n `${status.resources.lambdaFunctions} deployed`\n )}`\n );\n }\n\n if (status.resources.snsTopics) {\n resourceLines.push(\n ` ${pc.green(\"✓\")} SNS Topics: ${pc.cyan(`${status.resources.snsTopics} configured`)}`\n );\n }\n\n if (status.resources.archiveArn) {\n resourceLines.push(\n ` ${pc.green(\"✓\")} Mail Manager Archive: ${pc.cyan(status.resources.archiveArn)}`\n );\n }\n\n clack.note(resourceLines.join(\"\\n\"), \"Resources\");\n\n // Show DNS records for pending domains OR domains with pending MAIL FROM\n const domainsNeedingDNS = status.domains.filter(\n (d) =>\n (d.status === \"pending\" && d.dkimTokens) ||\n (d.mailFromDomain && d.mailFromStatus !== \"SUCCESS\")\n );\n if (domainsNeedingDNS.length > 0) {\n for (const domain of domainsNeedingDNS) {\n const dnsLines = [];\n\n // DKIM records (only for pending domains)\n if (\n domain.status === \"pending\" &&\n domain.dkimTokens &&\n domain.dkimTokens.length > 0\n ) {\n // Use MAIL FROM domain for DMARC rua if configured, otherwise use main domain\n const dmarcRuaDomain = domain.mailFromDomain || domain.domain;\n dnsLines.push(\n pc.bold(\"DKIM Records (CNAME):\"),\n ...domain.dkimTokens.map(\n (token) =>\n ` ${pc.cyan(`${token}._domainkey.${domain.domain}`)} ${pc.dim(\"CNAME\")} \"${token}.dkim.amazonses.com\"`\n ),\n \"\",\n pc.bold(\"SPF Record (TXT):\"),\n ` ${pc.cyan(domain.domain)} ${pc.dim(\"TXT\")} \"v=spf1 include:amazonses.com ~all\"`,\n pc.dim(\" Note: If you have an existing SPF record, add 'include:amazonses.com' to it\"),\n \"\",\n pc.bold(\"DMARC Record (TXT):\"),\n ` ${pc.cyan(`_dmarc.${domain.domain}`)} ${pc.dim(\"TXT\")} \"v=DMARC1; p=quarantine; rua=mailto:postmaster@${dmarcRuaDomain}\"`\n );\n }\n\n // MAIL FROM records (if configured but not verified)\n if (domain.mailFromDomain && domain.mailFromStatus !== \"SUCCESS\") {\n if (dnsLines.length > 0) {\n dnsLines.push(\"\");\n }\n dnsLines.push(\n pc.bold(\"MAIL FROM Domain Records (for DMARC alignment):\"),\n ` ${pc.cyan(domain.mailFromDomain)} ${pc.dim(\"MX\")} \"10 feedback-smtp.${status.region}.amazonses.com\"`,\n ` ${pc.cyan(domain.mailFromDomain)} ${pc.dim(\"TXT\")} \"v=spf1 include:amazonses.com ~all\"`\n );\n }\n\n if (dnsLines.length > 0) {\n clack.note(dnsLines.join(\"\\n\"), `DNS Records for ${domain.domain}`);\n }\n }\n\n // Show verify command with first domain needing DNS as example\n const exampleDomain = domainsNeedingDNS[0].domain;\n console.log(\n `\\n${pc.dim(\"Run:\")} ${pc.yellow(`wraps email verify --domain ${exampleDomain}`)} ${pc.dim(\n \"(after DNS propagates)\"\n )}\\n`\n );\n }\n\n console.log(`\\n${pc.bold(\"Dashboard:\")} ${pc.blue(\"https://app.wraps.dev\")}`);\n console.log(`${pc.bold(\"Docs:\")} ${pc.blue(\"https://wraps.dev/docs\")}\\n`);\n}\n\n/**\n * Preview output configuration\n */\nexport type PreviewOutputs = {\n changeSummary: {\n create?: number;\n update?: number;\n delete?: number;\n same?: number;\n replace?: number;\n };\n costEstimate?: string;\n commandName: string;\n};\n\n/**\n * Display preview results with resource changes and cost estimate\n */\nexport function displayPreview(outputs: PreviewOutputs): void {\n console.log(pc.yellow(\"\\n--- PREVIEW MODE (no changes will be made) ---\\n\"));\n\n // Display change summary\n const changes = outputs.changeSummary;\n const summaryLines: string[] = [];\n\n if (changes.create && changes.create > 0) {\n summaryLines.push(` ${pc.green(\"+\")} ${changes.create} to create`);\n }\n if (changes.update && changes.update > 0) {\n summaryLines.push(` ${pc.yellow(\"~\")} ${changes.update} to update`);\n }\n if (changes.delete && changes.delete > 0) {\n summaryLines.push(` ${pc.red(\"-\")} ${changes.delete} to destroy`);\n }\n if (changes.same && changes.same > 0) {\n summaryLines.push(` ${pc.dim(\"=\")} ${changes.same} unchanged`);\n }\n if (changes.replace && changes.replace > 0) {\n summaryLines.push(` ${pc.magenta(\"+-\")} ${changes.replace} to replace`);\n }\n\n if (summaryLines.length > 0) {\n clack.note(summaryLines.join(\"\\n\"), \"Resource Changes\");\n } else {\n clack.note(\"No changes detected\", \"Resource Changes\");\n }\n\n // Display cost estimate\n if (outputs.costEstimate) {\n clack.note(outputs.costEstimate, \"Estimated Monthly Cost\");\n }\n\n console.log(pc.yellow(\"\\n--- END PREVIEW (no changes were made) ---\\n\"));\n}\n","import * as clack from \"@clack/prompts\";\nimport * as pulumi from \"@pulumi/pulumi\";\nimport pc from \"picocolors\";\nimport { deployEmailStack } from \"../../infrastructure/email-stack.js\";\nimport { trackCommand, trackError } from \"../../telemetry/events.js\";\nimport type {\n EmailConfigOptions,\n EmailStackConfig,\n} from \"../../types/index.js\";\nimport {\n getAWSRegion,\n validateAWSCredentials,\n} from \"../../utils/shared/aws.js\";\nimport { errors } from \"../../utils/shared/errors.js\";\nimport {\n ensurePulumiWorkDir,\n getPulumiWorkDir,\n} from \"../../utils/shared/fs.js\";\nimport {\n loadConnectionMetadata,\n saveConnectionMetadata,\n} from \"../../utils/shared/metadata.js\";\nimport {\n DeploymentProgress,\n displayPreview,\n displaySuccess,\n} from \"../../utils/shared/output.js\";\nimport { ensurePulumiInstalled } from \"../../utils/shared/pulumi.js\";\n\n/**\n * Config command - Redeploy infrastructure to apply CLI updates\n * This command updates Lambda functions and other managed resources\n * without requiring configuration changes from the user.\n */\nexport async function config(options: EmailConfigOptions): Promise<void> {\n const startTime = Date.now();\n\n clack.intro(\n pc.bold(\n options.preview\n ? \"Wraps Config Preview\"\n : \"Wraps Config - Apply CLI Updates to Infrastructure\"\n )\n );\n\n const progress = new DeploymentProgress();\n\n // 1. Check Pulumi CLI is installed\n const wasAutoInstalled = await progress.execute(\n \"Checking Pulumi CLI installation\",\n async () => await ensurePulumiInstalled()\n );\n\n if (wasAutoInstalled) {\n progress.info(\"Pulumi CLI was automatically installed\");\n }\n\n // 2. Validate AWS credentials\n const identity = await progress.execute(\n \"Validating AWS credentials\",\n async () => validateAWSCredentials()\n );\n\n progress.info(`Connected to AWS account: ${pc.cyan(identity.accountId)}`);\n\n // 3. Get region\n let region = options.region;\n if (!region) {\n const defaultRegion = await getAWSRegion();\n region = defaultRegion;\n }\n\n // 4. Load existing connection metadata\n const metadata = await loadConnectionMetadata(identity.accountId, region);\n\n if (!metadata) {\n clack.log.error(\n `No Wraps connection found for account ${pc.cyan(identity.accountId)} in region ${pc.cyan(region)}`\n );\n clack.log.info(\n `Use ${pc.cyan(\"wraps email init\")} to create new infrastructure or ${pc.cyan(\"wraps email connect\")} to connect existing.`\n );\n process.exit(1);\n }\n\n progress.info(`Found existing connection created: ${metadata.timestamp}`);\n\n // 5. Display current configuration\n console.log(`\\n${pc.bold(\"Current Configuration:\")}\\n`);\n\n if (metadata.services.email?.preset) {\n console.log(` Preset: ${pc.cyan(metadata.services.email?.preset)}`);\n } else {\n console.log(` Preset: ${pc.cyan(\"custom\")}`);\n }\n\n const config = metadata.services.email?.config;\n\n if (!config) {\n clack.log.error(\"No email configuration found in metadata\");\n clack.log.info(\n `Use ${pc.cyan(\"wraps email init\")} to create new infrastructure.`\n );\n process.exit(1);\n }\n\n // Show sending domain if configured\n if (config.domain) {\n console.log(` Sending Domain: ${pc.cyan(config.domain)}`);\n }\n\n if (config.tracking?.enabled) {\n console.log(` ${pc.green(\"✓\")} Open & Click Tracking`);\n }\n\n if (config.suppressionList?.enabled) {\n console.log(` ${pc.green(\"✓\")} Bounce/Complaint Suppression`);\n }\n\n if (config.eventTracking?.enabled) {\n console.log(` ${pc.green(\"✓\")} Event Tracking (EventBridge)`);\n }\n\n if (config.dedicatedIp) {\n console.log(` ${pc.green(\"✓\")} Dedicated IP Address`);\n }\n\n console.log(\"\");\n\n // 6. Show what will be updated\n console.log(`${pc.bold(\"What will be updated:\")}\\n`);\n console.log(\n ` ${pc.cyan(\"•\")} Lambda function code (if event tracking enabled)`\n );\n console.log(\n ` ${pc.cyan(\"•\")} EventBridge rules (if event tracking enabled)`\n );\n console.log(` ${pc.cyan(\"•\")} IAM policies (security improvements)`);\n console.log(` ${pc.cyan(\"•\")} SES configuration set (feature updates)`);\n console.log(\"\");\n\n progress.info(\n \"Your current configuration will be preserved - no features will be added or removed\"\n );\n console.log(\"\");\n\n // 7. Confirm update (skip if --yes or --preview)\n if (!(options.yes || options.preview)) {\n const confirmed = await clack.confirm({\n message: \"Proceed with update?\",\n initialValue: true,\n });\n\n if (clack.isCancel(confirmed) || !confirmed) {\n clack.cancel(\"Update cancelled.\");\n process.exit(0);\n }\n }\n\n // 8. Get Vercel config if needed\n let vercelConfig;\n if (metadata.provider === \"vercel\" && metadata.vercel) {\n vercelConfig = metadata.vercel;\n }\n\n // 9. Build stack configuration (reuse existing config)\n const stackConfig: EmailStackConfig = {\n provider: metadata.provider,\n region,\n vercel: vercelConfig,\n emailConfig: config,\n };\n\n // 10. Preview or Update Pulumi stack\n if (options.preview) {\n // PREVIEW MODE - show what would be updated without deploying\n try {\n const previewResult = await progress.execute(\n \"Generating update preview\",\n async () => {\n await ensurePulumiWorkDir();\n\n const stack =\n await pulumi.automation.LocalWorkspace.createOrSelectStack(\n {\n stackName:\n metadata.services.email?.pulumiStackName ||\n `wraps-${identity.accountId}-${region}`,\n projectName: \"wraps-email\",\n program: async () => {\n const result = await deployEmailStack(stackConfig);\n return {\n roleArn: result.roleArn,\n configSetName: result.configSetName,\n tableName: result.tableName,\n region: result.region,\n lambdaFunctions: result.lambdaFunctions,\n domain: result.domain,\n dkimTokens: result.dkimTokens,\n customTrackingDomain: result.customTrackingDomain,\n };\n },\n },\n {\n workDir: getPulumiWorkDir(),\n envVars: {\n PULUMI_CONFIG_PASSPHRASE: \"\",\n AWS_REGION: region,\n },\n secretsProvider: \"passphrase\",\n }\n );\n\n await stack.setConfig(\"aws:region\", { value: region });\n\n // Refresh state to sync with AWS before previewing\n await stack.refresh({ onOutput: () => {} });\n\n // Run preview instead of deployment\n const result = await stack.preview({ diff: true });\n return result;\n }\n );\n\n // Display preview results\n displayPreview({\n changeSummary: previewResult.changeSummary,\n commandName: \"wraps email config\",\n });\n\n clack.outro(\n pc.green(\"Preview complete. Run without --preview to update.\")\n );\n\n // Track preview completion\n trackCommand(\"email:config\", {\n success: true,\n preview: true,\n duration_ms: Date.now() - startTime,\n });\n return;\n } catch (error: any) {\n trackError(\"PREVIEW_FAILED\", \"email:config\", { step: \"preview\" });\n if (error.message?.includes(\"stack is currently locked\")) {\n throw errors.stackLocked();\n }\n throw new Error(`Preview failed: ${error.message}`);\n }\n }\n\n // UPDATE MODE - actually update infrastructure\n let outputs;\n try {\n outputs = await progress.execute(\n \"Updating Wraps infrastructure (this may take 2-3 minutes)\",\n async () => {\n await ensurePulumiWorkDir();\n\n const stack =\n await pulumi.automation.LocalWorkspace.createOrSelectStack(\n {\n stackName:\n metadata.services.email?.pulumiStackName ||\n `wraps-${identity.accountId}-${region}`,\n projectName: \"wraps-email\",\n program: async () => {\n const result = await deployEmailStack(stackConfig);\n\n return {\n roleArn: result.roleArn,\n configSetName: result.configSetName,\n tableName: result.tableName,\n region: result.region,\n lambdaFunctions: result.lambdaFunctions,\n domain: result.domain,\n dkimTokens: result.dkimTokens,\n customTrackingDomain: result.customTrackingDomain,\n };\n },\n },\n {\n workDir: getPulumiWorkDir(),\n envVars: {\n PULUMI_CONFIG_PASSPHRASE: \"\",\n AWS_REGION: region,\n },\n secretsProvider: \"passphrase\",\n }\n );\n\n await stack.workspace.selectStack(\n metadata.services.email?.pulumiStackName ||\n `wraps-${identity.accountId}-${region}`\n );\n await stack.setConfig(\"aws:region\", { value: region });\n\n // Refresh state to sync with actual AWS resources (prevents AlreadyExists errors)\n await stack.refresh({ onOutput: () => {} });\n\n // Pulumi will automatically detect changes and only update what's needed\n const upResult = await stack.up({ onOutput: () => {} });\n const pulumiOutputs = upResult.outputs;\n\n return {\n roleArn: pulumiOutputs.roleArn?.value as string,\n configSetName: pulumiOutputs.configSetName?.value as\n | string\n | undefined,\n tableName: pulumiOutputs.tableName?.value as string | undefined,\n region: pulumiOutputs.region?.value as string,\n lambdaFunctions: pulumiOutputs.lambdaFunctions?.value as\n | string[]\n | undefined,\n domain: pulumiOutputs.domain?.value as string | undefined,\n dkimTokens: pulumiOutputs.dkimTokens?.value as string[] | undefined,\n customTrackingDomain: pulumiOutputs.customTrackingDomain?.value as\n | string\n | undefined,\n };\n }\n );\n } catch (error: any) {\n // Track update failure\n trackCommand(\"email:config\", {\n success: false,\n duration_ms: Date.now() - startTime,\n });\n\n // Check if it's a lock file error\n if (error.message?.includes(\"stack is currently locked\")) {\n trackError(\"STACK_LOCKED\", \"email:config\", { step: \"update\" });\n throw errors.stackLocked();\n }\n\n trackError(\"UPDATE_FAILED\", \"email:config\", { step: \"update\" });\n throw new Error(`Pulumi update failed: ${error.message}`);\n }\n\n // 11. Update metadata timestamp (config stays the same)\n metadata.timestamp = new Date().toISOString();\n await saveConnectionMetadata(metadata);\n\n progress.info(\"Connection metadata updated\");\n\n // 12. Display success message\n displaySuccess({\n roleArn: outputs.roleArn,\n configSetName: outputs.configSetName,\n region: outputs.region!,\n tableName: outputs.tableName,\n customTrackingDomain: outputs.customTrackingDomain,\n });\n\n // Show what was updated\n console.log(`\\n${pc.green(\"✓\")} ${pc.bold(\"Update complete!\")}\\n`);\n console.log(\n \"Infrastructure has been updated with the latest CLI improvements.\\n\"\n );\n console.log(`${pc.bold(\"Next steps:\")}\\n`);\n console.log(\n ` ${pc.cyan(\"1.\")} No code changes needed - your existing SDK integration continues to work`\n );\n console.log(\n ` ${pc.cyan(\"2.\")} Check ${pc.cyan(\"wraps status\")} to verify all resources are healthy`\n );\n console.log(\n ` ${pc.cyan(\"3.\")} View analytics at ${pc.cyan(\"wraps console\")}\\n`\n );\n\n // 13. Track successful update\n trackCommand(\"email:config\", {\n success: true,\n duration_ms: Date.now() - startTime,\n });\n}\n","import * as aws from \"@pulumi/aws\";\nimport * as pulumi from \"@pulumi/pulumi\";\nimport type { EmailStackConfig, StackOutputs } from \"../types/index.js\";\nimport { createDynamoDBTables } from \"./resources/dynamodb.js\";\nimport { createEventBridgeResources } from \"./resources/eventbridge.js\";\nimport { createIAMRole } from \"./resources/iam.js\";\nimport { deployLambdaFunctions } from \"./resources/lambda.js\";\nimport { createSESResources, eventDestinationExists } from \"./resources/ses.js\";\nimport { createSQSResources } from \"./resources/sqs.js\";\nimport { createVercelOIDC } from \"./vercel-oidc.js\";\n\n/**\n * Deploy email infrastructure stack using Pulumi\n */\nexport async function deployEmailStack(\n config: EmailStackConfig\n): Promise<StackOutputs> {\n // Get current AWS account\n const identity = await aws.getCallerIdentity();\n const accountId = identity.accountId;\n\n let oidcProvider: aws.iam.OpenIdConnectProvider | undefined;\n\n // 1. Create OIDC provider if Vercel\n if (config.provider === \"vercel\" && config.vercel) {\n oidcProvider = await createVercelOIDC({\n teamSlug: config.vercel.teamSlug,\n accountId,\n });\n }\n\n const emailConfig = config.emailConfig;\n\n // 2. Create IAM role\n const role = await createIAMRole({\n provider: config.provider,\n oidcProvider,\n vercelTeamSlug: config.vercel?.teamSlug,\n vercelProjectName: config.vercel?.projectName,\n emailConfig,\n });\n\n // 3. CloudFront + ACM (if HTTPS tracking enabled)\n let cloudFrontResources;\n let acmResources;\n\n if (\n emailConfig.tracking?.enabled &&\n emailConfig.tracking.customRedirectDomain &&\n emailConfig.tracking.httpsEnabled\n ) {\n // Check for Route53 hosted zone (for automatic DNS validation)\n const { findHostedZone } = await import(\"../utils/email/route53.js\");\n const hostedZone = await findHostedZone(\n emailConfig.tracking.customRedirectDomain,\n config.region\n );\n\n // Create ACM certificate (in us-east-1 for CloudFront)\n const { createACMCertificate } = await import(\"./resources/acm.js\");\n acmResources = await createACMCertificate({\n domain: emailConfig.tracking.customRedirectDomain,\n hostedZoneId: hostedZone?.id,\n });\n\n // Create CloudFront distribution with SSL certificate\n // Import CloudFront creation function\n const { createCloudFrontTracking } = await import(\n \"./resources/cloudfront.js\"\n );\n\n // Determine which certificate ARN to use:\n // - Route53: Use certificateValidation.certificateArn (waits for validation)\n // - Manual DNS: Use certificate.arn directly (CloudFront will fail if not validated)\n const certificateArn = acmResources.certificateValidation\n ? acmResources.certificateValidation.certificateArn\n : acmResources.certificate.arn;\n\n cloudFrontResources = await createCloudFrontTracking({\n customTrackingDomain: emailConfig.tracking.customRedirectDomain,\n region: config.region,\n certificateArn,\n hostedZoneId: hostedZone?.id, // Pass hosted zone ID for automatic DNS record creation\n });\n }\n\n // 4. SES resources (if tracking or event tracking enabled)\n let sesResources;\n if (emailConfig.tracking?.enabled || emailConfig.eventTracking?.enabled) {\n // Check if the event destination already exists in AWS but not in Pulumi state\n // This can happen if resources were created outside Pulumi or state got out of sync\n const shouldImportEventDest =\n emailConfig.eventTracking?.enabled &&\n (await eventDestinationExists(\n \"wraps-email-tracking\",\n \"wraps-email-eventbridge\",\n config.region\n ));\n\n // Compute mailFromDomain from mailFromSubdomain if provided\n let mailFromDomain = emailConfig.mailFromDomain;\n if (!mailFromDomain && emailConfig.mailFromSubdomain && emailConfig.domain) {\n mailFromDomain = `${emailConfig.mailFromSubdomain}.${emailConfig.domain}`;\n }\n\n sesResources = await createSESResources({\n domain: emailConfig.domain,\n mailFromDomain,\n region: config.region,\n trackingConfig: emailConfig.tracking,\n eventTypes: emailConfig.eventTracking?.events,\n eventTrackingEnabled: emailConfig.eventTracking?.enabled, // Pass flag to create EventBridge destination\n tlsRequired: emailConfig.tlsRequired, // Require TLS encryption for all emails\n importExistingEventDestination: shouldImportEventDest, // Import if exists to avoid AlreadyExistsException\n });\n }\n\n // 5. DynamoDB tables (if history storage enabled)\n let dynamoTables;\n if (emailConfig.eventTracking?.dynamoDBHistory) {\n dynamoTables = await createDynamoDBTables({\n retention: emailConfig.eventTracking.archiveRetention,\n });\n }\n\n // 6. SQS queues (if event tracking enabled)\n let sqsResources;\n if (emailConfig.eventTracking?.enabled) {\n sqsResources = await createSQSResources();\n }\n\n // 7. EventBridge rule to route SES events to SQS (if event tracking enabled)\n if (emailConfig.eventTracking?.enabled && sesResources && sqsResources) {\n await createEventBridgeResources({\n eventBusArn: sesResources.eventBus.arn,\n queueArn: sqsResources.queue.arn,\n queueUrl: sqsResources.queue.url,\n });\n }\n\n // 8. Lambda functions (if event tracking and DynamoDB enabled)\n let lambdaFunctions;\n if (\n emailConfig.eventTracking?.dynamoDBHistory &&\n dynamoTables &&\n sqsResources\n ) {\n lambdaFunctions = await deployLambdaFunctions({\n roleArn: role.arn,\n tableName: dynamoTables.emailHistory.name,\n queueArn: sqsResources.queue.arn,\n accountId,\n region: config.region,\n });\n }\n\n // 9. Mail Manager Archive (if email archiving enabled)\n let archiveResources;\n if (emailConfig.emailArchiving?.enabled && sesResources) {\n const { createMailManagerArchive } = await import(\n \"./resources/mail-manager.js\"\n );\n archiveResources = await createMailManagerArchive({\n name: \"email\",\n retention: emailConfig.emailArchiving.retention,\n configSetName: sesResources.configSet.configurationSetName,\n region: config.region,\n });\n }\n\n // Return outputs\n return {\n roleArn: role.arn as any as string,\n configSetName: sesResources?.configSet.configurationSetName as any as\n | string\n | undefined,\n tableName: dynamoTables?.emailHistory.name as any as string | undefined,\n region: config.region,\n lambdaFunctions: lambdaFunctions\n ? [lambdaFunctions.eventProcessor.arn as any as string]\n : undefined,\n domain: emailConfig.domain,\n dkimTokens: sesResources?.dkimTokens as any as string[] | undefined,\n dnsAutoCreated: sesResources?.dnsAutoCreated,\n eventBusName: sesResources?.eventBus.name as any as string | undefined,\n queueUrl: sqsResources?.queue.url as any as string | undefined,\n dlqUrl: sqsResources?.dlq.url as any as string | undefined,\n customTrackingDomain: sesResources?.customTrackingDomain,\n httpsTrackingEnabled: emailConfig.tracking?.httpsEnabled,\n cloudFrontDomain: cloudFrontResources?.domainName as any as\n | string\n | undefined,\n acmCertificateValidationRecords: acmResources?.validationRecords as any as\n | Array<{ name: string; type: string; value: string }>\n | undefined,\n mailFromDomain: sesResources?.mailFromDomain,\n archiveArn: archiveResources?.archiveArn,\n archivingEnabled: emailConfig.emailArchiving?.enabled,\n archiveRetention: emailConfig.emailArchiving?.enabled\n ? emailConfig.emailArchiving.retention\n : undefined,\n };\n}\n\n/**\n * Run Pulumi program inline\n */\nexport async function runPulumiProgram(\n stackName: string,\n program: () => Promise<StackOutputs>\n): Promise<StackOutputs> {\n const stack = await pulumi.automation.LocalWorkspace.createOrSelectStack(\n {\n stackName,\n projectName: \"wraps-email\",\n program,\n },\n {\n workDir: `${process.env.HOME}/.wraps/pulumi`,\n }\n );\n\n // Set AWS region\n await stack.setConfig(\"aws:region\", { value: \"us-east-1\" });\n\n // Run the deployment\n const upResult = await stack.up({\n onOutput: (msg) => process.stdout.write(msg),\n });\n\n // Get outputs\n const outputs = upResult.outputs;\n\n return {\n roleArn: outputs.roleArn?.value as string,\n configSetName: outputs.configSetName?.value as string | undefined,\n tableName: outputs.tableName?.value as string | undefined,\n region: outputs.region?.value as string,\n };\n}\n","import * as aws from \"@pulumi/aws\";\nimport type { ArchiveRetention } from \"../../types/index.js\";\n\n/**\n * DynamoDB configuration\n */\nexport type DynamoDBConfig = {\n retention?: ArchiveRetention;\n};\n\n/**\n * DynamoDB tables output\n */\nexport type DynamoDBTables = {\n emailHistory: aws.dynamodb.Table;\n};\n\n/**\n * Check if DynamoDB table exists\n */\nasync function tableExists(tableName: string): Promise<boolean> {\n try {\n const { DynamoDBClient, DescribeTableCommand } = await import(\n \"@aws-sdk/client-dynamodb\"\n );\n const dynamodb = new DynamoDBClient({\n region: process.env.AWS_REGION || \"us-east-1\",\n });\n\n await dynamodb.send(new DescribeTableCommand({ TableName: tableName }));\n return true;\n } catch (error: any) {\n if (error.name === \"ResourceNotFoundException\") {\n return false;\n }\n console.error(\"Error checking for existing DynamoDB table:\", error);\n return false;\n }\n}\n\n/**\n * Create DynamoDB tables for email tracking\n */\nexport async function createDynamoDBTables(\n _config?: DynamoDBConfig\n): Promise<DynamoDBTables> {\n // Check if table already exists\n const tableName = \"wraps-email-history\";\n const exists = await tableExists(tableName);\n\n // Email history table (TTL is set based on retention in Lambda via expiresAt field)\n // Note: retention config is passed but TTL is actually managed by Lambda setting expiresAt\n const emailHistory = exists\n ? new aws.dynamodb.Table(\n tableName,\n {\n name: tableName,\n billingMode: \"PAY_PER_REQUEST\",\n hashKey: \"messageId\",\n rangeKey: \"sentAt\",\n attributes: [\n { name: \"messageId\", type: \"S\" },\n { name: \"sentAt\", type: \"N\" },\n { name: \"accountId\", type: \"S\" },\n ],\n globalSecondaryIndexes: [\n {\n name: \"accountId-sentAt-index\",\n hashKey: \"accountId\",\n rangeKey: \"sentAt\",\n projectionType: \"ALL\",\n },\n ],\n ttl: {\n enabled: true,\n attributeName: \"expiresAt\",\n },\n tags: {\n ManagedBy: \"wraps-cli\",\n },\n },\n {\n import: tableName, // Import existing table\n }\n )\n : new aws.dynamodb.Table(tableName, {\n name: tableName,\n billingMode: \"PAY_PER_REQUEST\",\n hashKey: \"messageId\",\n rangeKey: \"sentAt\",\n attributes: [\n { name: \"messageId\", type: \"S\" },\n { name: \"sentAt\", type: \"N\" },\n { name: \"accountId\", type: \"S\" },\n ],\n globalSecondaryIndexes: [\n {\n name: \"accountId-sentAt-index\",\n hashKey: \"accountId\",\n rangeKey: \"sentAt\",\n projectionType: \"ALL\",\n },\n ],\n ttl: {\n enabled: true,\n attributeName: \"expiresAt\",\n },\n tags: {\n ManagedBy: \"wraps-cli\",\n },\n });\n\n return {\n emailHistory,\n };\n}\n","import * as aws from \"@pulumi/aws\";\nimport * as pulumi from \"@pulumi/pulumi\";\n\n/**\n * EventBridge resources configuration\n */\nexport type EventBridgeConfig = {\n eventBusArn: pulumi.Output<string>;\n queueArn: pulumi.Output<string>;\n queueUrl: pulumi.Output<string>;\n};\n\n/**\n * EventBridge resources output\n */\nexport type EventBridgeResources = {\n rule: aws.cloudwatch.EventRule;\n target: aws.cloudwatch.EventTarget;\n};\n\n/**\n * Create EventBridge rule to route SES events to SQS queue\n *\n * This rule captures all SES events from the default event bus\n * and routes them to the SQS queue for processing.\n *\n * Note: SES can only send to the default EventBridge bus, not custom buses.\n */\nexport async function createEventBridgeResources(\n config: EventBridgeConfig\n): Promise<EventBridgeResources> {\n // Extract event bus name from ARN (will be \"default\" for SES)\n const eventBusName = config.eventBusArn.apply((arn) => arn.split(\"/\").pop()!);\n\n // EventBridge rule to capture all SES events on default bus\n const rule = new aws.cloudwatch.EventRule(\"wraps-email-events-rule\", {\n name: \"wraps-email-events-to-sqs\",\n description: \"Route all SES email events to SQS for processing\",\n eventBusName,\n eventPattern: JSON.stringify({\n source: [\"aws.ses\"],\n // SES sends events with various detail-types based on event type\n // We capture all by not filtering on detail-type\n }),\n tags: {\n ManagedBy: \"wraps-cli\",\n },\n });\n\n // SQS queue policy to allow EventBridge to send messages\n new aws.sqs.QueuePolicy(\"wraps-email-events-queue-policy\", {\n queueUrl: config.queueUrl,\n policy: pulumi\n .all([config.queueArn, rule.arn])\n .apply(([queueArn, ruleArn]) =>\n JSON.stringify({\n Version: \"2012-10-17\",\n Statement: [\n {\n Effect: \"Allow\",\n Principal: {\n Service: \"events.amazonaws.com\",\n },\n Action: \"sqs:SendMessage\",\n Resource: queueArn,\n Condition: {\n ArnEquals: {\n \"aws:SourceArn\": ruleArn,\n },\n },\n },\n ],\n })\n ),\n });\n\n // EventBridge target to send events to SQS\n const target = new aws.cloudwatch.EventTarget(\"wraps-email-events-target\", {\n rule: rule.name,\n eventBusName,\n arn: config.queueArn,\n });\n\n return {\n rule,\n target,\n };\n}\n","import * as aws from \"@pulumi/aws\";\nimport * as pulumi from \"@pulumi/pulumi\";\nimport type { Provider, WrapsEmailConfig } from \"../../types/index.js\";\n\n/**\n * IAM role configuration\n */\nexport type IAMRoleConfig = {\n provider: Provider;\n oidcProvider?: aws.iam.OpenIdConnectProvider;\n vercelTeamSlug?: string;\n vercelProjectName?: string;\n emailConfig: WrapsEmailConfig;\n};\n\n/**\n * Check if IAM role exists\n */\nasync function roleExists(roleName: string): Promise<boolean> {\n try {\n const { IAMClient, GetRoleCommand } = await import(\"@aws-sdk/client-iam\");\n // IAM is global but SDK still requires a region\n const iam = new IAMClient({\n region: process.env.AWS_REGION || \"us-east-1\",\n });\n\n await iam.send(new GetRoleCommand({ RoleName: roleName }));\n return true;\n } catch (error: any) {\n if (error.name === \"NoSuchEntityException\") {\n return false;\n }\n console.error(\"Error checking for existing IAM role:\", error);\n return false;\n }\n}\n\n/**\n * Create IAM role for email infrastructure\n */\nexport async function createIAMRole(\n config: IAMRoleConfig\n): Promise<aws.iam.Role> {\n // Build assume role policy based on provider\n let assumeRolePolicy: pulumi.Output<string>;\n\n if (config.provider === \"vercel\" && config.oidcProvider) {\n assumeRolePolicy = pulumi.interpolate`{\n \"Version\": \"2012-10-17\",\n \"Statement\": [{\n \"Effect\": \"Allow\",\n \"Principal\": {\n \"Federated\": \"${config.oidcProvider.arn}\"\n },\n \"Action\": \"sts:AssumeRoleWithWebIdentity\",\n \"Condition\": {\n \"StringEquals\": {\n \"oidc.vercel.com/${config.vercelTeamSlug}:aud\": \"https://vercel.com/${config.vercelTeamSlug}\"\n },\n \"StringLike\": {\n \"oidc.vercel.com/${config.vercelTeamSlug}:sub\": \"owner:${config.vercelTeamSlug}:project:${config.vercelProjectName}:environment:*\"\n }\n }\n }]\n }`;\n } else if (config.provider === \"aws\") {\n // Native AWS - EC2, Lambda, ECS can assume\n assumeRolePolicy = pulumi.output(`{\n \"Version\": \"2012-10-17\",\n \"Statement\": [{\n \"Effect\": \"Allow\",\n \"Principal\": {\n \"Service\": [\"lambda.amazonaws.com\", \"ec2.amazonaws.com\", \"ecs-tasks.amazonaws.com\"]\n },\n \"Action\": \"sts:AssumeRole\"\n }]\n }`);\n } else {\n // Other providers - will use access keys\n throw new Error(\"Other providers not yet implemented\");\n }\n\n // Check if role already exists\n const roleName = \"wraps-email-role\";\n const exists = await roleExists(roleName);\n\n const role = exists\n ? new aws.iam.Role(\n roleName,\n {\n name: roleName,\n assumeRolePolicy,\n tags: {\n ManagedBy: \"wraps-cli\",\n Provider: config.provider,\n },\n },\n {\n import: roleName, // Import existing role (use role name, not ARN)\n }\n )\n : new aws.iam.Role(roleName, {\n name: roleName,\n assumeRolePolicy,\n tags: {\n ManagedBy: \"wraps-cli\",\n Provider: config.provider,\n },\n });\n\n // Build policy statements based on enabled features\n const statements: any[] = [];\n\n // Always allow reading SES metrics for dashboard\n statements.push({\n Effect: \"Allow\",\n Action: [\n \"ses:GetSendStatistics\",\n \"ses:ListIdentities\",\n \"ses:GetIdentityVerificationAttributes\",\n \"cloudwatch:GetMetricData\",\n \"cloudwatch:GetMetricStatistics\",\n ],\n Resource: \"*\",\n });\n\n // Allow sending if enabled\n if (config.emailConfig.sendingEnabled !== false) {\n statements.push({\n Effect: \"Allow\",\n Action: [\n \"ses:SendEmail\",\n \"ses:SendRawEmail\",\n \"ses:SendTemplatedEmail\",\n \"ses:SendBulkTemplatedEmail\",\n ],\n Resource: \"*\",\n });\n }\n\n // Allow DynamoDB access if history storage enabled\n if (config.emailConfig.eventTracking?.dynamoDBHistory) {\n statements.push({\n Effect: \"Allow\",\n Action: [\n \"dynamodb:PutItem\",\n \"dynamodb:GetItem\",\n \"dynamodb:Query\",\n \"dynamodb:Scan\",\n \"dynamodb:BatchGetItem\",\n \"dynamodb:DescribeTable\",\n ],\n Resource: [\n \"arn:aws:dynamodb:*:*:table/wraps-email-*\",\n \"arn:aws:dynamodb:*:*:table/wraps-email-*/index/*\",\n ],\n });\n }\n\n // Allow EventBridge access if event tracking enabled\n if (config.emailConfig.eventTracking?.enabled) {\n statements.push({\n Effect: \"Allow\",\n Action: [\"events:PutEvents\", \"events:DescribeEventBus\"],\n Resource: \"arn:aws:events:*:*:event-bus/wraps-email-*\",\n });\n }\n\n // Allow SQS access if event tracking enabled\n if (config.emailConfig.eventTracking?.enabled) {\n statements.push({\n Effect: \"Allow\",\n Action: [\n \"sqs:SendMessage\",\n \"sqs:ReceiveMessage\",\n \"sqs:DeleteMessage\",\n \"sqs:GetQueueAttributes\",\n ],\n Resource: \"arn:aws:sqs:*:*:wraps-email-*\",\n });\n }\n\n // Allow Mail Manager Archive access if email archiving enabled\n if (config.emailConfig.emailArchiving?.enabled) {\n statements.push({\n Effect: \"Allow\",\n Action: [\n // Archive search operations\n \"ses:StartArchiveSearch\",\n \"ses:GetArchiveSearchResults\",\n // Archive message retrieval\n \"ses:GetArchiveMessage\",\n \"ses:GetArchiveMessageContent\",\n // Archive metadata\n \"ses:GetArchive\",\n \"ses:ListArchives\",\n // Archive export (for future use)\n \"ses:StartArchiveExport\",\n \"ses:GetArchiveExport\",\n ],\n Resource: \"arn:aws:ses:*:*:mailmanager-archive/*\",\n });\n }\n\n // Attach policy to role\n new aws.iam.RolePolicy(\"wraps-email-policy\", {\n role: role.name,\n policy: JSON.stringify({\n Version: \"2012-10-17\",\n Statement: statements,\n }),\n });\n\n return role;\n}\n","import { randomBytes } from \"node:crypto\";\nimport { existsSync, mkdirSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport * as aws from \"@pulumi/aws\";\nimport * as pulumi from \"@pulumi/pulumi\";\nimport { build } from \"esbuild\";\n\n/**\n * Get the package root directory (where package.json lives)\n * Works both in development (src/) and production (dist/)\n */\nfunction getPackageRoot(): string {\n const currentFile = fileURLToPath(import.meta.url);\n let dir = dirname(currentFile);\n\n // Walk up the directory tree until we find package.json\n while (dir !== dirname(dir)) {\n if (existsSync(join(dir, \"package.json\"))) {\n return dir;\n }\n dir = dirname(dir);\n }\n\n throw new Error(\"Could not find package.json\");\n}\n\n/**\n * Lambda configuration\n */\nexport type LambdaConfig = {\n roleArn: pulumi.Output<string>;\n tableName: pulumi.Output<string>;\n queueArn: pulumi.Output<string>;\n accountId: string;\n region: string;\n};\n\n/**\n * Lambda functions output\n */\nexport type LambdaFunctions = {\n eventProcessor: aws.lambda.Function;\n eventSourceMapping: aws.lambda.EventSourceMapping;\n};\n\n/**\n * Check if Lambda function exists\n */\nasync function lambdaFunctionExists(functionName: string): Promise<boolean> {\n try {\n const { LambdaClient, GetFunctionCommand } = await import(\n \"@aws-sdk/client-lambda\"\n );\n const lambda = new LambdaClient({\n region: process.env.AWS_REGION || \"us-east-1\",\n });\n\n await lambda.send(new GetFunctionCommand({ FunctionName: functionName }));\n return true;\n } catch (error: any) {\n if (error.name === \"ResourceNotFoundException\") {\n return false;\n }\n console.error(\"Error checking for existing Lambda function:\", error);\n return false;\n }\n}\n\n/**\n * Find existing event source mapping for Lambda function and SQS queue\n */\nasync function findEventSourceMapping(\n functionName: string,\n queueArn: string\n): Promise<string | null> {\n try {\n const { LambdaClient, ListEventSourceMappingsCommand } = await import(\n \"@aws-sdk/client-lambda\"\n );\n const lambda = new LambdaClient({\n region: process.env.AWS_REGION || \"us-east-1\",\n });\n\n const response = await lambda.send(\n new ListEventSourceMappingsCommand({\n FunctionName: functionName,\n EventSourceArn: queueArn,\n })\n );\n\n // Return UUID of the first matching event source mapping\n return response.EventSourceMappings?.[0]?.UUID || null;\n } catch (error: any) {\n console.error(\"Error finding event source mapping:\", error);\n return null;\n }\n}\n\n/**\n * Get the Lambda function code directory\n *\n * In production (published package), uses pre-bundled code from dist/lambda/\n * In development, bundles the TypeScript source on-the-fly\n */\nasync function getLambdaCode(functionName: string): Promise<string> {\n const packageRoot = getPackageRoot();\n\n // Check for pre-bundled Lambda code in dist/ (production - published package)\n const distLambdaPath = join(packageRoot, \"dist\", \"lambda\", functionName);\n const distBundleMarker = join(distLambdaPath, \".bundled\");\n\n if (existsSync(distBundleMarker)) {\n // Use pre-bundled code from dist/\n return distLambdaPath;\n }\n\n // Check for pre-bundled Lambda code in lambda/ (development build)\n const lambdaPath = join(packageRoot, \"lambda\", functionName);\n const lambdaBundleMarker = join(lambdaPath, \".bundled\");\n\n if (existsSync(lambdaBundleMarker)) {\n // Use pre-bundled code from lambda/\n return lambdaPath;\n }\n\n // Development mode: bundle on-the-fly from TypeScript source\n const sourcePath = join(lambdaPath, \"index.ts\");\n\n if (!existsSync(sourcePath)) {\n throw new Error(\n `Lambda source not found: ${sourcePath}\\n` +\n `This usually means the build process didn't complete successfully.\\n` +\n \"Try running: pnpm build\"\n );\n }\n\n const buildId = randomBytes(8).toString(\"hex\");\n const outdir = join(tmpdir(), `wraps-lambda-${buildId}`);\n\n if (!existsSync(outdir)) {\n mkdirSync(outdir, { recursive: true });\n }\n\n // Bundle with esbuild\n await build({\n entryPoints: [sourcePath],\n bundle: true,\n platform: \"node\",\n target: \"node24\",\n format: \"esm\",\n outfile: join(outdir, \"index.mjs\"),\n external: [\"@aws-sdk/*\"], // AWS SDK v3 is included in Lambda runtime\n minify: true,\n sourcemap: false,\n });\n\n return outdir;\n}\n\n/**\n * Deploy Lambda functions for email event processing\n *\n * Architecture:\n * SQS Queue -> Lambda (event-processor) -> DynamoDB\n *\n * The Lambda function is triggered by SQS via Event Source Mapping.\n * Failed messages are automatically sent to the DLQ after 3 retries.\n */\nexport async function deployLambdaFunctions(\n config: LambdaConfig\n): Promise<LambdaFunctions> {\n // Get Lambda code directory (pre-bundled in production, bundled on-the-fly in dev)\n const eventProcessorCode = await getLambdaCode(\"event-processor\");\n\n // IAM role for Lambda execution\n const lambdaRole = new aws.iam.Role(\"wraps-email-lambda-role\", {\n assumeRolePolicy: JSON.stringify({\n Version: \"2012-10-17\",\n Statement: [\n {\n Effect: \"Allow\",\n Principal: { Service: \"lambda.amazonaws.com\" },\n Action: \"sts:AssumeRole\",\n },\n ],\n }),\n tags: {\n ManagedBy: \"wraps-cli\",\n },\n });\n\n // Attach basic Lambda execution policy\n new aws.iam.RolePolicyAttachment(\"wraps-email-lambda-basic-execution\", {\n role: lambdaRole.name,\n policyArn:\n \"arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole\",\n });\n\n // Lambda policy for DynamoDB and SQS\n new aws.iam.RolePolicy(\"wraps-email-lambda-policy\", {\n role: lambdaRole.name,\n policy: pulumi\n .all([config.tableName, config.queueArn])\n .apply(([tableName, queueArn]) =>\n JSON.stringify({\n Version: \"2012-10-17\",\n Statement: [\n {\n // DynamoDB access\n Effect: \"Allow\",\n Action: [\n \"dynamodb:PutItem\",\n \"dynamodb:GetItem\",\n \"dynamodb:Query\",\n \"dynamodb:Scan\",\n \"dynamodb:UpdateItem\",\n ],\n Resource: [\n `arn:aws:dynamodb:*:*:table/${tableName}`,\n `arn:aws:dynamodb:*:*:table/${tableName}/index/*`,\n ],\n },\n {\n // SQS access for event source mapping\n Effect: \"Allow\",\n Action: [\n \"sqs:ReceiveMessage\",\n \"sqs:DeleteMessage\",\n \"sqs:GetQueueAttributes\",\n ],\n Resource: queueArn,\n },\n ],\n })\n ),\n });\n\n // Check if Lambda function already exists\n const functionName = \"wraps-email-event-processor\";\n const exists = await lambdaFunctionExists(functionName);\n\n // Create event-processor Lambda\n const eventProcessor = exists\n ? new aws.lambda.Function(\n functionName,\n {\n name: functionName,\n runtime: \"nodejs24.x\",\n handler: \"index.handler\",\n role: lambdaRole.arn,\n code: new pulumi.asset.FileArchive(eventProcessorCode),\n timeout: 300, // 5 minutes (matches SQS visibility timeout)\n memorySize: 512,\n environment: {\n variables: {\n TABLE_NAME: config.tableName,\n AWS_ACCOUNT_ID: config.accountId,\n },\n },\n tags: {\n ManagedBy: \"wraps-cli\",\n Description:\n \"Process SES email events from SQS and store in DynamoDB\",\n },\n },\n {\n import: functionName, // Import existing function\n }\n )\n : new aws.lambda.Function(functionName, {\n name: functionName,\n runtime: \"nodejs24.x\",\n handler: \"index.handler\",\n role: lambdaRole.arn,\n code: new pulumi.asset.FileArchive(eventProcessorCode),\n timeout: 300, // 5 minutes (matches SQS visibility timeout)\n memorySize: 512,\n environment: {\n variables: {\n TABLE_NAME: config.tableName,\n AWS_ACCOUNT_ID: config.accountId,\n },\n },\n tags: {\n ManagedBy: \"wraps-cli\",\n Description:\n \"Process SES email events from SQS and store in DynamoDB\",\n },\n });\n\n // Check if event source mapping already exists\n // Construct the queue ARN from the known queue name, region, and account ID\n const queueArnValue = `arn:aws:sqs:${config.region}:${config.accountId}:wraps-email-events`;\n const existingMappingUuid = await findEventSourceMapping(\n functionName,\n queueArnValue\n );\n\n // Create SQS event source mapping for Lambda\n // This automatically polls SQS and invokes the Lambda function\n const mappingConfig = {\n eventSourceArn: config.queueArn,\n functionName: eventProcessor.name,\n batchSize: 10, // Process up to 10 messages per invocation\n maximumBatchingWindowInSeconds: 5, // Wait up to 5 seconds to batch messages\n functionResponseTypes: [\"ReportBatchItemFailures\"], // Enable partial batch responses\n };\n\n const eventSourceMapping = existingMappingUuid\n ? new aws.lambda.EventSourceMapping(\n \"wraps-email-event-source-mapping\",\n mappingConfig,\n {\n import: existingMappingUuid, // Import with the UUID\n }\n )\n : new aws.lambda.EventSourceMapping(\n \"wraps-email-event-source-mapping\",\n mappingConfig\n );\n\n return {\n eventProcessor,\n eventSourceMapping,\n };\n}\n","import * as aws from \"@pulumi/aws\";\nimport type { SESEventType } from \"../../types/index.js\";\n\n/**\n * SES resources configuration\n */\nexport type SESResourcesConfig = {\n domain?: string;\n mailFromDomain?: string;\n region: string;\n trackingConfig?: {\n enabled: boolean;\n opens?: boolean;\n clicks?: boolean;\n customRedirectDomain?: string;\n httpsEnabled?: boolean;\n };\n eventTypes?: SESEventType[];\n eventTrackingEnabled?: boolean; // NEW: Whether to create EventBridge event destination\n tlsRequired?: boolean; // Require TLS encryption for all emails\n importExistingEventDestination?: boolean; // Import existing event destination if it exists\n};\n\n/**\n * Check if SES configuration set exists\n */\nasync function configurationSetExists(\n configSetName: string,\n region: string\n): Promise<boolean> {\n try {\n const { SESv2Client, GetConfigurationSetCommand } = await import(\n \"@aws-sdk/client-sesv2\"\n );\n const ses = new SESv2Client({ region });\n\n await ses.send(\n new GetConfigurationSetCommand({ ConfigurationSetName: configSetName })\n );\n return true;\n } catch (error: any) {\n if (error.name === \"NotFoundException\") {\n return false;\n }\n console.error(\"Error checking for existing configuration set:\", error);\n return false;\n }\n}\n\n/**\n * Check if event destination exists for a configuration set\n */\nexport async function eventDestinationExists(\n configSetName: string,\n eventDestName: string,\n region: string\n): Promise<boolean> {\n try {\n const { SESv2Client, GetConfigurationSetEventDestinationsCommand } =\n await import(\"@aws-sdk/client-sesv2\");\n const ses = new SESv2Client({ region });\n\n const response = await ses.send(\n new GetConfigurationSetEventDestinationsCommand({\n ConfigurationSetName: configSetName,\n })\n );\n\n return (\n response.EventDestinations?.some((dest) => dest.Name === eventDestName) ??\n false\n );\n } catch (error: any) {\n if (error.name === \"NotFoundException\") {\n return false;\n }\n // Silently return false on other errors - we'll try to create and handle errors there\n return false;\n }\n}\n\n/**\n * Check if email identity exists\n */\nasync function emailIdentityExists(\n emailIdentity: string,\n region: string\n): Promise<boolean> {\n try {\n const { SESv2Client, GetEmailIdentityCommand } = await import(\n \"@aws-sdk/client-sesv2\"\n );\n const ses = new SESv2Client({ region });\n\n await ses.send(\n new GetEmailIdentityCommand({ EmailIdentity: emailIdentity })\n );\n return true;\n } catch (error: any) {\n if (error.name === \"NotFoundException\") {\n return false;\n }\n console.error(\"Error checking for existing email identity:\", error);\n return false;\n }\n}\n\n/**\n * SES resources output\n */\nexport type SESResources = {\n configSet: aws.sesv2.ConfigurationSet;\n eventBus: aws.cloudwatch.EventBus;\n domainIdentity?: aws.sesv2.EmailIdentity;\n dkimTokens?: string[];\n dnsAutoCreated?: boolean;\n customTrackingDomain?: string;\n mailFromDomain?: string;\n};\n\n/**\n * Create SES resources (configuration set, EventBridge event bus, domain identity)\n */\nexport async function createSESResources(\n config: SESResourcesConfig\n): Promise<SESResources> {\n // Configuration set for tracking (using SESv2 which supports tags)\n const configSetOptions: aws.sesv2.ConfigurationSetArgs = {\n configurationSetName: \"wraps-email-tracking\",\n deliveryOptions: config.tlsRequired\n ? {\n tlsPolicy: \"REQUIRE\", // Require TLS 1.2+ for all emails\n }\n : undefined,\n suppressionOptions: {\n // Automatically suppress hard bounces and complaints at the configuration set level\n // This provides protection even if account-level suppression isn't configured\n suppressedReasons: [\"BOUNCE\", \"COMPLAINT\"],\n },\n tags: {\n ManagedBy: \"wraps-cli\",\n Description: \"Wraps email tracking configuration set\",\n },\n };\n\n // Add custom tracking domain if provided\n // Note: The tracking domain only needs a CNAME DNS record\n // - Without HTTPS: CNAME points to r.{region}.awstrack.me\n // - With HTTPS: CNAME points to CloudFront distribution domain\n if (config.trackingConfig?.customRedirectDomain) {\n configSetOptions.trackingOptions = {\n customRedirectDomain: config.trackingConfig.customRedirectDomain,\n // HTTPS policy depends on whether HTTPS tracking is enabled\n // - REQUIRE: When using CloudFront with SSL certificate\n // - OPTIONAL: When using direct SES tracking endpoint (no SSL)\n httpsPolicy: config.trackingConfig.httpsEnabled ? \"REQUIRE\" : \"OPTIONAL\",\n };\n }\n\n // Check if configuration set already exists\n const configSetName = \"wraps-email-tracking\";\n const exists = await configurationSetExists(configSetName, config.region);\n\n const configSet = exists\n ? new aws.sesv2.ConfigurationSet(configSetName, configSetOptions, {\n import: configSetName, // Import existing configuration set\n })\n : new aws.sesv2.ConfigurationSet(configSetName, configSetOptions);\n\n // SES can only send to the default EventBridge bus\n // We'll use EventBridge rules to route from default bus to SQS\n // Get the default event bus (it always exists)\n const defaultEventBus = aws.cloudwatch.getEventBusOutput({\n name: \"default\",\n });\n\n // Event destination for all SES events -> EventBridge (default bus)\n // Only create if event tracking is enabled\n if (config.eventTrackingEnabled) {\n const eventDestName = \"wraps-email-eventbridge\";\n\n new aws.sesv2.ConfigurationSetEventDestination(\n \"wraps-email-all-events\",\n {\n configurationSetName: configSet.configurationSetName,\n eventDestinationName: eventDestName,\n eventDestination: {\n enabled: true,\n matchingEventTypes: [\n \"SEND\",\n \"DELIVERY\",\n \"OPEN\",\n \"CLICK\",\n \"BOUNCE\",\n \"COMPLAINT\",\n \"REJECT\",\n \"RENDERING_FAILURE\",\n \"DELIVERY_DELAY\",\n \"SUBSCRIPTION\",\n ],\n eventBridgeDestination: {\n // SES requires default bus - cannot use custom bus\n eventBusArn: defaultEventBus.arn,\n },\n },\n },\n {\n // Import existing resource if it already exists in AWS\n // This prevents AlreadyExistsException when the resource exists but isn't in Pulumi state\n import: config.importExistingEventDestination\n ? `wraps-email-tracking|${eventDestName}`\n : undefined,\n }\n );\n }\n\n // Optional: Verify domain if provided\n let domainIdentity: aws.sesv2.EmailIdentity | undefined;\n let dkimTokens: string[] | undefined;\n let mailFromDomain: string | undefined;\n\n if (config.domain) {\n // Check if email identity already exists\n const identityExists = await emailIdentityExists(\n config.domain,\n config.region\n );\n\n // Use SES v2 API to create email identity with configuration set\n domainIdentity = identityExists\n ? new aws.sesv2.EmailIdentity(\n \"wraps-email-domain\",\n {\n emailIdentity: config.domain,\n configurationSetName: configSet.configurationSetName, // Link configuration set to domain\n dkimSigningAttributes: {\n nextSigningKeyLength: \"RSA_2048_BIT\",\n },\n tags: {\n ManagedBy: \"wraps-cli\",\n },\n },\n {\n import: config.domain, // Import existing identity\n }\n )\n : new aws.sesv2.EmailIdentity(\"wraps-email-domain\", {\n emailIdentity: config.domain,\n configurationSetName: configSet.configurationSetName, // Link configuration set to domain\n dkimSigningAttributes: {\n nextSigningKeyLength: \"RSA_2048_BIT\",\n },\n tags: {\n ManagedBy: \"wraps-cli\",\n },\n });\n\n // Extract DKIM tokens for DNS configuration\n dkimTokens = domainIdentity.dkimSigningAttributes.apply(\n (attrs) => attrs?.tokens || []\n ) as any;\n\n // Configure MAIL FROM domain for better DMARC alignment (only if explicitly configured)\n if (config.mailFromDomain) {\n mailFromDomain = config.mailFromDomain;\n\n // Create/update MAIL FROM attributes\n // Note: This resource doesn't support import, but it will update existing config\n new aws.sesv2.EmailIdentityMailFromAttributes(\n \"wraps-email-mail-from\",\n {\n emailIdentity: config.domain,\n mailFromDomain,\n behaviorOnMxFailure: \"USE_DEFAULT_VALUE\", // Fallback to amazonses.com if MX record fails\n },\n {\n dependsOn: [domainIdentity], // Ensure domain identity exists first\n }\n );\n }\n }\n\n return {\n configSet,\n eventBus: defaultEventBus as any, // Return default bus reference\n domainIdentity,\n dkimTokens,\n dnsAutoCreated: false, // Will be set after deployment\n customTrackingDomain: config.trackingConfig?.customRedirectDomain,\n mailFromDomain,\n };\n}\n","import * as aws from \"@pulumi/aws\";\n\n/**\n * SQS resources output\n */\nexport type SQSResources = {\n queue: aws.sqs.Queue;\n dlq: aws.sqs.Queue;\n};\n\n/**\n * Create SQS queue with Dead Letter Queue for event processing\n *\n * Architecture:\n * EventBridge -> SQS Queue -> Lambda (event-processor)\n * ↓\n * DLQ (failed messages after 3 retries)\n */\nexport async function createSQSResources(): Promise<SQSResources> {\n // Dead Letter Queue for failed event processing\n const dlq = new aws.sqs.Queue(\"wraps-email-events-dlq\", {\n name: \"wraps-email-events-dlq\",\n messageRetentionSeconds: 1_209_600, // 14 days\n tags: {\n ManagedBy: \"wraps-cli\",\n Description: \"Dead letter queue for failed SES event processing\",\n },\n });\n\n // Main queue for SES events\n const queue = new aws.sqs.Queue(\"wraps-email-events\", {\n name: \"wraps-email-events\",\n visibilityTimeoutSeconds: 300, // 5 minutes (Lambda timeout)\n messageRetentionSeconds: 345_600, // 4 days\n receiveWaitTimeSeconds: 20, // Long polling\n redrivePolicy: dlq.arn.apply((arn) =>\n JSON.stringify({\n deadLetterTargetArn: arn,\n maxReceiveCount: 3, // Retry 3 times before sending to DLQ\n })\n ),\n tags: {\n ManagedBy: \"wraps-cli\",\n Description: \"Queue for SES email events from EventBridge\",\n },\n });\n\n return {\n queue,\n dlq,\n };\n}\n","import * as aws from \"@pulumi/aws\";\n\n/**\n * Vercel OIDC configuration\n */\nexport type VercelOIDCConfig = {\n teamSlug: string;\n accountId: string;\n};\n\n/**\n * Get existing OIDC provider ARN by URL\n */\nasync function getExistingOIDCProviderArn(url: string): Promise<string | null> {\n try {\n const { IAMClient, ListOpenIDConnectProvidersCommand } = await import(\n \"@aws-sdk/client-iam\"\n );\n // IAM is global but SDK still requires a region\n const iam = new IAMClient({\n region: process.env.AWS_REGION || \"us-east-1\",\n });\n\n const response = await iam.send(new ListOpenIDConnectProvidersCommand({}));\n\n // Find provider by ARN pattern (ARN includes the URL)\n // Format: arn:aws:iam::ACCOUNT:oidc-provider/oidc.vercel.com/TEAM\n const expectedArnSuffix = url.replace(\"https://\", \"\");\n const provider = response.OpenIDConnectProviderList?.find((p) =>\n p.Arn?.endsWith(expectedArnSuffix)\n );\n\n return provider?.Arn || null;\n } catch (error) {\n console.error(\"Error checking for existing OIDC provider:\", error);\n return null;\n }\n}\n\n/**\n * Create or get existing Vercel OIDC provider for AssumeRoleWithWebIdentity\n */\nexport async function createVercelOIDC(\n config: VercelOIDCConfig\n): Promise<aws.iam.OpenIdConnectProvider> {\n const url = `https://oidc.vercel.com/${config.teamSlug}`;\n\n // Check if OIDC provider already exists\n const existingArn = await getExistingOIDCProviderArn(url);\n\n if (existingArn) {\n // Import existing OIDC provider instead of creating new one\n return new aws.iam.OpenIdConnectProvider(\n \"wraps-vercel-oidc\",\n {\n url,\n clientIdLists: [`https://vercel.com/${config.teamSlug}`],\n thumbprintLists: [\n // Vercel OIDC thumbprints\n \"20032e77eca0785eece16b56b42c9b330b906320\",\n \"696db3af0dffc17e65c6a20d925c5a7bd24dec7e\",\n ],\n tags: {\n ManagedBy: \"wraps-cli\",\n Provider: \"vercel\",\n },\n },\n {\n import: existingArn, // Import existing resource\n }\n );\n }\n\n // Create new OIDC provider if it doesn't exist\n return new aws.iam.OpenIdConnectProvider(\"wraps-vercel-oidc\", {\n url,\n clientIdLists: [`https://vercel.com/${config.teamSlug}`],\n thumbprintLists: [\n // Vercel OIDC thumbprints\n \"20032e77eca0785eece16b56b42c9b330b906320\",\n \"696db3af0dffc17e65c6a20d925c5a7bd24dec7e\",\n ],\n tags: {\n ManagedBy: \"wraps-cli\",\n Provider: \"vercel\",\n },\n });\n}\n","import { exec } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport * as automation from \"@pulumi/pulumi/automation/index.js\";\nimport { errors } from \"./errors.js\";\n\nconst execAsync = promisify(exec);\n\n// Extract installPulumiCli from the automation module\nconst installPulumiCli = (automation as any).installPulumiCli;\n\n/**\n * Check if Pulumi CLI is installed\n */\nexport async function checkPulumiInstalled(): Promise<boolean> {\n try {\n await execAsync(\"pulumi version\");\n return true;\n } catch (_error) {\n return false;\n }\n}\n\n/**\n * Ensure Pulumi CLI is installed, auto-install if missing\n * @returns true if Pulumi was auto-installed, false if it was already installed\n */\nexport async function ensurePulumiInstalled(): Promise<boolean> {\n const isInstalled = await checkPulumiInstalled();\n\n if (!isInstalled) {\n try {\n // Try to auto-install Pulumi CLI using Automation API\n await installPulumiCli();\n return true; // Was auto-installed\n } catch (_error) {\n // If auto-install fails, throw helpful error\n throw errors.pulumiNotInstalled();\n }\n }\n\n return false; // Was already installed\n}\n","import * as clack from \"@clack/prompts\";\nimport * as pulumi from \"@pulumi/pulumi\";\nimport pc from \"picocolors\";\nimport { deployEmailStack } from \"../../infrastructure/email-stack.js\";\nimport {\n trackError,\n trackServiceDeployed,\n trackServiceInit,\n} from \"../../telemetry/events.js\";\nimport type { ConnectOptions, EmailStackConfig } from \"../../types/index.js\";\nimport { getPreset } from \"../../utils/email/presets.js\";\nimport {\n getAWSRegion,\n validateAWSCredentials,\n} from \"../../utils/shared/aws.js\";\nimport { errors } from \"../../utils/shared/errors.js\";\nimport {\n ensurePulumiWorkDir,\n getPulumiWorkDir,\n} from \"../../utils/shared/fs.js\";\nimport {\n createConnectionMetadata,\n loadConnectionMetadata,\n saveConnectionMetadata,\n} from \"../../utils/shared/metadata.js\";\nimport {\n DeploymentProgress,\n displayPreview,\n displaySuccess,\n} from \"../../utils/shared/output.js\";\nimport {\n confirmConnect,\n promptConfigPreset,\n promptProvider,\n promptRegion,\n promptSelectIdentities,\n promptVercelConfig,\n} from \"../../utils/shared/prompts.js\";\nimport { ensurePulumiInstalled } from \"../../utils/shared/pulumi.js\";\nimport { scanAWSResources } from \"../../utils/shared/scanner.js\";\n\n/**\n * Connect command - Connect to existing AWS SES infrastructure\n */\nexport async function connect(options: ConnectOptions): Promise<void> {\n const startTime = Date.now();\n\n clack.intro(\n pc.bold(\n options.preview\n ? \"Wraps Connect Preview\"\n : \"Wraps Connect - Link Existing Infrastructure\"\n )\n );\n\n const progress = new DeploymentProgress();\n\n // 1. Check Pulumi CLI is installed\n const wasAutoInstalled = await progress.execute(\n \"Checking Pulumi CLI installation\",\n async () => await ensurePulumiInstalled()\n );\n\n if (wasAutoInstalled) {\n progress.info(\"Pulumi CLI was automatically installed\");\n }\n\n // 2. Validate AWS credentials\n const identity = await progress.execute(\n \"Validating AWS credentials\",\n async () => validateAWSCredentials()\n );\n\n progress.info(`Connected to AWS account: ${pc.cyan(identity.accountId)}`);\n\n // 3. Get region\n let region = options.region;\n if (!region) {\n const defaultRegion = await getAWSRegion();\n region = await promptRegion(defaultRegion);\n }\n\n // 4. Check if connection already exists\n const existingConnection = await loadConnectionMetadata(\n identity.accountId,\n region\n );\n if (existingConnection) {\n clack.log.warn(\n `Connection already exists for account ${pc.cyan(identity.accountId)} in region ${pc.cyan(region)}`\n );\n clack.log.info(`Created: ${existingConnection.timestamp}`);\n clack.log.info(`Use ${pc.cyan(\"wraps status\")} to view current setup`);\n clack.log.info(`Use ${pc.cyan(\"wraps upgrade\")} to add more features`);\n process.exit(0);\n }\n\n // 5. Scan existing AWS resources\n const scan = await progress.execute(\n \"Scanning existing AWS resources\",\n async () => scanAWSResources(region)\n );\n\n // Display what we found\n progress.info(\n `Found: ${scan.identities.length} identities, ${scan.configurationSets.length} config sets`\n );\n\n // Check if any identities exist\n if (scan.identities.length === 0) {\n clack.log.warn(\"No SES identities found in this region.\");\n clack.log.info(\n `Use ${pc.cyan(\"wraps email init\")} to create new email infrastructure instead.`\n );\n process.exit(0);\n }\n\n // Show verified identities\n const verifiedIdentities = scan.identities.filter((id) => id.verified);\n if (verifiedIdentities.length > 0) {\n progress.info(\n `Verified identities: ${verifiedIdentities.map((id) => pc.cyan(id.name)).join(\", \")}`\n );\n }\n\n // 6. Get provider configuration\n let provider = options.provider;\n if (!provider) {\n provider = await promptProvider();\n }\n\n // Get Vercel config if needed\n let vercelConfig;\n if (provider === \"vercel\") {\n vercelConfig = await promptVercelConfig();\n }\n\n // 7. Select identities to connect\n const selectedIdentities = await promptSelectIdentities(\n scan.identities.map((id) => ({\n name: id.name,\n verified: id.verified,\n }))\n );\n\n if (selectedIdentities.length === 0) {\n clack.log.warn(\"No identities selected. Nothing to connect.\");\n process.exit(0);\n }\n\n // 8. Select configuration preset\n const preset = await promptConfigPreset();\n const emailConfig =\n preset === \"custom\"\n ? await import(\"../../utils/shared/prompts.js\").then((m) =>\n m.promptCustomConfig()\n )\n : getPreset(preset)!;\n\n // 8a. Set the domain from the first selected identity (if it's a domain, not an email)\n // Filter to only domains (exclude email addresses)\n const domainIdentities = selectedIdentities.filter((id) => !id.includes(\"@\"));\n if (domainIdentities.length > 0) {\n emailConfig.domain = domainIdentities[0];\n }\n\n // 9. Confirm deployment (skip if --yes or --preview)\n if (!(options.yes || options.preview)) {\n const confirmed = await confirmConnect();\n if (!confirmed) {\n clack.cancel(\"Connection cancelled.\");\n process.exit(0);\n }\n }\n\n // 10. Build stack configuration\n const stackConfig: EmailStackConfig = {\n provider,\n region,\n vercel: vercelConfig,\n emailConfig,\n };\n\n // 11. Preview or Deploy infrastructure using Pulumi\n if (options.preview) {\n // PREVIEW MODE - show what would be created without deploying\n try {\n const previewResult = await progress.execute(\n \"Generating infrastructure preview\",\n async () => {\n await ensurePulumiWorkDir();\n\n const stack =\n await pulumi.automation.LocalWorkspace.createOrSelectStack(\n {\n stackName: `wraps-${identity.accountId}-${region}`,\n projectName: \"wraps-email\",\n program: async () => {\n const result = await deployEmailStack(stackConfig);\n return {\n roleArn: result.roleArn,\n configSetName: result.configSetName,\n tableName: result.tableName,\n region: result.region,\n lambdaFunctions: result.lambdaFunctions,\n domain: result.domain,\n dkimTokens: result.dkimTokens,\n customTrackingDomain: result.customTrackingDomain,\n };\n },\n },\n {\n workDir: getPulumiWorkDir(),\n envVars: {\n PULUMI_CONFIG_PASSPHRASE: \"\",\n AWS_REGION: region,\n },\n secretsProvider: \"passphrase\",\n }\n );\n\n await stack.setConfig(\"aws:region\", { value: region });\n\n // Run preview instead of deployment\n const result = await stack.preview({ diff: true });\n return result;\n }\n );\n\n // Display preview results\n displayPreview({\n changeSummary: previewResult.changeSummary,\n commandName: \"wraps email connect\",\n });\n\n clack.outro(\n pc.green(\"Preview complete. Run without --preview to connect.\")\n );\n\n // Track preview completion\n trackServiceInit(\"email\", true, {\n preset,\n provider,\n preview: true,\n duration_ms: Date.now() - startTime,\n existing_identities: selectedIdentities.length,\n });\n return;\n } catch (error: any) {\n trackError(\"PREVIEW_FAILED\", \"email:connect\", { step: \"preview\" });\n if (error.message?.includes(\"stack is currently locked\")) {\n throw errors.stackLocked();\n }\n throw new Error(`Preview failed: ${error.message}`);\n }\n }\n\n // DEPLOY MODE - actually create infrastructure\n let outputs;\n try {\n outputs = await progress.execute(\n \"Deploying Wraps infrastructure (this may take 2-3 minutes)\",\n async () => {\n await ensurePulumiWorkDir();\n\n const stack =\n await pulumi.automation.LocalWorkspace.createOrSelectStack(\n {\n stackName: `wraps-${identity.accountId}-${region}`,\n projectName: \"wraps-email\",\n program: async () => {\n const result = await deployEmailStack(stackConfig);\n\n return {\n roleArn: result.roleArn,\n configSetName: result.configSetName,\n tableName: result.tableName,\n region: result.region,\n lambdaFunctions: result.lambdaFunctions,\n domain: result.domain,\n dkimTokens: result.dkimTokens,\n customTrackingDomain: result.customTrackingDomain,\n };\n },\n },\n {\n workDir: getPulumiWorkDir(),\n envVars: {\n PULUMI_CONFIG_PASSPHRASE: \"\",\n AWS_REGION: region,\n },\n secretsProvider: \"passphrase\",\n }\n );\n\n await stack.workspace.selectStack(\n `wraps-${identity.accountId}-${region}`\n );\n await stack.setConfig(\"aws:region\", { value: region });\n\n const upResult = await stack.up({ onOutput: () => {} });\n const pulumiOutputs = upResult.outputs;\n\n return {\n roleArn: pulumiOutputs.roleArn?.value as string,\n configSetName: pulumiOutputs.configSetName?.value as\n | string\n | undefined,\n tableName: pulumiOutputs.tableName?.value as string | undefined,\n region: pulumiOutputs.region?.value as string,\n lambdaFunctions: pulumiOutputs.lambdaFunctions?.value as\n | string[]\n | undefined,\n domain: pulumiOutputs.domain?.value as string | undefined,\n dkimTokens: pulumiOutputs.dkimTokens?.value as string[] | undefined,\n customTrackingDomain: pulumiOutputs.customTrackingDomain?.value as\n | string\n | undefined,\n };\n }\n );\n } catch (error: any) {\n // Track deployment failure\n trackServiceInit(\"email\", false, {\n preset,\n provider,\n duration_ms: Date.now() - startTime,\n });\n\n // Check if it's a lock file error\n if (error.message?.includes(\"stack is currently locked\")) {\n trackError(\"STACK_LOCKED\", \"email:connect\", { step: \"deploy\" });\n throw errors.stackLocked();\n }\n\n trackError(\"DEPLOYMENT_FAILED\", \"email:connect\", { step: \"deploy\" });\n throw new Error(`Pulumi deployment failed: ${error.message}`);\n }\n\n // 12. Create DNS records in Route53 (if hosted zone exists)\n if (outputs.domain && outputs.dkimTokens && outputs.dkimTokens.length > 0) {\n const { findHostedZone, createDNSRecords } = await import(\n \"../../utils/email/route53.js\"\n );\n const hostedZone = await findHostedZone(outputs.domain, region);\n\n if (hostedZone) {\n try {\n progress.start(\"Creating DNS records in Route53\");\n\n // Determine mailFromDomain - use outputs if available, otherwise construct default\n const mailFromDomain =\n emailConfig.mailFromDomain || `mail.${outputs.domain}`;\n\n await createDNSRecords(\n hostedZone.id,\n outputs.domain,\n outputs.dkimTokens,\n region,\n outputs.customTrackingDomain,\n mailFromDomain\n );\n progress.succeed(\"DNS records created in Route53\");\n } catch (error: any) {\n progress.fail(\n `Failed to create DNS records automatically: ${error.message}`\n );\n progress.info(\n \"You can manually add the required DNS records shown below\"\n );\n }\n }\n }\n\n // 13. Save metadata\n const metadata = createConnectionMetadata(\n identity.accountId,\n region,\n provider,\n emailConfig,\n preset === \"custom\" ? undefined : preset\n );\n if (metadata.services.email) {\n metadata.services.email.pulumiStackName = `wraps-${identity.accountId}-${region}`;\n }\n if (vercelConfig) {\n metadata.vercel = vercelConfig;\n }\n await saveConnectionMetadata(metadata);\n\n progress.info(\"Connection metadata saved\");\n\n // 14. Display success message\n displaySuccess({\n roleArn: outputs.roleArn,\n configSetName: outputs.configSetName,\n region: outputs.region!,\n tableName: outputs.tableName,\n });\n\n // Show next steps\n if (selectedIdentities.length > 0 && emailConfig.tracking?.enabled) {\n console.log(`\\n${pc.bold(\"Next Steps:\")}\\n`);\n console.log(\n `Update your code to use configuration set: ${pc.cyan(\"wraps-email-tracking\")}`\n );\n console.log(`\\n${pc.dim(\"Example:\")}`);\n console.log(\n pc.gray(` await ses.sendEmail({\n ConfigurationSetName: 'wraps-email-tracking',\n // ... other parameters\n });`)\n );\n console.log(\"\");\n }\n\n // 15. Track successful connection\n const duration = Date.now() - startTime;\n const enabledFeatures: string[] = [];\n if (emailConfig.tracking?.enabled) enabledFeatures.push(\"tracking\");\n if (emailConfig.suppressionList?.enabled)\n enabledFeatures.push(\"suppression_list\");\n if (emailConfig.eventTracking?.enabled)\n enabledFeatures.push(\"event_tracking\");\n if (emailConfig.eventTracking?.dynamoDBHistory)\n enabledFeatures.push(\"dynamodb_history\");\n\n trackServiceInit(\"email\", true, {\n preset,\n provider,\n features: enabledFeatures,\n duration_ms: duration,\n existing_identities: selectedIdentities.length,\n });\n\n trackServiceDeployed(\"email\", {\n duration_ms: duration,\n features: enabledFeatures,\n preset,\n });\n}\n","import {\n DescribeTableCommand,\n DynamoDBClient,\n ListTablesCommand,\n} from \"@aws-sdk/client-dynamodb\";\nimport { IAMClient, ListRolesCommand } from \"@aws-sdk/client-iam\";\nimport { LambdaClient, ListFunctionsCommand } from \"@aws-sdk/client-lambda\";\nimport {\n DescribeConfigurationSetCommand,\n GetIdentityVerificationAttributesCommand,\n ListConfigurationSetsCommand,\n ListIdentitiesCommand,\n SESClient,\n} from \"@aws-sdk/client-ses\";\nimport {\n GetTopicAttributesCommand,\n ListTopicsCommand,\n SNSClient,\n} from \"@aws-sdk/client-sns\";\n\n/**\n * SES Identity with configuration\n */\nexport type SESIdentity = {\n name: string;\n type: \"Domain\" | \"EmailAddress\";\n verified: boolean;\n configurationSet?: string;\n dkimEnabled?: boolean;\n};\n\n/**\n * SES Configuration Set\n */\nexport type SESConfigurationSet = {\n name: string;\n eventDestinations: Array<{\n name: string;\n enabled: boolean;\n matchingEventTypes: string[];\n snsDestination?: string;\n cloudWatchDestination?: any;\n }>;\n};\n\n/**\n * SNS Topic\n */\nexport type SNSTopic = {\n arn: string;\n name: string;\n subscriptions?: number;\n};\n\n/**\n * DynamoDB Table\n */\nexport type DynamoTable = {\n name: string;\n status: string;\n itemCount?: number;\n sizeBytes?: number;\n};\n\n/**\n * Lambda Function\n */\nexport type LambdaFunction = {\n name: string;\n arn: string;\n runtime?: string;\n handler?: string;\n};\n\n/**\n * IAM Role\n */\nexport type IAMRole = {\n name: string;\n arn: string;\n assumeRolePolicyDocument: string;\n};\n\n/**\n * Complete scan of existing AWS resources\n */\nexport type AWSResourceScan = {\n identities: SESIdentity[];\n configurationSets: SESConfigurationSet[];\n snsTopics: SNSTopic[];\n dynamoTables: DynamoTable[];\n lambdaFunctions: LambdaFunction[];\n iamRoles: IAMRole[];\n};\n\n/**\n * Scan all existing SES identities (domains and email addresses)\n */\nexport async function scanSESIdentities(\n region: string\n): Promise<SESIdentity[]> {\n const ses = new SESClient({ region });\n const identities: SESIdentity[] = [];\n\n try {\n // List all identities\n const listResponse = await ses.send(new ListIdentitiesCommand({}));\n const identityNames = listResponse.Identities || [];\n\n if (identityNames.length === 0) {\n return [];\n }\n\n // Get verification attributes\n const attrsResponse = await ses.send(\n new GetIdentityVerificationAttributesCommand({\n Identities: identityNames,\n })\n );\n\n const attrs = attrsResponse.VerificationAttributes || {};\n\n // Build identity objects\n for (const name of identityNames) {\n const attr = attrs[name];\n identities.push({\n name,\n type: name.includes(\"@\") ? \"EmailAddress\" : \"Domain\",\n verified: attr?.VerificationStatus === \"Success\",\n });\n }\n\n return identities;\n } catch (error: any) {\n console.error(\"Error scanning SES identities:\", error.message);\n return [];\n }\n}\n\n/**\n * Scan all SES configuration sets\n */\nexport async function scanSESConfigurationSets(\n region: string\n): Promise<SESConfigurationSet[]> {\n const ses = new SESClient({ region });\n const configSets: SESConfigurationSet[] = [];\n\n try {\n // List configuration sets\n const listResponse = await ses.send(new ListConfigurationSetsCommand({}));\n const configSetNames =\n listResponse.ConfigurationSets?.map((cs) => cs.Name!).filter(Boolean) ||\n [];\n\n // Get details for each config set\n for (const name of configSetNames) {\n try {\n const describeResponse = await ses.send(\n new DescribeConfigurationSetCommand({ ConfigurationSetName: name })\n );\n\n const eventDestinations =\n describeResponse.EventDestinations?.map((ed) => ({\n name: ed.Name!,\n enabled: ed.Enabled ?? false,\n matchingEventTypes: ed.MatchingEventTypes || [],\n snsDestination: ed.SNSDestination?.TopicARN,\n cloudWatchDestination: ed.CloudWatchDestination,\n })) || [];\n\n configSets.push({\n name,\n eventDestinations,\n });\n } catch (error: any) {\n console.error(`Error describing config set ${name}:`, error.message);\n }\n }\n\n return configSets;\n } catch (error: any) {\n console.error(\"Error scanning SES configuration sets:\", error.message);\n return [];\n }\n}\n\n/**\n * Scan SNS topics (filter for email-related ones)\n */\nexport async function scanSNSTopics(region: string): Promise<SNSTopic[]> {\n const sns = new SNSClient({ region });\n const topics: SNSTopic[] = [];\n\n try {\n // List all topics\n const listResponse = await sns.send(new ListTopicsCommand({}));\n const topicArns =\n listResponse.Topics?.map((t) => t.TopicArn!).filter(Boolean) || [];\n\n // Get details for each topic\n for (const arn of topicArns) {\n try {\n const attrsResponse = await sns.send(\n new GetTopicAttributesCommand({ TopicArn: arn })\n );\n\n const name = arn.split(\":\").pop() || arn;\n const subscriptions = Number.parseInt(\n attrsResponse.Attributes?.SubscriptionsConfirmed || \"0\",\n 10\n );\n\n topics.push({\n arn,\n name,\n subscriptions,\n });\n } catch (error: any) {\n console.error(\n `Error getting topic attributes for ${arn}:`,\n error.message\n );\n }\n }\n\n return topics;\n } catch (error: any) {\n console.error(\"Error scanning SNS topics:\", error.message);\n return [];\n }\n}\n\n/**\n * Scan DynamoDB tables (filter for email-related ones)\n */\nexport async function scanDynamoTables(region: string): Promise<DynamoTable[]> {\n const dynamo = new DynamoDBClient({ region });\n const tables: DynamoTable[] = [];\n\n try {\n // List all tables\n const listResponse = await dynamo.send(new ListTablesCommand({}));\n const tableNames = listResponse.TableNames || [];\n\n // Get details for each table\n for (const name of tableNames) {\n try {\n const describeResponse = await dynamo.send(\n new DescribeTableCommand({ TableName: name })\n );\n\n const table = describeResponse.Table;\n if (table) {\n tables.push({\n name,\n status: table.TableStatus || \"UNKNOWN\",\n itemCount: table.ItemCount,\n sizeBytes: table.TableSizeBytes,\n });\n }\n } catch (error: any) {\n console.error(`Error describing table ${name}:`, error.message);\n }\n }\n\n return tables;\n } catch (error: any) {\n console.error(\"Error scanning DynamoDB tables:\", error.message);\n return [];\n }\n}\n\n/**\n * Scan Lambda functions (filter for email-related ones)\n */\nexport async function scanLambdaFunctions(\n region: string\n): Promise<LambdaFunction[]> {\n const lambda = new LambdaClient({ region });\n const functions: LambdaFunction[] = [];\n\n try {\n // List all functions\n const listResponse = await lambda.send(new ListFunctionsCommand({}));\n const functionConfigs = listResponse.Functions || [];\n\n for (const func of functionConfigs) {\n if (func.FunctionName && func.FunctionArn) {\n functions.push({\n name: func.FunctionName,\n arn: func.FunctionArn,\n runtime: func.Runtime,\n handler: func.Handler,\n });\n }\n }\n\n return functions;\n } catch (error: any) {\n console.error(\"Error scanning Lambda functions:\", error.message);\n return [];\n }\n}\n\n/**\n * Scan IAM roles (filter for Wraps or email-related ones)\n */\nexport async function scanIAMRoles(region: string): Promise<IAMRole[]> {\n const iam = new IAMClient({ region });\n const roles: IAMRole[] = [];\n\n try {\n // List all roles (with pagination support)\n let marker: string | undefined;\n let hasMore = true;\n\n while (hasMore) {\n const listResponse = await iam.send(\n new ListRolesCommand({\n Marker: marker,\n MaxItems: 100,\n })\n );\n\n const roleList = listResponse.Roles || [];\n\n for (const role of roleList) {\n if (role.RoleName && role.Arn) {\n roles.push({\n name: role.RoleName,\n arn: role.Arn,\n assumeRolePolicyDocument: role.AssumeRolePolicyDocument || \"\",\n });\n }\n }\n\n marker = listResponse.Marker;\n hasMore = listResponse.IsTruncated ?? false;\n }\n\n return roles;\n } catch (error: any) {\n console.error(\"Error scanning IAM roles:\", error.message);\n return [];\n }\n}\n\n/**\n * Scan all relevant AWS resources for email infrastructure\n */\nexport async function scanAWSResources(\n region: string\n): Promise<AWSResourceScan> {\n const [\n identities,\n configurationSets,\n snsTopics,\n dynamoTables,\n lambdaFunctions,\n iamRoles,\n ] = await Promise.all([\n scanSESIdentities(region),\n scanSESConfigurationSets(region),\n scanSNSTopics(region),\n scanDynamoTables(region),\n scanLambdaFunctions(region),\n scanIAMRoles(region),\n ]);\n\n return {\n identities,\n configurationSets,\n snsTopics,\n dynamoTables,\n lambdaFunctions,\n iamRoles,\n };\n}\n\n/**\n * Filter resources to only Wraps-managed ones (wraps-* prefix)\n */\nexport function filterWrapsResources(scan: AWSResourceScan): AWSResourceScan {\n return {\n identities: scan.identities, // All identities are relevant\n configurationSets: scan.configurationSets.filter((cs) =>\n cs.name.startsWith(\"wraps-\")\n ),\n snsTopics: scan.snsTopics.filter((t) => t.name.startsWith(\"wraps-\")),\n dynamoTables: scan.dynamoTables.filter((t) => t.name.startsWith(\"wraps-\")),\n lambdaFunctions: scan.lambdaFunctions.filter((f) =>\n f.name.startsWith(\"wraps-\")\n ),\n iamRoles: scan.iamRoles.filter((r) => r.name.startsWith(\"wraps-\")),\n };\n}\n\n/**\n * Check if specific Wraps resources exist\n */\nexport function checkWrapsResourcesExist(scan: AWSResourceScan): {\n hasConfigSet: boolean;\n hasSNSTopics: boolean;\n hasDynamoTable: boolean;\n hasLambdaFunctions: boolean;\n hasIAMRole: boolean;\n} {\n const filtered = filterWrapsResources(scan);\n\n return {\n hasConfigSet: filtered.configurationSets.length > 0,\n hasSNSTopics: filtered.snsTopics.length > 0,\n hasDynamoTable: filtered.dynamoTables.length > 0,\n hasLambdaFunctions: filtered.lambdaFunctions.length > 0,\n hasIAMRole: filtered.iamRoles.length > 0,\n };\n}\n","import * as clack from \"@clack/prompts\";\nimport * as pulumi from \"@pulumi/pulumi\";\nimport pc from \"picocolors\";\nimport {\n trackError,\n trackServiceRemoved,\n} from \"../../telemetry/events.js\";\nimport type { DestroyOptions } from \"../../types/index.js\";\nimport {\n getAWSRegion,\n validateAWSCredentials,\n} from \"../../utils/shared/aws.js\";\nimport {\n ensurePulumiWorkDir,\n getPulumiWorkDir,\n} from \"../../utils/shared/fs.js\";\nimport {\n deleteConnectionMetadata,\n loadConnectionMetadata,\n} from \"../../utils/shared/metadata.js\";\nimport {\n DeploymentProgress,\n displayPreview,\n} from \"../../utils/shared/output.js\";\nimport { deleteDNSRecords, findHostedZone } from \"../../utils/route53.js\";\n\n/**\n * Get DKIM tokens for a domain from SES\n */\nasync function getDkimTokensFromSES(\n domain: string,\n region: string\n): Promise<string[]> {\n try {\n const { SESv2Client, GetEmailIdentityCommand } = await import(\n \"@aws-sdk/client-sesv2\"\n );\n const ses = new SESv2Client({ region });\n\n const response = await ses.send(\n new GetEmailIdentityCommand({ EmailIdentity: domain })\n );\n\n return response.DkimAttributes?.Tokens || [];\n } catch (_error) {\n return [];\n }\n}\n\n/**\n * Email Destroy command - Remove email infrastructure\n */\nexport async function emailDestroy(options: DestroyOptions): Promise<void> {\n const startTime = Date.now();\n\n clack.intro(\n pc.bold(\n options.preview\n ? \"Email Infrastructure Destruction Preview\"\n : \"Email Infrastructure Teardown\"\n )\n );\n\n const progress = new DeploymentProgress();\n\n // 1. Validate AWS credentials\n const identity = await progress.execute(\n \"Validating AWS credentials\",\n async () => validateAWSCredentials()\n );\n\n // 2. Get region\n const region = await getAWSRegion();\n\n // 3. Load connection metadata to get domain info and stack name\n const metadata = await loadConnectionMetadata(identity.accountId, region);\n const emailService = metadata?.services?.email;\n const emailConfig = emailService?.config;\n const domain = emailConfig?.domain;\n const storedStackName = emailService?.pulumiStackName;\n\n // 4. Confirm destruction (skip if --force or --preview)\n if (!(options.force || options.preview)) {\n const confirmed = await clack.confirm({\n message: pc.red(\n \"Are you sure you want to destroy all email infrastructure?\"\n ),\n initialValue: false,\n });\n\n if (clack.isCancel(confirmed) || !confirmed) {\n clack.cancel(\"Destruction cancelled.\");\n process.exit(0);\n }\n }\n\n // 5. Check for Route53 hosted zone and offer to clean up DNS\n let shouldCleanDNS = false;\n let hostedZone: { id: string; name: string } | null = null;\n let dkimTokens: string[] = [];\n\n if (domain && !options.preview) {\n hostedZone = await findHostedZone(domain, region);\n\n if (hostedZone) {\n // Get DKIM tokens from SES before we destroy\n dkimTokens = await getDkimTokensFromSES(domain, region);\n\n if (!options.force) {\n const cleanDNS = await clack.confirm({\n message: `Found Route53 hosted zone for ${pc.cyan(domain)}. Delete DNS records (DKIM, DMARC, MAIL FROM)?`,\n initialValue: true,\n });\n\n if (clack.isCancel(cleanDNS)) {\n clack.cancel(\"Destruction cancelled.\");\n process.exit(0);\n }\n\n shouldCleanDNS = cleanDNS;\n } else {\n shouldCleanDNS = true; // Auto-clean with --force\n }\n }\n }\n\n // 6. Preview or Destroy infrastructure using Pulumi\n if (options.preview) {\n // PREVIEW MODE - show what would be destroyed without actually destroying\n try {\n const previewResult = await progress.execute(\n \"Generating destruction preview\",\n async () => {\n await ensurePulumiWorkDir();\n\n // Use stored stack name from metadata, fallback to generated name\n const stackName =\n storedStackName || `wraps-email-${identity.accountId}-${region}`;\n\n // Try to select the stack\n let stack;\n try {\n stack = await pulumi.automation.LocalWorkspace.selectStack({\n stackName,\n workDir: getPulumiWorkDir(),\n });\n } catch (_error) {\n throw new Error(\"No email infrastructure found to preview\");\n }\n\n // Run preview to see what would be destroyed\n const result = await stack.preview({ diff: true });\n return result;\n }\n );\n\n // Display preview results\n displayPreview({\n changeSummary: previewResult.changeSummary,\n costEstimate: \"Monthly cost after destruction: $0.00\",\n commandName: \"wraps email destroy\",\n });\n\n // Show DNS cleanup info\n if (domain) {\n const previewHostedZone = await findHostedZone(domain, region);\n if (previewHostedZone) {\n clack.log.info(\n `DNS records in Route53 for ${pc.cyan(domain)} will also be deleted`\n );\n }\n }\n\n clack.outro(\n pc.green(\"Preview complete. Run without --preview to destroy.\")\n );\n\n // Track preview completion\n trackServiceRemoved(\"email\", {\n preview: true,\n duration_ms: Date.now() - startTime,\n });\n return;\n } catch (error: any) {\n progress.stop();\n if (error.message.includes(\"No email infrastructure found\")) {\n clack.log.warn(\"No email infrastructure found to preview\");\n process.exit(0);\n }\n trackError(\"PREVIEW_FAILED\", \"email destroy\", { step: \"preview\" });\n throw new Error(`Preview failed: ${error.message}`);\n }\n }\n\n // DESTROY MODE - actually remove infrastructure\n\n // 7. Clean up DNS records first (before destroying SES identity)\n if (shouldCleanDNS && hostedZone && domain && dkimTokens.length > 0) {\n try {\n await progress.execute(\n `Deleting DNS records for ${domain}`,\n async () => {\n await deleteDNSRecords(\n hostedZone.id,\n domain,\n dkimTokens,\n region,\n emailConfig?.tracking?.customRedirectDomain,\n emailConfig?.mailFromDomain\n );\n }\n );\n } catch (error: any) {\n clack.log.warn(`Could not delete DNS records: ${error.message}`);\n clack.log.info(\"You may need to delete them manually from Route53\");\n }\n }\n\n // 8. Destroy Pulumi infrastructure\n try {\n await progress.execute(\n \"Destroying email infrastructure (this may take 2-3 minutes)\",\n async () => {\n // Ensure Pulumi workspace directory exists\n await ensurePulumiWorkDir();\n\n // Use stored stack name from metadata, fallback to generated name\n const stackName =\n storedStackName || `wraps-email-${identity.accountId}-${region}`;\n\n // Try to select the stack\n let stack;\n try {\n stack = await pulumi.automation.LocalWorkspace.selectStack({\n stackName,\n workDir: getPulumiWorkDir(),\n });\n } catch (_error) {\n throw new Error(\"No email infrastructure found to destroy\");\n }\n\n // Run destroy\n await stack.destroy({ onOutput: () => {} }); // Suppress Pulumi output\n\n // Remove the stack from workspace\n await stack.workspace.removeStack(stackName);\n }\n );\n } catch (error: any) {\n progress.stop();\n if (error.message.includes(\"No email infrastructure found\")) {\n clack.log.warn(\"No email infrastructure found\");\n // Still delete metadata if it exists\n await deleteConnectionMetadata(identity.accountId, region);\n process.exit(0);\n }\n trackError(\"DESTROY_FAILED\", \"email destroy\", { step: \"destroy\" });\n clack.log.error(\"Email infrastructure destruction failed\");\n throw error;\n }\n\n // 9. Delete connection metadata\n await deleteConnectionMetadata(identity.accountId, region);\n\n // 10. Display success message\n progress.stop();\n\n const deletedItems = [\"AWS infrastructure\"];\n if (shouldCleanDNS && hostedZone) {\n deletedItems.push(\"Route53 DNS records\");\n }\n\n clack.outro(pc.green(`Email infrastructure has been removed`));\n\n if (domain) {\n console.log(`\\n${pc.bold(\"Cleaned up:\")}`);\n for (const item of deletedItems) {\n console.log(` ${pc.green(\"✓\")} ${item}`);\n }\n\n // Remind about SPF record\n console.log(\n `\\n${pc.dim(\"Note: SPF record was not deleted. Remove 'include:amazonses.com' manually if needed.\")}`\n );\n }\n\n console.log(\n `\\nRun ${pc.cyan(\"wraps email init\")} to deploy infrastructure again.\\n`\n );\n\n // 11. Track successful destruction\n trackServiceRemoved(\"email\", {\n reason: \"user_initiated\",\n duration_ms: Date.now() - startTime,\n dns_cleaned: shouldCleanDNS,\n });\n}\n","import {\n type Change,\n ChangeResourceRecordSetsCommand,\n ListHostedZonesByNameCommand,\n ListResourceRecordSetsCommand,\n Route53Client,\n} from \"@aws-sdk/client-route-53\";\n\n/**\n * Get existing TXT records for a domain\n * Returns all TXT record values and identifies which one is SPF\n */\nasync function getExistingTXTRecords(\n client: Route53Client,\n hostedZoneId: string,\n domain: string\n): Promise<{ allValues: string[]; spfValue: string | null; ttl: number }> {\n try {\n const response = await client.send(\n new ListResourceRecordSetsCommand({\n HostedZoneId: hostedZoneId,\n StartRecordName: domain,\n StartRecordType: \"TXT\",\n MaxItems: 100,\n })\n );\n\n // Find TXT records for the exact domain\n const txtRecordSet = response.ResourceRecordSets?.find(\n (rs) =>\n rs.Type === \"TXT\" &&\n (rs.Name === domain || rs.Name === `${domain}.`)\n );\n\n if (!txtRecordSet || !txtRecordSet.ResourceRecords) {\n return { allValues: [], spfValue: null, ttl: 1800 };\n }\n\n const allValues: string[] = [];\n let spfValue: string | null = null;\n\n for (const record of txtRecordSet.ResourceRecords) {\n const value = record.Value || \"\";\n allValues.push(value);\n\n // Check if this is the SPF record (strip quotes for comparison)\n const unquoted = value.replace(/^\"|\"$/g, \"\");\n if (unquoted.startsWith(\"v=spf1\")) {\n spfValue = unquoted;\n }\n }\n\n return {\n allValues,\n spfValue,\n ttl: txtRecordSet.TTL || 1800,\n };\n } catch (_error) {\n return { allValues: [], spfValue: null, ttl: 1800 };\n }\n}\n\n/**\n * Merge amazonses.com include into an existing SPF record\n * If the record already includes amazonses.com, returns unchanged\n */\nfunction mergeSPFRecord(existingSPF: string): string {\n const sesInclude = \"include:amazonses.com\";\n\n // Already includes SES\n if (existingSPF.includes(sesInclude)) {\n return existingSPF;\n }\n\n // Find the position before the \"all\" mechanism to insert our include\n // SPF format: v=spf1 [mechanisms...] [qualifier]all\n const allMatch = existingSPF.match(/\\s([~+?-]?all)$/);\n\n if (allMatch) {\n // Insert before the \"all\" mechanism\n const beforeAll = existingSPF.slice(0, allMatch.index);\n const allPart = allMatch[1];\n return `${beforeAll} ${sesInclude} ${allPart}`;\n }\n\n // No \"all\" mechanism found, append to end\n return `${existingSPF} ${sesInclude}`;\n}\n\n/**\n * Find Route53 hosted zone for a domain\n */\nexport async function findHostedZone(\n domain: string,\n region: string\n): Promise<{ id: string; name: string } | null> {\n const client = new Route53Client({ region });\n\n try {\n const response = await client.send(\n new ListHostedZonesByNameCommand({\n DNSName: domain,\n MaxItems: 1,\n })\n );\n\n const zone = response.HostedZones?.[0];\n if (zone && zone.Name === `${domain}.` && zone.Id) {\n return {\n id: zone.Id.replace(\"/hostedzone/\", \"\"),\n name: zone.Name,\n };\n }\n\n return null;\n } catch (_error) {\n return null;\n }\n}\n\n/**\n * Create DNS records in Route53\n */\nexport async function createDNSRecords(\n hostedZoneId: string,\n domain: string,\n dkimTokens: string[],\n region: string,\n customTrackingDomain?: string,\n mailFromDomain?: string\n): Promise<void> {\n const client = new Route53Client({ region });\n\n const changes: Change[] = [];\n\n // DKIM CNAME records\n for (const token of dkimTokens) {\n changes.push({\n Action: \"UPSERT\",\n ResourceRecordSet: {\n Name: `${token}._domainkey.${domain}`,\n Type: \"CNAME\",\n TTL: 1800,\n ResourceRecords: [{ Value: `${token}.dkim.amazonses.com` }],\n },\n });\n }\n\n // SPF TXT record - check for existing records and merge while preserving others\n const existingTXT = await getExistingTXTRecords(client, hostedZoneId, domain);\n\n // Build the new TXT record values, preserving all non-SPF records\n const newTXTValues: string[] = [];\n\n if (existingTXT.spfValue) {\n // Merge our include into the existing SPF record\n const mergedSPF = mergeSPFRecord(existingTXT.spfValue);\n newTXTValues.push(`\"${mergedSPF}\"`);\n\n // Add all other TXT values (non-SPF)\n for (const value of existingTXT.allValues) {\n const unquoted = value.replace(/^\"|\"$/g, \"\");\n if (!unquoted.startsWith(\"v=spf1\")) {\n newTXTValues.push(value);\n }\n }\n } else if (existingTXT.allValues.length > 0) {\n // No SPF exists, add new SPF and keep all existing values\n newTXTValues.push('\"v=spf1 include:amazonses.com ~all\"');\n newTXTValues.push(...existingTXT.allValues);\n } else {\n // No TXT records exist, create new SPF\n newTXTValues.push('\"v=spf1 include:amazonses.com ~all\"');\n }\n\n changes.push({\n Action: \"UPSERT\",\n ResourceRecordSet: {\n Name: domain,\n Type: \"TXT\",\n TTL: existingTXT.ttl,\n ResourceRecords: newTXTValues.map((v) => ({ Value: v })),\n },\n });\n\n // DMARC TXT record\n // Use MAIL FROM domain for rua if configured, otherwise use main domain\n const dmarcRuaDomain = mailFromDomain || domain;\n changes.push({\n Action: \"UPSERT\",\n ResourceRecordSet: {\n Name: `_dmarc.${domain}`,\n Type: \"TXT\",\n TTL: 1800,\n ResourceRecords: [\n { Value: `\"v=DMARC1; p=quarantine; rua=mailto:postmaster@${dmarcRuaDomain}\"` },\n ],\n },\n });\n\n // Custom tracking domain CNAME (if provided)\n // This allows SES to rewrite links for open/click tracking using your custom domain\n if (customTrackingDomain) {\n changes.push({\n Action: \"UPSERT\",\n ResourceRecordSet: {\n Name: customTrackingDomain,\n Type: \"CNAME\",\n TTL: 1800,\n ResourceRecords: [{ Value: `r.${region}.awstrack.me` }],\n },\n });\n }\n\n // MAIL FROM domain records (if provided)\n // These records enable DMARC alignment by using a custom subdomain for the envelope sender\n if (mailFromDomain) {\n // MX record pointing to SES feedback server\n changes.push({\n Action: \"UPSERT\",\n ResourceRecordSet: {\n Name: mailFromDomain,\n Type: \"MX\",\n TTL: 1800,\n ResourceRecords: [\n { Value: `10 feedback-smtp.${region}.amazonses.com` },\n ],\n },\n });\n\n // SPF record for MAIL FROM domain\n changes.push({\n Action: \"UPSERT\",\n ResourceRecordSet: {\n Name: mailFromDomain,\n Type: \"TXT\",\n TTL: 1800,\n ResourceRecords: [{ Value: '\"v=spf1 include:amazonses.com ~all\"' }],\n },\n });\n }\n\n await client.send(\n new ChangeResourceRecordSetsCommand({\n HostedZoneId: hostedZoneId,\n ChangeBatch: {\n Changes: changes,\n },\n })\n );\n}\n\n/**\n * Delete DNS records from Route53 that were created for SES\n */\nexport async function deleteDNSRecords(\n hostedZoneId: string,\n domain: string,\n dkimTokens: string[],\n region: string,\n customTrackingDomain?: string,\n mailFromDomain?: string\n): Promise<void> {\n const client = new Route53Client({ region });\n\n // First, we need to get the current record values to delete them\n // Route53 DELETE requires exact match of the record\n const response = await client.send(\n new ListResourceRecordSetsCommand({\n HostedZoneId: hostedZoneId,\n MaxItems: 500,\n })\n );\n\n const recordSets = response.ResourceRecordSets || [];\n const changes: Change[] = [];\n\n // Helper to find and add deletion for a record\n const addDeletionIfExists = (name: string, type: string) => {\n // Route53 names end with a dot\n const normalizedName = name.endsWith(\".\") ? name : `${name}.`;\n const record = recordSets.find(\n (rs) => rs.Name === normalizedName && rs.Type === type\n );\n if (record && record.ResourceRecords) {\n changes.push({\n Action: \"DELETE\",\n ResourceRecordSet: record,\n });\n }\n };\n\n // DKIM CNAME records\n for (const token of dkimTokens) {\n addDeletionIfExists(`${token}._domainkey.${domain}`, \"CNAME\");\n }\n\n // DMARC record\n addDeletionIfExists(`_dmarc.${domain}`, \"TXT\");\n\n // Custom tracking domain CNAME\n if (customTrackingDomain) {\n addDeletionIfExists(customTrackingDomain, \"CNAME\");\n }\n\n // MAIL FROM domain records\n if (mailFromDomain) {\n addDeletionIfExists(mailFromDomain, \"MX\");\n addDeletionIfExists(mailFromDomain, \"TXT\");\n }\n\n // Note: We don't delete the main domain SPF record as it might contain\n // other providers' includes. Users should manually remove amazonses.com\n // from their SPF if needed.\n\n if (changes.length === 0) {\n return; // Nothing to delete\n }\n\n await client.send(\n new ChangeResourceRecordSetsCommand({\n HostedZoneId: hostedZoneId,\n ChangeBatch: {\n Changes: changes,\n },\n })\n );\n}\n","import { Resolver } from \"node:dns/promises\";\nimport { GetEmailIdentityCommand, SESv2Client } from \"@aws-sdk/client-sesv2\";\nimport * as clack from \"@clack/prompts\";\nimport pc from \"picocolors\";\nimport { trackCommand, trackFeature } from \"../../telemetry/events.js\";\nimport type { EmailVerifyOptions } from \"../../types/index.js\";\nimport { getAWSRegion } from \"../../utils/shared/aws.js\";\nimport { DeploymentProgress } from \"../../utils/shared/output.js\";\n\n/**\n * Verify domain DNS records and verification status\n */\nexport async function verifyDomain(options: EmailVerifyOptions): Promise<void> {\n clack.intro(pc.bold(`Verifying ${options.domain}`));\n\n const progress = new DeploymentProgress();\n const region = await getAWSRegion();\n\n // 1. Check SES verification status\n const sesClient = new SESv2Client({ region });\n let identity;\n let dkimTokens: string[] = [];\n let mailFromDomain: string | undefined;\n\n try {\n identity = await progress.execute(\n \"Checking SES verification status\",\n async () => {\n const response = await sesClient.send(\n new GetEmailIdentityCommand({ EmailIdentity: options.domain })\n );\n return response;\n }\n );\n\n dkimTokens = identity.DkimAttributes?.Tokens || [];\n mailFromDomain = identity.MailFromAttributes?.MailFromDomain;\n } catch (_error: any) {\n progress.stop();\n clack.log.error(`Domain ${options.domain} not found in SES`);\n console.log(\n `\\nRun ${pc.cyan(`wraps email init --domain ${options.domain}`)} to add this domain.\\n`\n );\n process.exit(1);\n return; // Return after process.exit for testing\n }\n\n // 2. Check DNS records\n const resolver = new Resolver();\n // Use public DNS servers for more reliable results\n resolver.setServers([\"8.8.8.8\", \"1.1.1.1\"]);\n const dnsResults: Array<{\n name: string;\n type: string;\n status: string;\n records?: string[];\n }> = [];\n\n // Check DKIM records\n for (const token of dkimTokens) {\n const dkimRecord = `${token}._domainkey.${options.domain}`;\n try {\n const records = await resolver.resolveCname(dkimRecord);\n const expected = `${token}.dkim.amazonses.com`;\n const found = records.some((r) => r === expected || r === `${expected}.`);\n dnsResults.push({\n name: dkimRecord,\n type: \"CNAME\",\n status: found ? \"verified\" : \"incorrect\",\n records,\n });\n } catch (_error) {\n dnsResults.push({\n name: dkimRecord,\n type: \"CNAME\",\n status: \"missing\",\n });\n }\n }\n\n // Check SPF record\n try {\n const records = await resolver.resolveTxt(options.domain);\n const spfRecord = records.flat().find((r) => r.startsWith(\"v=spf1\"));\n const hasAmazonSES = spfRecord?.includes(\"include:amazonses.com\");\n dnsResults.push({\n name: options.domain,\n type: \"TXT (SPF)\",\n status: hasAmazonSES ? \"verified\" : spfRecord ? \"incorrect\" : \"missing\",\n records: spfRecord ? [spfRecord] : undefined,\n });\n } catch (_error) {\n dnsResults.push({\n name: options.domain,\n type: \"TXT (SPF)\",\n status: \"missing\",\n });\n }\n\n // Check DMARC record\n try {\n const records = await resolver.resolveTxt(`_dmarc.${options.domain}`);\n const dmarcRecord = records.flat().find((r) => r.startsWith(\"v=DMARC1\"));\n dnsResults.push({\n name: `_dmarc.${options.domain}`,\n type: \"TXT (DMARC)\",\n status: dmarcRecord ? \"verified\" : \"missing\",\n records: dmarcRecord ? [dmarcRecord] : undefined,\n });\n } catch (_error) {\n dnsResults.push({\n name: `_dmarc.${options.domain}`,\n type: \"TXT (DMARC)\",\n status: \"missing\",\n });\n }\n\n // Check MAIL FROM domain records (if configured)\n if (mailFromDomain) {\n // Check MX record for MAIL FROM domain\n try {\n const mxRecords = await resolver.resolveMx(mailFromDomain);\n const expectedMx = `feedback-smtp.${region}.amazonses.com`;\n const hasMx = mxRecords.some(\n (r) => r.exchange === expectedMx || r.exchange === `${expectedMx}.`\n );\n dnsResults.push({\n name: mailFromDomain,\n type: \"MX\",\n status: hasMx\n ? \"verified\"\n : mxRecords.length > 0\n ? \"incorrect\"\n : \"missing\",\n records: mxRecords.map((r) => `${r.priority} ${r.exchange}`),\n });\n } catch (_error) {\n dnsResults.push({\n name: mailFromDomain,\n type: \"MX\",\n status: \"missing\",\n });\n }\n\n // Check SPF record for MAIL FROM domain\n try {\n const records = await resolver.resolveTxt(mailFromDomain);\n const spfRecord = records.flat().find((r) => r.startsWith(\"v=spf1\"));\n const hasAmazonSES = spfRecord?.includes(\"include:amazonses.com\");\n dnsResults.push({\n name: mailFromDomain,\n type: \"TXT (SPF)\",\n status: hasAmazonSES ? \"verified\" : spfRecord ? \"incorrect\" : \"missing\",\n records: spfRecord ? [spfRecord] : undefined,\n });\n } catch (_error) {\n dnsResults.push({\n name: mailFromDomain,\n type: \"TXT (SPF)\",\n status: \"missing\",\n });\n }\n }\n\n progress.stop();\n\n // 3. Display results\n const verificationStatus = identity.VerifiedForSendingStatus\n ? \"verified\"\n : \"pending\";\n const dkimStatus = identity.DkimAttributes?.Status || \"PENDING\";\n const mailFromStatus =\n identity.MailFromAttributes?.MailFromDomainStatus || \"NOT_CONFIGURED\";\n\n const statusLines = [\n `${pc.bold(\"Domain:\")} ${options.domain}`,\n `${pc.bold(\"Verification Status:\")} ${\n verificationStatus === \"verified\"\n ? pc.green(\"✓ Verified\")\n : pc.yellow(\"⏱ Pending\")\n }`,\n `${pc.bold(\"DKIM Status:\")} ${\n dkimStatus === \"SUCCESS\"\n ? pc.green(\"✓ Success\")\n : pc.yellow(`⏱ ${dkimStatus}`)\n }`,\n ];\n\n if (mailFromDomain) {\n statusLines.push(\n `${pc.bold(\"MAIL FROM Domain:\")} ${mailFromDomain}`,\n `${pc.bold(\"MAIL FROM Status:\")} ${\n mailFromStatus === \"SUCCESS\"\n ? pc.green(\"✓ Success\")\n : mailFromStatus === \"NOT_CONFIGURED\"\n ? pc.yellow(\"⏱ Not Configured\")\n : pc.yellow(`⏱ ${mailFromStatus}`)\n }`\n );\n }\n\n clack.note(statusLines.join(\"\\n\"), \"SES Status\");\n\n // DNS Records\n const dnsLines = dnsResults.map((record) => {\n let statusIcon: string;\n let statusColor: (s: string) => string;\n\n if (record.status === \"verified\") {\n statusIcon = \"✓\";\n statusColor = pc.green;\n } else if (record.status === \"incorrect\") {\n statusIcon = \"✗\";\n statusColor = pc.red;\n } else {\n statusIcon = \"✗\";\n statusColor = pc.red;\n }\n\n const recordInfo = record.records ? ` → ${record.records.join(\", \")}` : \"\";\n return ` ${statusColor(statusIcon)} ${record.name} (${record.type}) ${statusColor(\n record.status\n )}${recordInfo}`;\n });\n\n clack.note(dnsLines.join(\"\\n\"), \"DNS Records\");\n\n // Summary\n const allVerified = dnsResults.every((r) => r.status === \"verified\");\n const someIncorrect = dnsResults.some((r) => r.status === \"incorrect\");\n\n if (verificationStatus === \"verified\" && allVerified) {\n clack.outro(\n pc.green(\"✓ Domain is fully verified and ready to send emails!\")\n );\n trackFeature(\"domain_verified\", { dns_auto_detected: true });\n } else if (someIncorrect) {\n clack.outro(\n pc.red(\"✗ Some DNS records are incorrect. Please update them.\")\n );\n console.log(\n `\\nRun ${pc.cyan(\"wraps email status\")} to see the correct DNS records.\\n`\n );\n } else {\n clack.outro(\n pc.yellow(\"⏱ Waiting for DNS propagation and SES verification\")\n );\n console.log(\"\\nDNS records can take up to 48 hours to propagate.\");\n console.log(\n \"SES verification usually completes within 72 hours after DNS propagation.\\n\"\n );\n }\n\n // Track verify command\n trackCommand(\"email:domains:verify\", {\n success: true,\n verified: verificationStatus === \"verified\" && allVerified,\n dkim_status: dkimStatus,\n });\n}\n\n/**\n * Add a domain to SES for email sending\n */\nexport async function addDomain(options: { domain: string }): Promise<void> {\n clack.intro(pc.bold(`Adding domain ${options.domain} to SES`));\n\n const progress = new DeploymentProgress();\n const region = await getAWSRegion();\n const sesClient = new SESv2Client({ region });\n\n try {\n // Check if domain already exists\n try {\n await sesClient.send(\n new GetEmailIdentityCommand({ EmailIdentity: options.domain })\n );\n progress.stop();\n clack.log.warn(`Domain ${options.domain} already exists in SES`);\n console.log(\n `\\nRun ${pc.cyan(`wraps email domains verify --domain ${options.domain}`)} to check verification status.\\n`\n );\n return;\n } catch (error: any) {\n // Domain doesn't exist, continue with creation\n if (error.name !== \"NotFoundException\") {\n throw error;\n }\n }\n\n // Create the email identity\n const { CreateEmailIdentityCommand } = await import(\n \"@aws-sdk/client-sesv2\"\n );\n await progress.execute(\"Adding domain to SES\", async () => {\n await sesClient.send(\n new CreateEmailIdentityCommand({\n EmailIdentity: options.domain,\n DkimSigningAttributes: {\n NextSigningKeyLength: \"RSA_2048_BIT\",\n },\n })\n );\n });\n\n // Get the DKIM tokens\n const identity = await sesClient.send(\n new GetEmailIdentityCommand({ EmailIdentity: options.domain })\n );\n const dkimTokens = identity.DkimAttributes?.Tokens || [];\n\n progress.stop();\n\n clack.outro(pc.green(`✓ Domain ${options.domain} added successfully!`));\n\n // Show next steps\n console.log(`\\n${pc.bold(\"Next steps:\")}\\n`);\n console.log(\"1. Add the following DKIM records to your DNS:\\n\");\n\n for (const token of dkimTokens) {\n console.log(` ${pc.cyan(`${token}._domainkey.${options.domain}`)}`);\n console.log(\n ` ${pc.dim(\"Type:\")} CNAME ${pc.dim(\"Value:\")} ${token}.dkim.amazonses.com\\n`\n );\n }\n\n console.log(\n `2. Verify DNS propagation: ${pc.cyan(`wraps email domains verify --domain ${options.domain}`)}`\n );\n console.log(`3. Check status: ${pc.cyan(\"wraps email status\")}\\n`);\n\n // Track add domain success\n trackCommand(\"email:domains:add\", {\n success: true,\n });\n trackFeature(\"domain_added\", {});\n } catch (error: any) {\n progress.stop();\n trackCommand(\"email:domains:add\", {\n success: false,\n });\n throw error;\n }\n}\n\n/**\n * List all domains configured in SES\n */\nexport async function listDomains(): Promise<void> {\n clack.intro(pc.bold(\"SES Email Domains\"));\n\n const progress = new DeploymentProgress();\n const region = await getAWSRegion();\n const sesClient = new SESv2Client({ region });\n\n try {\n const { ListEmailIdentitiesCommand } = await import(\n \"@aws-sdk/client-sesv2\"\n );\n\n const identities = await progress.execute(\n \"Loading domains from SES\",\n async () => {\n const response = await sesClient.send(\n new ListEmailIdentitiesCommand({})\n );\n return response.EmailIdentities || [];\n }\n );\n\n // Filter to only domains (not email addresses)\n const domains = identities.filter(\n (identity) =>\n identity.IdentityType === \"DOMAIN\" ||\n (identity.IdentityName && !identity.IdentityName.includes(\"@\"))\n );\n\n progress.stop();\n\n if (domains.length === 0) {\n clack.outro(\"No domains found in SES\");\n console.log(\n `\\nRun ${pc.cyan(\"wraps email domains add <domain>\")} to add a domain.\\n`\n );\n return;\n }\n\n // Get detailed info for each domain\n const domainDetails = await Promise.all(\n domains.map(async (domain) => {\n try {\n const details = await sesClient.send(\n new GetEmailIdentityCommand({\n EmailIdentity: domain.IdentityName!,\n })\n );\n return {\n name: domain.IdentityName!,\n verified: details.VerifiedForSendingStatus,\n dkimStatus: details.DkimAttributes?.Status || \"PENDING\",\n };\n } catch {\n return {\n name: domain.IdentityName!,\n verified: false,\n dkimStatus: \"UNKNOWN\",\n };\n }\n })\n );\n\n // Display domains in a formatted table\n const domainLines = domainDetails.map((domain) => {\n const statusIcon = domain.verified ? pc.green(\"✓\") : pc.yellow(\"⏱\");\n const dkimIcon =\n domain.dkimStatus === \"SUCCESS\" ? pc.green(\"✓\") : pc.yellow(\"⏱\");\n return ` ${statusIcon} ${pc.bold(domain.name)} DKIM: ${dkimIcon} ${domain.dkimStatus}`;\n });\n\n clack.note(\n domainLines.join(\"\\n\"),\n `${domains.length} domain(s) in ${region}`\n );\n clack.outro(\n pc.dim(\n `Run ${pc.cyan(\"wraps email domains verify --domain <domain>\")} for details`\n )\n );\n\n // Track list domains success\n trackCommand(\"email:domains:list\", {\n success: true,\n domain_count: domains.length,\n });\n } catch (error: any) {\n progress.stop();\n trackCommand(\"email:domains:list\", { success: false });\n throw error;\n }\n}\n\n/**\n * Get DKIM tokens for a domain\n */\nexport async function getDkim(options: { domain: string }): Promise<void> {\n clack.intro(pc.bold(`DKIM Tokens for ${options.domain}`));\n\n const progress = new DeploymentProgress();\n const region = await getAWSRegion();\n const sesClient = new SESv2Client({ region });\n\n try {\n const identity = await progress.execute(\n \"Fetching DKIM configuration\",\n async () => {\n const response = await sesClient.send(\n new GetEmailIdentityCommand({ EmailIdentity: options.domain })\n );\n return response;\n }\n );\n\n const dkimTokens = identity.DkimAttributes?.Tokens || [];\n const dkimStatus = identity.DkimAttributes?.Status || \"PENDING\";\n\n progress.stop();\n\n if (dkimTokens.length === 0) {\n clack.outro(pc.yellow(\"No DKIM tokens found for this domain\"));\n return;\n }\n\n // Display DKIM status\n const statusLine = `${pc.bold(\"DKIM Status:\")} ${\n dkimStatus === \"SUCCESS\"\n ? pc.green(\"✓ Verified\")\n : pc.yellow(`⏱ ${dkimStatus}`)\n }`;\n clack.note(statusLine, \"Status\");\n\n // Display DKIM records\n console.log(`\\n${pc.bold(\"DNS Records to add:\")}\\n`);\n for (const token of dkimTokens) {\n console.log(`${pc.cyan(`${token}._domainkey.${options.domain}`)}`);\n console.log(` ${pc.dim(\"Type:\")} CNAME`);\n console.log(` ${pc.dim(\"Value:\")} ${token}.dkim.amazonses.com\\n`);\n }\n\n if (dkimStatus !== \"SUCCESS\") {\n console.log(\n `${pc.dim(\"After adding these records, run:\")} ${pc.cyan(`wraps email domains verify --domain ${options.domain}`)}\\n`\n );\n }\n\n // Track get-dkim success\n trackCommand(\"email:domains:get-dkim\", {\n success: true,\n dkim_status: dkimStatus,\n });\n } catch (error: any) {\n progress.stop();\n trackCommand(\"email:domains:get-dkim\", { success: false });\n if (error.name === \"NotFoundException\") {\n clack.log.error(`Domain ${options.domain} not found in SES`);\n console.log(\n `\\nRun ${pc.cyan(`wraps email domains add ${options.domain}`)} to add this domain.\\n`\n );\n process.exit(1);\n return; // Return after process.exit for testing\n }\n throw error;\n }\n}\n\n/**\n * Remove a domain from SES\n */\nexport async function removeDomain(options: {\n domain: string;\n force?: boolean; // Destructive operation\n}): Promise<void> {\n clack.intro(pc.bold(`Remove domain ${options.domain} from SES`));\n\n const progress = new DeploymentProgress();\n const region = await getAWSRegion();\n const sesClient = new SESv2Client({ region });\n\n try {\n // Check if domain exists\n await progress.execute(\"Checking if domain exists\", async () => {\n await sesClient.send(\n new GetEmailIdentityCommand({ EmailIdentity: options.domain })\n );\n });\n\n progress.stop();\n\n // Confirm deletion\n if (!options.force) {\n const shouldContinue = await clack.confirm({\n message: `Are you sure you want to remove ${pc.red(options.domain)} from SES?`,\n initialValue: false,\n });\n\n if (clack.isCancel(shouldContinue) || !shouldContinue) {\n clack.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n }\n\n // Delete the identity\n const { DeleteEmailIdentityCommand } = await import(\n \"@aws-sdk/client-sesv2\"\n );\n await progress.execute(\"Removing domain from SES\", async () => {\n await sesClient.send(\n new DeleteEmailIdentityCommand({\n EmailIdentity: options.domain,\n })\n );\n });\n\n progress.stop();\n clack.outro(pc.green(`✓ Domain ${options.domain} removed successfully`));\n\n // Track remove domain success\n trackCommand(\"email:domains:remove\", {\n success: true,\n });\n trackFeature(\"domain_removed\", {});\n } catch (error: any) {\n progress.stop();\n trackCommand(\"email:domains:remove\", { success: false });\n if (error.name === \"NotFoundException\") {\n clack.log.error(`Domain ${options.domain} not found in SES`);\n process.exit(1);\n return; // Return after process.exit for testing\n }\n throw error;\n }\n}\n","import * as clack from \"@clack/prompts\";\nimport * as pulumi from \"@pulumi/pulumi\";\nimport pc from \"picocolors\";\nimport { deployEmailStack } from \"../../infrastructure/email-stack.js\";\nimport {\n trackError,\n trackServiceDeployed,\n trackServiceInit,\n} from \"../../telemetry/events.js\";\nimport type {\n EmailStackConfig,\n InitOptions,\n WrapsEmailConfig,\n} from \"../../types/index.js\";\nimport { getCostSummary } from \"../../utils/email/costs.js\";\nimport { getPreset, validateConfig } from \"../../utils/email/presets.js\";\nimport {\n getAWSRegion,\n validateAWSCredentials,\n} from \"../../utils/shared/aws.js\";\nimport { errors } from \"../../utils/shared/errors.js\";\nimport {\n ensurePulumiWorkDir,\n getPulumiWorkDir,\n} from \"../../utils/shared/fs.js\";\nimport {\n createConnectionMetadata,\n loadConnectionMetadata,\n saveConnectionMetadata,\n} from \"../../utils/shared/metadata.js\";\nimport {\n DeploymentProgress,\n displayPreview,\n displaySuccess,\n} from \"../../utils/shared/output.js\";\nimport {\n confirmDeploy,\n promptConfigPreset,\n promptCustomConfig,\n promptDomain,\n promptEstimatedVolume,\n promptProvider,\n promptRegion,\n promptVercelConfig,\n} from \"../../utils/shared/prompts.js\";\nimport { ensurePulumiInstalled } from \"../../utils/shared/pulumi.js\";\n\n/**\n * Init command - Deploy new email infrastructure\n */\nexport async function init(options: InitOptions): Promise<void> {\n const startTime = Date.now();\n\n clack.intro(\n pc.bold(\n options.preview\n ? \"Wraps Email Infrastructure Preview\"\n : \"Wraps Email Infrastructure Setup\"\n )\n );\n\n const progress = new DeploymentProgress();\n\n // 1. Check Pulumi CLI is installed (auto-install if missing)\n const wasAutoInstalled = await progress.execute(\n \"Checking Pulumi CLI installation\",\n async () => await ensurePulumiInstalled()\n );\n\n if (wasAutoInstalled) {\n progress.info(\"Pulumi CLI was automatically installed\");\n }\n\n // 2. Validate AWS credentials\n const identity = await progress.execute(\n \"Validating AWS credentials\",\n async () => validateAWSCredentials()\n );\n\n progress.info(`Connected to AWS account: ${pc.cyan(identity.accountId)}`);\n\n // 3. Get configuration (from options or prompts)\n let provider = options.provider;\n if (!provider) {\n provider = await promptProvider();\n }\n\n let region = options.region;\n if (!region) {\n const defaultRegion = await getAWSRegion();\n region = await promptRegion(defaultRegion);\n }\n\n let domain = options.domain;\n if (!domain) {\n domain = await promptDomain();\n }\n\n // Get Vercel config if needed\n let vercelConfig;\n if (provider === \"vercel\") {\n vercelConfig = await promptVercelConfig();\n }\n\n // 4. Check if connection already exists\n const existingConnection = await loadConnectionMetadata(\n identity.accountId,\n region\n );\n if (existingConnection) {\n clack.log.warn(\n `Connection already exists for account ${pc.cyan(identity.accountId)} in region ${pc.cyan(region)}`\n );\n clack.log.info(`Created: ${existingConnection.timestamp}`);\n clack.log.info(`Use ${pc.cyan(\"wraps status\")} to view current setup`);\n clack.log.info(`Use ${pc.cyan(\"wraps upgrade\")} to add more features`);\n process.exit(0);\n }\n\n // 5. Configuration selection\n let preset = options.preset;\n if (!preset) {\n preset = await promptConfigPreset();\n }\n\n let emailConfig: WrapsEmailConfig;\n if (preset === \"custom\") {\n emailConfig = await promptCustomConfig();\n } else {\n emailConfig = getPreset(preset)!;\n\n // Prompt for email archiving (optional feature for presets)\n const { promptEmailArchiving } = await import(\n \"../../utils/shared/prompts.js\"\n );\n const archivingConfig = await promptEmailArchiving();\n emailConfig.emailArchiving = archivingConfig;\n }\n\n // Set domain if provided\n if (domain) {\n emailConfig.domain = domain;\n }\n\n // Get estimated volume for cost calculation\n const estimatedVolume = await promptEstimatedVolume();\n\n // Display cost summary\n progress.info(`\\n${pc.bold(\"Cost Estimate:\")}`);\n const costSummary = getCostSummary(emailConfig, estimatedVolume);\n clack.log.info(costSummary);\n\n // Validate configuration and show warnings\n const warnings = validateConfig(emailConfig);\n if (warnings.length > 0) {\n progress.info(`\\n${pc.yellow(pc.bold(\"Configuration Warnings:\"))}`);\n for (const warning of warnings) {\n clack.log.warn(warning);\n }\n }\n\n // 6. Create metadata to track deployment\n const metadata = createConnectionMetadata(\n identity.accountId,\n region,\n provider,\n emailConfig,\n preset === \"custom\" ? undefined : preset\n );\n if (vercelConfig) {\n metadata.vercel = vercelConfig;\n }\n\n // Confirm deployment (skip if --yes flag or --preview flag)\n if (!(options.yes || options.preview)) {\n const confirmed = await confirmDeploy();\n if (!confirmed) {\n clack.cancel(\"Deployment cancelled.\");\n process.exit(0);\n }\n }\n\n // 7. Build stack configuration\n const stackConfig: EmailStackConfig = {\n provider,\n region,\n vercel: vercelConfig,\n emailConfig,\n };\n\n // 8. Preview or Deploy infrastructure using Pulumi\n if (options.preview) {\n // PREVIEW MODE - show what would be created without deploying\n try {\n const previewResult = await progress.execute(\n \"Generating infrastructure preview\",\n async () => {\n await ensurePulumiWorkDir();\n\n const stack =\n await pulumi.automation.LocalWorkspace.createOrSelectStack(\n {\n stackName: `wraps-${identity.accountId}-${region}`,\n projectName: \"wraps-email\",\n program: async () => {\n const result = await deployEmailStack(stackConfig);\n return {\n roleArn: result.roleArn,\n configSetName: result.configSetName,\n tableName: result.tableName,\n region: result.region,\n lambdaFunctions: result.lambdaFunctions,\n domain: result.domain,\n dkimTokens: result.dkimTokens,\n customTrackingDomain: result.customTrackingDomain,\n mailFromDomain: result.mailFromDomain,\n archiveArn: result.archiveArn,\n archivingEnabled: result.archivingEnabled,\n archiveRetention: result.archiveRetention,\n };\n },\n },\n {\n workDir: getPulumiWorkDir(),\n envVars: {\n PULUMI_CONFIG_PASSPHRASE: \"\",\n AWS_REGION: region,\n },\n secretsProvider: \"passphrase\",\n }\n );\n\n await stack.setConfig(\"aws:region\", { value: region });\n\n // Run preview instead of deployment\n const result = await stack.preview({ diff: true });\n return result;\n }\n );\n\n // Display preview results\n displayPreview({\n changeSummary: previewResult.changeSummary,\n costEstimate: costSummary,\n commandName: \"wraps email init\",\n });\n\n clack.outro(\n pc.green(\"Preview complete. Run without --preview to deploy.\")\n );\n\n // Track preview completion\n trackServiceInit(\"email\", true, {\n preset,\n provider,\n preview: true,\n duration_ms: Date.now() - startTime,\n });\n return;\n } catch (error: any) {\n trackError(\"PREVIEW_FAILED\", \"email:init\", { step: \"preview\" });\n if (error.message?.includes(\"stack is currently locked\")) {\n throw errors.stackLocked();\n }\n throw new Error(`Preview failed: ${error.message}`);\n }\n }\n\n // DEPLOY MODE - actually create infrastructure\n let outputs;\n try {\n outputs = await progress.execute(\n \"Deploying infrastructure (this may take 2-3 minutes)\",\n async () => {\n // Ensure Pulumi workspace directory exists\n await ensurePulumiWorkDir();\n\n // Run Pulumi inline program with local backend (no cloud required)\n const stack =\n await pulumi.automation.LocalWorkspace.createOrSelectStack(\n {\n stackName: `wraps-${identity.accountId}-${region}`,\n projectName: \"wraps-email\",\n program: async () => {\n const result = await deployEmailStack(stackConfig);\n\n // Export outputs\n return {\n roleArn: result.roleArn,\n configSetName: result.configSetName,\n tableName: result.tableName,\n region: result.region,\n lambdaFunctions: result.lambdaFunctions,\n domain: result.domain,\n dkimTokens: result.dkimTokens,\n customTrackingDomain: result.customTrackingDomain,\n mailFromDomain: result.mailFromDomain,\n archiveArn: result.archiveArn,\n archivingEnabled: result.archivingEnabled,\n archiveRetention: result.archiveRetention,\n };\n },\n },\n {\n workDir: getPulumiWorkDir(),\n // Use local file-based backend (no Pulumi Cloud login required)\n envVars: {\n PULUMI_CONFIG_PASSPHRASE: \"\", // Use empty passphrase for local state\n AWS_REGION: region,\n },\n secretsProvider: \"passphrase\",\n }\n );\n\n // Set backend to local file system\n await stack.workspace.selectStack(\n `wraps-${identity.accountId}-${region}`\n );\n\n // Set AWS region\n await stack.setConfig(\"aws:region\", { value: region });\n\n // Run the deployment\n const upResult = await stack.up({ onOutput: () => {} }); // Suppress Pulumi output\n\n // Get outputs\n const pulumiOutputs = upResult.outputs;\n\n return {\n roleArn: pulumiOutputs.roleArn?.value as string,\n configSetName: pulumiOutputs.configSetName?.value as\n | string\n | undefined,\n tableName: pulumiOutputs.tableName?.value as string | undefined,\n region: pulumiOutputs.region?.value as string,\n lambdaFunctions: pulumiOutputs.lambdaFunctions?.value as\n | string[]\n | undefined,\n domain: pulumiOutputs.domain?.value as string | undefined,\n dkimTokens: pulumiOutputs.dkimTokens?.value as string[] | undefined,\n customTrackingDomain: pulumiOutputs.customTrackingDomain?.value as\n | string\n | undefined,\n mailFromDomain: pulumiOutputs.mailFromDomain?.value as\n | string\n | undefined,\n archiveArn: pulumiOutputs.archiveArn?.value as string | undefined,\n archivingEnabled: pulumiOutputs.archivingEnabled?.value as\n | boolean\n | undefined,\n archiveRetention: pulumiOutputs.archiveRetention?.value as\n | string\n | undefined,\n };\n }\n );\n } catch (error: any) {\n // Track deployment failure\n trackServiceInit(\"email\", false, {\n preset,\n provider,\n duration_ms: Date.now() - startTime,\n });\n\n // Check if it's a lock file error\n if (error.message?.includes(\"stack is currently locked\")) {\n trackError(\"STACK_LOCKED\", \"email:init\", { step: \"deploy\" });\n throw errors.stackLocked();\n }\n\n trackError(\"DEPLOYMENT_FAILED\", \"email:init\", { step: \"deploy\" });\n throw new Error(`Pulumi deployment failed: ${error.message}`);\n }\n\n // 9. Save metadata for future upgrades and restore\n if (metadata.services.email) {\n metadata.services.email.pulumiStackName = `wraps-${identity.accountId}-${region}`;\n }\n await saveConnectionMetadata(metadata);\n\n progress.info(\"Connection metadata saved for upgrade and restore capability\");\n\n // 10. Check if Route53 hosted zone exists and create DNS records automatically\n let dnsAutoCreated = false;\n if (outputs.domain && outputs.dkimTokens && outputs.dkimTokens.length > 0) {\n const { findHostedZone, createDNSRecords } = await import(\n \"../../utils/email/route53.js\"\n );\n const hostedZone = await findHostedZone(outputs.domain, region);\n\n if (hostedZone) {\n try {\n progress.start(\"Creating DNS records in Route53\");\n await createDNSRecords(\n hostedZone.id,\n outputs.domain,\n outputs.dkimTokens,\n region,\n outputs.customTrackingDomain,\n outputs.mailFromDomain\n );\n progress.succeed(\"DNS records created in Route53\");\n dnsAutoCreated = true;\n } catch (error: any) {\n progress.fail(\"Failed to create DNS records in Route53\");\n clack.log.warn(`Could not auto-create DNS records: ${error.message}`);\n }\n }\n }\n\n // 11. Format DNS records if domain was provided and DNS wasn't auto-created\n const dnsRecords = [];\n if (\n outputs.domain &&\n outputs.dkimTokens &&\n outputs.dkimTokens.length > 0 &&\n !dnsAutoCreated\n ) {\n // Add DKIM CNAME records\n for (const token of outputs.dkimTokens) {\n dnsRecords.push({\n name: `${token}._domainkey.${outputs.domain}`,\n type: \"CNAME\",\n value: `${token}.dkim.amazonses.com`,\n });\n }\n }\n\n // 12. Display success message\n displaySuccess({\n roleArn: outputs.roleArn,\n configSetName: outputs.configSetName,\n region: outputs.region!,\n tableName: outputs.tableName,\n dnsRecords: dnsRecords.length > 0 ? dnsRecords : undefined,\n dnsAutoCreated,\n domain: outputs.domain,\n mailFromDomain: outputs.mailFromDomain,\n });\n\n // 13. Track successful deployment\n const duration = Date.now() - startTime;\n const enabledFeatures: string[] = [];\n if (emailConfig.tracking?.enabled) enabledFeatures.push(\"tracking\");\n if (emailConfig.suppressionList?.enabled)\n enabledFeatures.push(\"suppression_list\");\n if (emailConfig.eventTracking?.enabled)\n enabledFeatures.push(\"event_tracking\");\n if (emailConfig.eventTracking?.dynamoDBHistory)\n enabledFeatures.push(\"dynamodb_history\");\n if (emailConfig.dedicatedIp) enabledFeatures.push(\"dedicated_ip\");\n if (emailConfig.emailArchiving?.enabled)\n enabledFeatures.push(\"email_archiving\");\n\n trackServiceInit(\"email\", true, {\n preset,\n provider,\n features: enabledFeatures,\n duration_ms: duration,\n });\n\n trackServiceDeployed(\"email\", {\n duration_ms: duration,\n features: enabledFeatures,\n preset,\n });\n}\n","import * as clack from \"@clack/prompts\";\nimport * as pulumi from \"@pulumi/pulumi\";\nimport pc from \"picocolors\";\nimport {\n trackError,\n trackServiceRemoved,\n} from \"../../telemetry/events.js\";\nimport type { EmailRestoreOptions } from \"../../types/index.js\";\nimport {\n getAWSRegion,\n validateAWSCredentials,\n} from \"../../utils/shared/aws.js\";\nimport { getPulumiWorkDir } from \"../../utils/shared/fs.js\";\nimport {\n deleteConnectionMetadata,\n loadConnectionMetadata,\n} from \"../../utils/shared/metadata.js\";\nimport {\n DeploymentProgress,\n displayPreview,\n} from \"../../utils/shared/output.js\";\n\n/**\n * Restore command - Remove Wraps infrastructure (alias for destroy)\n *\n * Note: This command removes all Wraps-managed resources.\n * Since Wraps always creates NEW resources (wraps- prefix) and never modifies\n * existing infrastructure, there's nothing to \"restore\" - only to remove.\n */\nexport async function restore(options: EmailRestoreOptions): Promise<void> {\n const startTime = Date.now();\n\n clack.intro(\n pc.bold(\n options.preview\n ? \"Wraps Restore Preview\"\n : \"Wraps Restore - Remove Wraps Infrastructure\"\n )\n );\n\n clack.log.info(\n `${pc.yellow(\"Note:\")} This will remove all Wraps-managed infrastructure.`\n );\n clack.log.info(\n \"Your original AWS resources remain untouched (Wraps never modifies them).\\n\"\n );\n\n const progress = new DeploymentProgress();\n\n // 1. Validate AWS credentials\n const identity = await progress.execute(\n \"Validating AWS credentials\",\n async () => validateAWSCredentials()\n );\n\n progress.info(`Connected to AWS account: ${pc.cyan(identity.accountId)}`);\n\n // 2. Get region\n let region = options.region;\n if (!region) {\n const defaultRegion = await getAWSRegion();\n region = defaultRegion;\n }\n\n // 3. Load connection metadata\n const metadata = await loadConnectionMetadata(identity.accountId, region);\n\n if (!metadata) {\n clack.log.error(\n `No Wraps connection found for account ${pc.cyan(identity.accountId)} in region ${pc.cyan(region)}`\n );\n clack.log.info(\n `Use ${pc.cyan(\"wraps email init\")} or ${pc.cyan(\"wraps email connect\")} to create a connection first.`\n );\n process.exit(1);\n }\n\n progress.info(`Found connection created: ${metadata.timestamp}`);\n\n // 4. Display what will be removed\n console.log(\n `\\n${pc.bold(\"The following Wraps resources will be removed:\")}\\n`\n );\n\n if (metadata.services.email?.config.tracking?.enabled) {\n console.log(` ${pc.cyan(\"✓\")} Configuration Set (wraps-email-tracking)`);\n }\n if (metadata.services.email?.config.eventTracking?.dynamoDBHistory) {\n console.log(` ${pc.cyan(\"✓\")} DynamoDB Table (wraps-email-history)`);\n }\n if (metadata.services.email?.config.eventTracking?.enabled) {\n console.log(` ${pc.cyan(\"✓\")} EventBridge Rules`);\n console.log(` ${pc.cyan(\"✓\")} SQS Queues`);\n console.log(` ${pc.cyan(\"✓\")} Lambda Functions`);\n }\n console.log(` ${pc.cyan(\"✓\")} IAM Role (wraps-email-role)`);\n console.log(\"\");\n\n // 5. Confirm removal (skip if --force or --preview)\n if (!(options.force || options.preview)) {\n const confirmed = await clack.confirm({\n message: \"Proceed with removal? This cannot be undone.\",\n initialValue: false,\n });\n\n if (clack.isCancel(confirmed) || !confirmed) {\n clack.cancel(\"Removal cancelled.\");\n process.exit(0);\n }\n }\n\n // 6. Preview or Destroy Pulumi stack\n if (options.preview) {\n // PREVIEW MODE - show what would be destroyed without actually destroying\n if (metadata.services.email?.pulumiStackName) {\n try {\n const previewResult = await progress.execute(\n \"Generating removal preview\",\n async () => {\n const stack = await pulumi.automation.LocalWorkspace.selectStack(\n {\n stackName: metadata.services.email!.pulumiStackName!,\n projectName: \"wraps-email\",\n program: async () => {}, // Empty program for destroy\n },\n {\n workDir: getPulumiWorkDir(),\n envVars: {\n PULUMI_CONFIG_PASSPHRASE: \"\",\n AWS_REGION: region,\n },\n secretsProvider: \"passphrase\",\n }\n );\n\n // Run preview to see what would be destroyed\n const result = await stack.preview({ diff: true });\n return result;\n }\n );\n\n // Display preview results\n displayPreview({\n changeSummary: previewResult.changeSummary,\n costEstimate: \"Monthly cost after removal: $0.00\",\n commandName: \"wraps email restore\",\n });\n\n clack.outro(\n pc.green(\n \"Preview complete. Run without --preview to remove infrastructure.\"\n )\n );\n\n // Track preview completion\n trackServiceRemoved(\"email\", {\n preview: true,\n duration_ms: Date.now() - startTime,\n });\n return;\n } catch (error: any) {\n trackError(\"PREVIEW_FAILED\", \"email:restore\", { step: \"preview\" });\n throw new Error(`Preview failed: ${error.message}`);\n }\n }\n return;\n }\n\n // DESTROY MODE - actually remove infrastructure\n if (metadata.services.email?.pulumiStackName) {\n await progress.execute(\"Removing Wraps infrastructure\", async () => {\n try {\n if (!metadata.services.email?.pulumiStackName) {\n throw new Error(\"No Pulumi stack name found in metadata\");\n }\n\n const stack = await pulumi.automation.LocalWorkspace.selectStack(\n {\n stackName: metadata.services.email.pulumiStackName,\n projectName: \"wraps-email\",\n program: async () => {}, // Empty program\n },\n {\n workDir: getPulumiWorkDir(),\n envVars: {\n PULUMI_CONFIG_PASSPHRASE: \"\",\n AWS_REGION: region,\n },\n secretsProvider: \"passphrase\",\n }\n );\n\n // Destroy the stack\n await stack.destroy({ onOutput: () => {} });\n\n // Remove the stack\n await stack.workspace.removeStack(\n metadata.services.email.pulumiStackName\n );\n } catch (error: any) {\n trackError(\"DESTROY_FAILED\", \"email:restore\", { step: \"destroy\" });\n throw new Error(`Failed to destroy Pulumi stack: ${error.message}`);\n }\n });\n }\n\n // 7. Delete connection metadata\n await deleteConnectionMetadata(identity.accountId, region);\n\n progress.info(\"Connection metadata deleted\");\n\n // 8. Success message\n console.log(\n `\\n${pc.green(\"✓\")} ${pc.bold(\"Infrastructure removed successfully!\")}\\n`\n );\n console.log(\n `${pc.dim(\"All Wraps resources have been deleted from your AWS account.\")}`\n );\n console.log(`${pc.dim(\"Your original AWS resources remain unchanged.\")}\\n`);\n\n // 9. Track successful removal\n trackServiceRemoved(\"email\", {\n reason: \"user_initiated\",\n duration_ms: Date.now() - startTime,\n });\n}\n","import * as clack from \"@clack/prompts\";\nimport * as pulumi from \"@pulumi/pulumi\";\nimport pc from \"picocolors\";\nimport { trackCommand } from \"../../telemetry/events.js\";\nimport type { StatusOptions } from \"../../types/index.js\";\nimport {\n getAWSRegion,\n listSESDomains,\n validateAWSCredentials,\n} from \"../../utils/shared/aws.js\";\nimport {\n ensurePulumiWorkDir,\n getPulumiWorkDir,\n} from \"../../utils/shared/fs.js\";\nimport {\n DeploymentProgress,\n displayStatus,\n} from \"../../utils/shared/output.js\";\n\n/**\n * Email Status command - Show current email infrastructure setup\n */\nexport async function emailStatus(_options: StatusOptions): Promise<void> {\n const startTime = Date.now();\n const progress = new DeploymentProgress();\n\n clack.intro(pc.bold(\"Wraps Email Status\"));\n\n // 1. Validate AWS credentials\n const identity = await progress.execute(\n \"Loading email infrastructure status\",\n async () => validateAWSCredentials()\n );\n\n // 2. Get region\n const region = await getAWSRegion();\n\n // 3. Try to load Pulumi stack\n let stackOutputs: any = {};\n try {\n // Ensure Pulumi workspace is configured (sets backend URL)\n await ensurePulumiWorkDir();\n\n const stack = await pulumi.automation.LocalWorkspace.selectStack({\n stackName: `wraps-${identity.accountId}-${region}`,\n workDir: getPulumiWorkDir(),\n });\n\n stackOutputs = await stack.outputs();\n } catch (_error: any) {\n progress.stop();\n clack.log.error(\"No email infrastructure found\");\n console.log(\n `\\nRun ${pc.cyan(\"wraps email init\")} to deploy email infrastructure.\\n`\n );\n process.exit(1);\n }\n\n // 4. Get SES domains with DKIM tokens\n const domains = await listSESDomains(region);\n\n // 4a. Fetch DKIM tokens for each domain\n const { SESv2Client, GetEmailIdentityCommand } = await import(\n \"@aws-sdk/client-sesv2\"\n );\n const sesv2Client = new SESv2Client({ region });\n\n const domainsWithTokens = await Promise.all(\n domains.map(async (d) => {\n try {\n const identity = await sesv2Client.send(\n new GetEmailIdentityCommand({ EmailIdentity: d.domain })\n );\n return {\n domain: d.domain,\n status: d.verified ? (\"verified\" as const) : (\"pending\" as const),\n dkimTokens: identity.DkimAttributes?.Tokens || [],\n mailFromDomain: identity.MailFromAttributes?.MailFromDomain,\n mailFromStatus: identity.MailFromAttributes?.MailFromDomainStatus,\n };\n } catch (_error) {\n return {\n domain: d.domain,\n status: d.verified ? (\"verified\" as const) : (\"pending\" as const),\n dkimTokens: undefined,\n mailFromDomain: undefined,\n mailFromStatus: undefined,\n };\n }\n })\n );\n\n // 5. Determine integration level\n const integrationLevel = stackOutputs.configSetName\n ? \"enhanced\"\n : \"dashboard-only\";\n\n // 6. Display status\n progress.stop();\n displayStatus({\n integrationLevel: integrationLevel as \"dashboard-only\" | \"enhanced\",\n region,\n domains: domainsWithTokens,\n resources: {\n roleArn: stackOutputs.roleArn?.value,\n configSetName: stackOutputs.configSetName?.value,\n tableName: stackOutputs.tableName?.value,\n lambdaFunctions: stackOutputs.lambdaFunctions?.value?.length || 0,\n snsTopics: integrationLevel === \"enhanced\" ? 1 : 0,\n archiveArn: stackOutputs.archiveArn?.value,\n archivingEnabled: stackOutputs.archivingEnabled?.value,\n archiveRetention: stackOutputs.archiveRetention?.value,\n },\n tracking: stackOutputs.customTrackingDomain?.value\n ? {\n customTrackingDomain: stackOutputs.customTrackingDomain?.value,\n httpsEnabled: stackOutputs.httpsTrackingEnabled?.value,\n cloudFrontDomain: stackOutputs.cloudFrontDomain?.value,\n }\n : undefined,\n });\n\n // 7. Track status command\n trackCommand(\"email:status\", {\n success: true,\n domain_count: domainsWithTokens.length,\n integration_level: integrationLevel,\n duration_ms: Date.now() - startTime,\n });\n}\n","import * as clack from \"@clack/prompts\";\nimport * as pulumi from \"@pulumi/pulumi\";\nimport pc from \"picocolors\";\nimport { deployEmailStack } from \"../../infrastructure/email-stack.js\";\nimport {\n trackError,\n trackServiceUpgrade,\n} from \"../../telemetry/events.js\";\nimport type {\n EmailStackConfig,\n UpgradeOptions,\n WrapsEmailConfig,\n} from \"../../types/index.js\";\nimport { calculateCosts, formatCost } from \"../../utils/email/costs.js\";\nimport { getAllPresetInfo, getPreset } from \"../../utils/email/presets.js\";\nimport {\n getAWSRegion,\n validateAWSCredentials,\n} from \"../../utils/shared/aws.js\";\nimport { errors } from \"../../utils/shared/errors.js\";\nimport {\n ensurePulumiWorkDir,\n getPulumiWorkDir,\n} from \"../../utils/shared/fs.js\";\nimport {\n applyConfigUpdates,\n loadConnectionMetadata,\n saveConnectionMetadata,\n updateEmailConfig,\n} from \"../../utils/shared/metadata.js\";\nimport {\n DeploymentProgress,\n displayPreview,\n displaySuccess,\n} from \"../../utils/shared/output.js\";\nimport { promptVercelConfig } from \"../../utils/shared/prompts.js\";\nimport { ensurePulumiInstalled } from \"../../utils/shared/pulumi.js\";\n\n/**\n * Upgrade command - Enhance existing Wraps infrastructure\n */\nexport async function upgrade(options: UpgradeOptions): Promise<void> {\n const startTime = Date.now();\n let upgradeAction: string | symbol = \"\";\n\n clack.intro(\n pc.bold(\n options.preview\n ? \"Wraps Upgrade Preview\"\n : \"Wraps Upgrade - Enhance Your Email Infrastructure\"\n )\n );\n\n const progress = new DeploymentProgress();\n\n // 1. Check Pulumi CLI is installed\n const wasAutoInstalled = await progress.execute(\n \"Checking Pulumi CLI installation\",\n async () => await ensurePulumiInstalled()\n );\n\n if (wasAutoInstalled) {\n progress.info(\"Pulumi CLI was automatically installed\");\n }\n\n // 2. Validate AWS credentials\n const identity = await progress.execute(\n \"Validating AWS credentials\",\n async () => validateAWSCredentials()\n );\n\n progress.info(`Connected to AWS account: ${pc.cyan(identity.accountId)}`);\n\n // 3. Get region\n let region = options.region;\n if (!region) {\n const defaultRegion = await getAWSRegion();\n region = defaultRegion;\n }\n\n // 4. Load existing connection metadata\n const metadata = await loadConnectionMetadata(identity.accountId, region);\n\n if (!metadata) {\n clack.log.error(\n `No Wraps connection found for account ${pc.cyan(identity.accountId)} in region ${pc.cyan(region)}`\n );\n clack.log.info(\n `Use ${pc.cyan(\"wraps email init\")} to create new infrastructure or ${pc.cyan(\"wraps email connect\")} to connect existing.`\n );\n process.exit(1);\n }\n\n progress.info(`Found existing connection created: ${metadata.timestamp}`);\n\n // 5. Display current configuration\n console.log(`\\n${pc.bold(\"Current Configuration:\")}\\n`);\n\n if (metadata.services.email?.preset) {\n console.log(` Preset: ${pc.cyan(metadata.services.email?.preset)}`);\n } else {\n console.log(` Preset: ${pc.cyan(\"custom\")}`);\n }\n\n const config = metadata.services.email?.config;\n\n if (!config) {\n clack.log.error(\"No email configuration found in metadata\");\n clack.log.info(\n `Use ${pc.cyan(\"wraps email init\")} to create new infrastructure.`\n );\n process.exit(1);\n }\n\n // Show sending domain if configured\n if (config.domain) {\n console.log(` Sending Domain: ${pc.cyan(config.domain)}`);\n }\n\n if (config.tracking?.enabled) {\n console.log(` ${pc.green(\"✓\")} Open & Click Tracking`);\n if (config.tracking.customRedirectDomain) {\n console.log(\n ` ${pc.dim(\"└─\")} Custom domain: ${pc.cyan(config.tracking.customRedirectDomain)}`\n );\n }\n }\n\n if (config.suppressionList?.enabled) {\n console.log(` ${pc.green(\"✓\")} Bounce/Complaint Suppression`);\n }\n\n if (config.eventTracking?.enabled) {\n console.log(` ${pc.green(\"✓\")} Event Tracking (EventBridge)`);\n if (config.eventTracking.dynamoDBHistory) {\n console.log(\n ` ${pc.dim(\"└─\")} Email History: ${pc.cyan(config.eventTracking.archiveRetention || \"90days\")}`\n );\n }\n }\n\n if (config.dedicatedIp) {\n console.log(` ${pc.green(\"✓\")} Dedicated IP Address`);\n }\n\n if (config.emailArchiving?.enabled) {\n const retentionLabel =\n {\n \"7days\": \"7 days\",\n \"30days\": \"30 days\",\n \"90days\": \"90 days\",\n \"3months\": \"3 months\",\n \"6months\": \"6 months\",\n \"9months\": \"9 months\",\n \"1year\": \"1 year\",\n \"18months\": \"18 months\",\n \"2years\": \"2 years\",\n \"30months\": \"30 months\",\n \"3years\": \"3 years\",\n \"4years\": \"4 years\",\n \"5years\": \"5 years\",\n \"6years\": \"6 years\",\n \"7years\": \"7 years\",\n \"8years\": \"8 years\",\n \"9years\": \"9 years\",\n \"10years\": \"10 years\",\n indefinite: \"indefinite\",\n permanent: \"permanent\",\n }[config.emailArchiving.retention] || \"90 days\";\n console.log(` ${pc.green(\"✓\")} Email Archiving (${retentionLabel})`);\n }\n\n // Calculate current cost\n const currentCostData = calculateCosts(config, 50_000); // Assume 50k emails/mo for estimate\n console.log(\n `\\n Estimated Cost: ${pc.cyan(`~${formatCost(currentCostData.total.monthly)}/mo`)}`\n );\n\n console.log(\"\");\n\n // 6. Prompt for upgrade action\n upgradeAction = await clack.select({\n message: \"What would you like to do?\",\n options: [\n {\n value: \"preset\",\n label: \"Upgrade to a different preset\",\n hint: \"Starter → Production → Enterprise\",\n },\n {\n value: \"archiving\",\n label: config.emailArchiving?.enabled\n ? \"Change email archiving settings\"\n : \"Enable email archiving\",\n hint: config.emailArchiving?.enabled\n ? \"Update retention or disable\"\n : \"Store full email content with HTML\",\n },\n {\n value: \"tracking-domain\",\n label: \"Add/change custom tracking domain\",\n hint: \"Use your own domain for email links\",\n },\n {\n value: \"retention\",\n label: \"Change email history retention\",\n hint: \"7 days, 30 days, 90 days, 6 months, 1 year, 18 months\",\n },\n {\n value: \"events\",\n label: \"Customize tracked event types\",\n hint: \"Choose which SES events to track\",\n },\n {\n value: \"dedicated-ip\",\n label: \"Enable dedicated IP address\",\n hint: \"Requires 100k+ emails/day ($50-100/mo)\",\n },\n {\n value: \"custom\",\n label: \"Custom configuration\",\n hint: \"Modify multiple settings at once\",\n },\n ],\n });\n\n if (clack.isCancel(upgradeAction)) {\n clack.cancel(\"Upgrade cancelled.\");\n process.exit(0);\n }\n\n let updatedConfig: WrapsEmailConfig = { ...config };\n let newPreset: string | undefined = metadata.services.email?.preset;\n\n // 7. Handle upgrade action\n switch (upgradeAction) {\n case \"preset\": {\n // Show available presets\n const presets = getAllPresetInfo();\n const currentPresetIdx = presets.findIndex(\n (p) => p.name.toLowerCase() === metadata.services.email?.preset\n );\n\n const availablePresets = presets\n .map((p, idx) => ({\n value: p.name.toLowerCase(),\n label: `${p.name} - ${p.description}`,\n hint: `${p.volume} | Est. ${p.estimatedCost}/mo`,\n disabled:\n currentPresetIdx >= 0 && idx <= currentPresetIdx\n ? \"Current or lower tier\"\n : undefined,\n }))\n .filter((p) => !p.disabled);\n\n if (availablePresets.length === 0) {\n clack.log.warn(\"Already on highest preset (Enterprise)\");\n process.exit(0);\n }\n\n const selectedPreset = await clack.select({\n message: \"Select new preset:\",\n options: availablePresets,\n });\n\n if (clack.isCancel(selectedPreset)) {\n clack.cancel(\"Upgrade cancelled.\");\n process.exit(0);\n }\n\n // Get preset config but preserve user-customized fields from existing config\n const presetConfig = getPreset(selectedPreset as any)!;\n\n // Apply preset updates to existing config (preserves user customizations)\n updatedConfig = applyConfigUpdates(config, presetConfig);\n newPreset = selectedPreset as string;\n break;\n }\n\n case \"archiving\": {\n if (config.emailArchiving?.enabled) {\n // Already enabled - allow changing retention or disabling\n const archivingAction = await clack.select({\n message: \"What would you like to do with email archiving?\",\n options: [\n {\n value: \"change-retention\",\n label: \"Change retention period\",\n hint: `Current: ${config.emailArchiving.retention}`,\n },\n {\n value: \"disable\",\n label: \"Disable email archiving\",\n hint: \"Stop storing full email content\",\n },\n ],\n });\n\n if (clack.isCancel(archivingAction)) {\n clack.cancel(\"Upgrade cancelled.\");\n process.exit(0);\n }\n\n if (archivingAction === \"disable\") {\n const confirmDisable = await clack.confirm({\n message:\n \"Are you sure? Existing archived emails will remain, but new emails won't be archived.\",\n initialValue: false,\n });\n\n if (clack.isCancel(confirmDisable) || !confirmDisable) {\n clack.cancel(\"Archiving not disabled.\");\n process.exit(0);\n }\n\n updatedConfig = {\n ...config,\n emailArchiving: {\n enabled: false,\n retention: config.emailArchiving.retention,\n },\n };\n } else {\n // Change retention\n const retention = await clack.select({\n message: \"Email archive retention period:\",\n options: [\n {\n value: \"7days\",\n label: \"7 days\",\n hint: \"~$1-2/mo for 10k emails\",\n },\n {\n value: \"30days\",\n label: \"30 days\",\n hint: \"~$2-4/mo for 10k emails\",\n },\n {\n value: \"90days\",\n label: \"90 days (recommended)\",\n hint: \"~$5-10/mo for 10k emails\",\n },\n {\n value: \"6months\",\n label: \"6 months\",\n hint: \"~$15-25/mo for 10k emails\",\n },\n {\n value: \"1year\",\n label: \"1 year\",\n hint: \"~$25-40/mo for 10k emails\",\n },\n {\n value: \"18months\",\n label: \"18 months\",\n hint: \"~$35-60/mo for 10k emails\",\n },\n ],\n initialValue: config.emailArchiving.retention,\n });\n\n if (clack.isCancel(retention)) {\n clack.cancel(\"Upgrade cancelled.\");\n process.exit(0);\n }\n\n updatedConfig = {\n ...config,\n emailArchiving: {\n enabled: true,\n retention: retention as any,\n },\n };\n }\n } else {\n // Not enabled - prompt to enable with retention selection\n const enableArchiving = await clack.confirm({\n message:\n \"Enable email archiving? (Store full email content with HTML for viewing)\",\n initialValue: true,\n });\n\n if (clack.isCancel(enableArchiving)) {\n clack.cancel(\"Upgrade cancelled.\");\n process.exit(0);\n }\n\n if (!enableArchiving) {\n clack.log.info(\"Email archiving not enabled.\");\n process.exit(0);\n }\n\n const retention = await clack.select({\n message: \"Email archive retention period:\",\n options: [\n {\n value: \"7days\",\n label: \"7 days\",\n hint: \"~$1-2/mo for 10k emails\",\n },\n {\n value: \"30days\",\n label: \"30 days\",\n hint: \"~$2-4/mo for 10k emails\",\n },\n {\n value: \"90days\",\n label: \"90 days (recommended)\",\n hint: \"~$5-10/mo for 10k emails\",\n },\n {\n value: \"6months\",\n label: \"6 months\",\n hint: \"~$15-25/mo for 10k emails\",\n },\n {\n value: \"1year\",\n label: \"1 year\",\n hint: \"~$25-40/mo for 10k emails\",\n },\n {\n value: \"18months\",\n label: \"18 months\",\n hint: \"~$35-60/mo for 10k emails\",\n },\n ],\n initialValue: \"90days\",\n });\n\n if (clack.isCancel(retention)) {\n clack.cancel(\"Upgrade cancelled.\");\n process.exit(0);\n }\n\n clack.log.info(\n pc.dim(\n \"Archiving stores full RFC 822 emails with HTML, attachments, and headers\"\n )\n );\n clack.log.info(\n pc.dim(\n \"Cost: $2/GB ingestion + $0.19/GB/month storage (~50KB per email)\"\n )\n );\n\n updatedConfig = {\n ...config,\n emailArchiving: {\n enabled: true,\n retention: retention as any,\n },\n };\n }\n newPreset = undefined; // Custom config\n break;\n }\n\n case \"tracking-domain\": {\n // First, check if a sending identity (domain) is configured and verified\n if (!config.domain) {\n clack.log.error(\n \"No sending domain configured. You must configure a sending domain before adding a custom tracking domain.\"\n );\n clack.log.info(\n `Use ${pc.cyan(\"wraps email init\")} to set up a sending domain first.`\n );\n process.exit(1);\n }\n\n // Verify that the sending identity is verified\n const { listSESDomains } = await import(\"../../utils/shared/aws.js\");\n const domains = await progress.execute(\n \"Checking domain verification status\",\n async () => await listSESDomains(region)\n );\n\n const sendingDomain = domains.find((d) => d.domain === config.domain);\n\n if (!sendingDomain?.verified) {\n clack.log.error(\n `Sending domain ${pc.cyan(config.domain)} is not verified.`\n );\n clack.log.info(\n \"You must verify your sending domain before adding a custom tracking domain.\"\n );\n clack.log.info(\n `Use ${pc.cyan(\"wraps email verify\")} to check DNS records and complete verification.`\n );\n process.exit(1);\n }\n\n progress.info(\n `Sending domain ${pc.cyan(config.domain)} is verified ${pc.green(\"✓\")}`\n );\n\n const trackingDomain = await clack.text({\n message: \"Custom tracking redirect domain:\",\n placeholder: \"track.yourdomain.com\",\n initialValue: config.tracking?.customRedirectDomain || \"\",\n validate: (value) => {\n if (value && !/^[a-z0-9.-]+\\.[a-z]{2,}$/.test(value)) {\n return \"Please enter a valid domain\";\n }\n },\n });\n\n if (clack.isCancel(trackingDomain)) {\n clack.cancel(\"Upgrade cancelled.\");\n process.exit(0);\n }\n\n // Ask if HTTPS tracking should be enabled\n const enableHttps = await clack.confirm({\n message: \"Enable HTTPS tracking with CloudFront + SSL certificate?\",\n initialValue: true,\n });\n\n if (clack.isCancel(enableHttps)) {\n clack.cancel(\"Upgrade cancelled.\");\n process.exit(0);\n }\n\n if (enableHttps) {\n clack.log.info(\n pc.dim(\n \"HTTPS tracking creates a CloudFront distribution with an SSL certificate.\"\n )\n );\n clack.log.info(\n pc.dim(\n \"This ensures all tracking links use secure HTTPS connections.\"\n )\n );\n\n // Check if domain has Route53 hosted zone\n const { findHostedZone } = await import(\"../../utils/email/route53.js\");\n const hostedZone = await progress.execute(\n \"Checking for Route53 hosted zone\",\n async () =>\n await findHostedZone(trackingDomain || config.domain!, region)\n );\n\n if (hostedZone) {\n progress.info(\n `Found Route53 hosted zone: ${pc.cyan(hostedZone.name)} ${pc.green(\"✓\")}`\n );\n clack.log.info(\n pc.dim(\n \"DNS records (SSL certificate validation + CloudFront) will be created automatically.\"\n )\n );\n } else {\n clack.log.warn(\n `No Route53 hosted zone found for ${pc.cyan(trackingDomain || config.domain!)}`\n );\n clack.log.info(\n pc.dim(\n \"You'll need to manually create DNS records for SSL certificate validation and CloudFront.\"\n )\n );\n clack.log.info(\n pc.dim(\"DNS record details will be shown after deployment.\")\n );\n }\n\n const confirmHttps = await clack.confirm({\n message: hostedZone\n ? \"Proceed with automatic HTTPS setup?\"\n : \"Proceed with manual HTTPS setup (requires DNS configuration)?\",\n initialValue: true,\n });\n\n if (clack.isCancel(confirmHttps) || !confirmHttps) {\n clack.log.info(\"HTTPS tracking not enabled. Using HTTP tracking.\");\n updatedConfig = {\n ...config,\n tracking: {\n ...config.tracking,\n enabled: true,\n customRedirectDomain: trackingDomain || undefined,\n httpsEnabled: false,\n },\n };\n } else {\n updatedConfig = {\n ...config,\n tracking: {\n ...config.tracking,\n enabled: true,\n customRedirectDomain: trackingDomain || undefined,\n httpsEnabled: true,\n },\n };\n }\n } else {\n clack.log.info(\n pc.dim(\n \"Using HTTP tracking (standard). Links will use http:// protocol.\"\n )\n );\n updatedConfig = {\n ...config,\n tracking: {\n ...config.tracking,\n enabled: true,\n customRedirectDomain: trackingDomain || undefined,\n httpsEnabled: false,\n },\n };\n }\n\n newPreset = undefined; // Custom config\n break;\n }\n\n case \"retention\": {\n const retention = await clack.select({\n message: \"Email history retention period (event data in DynamoDB):\",\n options: [\n { value: \"7days\", label: \"7 days\", hint: \"Minimal storage cost\" },\n { value: \"30days\", label: \"30 days\", hint: \"Development/testing\" },\n {\n value: \"90days\",\n label: \"90 days (recommended)\",\n hint: \"Standard retention\",\n },\n {\n value: \"6months\",\n label: \"6 months\",\n hint: \"Extended retention\",\n },\n { value: \"1year\", label: \"1 year\", hint: \"Compliance requirements\" },\n {\n value: \"18months\",\n label: \"18 months\",\n hint: \"Long-term retention\",\n },\n ],\n initialValue: config.eventTracking?.archiveRetention || \"90days\",\n });\n\n if (clack.isCancel(retention)) {\n clack.cancel(\"Upgrade cancelled.\");\n process.exit(0);\n }\n\n clack.log.info(\n pc.dim(\n \"Note: This is for event data (sent, delivered, opened, etc.) stored in DynamoDB.\"\n )\n );\n clack.log.info(\n pc.dim(\n \"For full email content storage, use 'Enable email archiving' option.\"\n )\n );\n\n updatedConfig = {\n ...config,\n eventTracking: {\n ...config.eventTracking,\n enabled: true,\n dynamoDBHistory: true,\n archiveRetention: retention as any,\n },\n };\n newPreset = undefined; // Custom config\n break;\n }\n\n case \"events\": {\n const selectedEvents = await clack.multiselect({\n message: \"Select SES event types to track:\",\n options: [\n { value: \"SEND\", label: \"Send\", hint: \"Email sent to SES\" },\n {\n value: \"DELIVERY\",\n label: \"Delivery\",\n hint: \"Email delivered successfully\",\n },\n { value: \"OPEN\", label: \"Open\", hint: \"Recipient opened email\" },\n { value: \"CLICK\", label: \"Click\", hint: \"Recipient clicked link\" },\n { value: \"BOUNCE\", label: \"Bounce\", hint: \"Email bounced\" },\n {\n value: \"COMPLAINT\",\n label: \"Complaint\",\n hint: \"Spam complaint received\",\n },\n { value: \"REJECT\", label: \"Reject\", hint: \"Email rejected by SES\" },\n {\n value: \"RENDERING_FAILURE\",\n label: \"Rendering Failure\",\n hint: \"Template rendering failed\",\n },\n {\n value: \"DELIVERY_DELAY\",\n label: \"Delivery Delay\",\n hint: \"Temporary delivery delay\",\n },\n {\n value: \"SUBSCRIPTION\",\n label: \"Subscription\",\n hint: \"List subscription event\",\n },\n ],\n initialValues: config.eventTracking?.events || [\n \"SEND\",\n \"DELIVERY\",\n \"OPEN\",\n \"CLICK\",\n \"BOUNCE\",\n \"COMPLAINT\",\n ],\n required: true,\n });\n\n if (clack.isCancel(selectedEvents)) {\n clack.cancel(\"Upgrade cancelled.\");\n process.exit(0);\n }\n\n updatedConfig = {\n ...config,\n eventTracking: {\n ...config.eventTracking,\n enabled: true,\n events: selectedEvents as any,\n },\n };\n newPreset = undefined; // Custom config\n break;\n }\n\n case \"dedicated-ip\": {\n const confirmed = await clack.confirm({\n message:\n \"Enable dedicated IP? (Requires 100k+ emails/day, adds ~$50-100/mo)\",\n initialValue: false,\n });\n\n if (clack.isCancel(confirmed)) {\n clack.cancel(\"Upgrade cancelled.\");\n process.exit(0);\n }\n\n if (!confirmed) {\n clack.log.info(\"Dedicated IP not enabled.\");\n process.exit(0);\n }\n\n updatedConfig = {\n ...config,\n dedicatedIp: true,\n };\n newPreset = undefined; // Custom config\n break;\n }\n\n case \"custom\": {\n // Full custom configuration\n const { promptCustomConfig } = await import(\n \"../../utils/shared/prompts.js\"\n );\n\n // Pass existing config to preserve values\n const customConfig = await promptCustomConfig(config);\n\n // Apply custom config updates to existing config (preserves user-customized fields)\n updatedConfig = applyConfigUpdates(config, customConfig);\n newPreset = undefined;\n break;\n }\n }\n\n // 8. Show cost comparison\n const newCostData = calculateCosts(updatedConfig, 50_000);\n const costDiff = newCostData.total.monthly - currentCostData.total.monthly;\n\n console.log(`\\n${pc.bold(\"Cost Impact:\")}`);\n console.log(\n ` Current: ${pc.cyan(`${formatCost(currentCostData.total.monthly)}/mo`)}`\n );\n console.log(\n ` New: ${pc.cyan(`${formatCost(newCostData.total.monthly)}/mo`)}`\n );\n if (costDiff > 0) {\n console.log(` Change: ${pc.yellow(`+${formatCost(costDiff)}/mo`)}`);\n } else if (costDiff < 0) {\n console.log(\n ` Change: ${pc.green(`${formatCost(Math.abs(costDiff))}/mo`)}`\n );\n }\n console.log(\"\");\n\n // 9. Confirm upgrade (skip if --yes or --preview)\n if (!(options.yes || options.preview)) {\n const confirmed = await clack.confirm({\n message: \"Proceed with upgrade?\",\n initialValue: true,\n });\n\n if (clack.isCancel(confirmed) || !confirmed) {\n clack.cancel(\"Upgrade cancelled.\");\n process.exit(0);\n }\n }\n\n // 10. Get Vercel config if needed and not already stored\n let vercelConfig;\n if (metadata.provider === \"vercel\" && !metadata.vercel) {\n vercelConfig = await promptVercelConfig();\n } else if (metadata.provider === \"vercel\") {\n vercelConfig = metadata.vercel;\n }\n\n // 11. Build stack configuration\n const stackConfig: EmailStackConfig = {\n provider: metadata.provider,\n region,\n vercel: vercelConfig,\n emailConfig: updatedConfig,\n };\n\n // 12. Preview or Update Pulumi stack\n if (options.preview) {\n // PREVIEW MODE - show what would be changed without deploying\n try {\n const previewResult = await progress.execute(\n \"Generating upgrade preview\",\n async () => {\n await ensurePulumiWorkDir();\n\n const stack =\n await pulumi.automation.LocalWorkspace.createOrSelectStack(\n {\n stackName:\n metadata.services.email?.pulumiStackName ||\n `wraps-${identity.accountId}-${region}`,\n projectName: \"wraps-email\",\n program: async () => {\n const result = await deployEmailStack(stackConfig);\n return {\n roleArn: result.roleArn,\n configSetName: result.configSetName,\n tableName: result.tableName,\n region: result.region,\n lambdaFunctions: result.lambdaFunctions,\n domain: result.domain,\n dkimTokens: result.dkimTokens,\n customTrackingDomain: result.customTrackingDomain,\n httpsTrackingEnabled: result.httpsTrackingEnabled,\n cloudFrontDomain: result.cloudFrontDomain,\n acmCertificateValidationRecords:\n result.acmCertificateValidationRecords,\n archiveArn: result.archiveArn,\n archivingEnabled: result.archivingEnabled,\n archiveRetention: result.archiveRetention,\n };\n },\n },\n {\n workDir: getPulumiWorkDir(),\n envVars: {\n PULUMI_CONFIG_PASSPHRASE: \"\",\n AWS_REGION: region,\n },\n secretsProvider: \"passphrase\",\n }\n );\n\n await stack.setConfig(\"aws:region\", { value: region });\n\n // Refresh state to sync with AWS before previewing\n await stack.refresh({ onOutput: () => {} });\n\n // Run preview instead of deployment\n const result = await stack.preview({ diff: true });\n return result;\n }\n );\n\n // Build cost comparison string\n const costComparison = [\n `Current: ${formatCost(currentCostData.total.monthly)}/mo`,\n `After upgrade: ${formatCost(newCostData.total.monthly)}/mo`,\n costDiff > 0\n ? `Change: +${formatCost(costDiff)}/mo`\n : costDiff < 0\n ? `Change: -${formatCost(Math.abs(costDiff))}/mo`\n : \"Change: No cost difference\",\n ].join(\"\\n\");\n\n // Display preview results\n displayPreview({\n changeSummary: previewResult.changeSummary,\n costEstimate: costComparison,\n commandName: \"wraps email upgrade\",\n });\n\n clack.outro(\n pc.green(\"Preview complete. Run without --preview to upgrade.\")\n );\n\n // Track preview completion\n trackServiceUpgrade(\"email\", {\n from_preset: metadata.services.email?.preset,\n to_preset: newPreset,\n preview: true,\n action: typeof upgradeAction === \"string\" ? upgradeAction : undefined,\n duration_ms: Date.now() - startTime,\n });\n return;\n } catch (error: any) {\n trackError(\"PREVIEW_FAILED\", \"email:upgrade\", { step: \"preview\" });\n if (error.message?.includes(\"stack is currently locked\")) {\n throw errors.stackLocked();\n }\n throw new Error(`Preview failed: ${error.message}`);\n }\n }\n\n // DEPLOY MODE - actually update infrastructure\n let outputs;\n try {\n outputs = await progress.execute(\n \"Updating Wraps infrastructure (this may take 2-3 minutes)\",\n async () => {\n await ensurePulumiWorkDir();\n\n const stack =\n await pulumi.automation.LocalWorkspace.createOrSelectStack(\n {\n stackName:\n metadata.services.email?.pulumiStackName ||\n `wraps-${identity.accountId}-${region}`,\n projectName: \"wraps-email\",\n program: async () => {\n const result = await deployEmailStack(stackConfig);\n\n return {\n roleArn: result.roleArn,\n configSetName: result.configSetName,\n tableName: result.tableName,\n region: result.region,\n lambdaFunctions: result.lambdaFunctions,\n domain: result.domain,\n dkimTokens: result.dkimTokens,\n customTrackingDomain: result.customTrackingDomain,\n httpsTrackingEnabled: result.httpsTrackingEnabled,\n cloudFrontDomain: result.cloudFrontDomain,\n acmCertificateValidationRecords:\n result.acmCertificateValidationRecords,\n archiveArn: result.archiveArn,\n archivingEnabled: result.archivingEnabled,\n archiveRetention: result.archiveRetention,\n };\n },\n },\n {\n workDir: getPulumiWorkDir(),\n envVars: {\n PULUMI_CONFIG_PASSPHRASE: \"\",\n AWS_REGION: region,\n },\n secretsProvider: \"passphrase\",\n }\n );\n\n await stack.workspace.selectStack(\n metadata.services.email?.pulumiStackName ||\n `wraps-${identity.accountId}-${region}`\n );\n await stack.setConfig(\"aws:region\", { value: region });\n\n // Refresh state to sync with AWS before upgrading\n // This ensures Pulumi knows about resources that already exist\n await stack.refresh({ onOutput: () => {} });\n\n // Pulumi will automatically detect changes and only update what's needed\n const upResult = await stack.up({ onOutput: () => {} });\n const pulumiOutputs = upResult.outputs;\n\n return {\n roleArn: pulumiOutputs.roleArn?.value as string,\n configSetName: pulumiOutputs.configSetName?.value as\n | string\n | undefined,\n tableName: pulumiOutputs.tableName?.value as string | undefined,\n region: pulumiOutputs.region?.value as string,\n lambdaFunctions: pulumiOutputs.lambdaFunctions?.value as\n | string[]\n | undefined,\n domain: pulumiOutputs.domain?.value as string | undefined,\n dkimTokens: pulumiOutputs.dkimTokens?.value as string[] | undefined,\n customTrackingDomain: pulumiOutputs.customTrackingDomain?.value as\n | string\n | undefined,\n httpsTrackingEnabled: pulumiOutputs.httpsTrackingEnabled?.value as\n | boolean\n | undefined,\n cloudFrontDomain: pulumiOutputs.cloudFrontDomain?.value as\n | string\n | undefined,\n acmCertificateValidationRecords: pulumiOutputs\n .acmCertificateValidationRecords?.value as\n | Array<{ name: string; type: string; value: string }>\n | undefined,\n archiveArn: pulumiOutputs.archiveArn?.value as string | undefined,\n archivingEnabled: pulumiOutputs.archivingEnabled?.value as\n | boolean\n | undefined,\n archiveRetention: pulumiOutputs.archiveRetention?.value as\n | string\n | undefined,\n };\n }\n );\n } catch (error: any) {\n // Track upgrade failure\n trackServiceUpgrade(\"email\", {\n from_preset: metadata.services.email?.preset,\n to_preset: newPreset,\n action: typeof upgradeAction === \"string\" ? upgradeAction : undefined,\n duration_ms: Date.now() - startTime,\n });\n\n // Check if it's a lock file error\n if (error.message?.includes(\"stack is currently locked\")) {\n trackError(\"STACK_LOCKED\", \"email:upgrade\", { step: \"deploy\" });\n throw errors.stackLocked();\n }\n\n trackError(\"UPGRADE_FAILED\", \"email:upgrade\", { step: \"deploy\" });\n throw new Error(`Pulumi upgrade failed: ${error.message}`);\n }\n\n // 13. Create DNS records in Route53 (if hosted zone exists)\n if (outputs.domain && outputs.dkimTokens && outputs.dkimTokens.length > 0) {\n const { findHostedZone, createDNSRecords } = await import(\n \"../../utils/email/route53.js\"\n );\n const hostedZone = await findHostedZone(outputs.domain, region);\n\n if (hostedZone) {\n try {\n progress.start(\"Creating DNS records in Route53\");\n\n // Determine mailFromDomain - use updatedConfig if available, otherwise construct default\n const mailFromDomain =\n updatedConfig.mailFromDomain || `mail.${outputs.domain}`;\n\n await createDNSRecords(\n hostedZone.id,\n outputs.domain,\n outputs.dkimTokens,\n region,\n outputs.customTrackingDomain,\n mailFromDomain,\n outputs.cloudFrontDomain\n );\n progress.succeed(\"DNS records created in Route53\");\n } catch (error: any) {\n progress.fail(\n `Failed to create DNS records automatically: ${error.message}`\n );\n progress.info(\n \"You can manually add the required DNS records shown below\"\n );\n }\n }\n }\n\n // 14. Update metadata\n updateEmailConfig(metadata, updatedConfig);\n if (metadata.services.email) {\n metadata.services.email.preset = newPreset as any;\n }\n await saveConnectionMetadata(metadata);\n\n progress.info(\"Connection metadata updated\");\n\n // 15. Format tracking domain DNS records if custom tracking domain was added\n const trackingDomainDnsRecords = [];\n const acmValidationRecords = [];\n\n if (outputs.customTrackingDomain) {\n // For HTTPS tracking, only show CNAME if CloudFront exists\n // For HTTP tracking, point to SES tracking endpoint\n if (outputs.httpsTrackingEnabled) {\n // Only add tracking domain CNAME if CloudFront is created\n if (outputs.cloudFrontDomain) {\n trackingDomainDnsRecords.push({\n name: outputs.customTrackingDomain,\n type: \"CNAME\",\n value: outputs.cloudFrontDomain,\n });\n }\n } else {\n // HTTP tracking - use SES tracking endpoint\n trackingDomainDnsRecords.push({\n name: outputs.customTrackingDomain,\n type: \"CNAME\",\n value: `r.${outputs.region}.awstrack.me`,\n });\n }\n }\n\n // Add ACM certificate validation records if HTTPS tracking is enabled\n if (outputs.httpsTrackingEnabled && outputs.acmCertificateValidationRecords) {\n acmValidationRecords.push(...outputs.acmCertificateValidationRecords);\n }\n\n // Check if HTTPS tracking was enabled but CloudFront wasn't created (manual DNS validation needed)\n const needsCertificateValidation =\n outputs.httpsTrackingEnabled &&\n acmValidationRecords.length > 0 &&\n !outputs.cloudFrontDomain;\n\n // 15. Display success message\n displaySuccess({\n roleArn: outputs.roleArn,\n configSetName: outputs.configSetName,\n region: outputs.region!,\n tableName: outputs.tableName,\n trackingDomainDnsRecords:\n trackingDomainDnsRecords.length > 0\n ? trackingDomainDnsRecords\n : undefined,\n acmValidationRecords:\n acmValidationRecords.length > 0 ? acmValidationRecords : undefined,\n customTrackingDomain: outputs.customTrackingDomain,\n httpsTrackingEnabled: outputs.httpsTrackingEnabled,\n });\n\n // Show what was upgraded\n console.log(`\\n${pc.green(\"✓\")} ${pc.bold(\"Upgrade complete!\")}\\n`);\n\n if (upgradeAction === \"preset\" && newPreset) {\n console.log(\n `Upgraded to ${pc.cyan(newPreset)} preset (${pc.green(`${formatCost(newCostData.total.monthly)}/mo`)})\\n`\n );\n } else {\n console.log(\n `Updated configuration (${pc.green(`${formatCost(newCostData.total.monthly)}/mo`)})\\n`\n );\n }\n\n // Show next steps for HTTPS tracking if certificate validation is pending\n if (needsCertificateValidation) {\n console.log(pc.bold(\"⚠️ HTTPS Tracking - Next Steps:\\n\"));\n console.log(\n \" 1. Add the SSL certificate validation DNS record shown above to your DNS provider\"\n );\n console.log(\n \" 2. Wait for DNS propagation and certificate validation (5-30 minutes)\"\n );\n console.log(\n ` 3. Run ${pc.cyan(\"wraps email upgrade\")} again to complete CloudFront setup\\n`\n );\n console.log(\n pc.dim(\n \" Note: CloudFront distribution will be created once the certificate is validated.\\n\"\n )\n );\n } else if (outputs.httpsTrackingEnabled && outputs.cloudFrontDomain) {\n console.log(\n pc.green(\"✓\") +\n \" \" +\n pc.bold(\"HTTPS tracking is fully configured and ready to use!\\n\")\n );\n }\n\n // 16. Track successful upgrade\n const enabledFeatures: string[] = [];\n if (updatedConfig.tracking?.enabled) enabledFeatures.push(\"tracking\");\n if (updatedConfig.suppressionList?.enabled)\n enabledFeatures.push(\"suppression_list\");\n if (updatedConfig.eventTracking?.enabled)\n enabledFeatures.push(\"event_tracking\");\n if (updatedConfig.eventTracking?.dynamoDBHistory)\n enabledFeatures.push(\"dynamodb_history\");\n if (updatedConfig.dedicatedIp) enabledFeatures.push(\"dedicated_ip\");\n if (updatedConfig.emailArchiving?.enabled)\n enabledFeatures.push(\"email_archiving\");\n\n trackServiceUpgrade(\"email\", {\n from_preset: metadata.services.email?.preset,\n to_preset: newPreset,\n added_features: enabledFeatures,\n action: typeof upgradeAction === \"string\" ? upgradeAction : undefined,\n duration_ms: Date.now() - startTime,\n });\n}\n","import * as clack from \"@clack/prompts\";\nimport * as pulumi from \"@pulumi/pulumi\";\nimport getPort from \"get-port\";\nimport open from \"open\";\nimport pc from \"picocolors\";\nimport { startConsoleServer } from \"../../console/server.js\";\nimport { trackCommand } from \"../../telemetry/events.js\";\nimport type { DashboardOptions } from \"../../types/index.js\";\nimport {\n getAWSRegion,\n validateAWSCredentials,\n} from \"../../utils/shared/aws.js\";\nimport {\n ensurePulumiWorkDir,\n getPulumiWorkDir,\n} from \"../../utils/shared/fs.js\";\nimport { DeploymentProgress } from \"../../utils/shared/output.js\";\n\n/**\n * Dashboard command - Start local web dashboard\n */\nexport async function dashboard(options: DashboardOptions): Promise<void> {\n clack.intro(pc.bold(\"Wraps Dashboard\"));\n\n const progress = new DeploymentProgress();\n\n // 1. Validate AWS credentials\n const identity = await progress.execute(\n \"Validating AWS credentials\",\n async () => validateAWSCredentials()\n );\n\n // 2. Get region\n const region = await getAWSRegion();\n\n // 3. Load stack outputs to get IAM role ARN\n let stackOutputs: any = {};\n try {\n // Ensure Pulumi workspace is configured (sets backend URL)\n await ensurePulumiWorkDir();\n\n const stack = await pulumi.automation.LocalWorkspace.selectStack({\n stackName: `wraps-${identity.accountId}-${region}`,\n workDir: getPulumiWorkDir(),\n });\n\n stackOutputs = await stack.outputs();\n } catch (_error: unknown) {\n progress.stop();\n clack.log.error(\"No Wraps infrastructure found\");\n console.log(\n `\\\\nRun ${pc.cyan(\"wraps email init\")} to deploy infrastructure first.\\\\n`\n );\n process.exit(1);\n }\n\n // Extract outputs from stack (optional - console uses current AWS credentials)\n const tableName = stackOutputs.tableName?.value;\n const archiveArn = stackOutputs.archiveArn?.value;\n const archivingEnabled = stackOutputs.archivingEnabled?.value ?? false;\n\n // 4. Find available port\n const port =\n options.port || (await getPort({ port: [5555, 5556, 5557, 5558, 5559] }));\n\n // 5. Start server\n progress.stop();\n clack.log.success(\"Starting dashboard server...\");\n console.log(\n `${pc.dim(\"Using current AWS credentials (no role assumption)\")}\\\\n`\n );\n\n const { url } = await startConsoleServer({\n port,\n roleArn: undefined, // Use current credentials instead of assuming role\n region,\n tableName,\n accountId: identity.accountId,\n noOpen: options.noOpen ?? false,\n archiveArn,\n archivingEnabled,\n });\n\n console.log(`\\\\n${pc.bold(\"Dashboard:\")} ${pc.cyan(url)}`);\n console.log(`${pc.dim(\"Press Ctrl+C to stop\")}\\\\n`);\n\n // 6. Open browser (unless --no-open)\n if (!options.noOpen) {\n await open(url);\n }\n\n // 7. Track console launch\n trackCommand(\"console\", {\n success: true,\n port,\n no_open: options.noOpen ?? false,\n });\n\n // Keep process alive\n await new Promise(() => {});\n}\n","import crypto from \"node:crypto\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport express from \"express\";\nimport { createHttpTerminator } from \"http-terminator\";\nimport { authenticateToken } from \"./middleware/auth.js\";\nimport { errorHandler } from \"./middleware/error.js\";\nimport { createDomainsRouter } from \"./routes/domains.js\";\nimport { createEmailsRouter } from \"./routes/emails.js\";\nimport { createMetricsRouter } from \"./routes/metrics.js\";\nimport { createSettingsRouter } from \"./routes/settings.js\";\nimport { createUserRouter } from \"./routes/user.js\";\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\nexport type ServerConfig = {\n port: number;\n roleArn: string | undefined;\n region: string;\n tableName?: string;\n accountId?: string;\n noOpen: boolean;\n archiveArn?: string;\n archivingEnabled?: boolean;\n};\n\nexport type ServerInfo = {\n url: string;\n token: string;\n};\n\n/**\n * Start console server\n */\nexport async function startConsoleServer(\n config: ServerConfig\n): Promise<ServerInfo> {\n const app = express();\n\n // Generate auth token\n const authToken = crypto.randomBytes(32).toString(\"hex\");\n\n // Middleware\n app.use(express.json());\n\n // Simple rate limiting for static file requests (defense-in-depth)\n // Note: This is a localhost-only dev server with token auth, so this is just\n // a safeguard against accidental abuse or runaway scripts\n const requestCounts = new Map<string, { count: number; resetTime: number }>();\n const RATE_LIMIT_WINDOW = 60 * 1000; // 1 minute\n const RATE_LIMIT_MAX_REQUESTS = 1000; // 1000 requests per minute per IP\n\n app.use((req, res, next) => {\n const ip = req.ip || req.socket.remoteAddress || \"unknown\";\n const now = Date.now();\n const record = requestCounts.get(ip);\n\n if (!record || now > record.resetTime) {\n // New window\n requestCounts.set(ip, { count: 1, resetTime: now + RATE_LIMIT_WINDOW });\n next();\n } else if (record.count < RATE_LIMIT_MAX_REQUESTS) {\n // Within limit\n record.count++;\n next();\n } else {\n // Rate limit exceeded\n res.status(429).json({\n error: \"Too many requests, please slow down\",\n retryAfter: Math.ceil((record.resetTime - now) / 1000),\n });\n }\n });\n\n // Security headers\n app.use((_req, res, next) => {\n res.setHeader(\"X-Frame-Options\", \"DENY\");\n res.setHeader(\"X-Content-Type-Options\", \"nosniff\");\n res.setHeader(\n \"Content-Security-Policy\",\n \"default-src 'self' 'unsafe-inline' 'unsafe-eval'; \" +\n \"style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; \" +\n \"font-src 'self' https://fonts.gstatic.com; \" +\n \"connect-src 'self'\"\n );\n next();\n });\n\n // Request logging middleware\n app.use((req, _res, next) => {\n console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);\n next();\n });\n\n // API routes (with authentication)\n app.use(\n \"/api/metrics\",\n authenticateToken(authToken),\n createMetricsRouter(config)\n );\n app.use(\n \"/api/domains\",\n authenticateToken(authToken),\n createDomainsRouter(config)\n );\n app.use(\n \"/api/emails\",\n authenticateToken(authToken),\n createEmailsRouter(config)\n );\n app.use(\n \"/api/settings\",\n authenticateToken(authToken),\n createSettingsRouter(config)\n );\n app.use(\"/api/user\", authenticateToken(authToken), createUserRouter(config));\n\n // Serve static files from console-ui build\n // __dirname will be dist/ after compilation, console UI is in dist/console/\n const staticDir = path.join(__dirname, \"console\");\n app.use(express.static(staticDir));\n\n // SPA fallback\n app.get(\"*\", (_req, res) => {\n res.sendFile(path.join(staticDir, \"index.html\"));\n });\n\n // Error handler\n app.use(errorHandler);\n\n // Start server\n const server = app.listen(config.port, \"127.0.0.1\");\n\n // Setup graceful shutdown\n const httpTerminator = createHttpTerminator({ server });\n\n process.on(\"SIGTERM\", async () => {\n console.log(\"\\\\nShutting down gracefully...\");\n await httpTerminator.terminate();\n process.exit(0);\n });\n\n process.on(\"SIGINT\", async () => {\n console.log(\"\\\\nShutting down gracefully...\");\n await httpTerminator.terminate();\n process.exit(0);\n });\n\n const url = `http://localhost:${config.port}?token=${authToken}`;\n\n return { url, token: authToken };\n}\n","import type { NextFunction, Request, Response } from \"express\";\n\n/**\n * Token-based authentication middleware\n */\nexport function authenticateToken(expectedToken: string) {\n return (req: Request, res: Response, next: NextFunction) => {\n // Get token from query param or header\n const token = req.query.token || req.headers[\"x-auth-token\"];\n\n if (!token || token !== expectedToken) {\n return res.status(401).json({ error: \"Unauthorized\" });\n }\n\n next();\n };\n}\n","import type { NextFunction, Request, Response } from \"express\";\n\n/**\n * Error handling middleware\n */\nexport function errorHandler(\n err: Error,\n _req: Request,\n res: Response,\n _next: NextFunction\n) {\n console.error(\"Server error:\", err);\n\n res.status(500).json({\n error: \"Internal server error\",\n message: err.message,\n });\n}\n","import type { Request, Response, Router } from \"express\";\nimport { Router as createRouter } from \"express\";\nimport type { ServerConfig } from \"../server.js\";\nimport { fetchDomainInfo } from \"../services/ses-service.js\";\n\nexport function createDomainsRouter(config: ServerConfig): Router {\n const router = createRouter();\n\n /**\n * Get domain verification status\n */\n router.get(\"/:domain\", async (req: Request, res: Response) => {\n try {\n const { domain } = req.params;\n\n if (!domain) {\n return res.status(400).json({ error: \"Domain parameter required\" });\n }\n\n const domainInfo = await fetchDomainInfo(\n config.roleArn,\n config.region,\n domain\n );\n\n res.json(domainInfo);\n } catch (error: unknown) {\n const errorMessage =\n error instanceof Error ? error.message : \"Unknown error\";\n res.status(500).json({ error: errorMessage });\n }\n });\n\n return router;\n}\n","import { GetSendQuotaCommand, SESClient } from \"@aws-sdk/client-ses\";\nimport { GetEmailIdentityCommand, SESv2Client } from \"@aws-sdk/client-sesv2\";\nimport { assumeRole } from \"../../utils/shared/assume-role.js\";\n\nexport type SendQuota = {\n max24HourSend: number;\n maxSendRate: number;\n sentLast24Hours: number;\n};\n\nexport type DomainInfo = {\n domain: string;\n verified: boolean;\n dkimStatus: string;\n dkimTokens: string[];\n};\n\n/**\n * Fetch SES send quota\n */\nexport async function fetchSendQuota(\n roleArn: string | undefined,\n region: string\n): Promise<SendQuota> {\n // For console usage, use current credentials instead of assuming role\n const credentials = roleArn ? await assumeRole(roleArn, region) : undefined;\n const ses = new SESClient({ region, credentials });\n\n const response = await ses.send(new GetSendQuotaCommand({}));\n\n return {\n max24HourSend: response.Max24HourSend || 0,\n maxSendRate: response.MaxSendRate || 0,\n sentLast24Hours: response.SentLast24Hours || 0,\n };\n}\n\n/**\n * Fetch domain verification status\n */\nexport async function fetchDomainInfo(\n roleArn: string | undefined,\n region: string,\n domain: string\n): Promise<DomainInfo> {\n // For console usage, use current credentials instead of assuming role\n const credentials = roleArn ? await assumeRole(roleArn, region) : undefined;\n const sesv2 = new SESv2Client({ region, credentials });\n\n const response = await sesv2.send(\n new GetEmailIdentityCommand({\n EmailIdentity: domain,\n })\n );\n\n return {\n domain,\n verified: response.VerifiedForSendingStatus ?? false,\n dkimStatus: response.DkimAttributes?.Status || \"PENDING\",\n dkimTokens: response.DkimAttributes?.Tokens || [],\n };\n}\n","import type { Request, Response, Router } from \"express\";\nimport { Router as createRouter } from \"express\";\nimport type { ServerConfig } from \"../server.js\";\nimport { fetchEmailById, fetchEmailLogs } from \"../services/email-logs.js\";\n\nexport function createEmailsRouter(config: ServerConfig): Router {\n const router = createRouter();\n\n /**\n * Get email logs\n */\n router.get(\"/\", async (req: Request, res: Response) => {\n try {\n console.log(\"Email logs request received\");\n console.log(\"Query params:\", req.query);\n console.log(\"Config:\", {\n tableName: config.tableName,\n region: config.region,\n accountId: config.accountId,\n });\n\n // Parse query parameters\n const limit = req.query.limit\n ? Number.parseInt(req.query.limit as string, 10)\n : 100;\n const startTime = req.query.startTime\n ? Number.parseInt(req.query.startTime as string, 10)\n : undefined;\n const endTime = req.query.endTime\n ? Number.parseInt(req.query.endTime as string, 10)\n : undefined;\n\n if (!config.tableName) {\n console.log(\"No table name configured\");\n return res.status(400).json({\n error:\n \"Email tracking not enabled. Deploy with enhanced integration to enable email logs.\",\n });\n }\n\n console.log(\"Fetching email logs from DynamoDB...\");\n const logs = await fetchEmailLogs({\n region: config.region,\n tableName: config.tableName,\n accountId: config.accountId,\n limit,\n startTime,\n endTime,\n });\n\n console.log(`Found ${logs.length} email logs`);\n res.json({ logs });\n } catch (error: unknown) {\n const errorMessage =\n error instanceof Error ? error.message : \"Unknown error\";\n console.error(\"Error fetching email logs:\", error);\n res.status(500).json({ error: errorMessage });\n }\n });\n\n /**\n * Get email details by ID\n */\n router.get(\"/:id\", async (req: Request, res: Response) => {\n try {\n const { id } = req.params;\n console.log(\"Email detail request received for ID:\", id);\n console.log(\"Request headers:\", req.headers);\n console.log(\"Request query:\", req.query);\n\n if (!config.tableName) {\n console.log(\"No table name configured\");\n return res.status(400).json({\n error:\n \"Email tracking not enabled. Deploy with enhanced integration to enable email logs.\",\n });\n }\n\n console.log(\"Fetching email details from DynamoDB...\");\n const email = await fetchEmailById(id, {\n region: config.region,\n tableName: config.tableName,\n });\n\n if (!email) {\n console.log(\"Email not found for ID:\", id);\n return res.status(404).json({ error: \"Email not found\" });\n }\n\n console.log(\"Email details found:\", email.messageId);\n console.log(\"Sending response with\", email.events.length, \"events\");\n res.json(email);\n } catch (error: unknown) {\n const errorMessage =\n error instanceof Error ? error.message : \"Unknown error\";\n console.error(\"Error fetching email details:\", error);\n console.error(\n \"Stack trace:\",\n error instanceof Error ? error.stack : \"N/A\"\n );\n res.status(500).json({ error: errorMessage });\n }\n });\n\n /**\n * Get archived email content by message ID\n */\n router.get(\"/:id/archive\", async (req: Request, res: Response) => {\n try {\n const { id } = req.params;\n console.log(\"Archived email request received for message ID:\", id);\n\n if (!config.archivingEnabled) {\n console.log(\"Email archiving not enabled\");\n return res.status(400).json({\n error: \"Email archiving not enabled for this deployment.\",\n });\n }\n\n if (!config.archiveArn) {\n console.log(\"No archive ARN configured\");\n return res.status(400).json({\n error: \"Archive ARN not configured.\",\n });\n }\n\n if (!config.tableName) {\n console.log(\"No table name configured\");\n return res.status(400).json({\n error:\n \"Email tracking not enabled. Need email metadata to search archive.\",\n });\n }\n\n // First, fetch email details from DynamoDB to get search metadata\n console.log(\"Fetching email metadata from DynamoDB...\");\n const emailDetails = await fetchEmailById(id, {\n region: config.region,\n tableName: config.tableName,\n });\n\n if (!emailDetails) {\n console.log(\"Email metadata not found in DynamoDB for ID:\", id);\n return res.status(404).json({\n error: \"Email metadata not found. Cannot search archive.\",\n });\n }\n\n console.log(\"Fetching archived email from Mail Manager...\");\n const { fetchArchivedEmail } = await import(\n \"../services/email-archive.js\"\n );\n const archivedEmail = await fetchArchivedEmail(id, {\n region: config.region,\n archiveArn: config.archiveArn,\n from: emailDetails.from,\n to: emailDetails.to[0], // Use first recipient for search\n subject: emailDetails.subject,\n timestamp: new Date(emailDetails.sentAt),\n });\n\n if (!archivedEmail) {\n console.log(\"Archived email not found for message ID:\", id);\n return res.status(404).json({\n error:\n \"Archived email not found. It may have been sent before archiving was enabled.\",\n });\n }\n\n console.log(\"Archived email found:\", archivedEmail.messageId);\n res.json(archivedEmail);\n } catch (error: unknown) {\n const errorMessage =\n error instanceof Error ? error.message : \"Unknown error\";\n console.error(\"Error fetching archived email:\", error);\n console.error(\n \"Stack trace:\",\n error instanceof Error ? error.stack : \"N/A\"\n );\n res.status(500).json({ error: errorMessage });\n }\n });\n\n return router;\n}\n","import {\n DynamoDBClient,\n QueryCommand,\n ScanCommand,\n} from \"@aws-sdk/client-dynamodb\";\nimport { unmarshall } from \"@aws-sdk/util-dynamodb\";\n\nexport type EmailLog = {\n messageId: string;\n to: string[]; // Array of recipients\n from: string;\n subject: string;\n status:\n | \"delivered\"\n | \"bounced\"\n | \"complained\"\n | \"sent\"\n | \"failed\"\n | \"opened\"\n | \"clicked\";\n sentAt: number;\n accountId?: string;\n errorMessage?: string;\n};\n\nexport type EmailEvent = {\n type:\n | \"sent\"\n | \"delivered\"\n | \"bounced\"\n | \"complained\"\n | \"opened\"\n | \"clicked\"\n | \"failed\";\n timestamp: number;\n metadata?: Record<string, any>;\n};\n\nexport type EmailDetails = {\n id: string;\n messageId: string;\n from: string;\n to: string[];\n replyTo?: string;\n subject: string;\n htmlBody?: string;\n textBody?: string;\n status:\n | \"delivered\"\n | \"bounced\"\n | \"complained\"\n | \"sent\"\n | \"failed\"\n | \"opened\"\n | \"clicked\";\n sentAt: number;\n events: EmailEvent[];\n};\n\ntype FetchEmailLogsOptions = {\n region: string;\n tableName: string;\n accountId?: string;\n limit?: number;\n startTime?: number;\n endTime?: number;\n};\n\n/**\n * Fetch email logs from DynamoDB\n */\nexport async function fetchEmailLogs(\n options: FetchEmailLogsOptions\n): Promise<EmailLog[]> {\n const {\n region,\n tableName,\n accountId,\n limit = 100,\n startTime,\n endTime,\n } = options;\n\n const dynamodb = new DynamoDBClient({ region });\n\n try {\n // If we have accountId, use GSI for better performance\n let items: any[] = [];\n if (accountId) {\n let keyConditionExpression = \"accountId = :accountId\";\n const expressionAttributeValues: Record<string, any> = {\n \":accountId\": { S: accountId },\n };\n\n // Add time range if provided\n if (startTime && endTime) {\n keyConditionExpression += \" AND sentAt BETWEEN :startTime AND :endTime\";\n expressionAttributeValues[\":startTime\"] = { N: startTime.toString() };\n expressionAttributeValues[\":endTime\"] = { N: endTime.toString() };\n } else if (startTime) {\n keyConditionExpression += \" AND sentAt >= :startTime\";\n expressionAttributeValues[\":startTime\"] = { N: startTime.toString() };\n }\n\n const response = await dynamodb.send(\n new QueryCommand({\n TableName: tableName,\n IndexName: \"accountId-sentAt-index\",\n KeyConditionExpression: keyConditionExpression,\n ExpressionAttributeValues: expressionAttributeValues,\n ScanIndexForward: false, // Sort by sentAt descending (newest first)\n })\n );\n\n items = response.Items || [];\n } else {\n // Otherwise, scan the table (less efficient but works without accountId)\n const response = await dynamodb.send(\n new ScanCommand({\n TableName: tableName,\n })\n );\n\n items = response.Items || [];\n }\n\n // Unmarshall all items\n const unmarshalled = items.map((item) => unmarshall(item));\n\n // Group events by messageId to get the latest status for each email\n const emailMap = new Map<string, any>();\n\n for (const item of unmarshalled) {\n const messageId = item.messageId;\n const existing = emailMap.get(messageId);\n\n if (existing) {\n // Keep the event with the most important status\n // Priority: Complaint > Permanent Bounce > Delivery > Transient Bounce > Send\n const currentPriority = getEventPriority(item);\n const existingPriority = getEventPriority(existing);\n\n if (currentPriority > existingPriority) {\n emailMap.set(messageId, item);\n }\n } else {\n emailMap.set(messageId, item);\n }\n }\n\n // Convert map to array and normalize\n const logs = Array.from(emailMap.values())\n .map(normalizeEmailLog)\n .sort((a, b) => b.sentAt - a.sentAt);\n\n // Apply limit\n return logs.slice(0, limit);\n } catch (error) {\n console.error(\"Error fetching email logs:\", error);\n throw error;\n }\n}\n\n/**\n * Get priority for event type (higher = more important to display)\n * Priority: Complaint > Permanent Bounce > Click > Open > Delivery > Transient Bounce > Send\n */\nfunction getEventPriority(item: any): number {\n const type = item.eventType?.toLowerCase();\n\n switch (type) {\n case \"complaint\":\n return 7;\n case \"bounce\": {\n // Permanent bounces (hard bounces) are more important than delivery\n // Transient bounces (OOTO, mailbox full) are less important than delivery\n const bounceType = item.bounceType?.toLowerCase();\n return bounceType === \"permanent\" ? 6 : 2;\n }\n case \"click\":\n return 5;\n case \"open\":\n return 4;\n case \"delivery\":\n return 3;\n case \"send\":\n return 1;\n default:\n return 0;\n }\n}\n\n/**\n * Normalize email log data from DynamoDB\n */\nfunction normalizeEmailLog(data: any): EmailLog {\n // Determine status based on eventType\n let status: EmailLog[\"status\"] = \"sent\";\n const eventType = data.eventType?.toLowerCase();\n\n if (eventType === \"complaint\") {\n status = \"complained\";\n } else if (eventType === \"bounce\") {\n status = \"bounced\";\n } else if (eventType === \"click\") {\n status = \"clicked\";\n } else if (eventType === \"open\") {\n status = \"opened\";\n } else if (eventType === \"delivery\") {\n status = \"delivered\";\n } else if (eventType === \"send\") {\n status = \"sent\";\n } else if (data.errorMessage) {\n status = \"failed\";\n }\n\n // Handle 'to' field - it's stored as a String Set in DynamoDB\n // DynamoDB String Sets get unmarshalled as JavaScript Set objects\n let toAddresses: string[] = [];\n const toField = data.to || data.destination; // CSV export might show as 'destination'\n\n if (toField) {\n console.log(\n \"Raw 'to' field:\",\n toField,\n \"Type:\",\n typeof toField,\n \"Constructor:\",\n toField.constructor?.name\n );\n\n if (toField instanceof Set) {\n // DynamoDB String Set -> JavaScript Set\n toAddresses = Array.from(toField);\n } else if (Array.isArray(toField)) {\n toAddresses = toField;\n } else if (typeof toField === \"string\") {\n toAddresses = [toField];\n }\n }\n\n console.log(\"Normalized toAddresses:\", toAddresses);\n\n return {\n messageId: data.messageId,\n to: toAddresses,\n from: data.from || \"unknown\",\n subject: data.subject || \"(no subject)\",\n status,\n sentAt: Number(data.sentAt),\n accountId: data.accountId,\n errorMessage: data.errorMessage,\n };\n}\n\n/**\n * Fetch email details by message ID (with all events)\n */\nexport async function fetchEmailById(\n messageId: string,\n options: { region: string; tableName: string }\n): Promise<EmailDetails | null> {\n const { region, tableName } = options;\n const dynamodb = new DynamoDBClient({ region });\n\n try {\n // Query all events for this messageId\n const response = await dynamodb.send(\n new QueryCommand({\n TableName: tableName,\n KeyConditionExpression: \"messageId = :messageId\",\n ExpressionAttributeValues: {\n \":messageId\": { S: messageId },\n },\n })\n );\n\n const items = response.Items || [];\n\n if (items.length === 0) {\n return null;\n }\n\n // Unmarshall all events\n const events = items.map((item) => unmarshall(item));\n\n // Get the send event (has the email content)\n const sendEvent = events.find((e) => e.eventType?.toLowerCase() === \"send\");\n\n if (!sendEvent) {\n return null;\n }\n\n console.log(\"Send event fields:\", {\n from: sendEvent.from,\n source: sendEvent.source,\n subject: sendEvent.subject,\n to: sendEvent.to,\n destination: sendEvent.destination,\n availableKeys: Object.keys(sendEvent),\n });\n\n // Try to extract email content from eventData\n let htmlBody: string | undefined;\n let textBody: string | undefined;\n\n if (sendEvent.eventData) {\n try {\n const eventData = JSON.parse(sendEvent.eventData);\n console.log(\"Send event data keys:\", Object.keys(eventData));\n\n // SES doesn't include email content in events by default\n // Check if content was somehow included\n if (eventData.content) {\n htmlBody = eventData.content.html;\n textBody = eventData.content.text;\n }\n\n // Check mail.content (unlikely but worth trying)\n if (eventData.mail?.content) {\n htmlBody = eventData.mail.content.html;\n textBody = eventData.mail.content.text;\n }\n } catch (e) {\n console.error(\"Failed to parse eventData:\", e);\n }\n }\n\n // Parse to addresses\n let toAddresses: string[] = [];\n const toField = sendEvent.to || sendEvent.destination;\n\n if (toField) {\n if (toField instanceof Set) {\n toAddresses = Array.from(toField);\n } else if (Array.isArray(toField)) {\n toAddresses = toField;\n } else if (typeof toField === \"string\") {\n toAddresses = [toField];\n }\n }\n\n // Determine final status (priority order: complaint > bounce > click > open > delivery > sent)\n let status: EmailDetails[\"status\"] = \"sent\";\n const hasDelivery = events.some(\n (e) => e.eventType?.toLowerCase() === \"delivery\"\n );\n const hasBounce = events.some(\n (e) => e.eventType?.toLowerCase() === \"bounce\"\n );\n const hasComplaint = events.some(\n (e) => e.eventType?.toLowerCase() === \"complaint\"\n );\n const hasOpen = events.some((e) => e.eventType?.toLowerCase() === \"open\");\n const hasClick = events.some((e) => e.eventType?.toLowerCase() === \"click\");\n\n if (hasComplaint) {\n status = \"complained\";\n } else if (hasBounce) {\n status = \"bounced\";\n } else if (hasClick) {\n status = \"clicked\";\n } else if (hasOpen) {\n status = \"opened\";\n } else if (hasDelivery) {\n status = \"delivered\";\n }\n\n // Map events to simplified timeline\n const timeline: EmailEvent[] = events\n .map((event) => {\n const eventType = event.eventType?.toLowerCase();\n let type: EmailEvent[\"type\"] = \"sent\";\n\n switch (eventType) {\n case \"send\":\n type = \"sent\";\n break;\n case \"delivery\":\n type = \"delivered\";\n break;\n case \"bounce\":\n type = \"bounced\";\n break;\n case \"complaint\":\n type = \"complained\";\n break;\n case \"open\":\n type = \"opened\";\n break;\n case \"click\":\n type = \"clicked\";\n break;\n default:\n type = \"sent\";\n }\n\n const metadata: Record<string, any> = {};\n\n // Add relevant metadata based on event type\n if (eventType === \"bounce\" && event.bounceType) {\n metadata.bounceType = event.bounceType;\n metadata.bounceSubType = event.bounceSubType;\n }\n\n if (eventType === \"complaint\" && event.complaintFeedbackType) {\n metadata.feedbackType = event.complaintFeedbackType;\n }\n\n if (eventType === \"click\" && event.link) {\n metadata.link = event.link;\n }\n\n if (event.userAgent) {\n metadata.userAgent = event.userAgent;\n }\n\n return {\n type,\n timestamp: Number(event.sentAt || event.timestamp),\n metadata: Object.keys(metadata).length > 0 ? metadata : undefined,\n };\n })\n .sort((a, b) => a.timestamp - b.timestamp);\n\n return {\n id: messageId,\n messageId,\n from: sendEvent.from || \"unknown\",\n to: toAddresses,\n replyTo: sendEvent.replyTo,\n subject: sendEvent.subject || \"(no subject)\",\n htmlBody: htmlBody || sendEvent.htmlBody,\n textBody: textBody || sendEvent.textBody,\n status,\n sentAt: Number(sendEvent.sentAt),\n events: timeline,\n };\n } catch (error) {\n console.error(\"Error fetching email by ID:\", error);\n throw error;\n }\n}\n","import type { Request, Response, Router } from \"express\";\nimport { Router as createRouter } from \"express\";\nimport type { ServerConfig } from \"../server.js\";\nimport { fetchSESMetrics } from \"../services/aws-metrics.js\";\nimport { fetchSendQuota } from \"../services/ses-service.js\";\n\nexport function createMetricsRouter(config: ServerConfig): Router {\n const router = createRouter();\n\n /**\n * SSE endpoint for real-time metrics\n */\n router.get(\"/stream\", async (req: Request, res: Response) => {\n // Set SSE headers\n res.setHeader(\"Content-Type\", \"text/event-stream\");\n res.setHeader(\"Cache-Control\", \"no-cache\");\n res.setHeader(\"Connection\", \"keep-alive\");\n\n // Send initial connection event\n res.write('data: {\"type\":\"connected\"}\\n\\n');\n\n // Get time range from query params, default to last 24 hours\n const { startTime, endTime } = req.query;\n const getTimeRange = () => ({\n start: startTime\n ? new Date(Number.parseInt(startTime as string, 10))\n : new Date(Date.now() - 24 * 60 * 60 * 1000),\n end: endTime\n ? new Date(Number.parseInt(endTime as string, 10))\n : new Date(),\n });\n\n // Function to fetch and send metrics\n const sendMetrics = async () => {\n try {\n console.log(\"Fetching metrics from AWS...\");\n\n const timeRange = getTimeRange();\n\n console.log(\"Time range:\", timeRange);\n console.log(\"Config:\", {\n region: config.region,\n roleArn: config.roleArn\n ? `${config.roleArn.substring(0, 30)}...`\n : \"using current credentials\",\n });\n\n const [metrics, quota] = await Promise.all([\n fetchSESMetrics(\n config.roleArn,\n config.region,\n timeRange,\n config.tableName\n ),\n fetchSendQuota(config.roleArn, config.region),\n ]);\n\n console.log(\"Metrics fetched successfully\");\n\n const data = {\n type: \"metrics\",\n timestamp: Date.now(),\n metrics,\n quota,\n };\n\n res.write(`data: ${JSON.stringify(data)}\\n\\n`);\n } catch (error: unknown) {\n const errorMessage =\n error instanceof Error ? error.message : \"Unknown error\";\n console.error(\"Error fetching metrics:\", error);\n res.write(\n `data: ${JSON.stringify({ type: \"error\", error: errorMessage })}\\n\\n`\n );\n }\n };\n\n // Send immediately on connect\n await sendMetrics();\n\n // Poll every 60 seconds\n const interval = setInterval(sendMetrics, 60_000);\n\n // Clean up on disconnect\n req.on(\"close\", () => {\n clearInterval(interval);\n });\n });\n\n /**\n * Get current metrics snapshot (REST endpoint)\n */\n router.get(\"/snapshot\", async (_req: Request, res: Response) => {\n try {\n const timeRange = {\n start: new Date(Date.now() - 24 * 60 * 60 * 1000),\n end: new Date(),\n };\n\n const [metrics, quota] = await Promise.all([\n fetchSESMetrics(\n config.roleArn,\n config.region,\n timeRange,\n config.tableName\n ),\n fetchSendQuota(config.roleArn, config.region),\n ]);\n\n res.json({ metrics, quota });\n } catch (error: unknown) {\n const errorMessage =\n error instanceof Error ? error.message : \"Unknown error\";\n res.status(500).json({ error: errorMessage });\n }\n });\n\n /**\n * Get metrics for a specific time range\n */\n router.get(\"/\", async (req: Request, res: Response) => {\n try {\n const { startTime, endTime } = req.query;\n\n // Default to last 24 hours if no time range provided\n const timeRange = {\n start: startTime\n ? new Date(Number.parseInt(startTime as string, 10))\n : new Date(Date.now() - 24 * 60 * 60 * 1000),\n end: endTime\n ? new Date(Number.parseInt(endTime as string, 10))\n : new Date(),\n };\n\n const [metrics, quota] = await Promise.all([\n fetchSESMetrics(\n config.roleArn,\n config.region,\n timeRange,\n config.tableName\n ),\n fetchSendQuota(config.roleArn, config.region),\n ]);\n\n res.json({\n metrics,\n quota,\n timestamp: Date.now(),\n });\n } catch (error: unknown) {\n const errorMessage =\n error instanceof Error ? error.message : \"Unknown error\";\n console.error(\"Error fetching metrics:\", error);\n res.status(500).json({ error: errorMessage });\n }\n });\n\n return router;\n}\n","import {\n CloudWatchClient,\n GetMetricDataCommand,\n type MetricDataQuery,\n} from \"@aws-sdk/client-cloudwatch\";\nimport { assumeRole } from \"../../utils/shared/assume-role.js\";\n\nexport type MetricsData = {\n sends: Array<{ timestamp: number; value: number }>;\n bounces: Array<{ timestamp: number; value: number }>;\n complaints: Array<{ timestamp: number; value: number }>;\n deliveries: Array<{ timestamp: number; value: number }>;\n opens: Array<{ timestamp: number; value: number }>;\n clicks: Array<{ timestamp: number; value: number }>;\n};\n\n/**\n * Fetch SES metrics from CloudWatch\n */\nexport async function fetchSESMetrics(\n roleArn: string | undefined,\n region: string,\n timeRange: { start: Date; end: Date },\n tableName?: string\n): Promise<MetricsData> {\n // For console usage, use current credentials instead of assuming role\n const credentials = roleArn ? await assumeRole(roleArn, region) : undefined;\n\n // Create CloudWatch client\n const cloudwatch = new CloudWatchClient({ region, credentials });\n\n // Define metric queries\n const queries: MetricDataQuery[] = [\n {\n Id: \"sends\",\n MetricStat: {\n Metric: {\n Namespace: \"AWS/SES\",\n MetricName: \"Send\",\n },\n Period: 300, // 5 minutes\n Stat: \"Sum\",\n },\n },\n {\n Id: \"bounces\",\n MetricStat: {\n Metric: {\n Namespace: \"AWS/SES\",\n MetricName: \"Bounce\",\n },\n Period: 300,\n Stat: \"Sum\",\n },\n },\n {\n Id: \"complaints\",\n MetricStat: {\n Metric: {\n Namespace: \"AWS/SES\",\n MetricName: \"Complaint\",\n },\n Period: 300,\n Stat: \"Sum\",\n },\n },\n {\n Id: \"deliveries\",\n MetricStat: {\n Metric: {\n Namespace: \"AWS/SES\",\n MetricName: \"Delivery\",\n },\n Period: 300,\n Stat: \"Sum\",\n },\n },\n ];\n\n // Fetch metrics\n const response = await cloudwatch.send(\n new GetMetricDataCommand({\n MetricDataQueries: queries,\n StartTime: timeRange.start,\n EndTime: timeRange.end,\n })\n );\n\n // Parse results\n const results = response.MetricDataResults || [];\n\n const parseMetric = (id: string) => {\n const metric = results.find((r) => r.Id === id);\n if (!(metric?.Timestamps && metric.Values)) {\n return [];\n }\n\n return metric.Timestamps.map((timestamp, i) => ({\n timestamp: timestamp.getTime(),\n value: metric.Values?.[i] || 0,\n }));\n };\n\n // Fetch Opens and Clicks from DynamoDB if table name is provided\n let opens: Array<{ timestamp: number; value: number }> = [];\n let clicks: Array<{ timestamp: number; value: number }> = [];\n\n if (tableName) {\n try {\n const { fetchDynamoDBMetrics } = await import(\"./dynamodb-metrics.js\");\n const dynamoMetrics = await fetchDynamoDBMetrics(\n region,\n tableName,\n timeRange\n );\n opens = dynamoMetrics.opens;\n clicks = dynamoMetrics.clicks;\n } catch (error) {\n console.error(\"Error fetching DynamoDB metrics:\", error);\n // Continue with empty arrays\n }\n }\n\n return {\n sends: parseMetric(\"sends\"),\n bounces: parseMetric(\"bounces\"),\n complaints: parseMetric(\"complaints\"),\n deliveries: parseMetric(\"deliveries\"),\n opens,\n clicks,\n };\n}\n","import dns from \"node:dns/promises\";\nimport type { Request, Response, Router } from \"express\";\nimport { Router as createRouter } from \"express\";\nimport { loadConnectionMetadata } from \"../../utils/shared/metadata.js\";\nimport type { ServerConfig } from \"../server.js\";\nimport { fetchEmailSettings } from \"../services/settings-service.js\";\n\nexport function createSettingsRouter(config: ServerConfig): Router {\n const router = createRouter();\n\n /**\n * Get deployment configuration\n */\n router.get(\"/deployment\", async (_req: Request, res: Response) => {\n try {\n res.json({\n archivingEnabled: config.archivingEnabled ?? false,\n archiveArn: config.archiveArn,\n tableName: config.tableName,\n region: config.region,\n });\n } catch (error: unknown) {\n const errorMessage =\n error instanceof Error ? error.message : \"Unknown error\";\n console.error(\"Error fetching deployment config:\", error);\n res.status(500).json({ error: errorMessage });\n }\n });\n\n /**\n * Get email settings (configuration set + identity)\n */\n router.get(\"/\", async (_req: Request, res: Response) => {\n try {\n // Load metadata to get configuration\n const metadata = await loadConnectionMetadata(\n config.accountId || \"\",\n config.region\n );\n\n if (!metadata) {\n return res.status(404).json({\n error: \"No Wraps infrastructure found for this account and region\",\n });\n }\n\n // Get configuration set name and domain from metadata\n const configSetName = \"wraps-email-tracking\"; // Always use this name\n const domain = metadata.services.email?.config.domain;\n\n // Fetch settings from AWS\n const settings = await fetchEmailSettings(\n config.roleArn,\n config.region,\n configSetName,\n domain\n );\n\n // Add region to response\n res.json({\n ...settings,\n region: config.region,\n });\n } catch (error: unknown) {\n const errorMessage =\n error instanceof Error ? error.message : \"Unknown error\";\n console.error(\"Error fetching settings:\", error);\n res.status(500).json({ error: errorMessage });\n }\n });\n\n /**\n * Verify tracking domain CNAME\n */\n router.get(\"/verify-tracking-domain\", async (req: Request, res: Response) => {\n try {\n const { domain, expectedTarget } = req.query;\n\n if (!domain || typeof domain !== \"string\") {\n return res.status(400).json({ error: \"Domain parameter required\" });\n }\n\n if (!expectedTarget || typeof expectedTarget !== \"string\") {\n return res\n .status(400)\n .json({ error: \"Expected target parameter required\" });\n }\n\n console.log(`[Verify] Checking CNAME for: ${domain}`);\n console.log(`[Verify] Expected target: ${expectedTarget}`);\n\n // Check CNAME record using DNS\n const records = await dns.resolveCname(domain);\n\n console.log(\"[Verify] CNAME records found:\", records);\n\n // Check if any CNAME points to the expected target\n const verified = records.some((record) =>\n record.toLowerCase().includes(expectedTarget.toLowerCase())\n );\n\n console.log(`[Verify] Verified: ${verified}`);\n\n res.json({\n verified,\n error: verified\n ? undefined\n : `CNAME not pointing to ${expectedTarget}. Found: ${records.join(\", \")}`,\n });\n } catch (error: any) {\n console.error(\"[Verify] Error verifying tracking domain:\", error);\n\n // If no CNAME record exists, DNS will throw ENODATA or ENOTFOUND\n if (error.code === \"ENODATA\" || error.code === \"ENOTFOUND\") {\n return res.json({\n verified: false,\n error: \"No CNAME record found for this domain\",\n });\n }\n\n const errorMessage =\n error instanceof Error ? error.message : \"Failed to verify\";\n res.json({\n verified: false,\n error: errorMessage,\n });\n }\n });\n\n /**\n * Verify DMARC TXT record\n */\n router.get(\"/verify-dmarc\", async (req: Request, res: Response) => {\n try {\n const { domain } = req.query;\n\n if (!domain || typeof domain !== \"string\") {\n return res.status(400).json({ error: \"Domain parameter required\" });\n }\n\n const dmarcDomain = `_dmarc.${domain}`;\n\n console.log(`[Verify] Checking DMARC for: ${dmarcDomain}`);\n\n // Use Node.js DNS to resolve TXT records\n const records = await dns.resolveTxt(dmarcDomain);\n\n console.log(\"[Verify] TXT records found:\", records);\n\n // Check if there's a TXT record that starts with \"v=DMARC1\"\n // TXT records are arrays of strings, so we need to join them\n const hasDmarc = records.some((record) => {\n const value = record.join(\"\");\n return value.startsWith(\"v=DMARC1\");\n });\n\n console.log(`[Verify] DMARC verified: ${hasDmarc}`);\n\n res.json({\n verified: hasDmarc,\n error: hasDmarc ? undefined : \"DMARC record not found\",\n });\n } catch (error: any) {\n console.error(\"[Verify] Error verifying DMARC:\", error);\n\n // If no TXT record exists, DNS will throw ENODATA or ENOTFOUND\n if (error.code === \"ENODATA\" || error.code === \"ENOTFOUND\") {\n return res.json({\n verified: false,\n error: \"No DMARC record found for this domain\",\n });\n }\n\n const errorMessage =\n error instanceof Error ? error.message : \"Failed to verify\";\n res.json({\n verified: false,\n error: errorMessage,\n });\n }\n });\n\n /**\n * Update configuration set sending options\n */\n router.put(\"/config-set/sending\", async (req: Request, res: Response) => {\n try {\n const { enabled } = req.body;\n\n if (typeof enabled !== \"boolean\") {\n return res.status(400).json({ error: \"enabled must be a boolean\" });\n }\n\n // Load metadata to get configuration set name\n const metadata = await loadConnectionMetadata(\n config.accountId || \"\",\n config.region\n );\n\n if (!metadata) {\n return res.status(404).json({\n error: \"No Wraps infrastructure found for this account and region\",\n });\n }\n\n const configSetName = \"wraps-email-tracking\";\n\n console.log(\n `[Settings] Updating sending options for ${configSetName}: ${enabled}`\n );\n\n // Update sending options via AWS SDK\n const { SESv2Client, PutConfigurationSetSendingOptionsCommand } =\n await import(\"@aws-sdk/client-sesv2\");\n const { assumeRole } = await import(\"../../utils/shared/assume-role.js\");\n\n const credentials = config.roleArn\n ? await assumeRole(config.roleArn, config.region)\n : undefined;\n const sesClient = new SESv2Client({ region: config.region, credentials });\n\n await sesClient.send(\n new PutConfigurationSetSendingOptionsCommand({\n ConfigurationSetName: configSetName,\n SendingEnabled: enabled,\n })\n );\n\n console.log(\"[Settings] Successfully updated sending options\");\n\n res.json({ success: true });\n } catch (error: unknown) {\n const errorMessage =\n error instanceof Error ? error.message : \"Unknown error\";\n console.error(\"[Settings] Error updating sending options:\", error);\n res.status(500).json({ error: errorMessage });\n }\n });\n\n /**\n * Update configuration set reputation options\n */\n router.put(\"/config-set/reputation\", async (req: Request, res: Response) => {\n try {\n const { enabled } = req.body;\n\n if (typeof enabled !== \"boolean\") {\n return res.status(400).json({ error: \"enabled must be a boolean\" });\n }\n\n // Load metadata to get configuration set name\n const metadata = await loadConnectionMetadata(\n config.accountId || \"\",\n config.region\n );\n\n if (!metadata) {\n return res.status(404).json({\n error: \"No Wraps infrastructure found for this account and region\",\n });\n }\n\n const configSetName = \"wraps-email-tracking\";\n\n console.log(\n `[Settings] Updating reputation options for ${configSetName}: ${enabled}`\n );\n\n // Update reputation options via AWS SDK\n const { SESv2Client, PutConfigurationSetReputationOptionsCommand } =\n await import(\"@aws-sdk/client-sesv2\");\n const { assumeRole } = await import(\"../../utils/shared/assume-role.js\");\n\n const credentials = config.roleArn\n ? await assumeRole(config.roleArn, config.region)\n : undefined;\n const sesClient = new SESv2Client({ region: config.region, credentials });\n\n await sesClient.send(\n new PutConfigurationSetReputationOptionsCommand({\n ConfigurationSetName: configSetName,\n ReputationMetricsEnabled: enabled,\n })\n );\n\n console.log(\"[Settings] Successfully updated reputation options\");\n\n res.json({ success: true });\n } catch (error: unknown) {\n const errorMessage =\n error instanceof Error ? error.message : \"Unknown error\";\n console.error(\"[Settings] Error updating reputation options:\", error);\n res.status(500).json({ error: errorMessage });\n }\n });\n\n /**\n * Update tracking domain\n */\n router.put(\n \"/config-set/tracking-domain\",\n async (req: Request, res: Response) => {\n try {\n const { domain } = req.body;\n\n if (!domain || typeof domain !== \"string\") {\n return res.status(400).json({ error: \"domain must be a string\" });\n }\n\n // Validate domain format (basic check)\n const domainRegex = /^[a-zA-Z0-9][a-zA-Z0-9-_.]+[a-zA-Z0-9]$/;\n if (!domainRegex.test(domain)) {\n return res.status(400).json({ error: \"Invalid domain format\" });\n }\n\n // Load metadata to get configuration set name\n const metadata = await loadConnectionMetadata(\n config.accountId || \"\",\n config.region\n );\n\n if (!metadata) {\n return res.status(404).json({\n error: \"No Wraps infrastructure found for this account and region\",\n });\n }\n\n const configSetName = \"wraps-email-tracking\";\n\n console.log(\n `[Settings] Updating tracking domain for ${configSetName}: ${domain}`\n );\n\n // Update tracking options via AWS SDK\n const { SESv2Client, PutConfigurationSetTrackingOptionsCommand } =\n await import(\"@aws-sdk/client-sesv2\");\n const { assumeRole } = await import(\n \"../../utils/shared/assume-role.js\"\n );\n\n const credentials = config.roleArn\n ? await assumeRole(config.roleArn, config.region)\n : undefined;\n const sesClient = new SESv2Client({\n region: config.region,\n credentials,\n });\n\n await sesClient.send(\n new PutConfigurationSetTrackingOptionsCommand({\n ConfigurationSetName: configSetName,\n CustomRedirectDomain: domain,\n })\n );\n\n console.log(\"[Settings] Successfully updated tracking domain\");\n\n res.json({ success: true });\n } catch (error: unknown) {\n const errorMessage =\n error instanceof Error ? error.message : \"Unknown error\";\n console.error(\"[Settings] Error updating tracking domain:\", error);\n res.status(500).json({ error: errorMessage });\n }\n }\n );\n\n return router;\n}\n","import {\n GetConfigurationSetCommand,\n GetEmailIdentityCommand,\n SESv2Client,\n} from \"@aws-sdk/client-sesv2\";\nimport { assumeRole } from \"../../utils/shared/assume-role.js\";\n\nexport type EmailSettings = {\n configurationSet?: ConfigurationSetDetails;\n identity?: EmailIdentityDetails;\n};\n\nexport type ConfigurationSetDetails = {\n name: string;\n trackingOptions?: {\n customRedirectDomain?: string;\n httpsPolicy?: \"REQUIRE\" | \"OPTIONAL\";\n };\n deliveryOptions?: {\n tlsPolicy?: \"REQUIRE\" | \"OPTIONAL\";\n sendingPoolName?: string;\n };\n reputationOptions?: {\n reputationMetricsEnabled: boolean;\n lastFreshStart?: Date;\n };\n sendingOptions?: {\n sendingEnabled: boolean;\n };\n suppressionOptions?: {\n suppressedReasons?: (\"BOUNCE\" | \"COMPLAINT\")[];\n };\n};\n\nexport type EmailIdentityDetails = {\n identityType: \"EMAIL_ADDRESS\" | \"DOMAIN\";\n identityName: string;\n verificationStatus:\n | \"PENDING\"\n | \"SUCCESS\"\n | \"FAILED\"\n | \"TEMPORARY_FAILURE\"\n | \"NOT_STARTED\";\n dkimAttributes?: {\n status: \"SUCCESS\" | \"PENDING\" | \"FAILED\" | \"NOT_STARTED\";\n tokens?: string[];\n signingEnabled: boolean;\n signingKeyLength?: \"RSA_1024_BIT\" | \"RSA_2048_BIT\";\n };\n mailFromAttributes?: {\n mailFromDomain?: string;\n mailFromDomainStatus?: \"PENDING\" | \"SUCCESS\" | \"FAILED\";\n behaviorOnMxFailure?: \"USE_DEFAULT_VALUE\" | \"REJECT_MESSAGE\";\n };\n configurationSetName?: string;\n verifiedForSendingStatus: boolean;\n tags?: Record<string, string>;\n};\n\n/**\n * Fetch configuration set details\n */\nexport async function fetchConfigurationSet(\n roleArn: string | undefined,\n region: string,\n configSetName: string\n): Promise<ConfigurationSetDetails> {\n const credentials = roleArn ? await assumeRole(roleArn, region) : undefined;\n const sesv2 = new SESv2Client({ region, credentials });\n\n const response = await sesv2.send(\n new GetConfigurationSetCommand({\n ConfigurationSetName: configSetName,\n })\n );\n\n return {\n name: configSetName,\n trackingOptions: response.TrackingOptions\n ? {\n customRedirectDomain: response.TrackingOptions.CustomRedirectDomain,\n httpsPolicy: response.TrackingOptions.HttpsPolicy as\n | \"REQUIRE\"\n | \"OPTIONAL\",\n }\n : undefined,\n deliveryOptions: response.DeliveryOptions\n ? {\n tlsPolicy: response.DeliveryOptions.TlsPolicy as\n | \"REQUIRE\"\n | \"OPTIONAL\",\n sendingPoolName: response.DeliveryOptions.SendingPoolName,\n }\n : undefined,\n reputationOptions: response.ReputationOptions\n ? {\n reputationMetricsEnabled:\n response.ReputationOptions.ReputationMetricsEnabled ?? false,\n lastFreshStart: response.ReputationOptions.LastFreshStart,\n }\n : undefined,\n sendingOptions: response.SendingOptions\n ? {\n sendingEnabled: response.SendingOptions.SendingEnabled ?? true,\n }\n : undefined,\n suppressionOptions: response.SuppressionOptions\n ? {\n suppressedReasons: response.SuppressionOptions.SuppressedReasons as\n | (\"BOUNCE\" | \"COMPLAINT\")[]\n | undefined,\n }\n : undefined,\n };\n}\n\n/**\n * Fetch email identity details\n */\nexport async function fetchEmailIdentity(\n roleArn: string | undefined,\n region: string,\n identityName: string\n): Promise<EmailIdentityDetails> {\n const credentials = roleArn ? await assumeRole(roleArn, region) : undefined;\n const sesv2 = new SESv2Client({ region, credentials });\n\n const response = await sesv2.send(\n new GetEmailIdentityCommand({\n EmailIdentity: identityName,\n })\n );\n\n return {\n identityType: response.IdentityType as \"EMAIL_ADDRESS\" | \"DOMAIN\",\n identityName,\n verificationStatus:\n response.VerificationStatus as EmailIdentityDetails[\"verificationStatus\"],\n dkimAttributes: response.DkimAttributes\n ? {\n status: response.DkimAttributes.Status as\n | \"SUCCESS\"\n | \"PENDING\"\n | \"FAILED\"\n | \"NOT_STARTED\",\n tokens: response.DkimAttributes.Tokens,\n signingEnabled: response.DkimAttributes.SigningEnabled ?? false,\n signingKeyLength: response.DkimAttributes\n .NextSigningKeyLength as NonNullable<\n EmailIdentityDetails[\"dkimAttributes\"]\n >[\"signingKeyLength\"],\n }\n : undefined,\n mailFromAttributes: response.MailFromAttributes\n ? {\n mailFromDomain: response.MailFromAttributes.MailFromDomain,\n mailFromDomainStatus: response.MailFromAttributes\n .MailFromDomainStatus as NonNullable<\n EmailIdentityDetails[\"mailFromAttributes\"]\n >[\"mailFromDomainStatus\"],\n behaviorOnMxFailure: response.MailFromAttributes\n .BehaviorOnMxFailure as NonNullable<\n EmailIdentityDetails[\"mailFromAttributes\"]\n >[\"behaviorOnMxFailure\"],\n }\n : undefined,\n configurationSetName: response.ConfigurationSetName,\n verifiedForSendingStatus: response.VerifiedForSendingStatus ?? false,\n tags: response.Tags?.reduce(\n (acc, tag) => {\n if (tag.Key) {\n acc[tag.Key] = tag.Value || \"\";\n }\n return acc;\n },\n {} as Record<string, string>\n ),\n };\n}\n\n/**\n * Fetch complete email settings\n */\nexport async function fetchEmailSettings(\n roleArn: string | undefined,\n region: string,\n configSetName?: string,\n domain?: string\n): Promise<EmailSettings> {\n const settings: EmailSettings = {};\n\n if (configSetName) {\n try {\n settings.configurationSet = await fetchConfigurationSet(\n roleArn,\n region,\n configSetName\n );\n } catch (error) {\n console.error(\"Failed to fetch configuration set:\", error);\n }\n }\n\n if (domain) {\n try {\n settings.identity = await fetchEmailIdentity(roleArn, region, domain);\n } catch (error) {\n console.error(\"Failed to fetch email identity:\", error);\n }\n }\n\n return settings;\n}\n","import type { Request, Response, Router } from \"express\";\nimport { Router as createRouter } from \"express\";\nimport { loadConnectionMetadata } from \"../../utils/shared/metadata.js\";\nimport type { ServerConfig } from \"../server.js\";\n\nexport function createUserRouter(config: ServerConfig): Router {\n const router = createRouter();\n\n /**\n * Get current AWS user/account information\n */\n router.get(\"/\", async (_req: Request, res: Response) => {\n try {\n const accountId = config.accountId || \"Unknown\";\n const region = config.region;\n\n console.log(\n \"[User API] Fetching user info for account:\",\n accountId,\n \"region:\",\n region\n );\n\n // Load metadata to get additional details\n const metadata = await loadConnectionMetadata(accountId, region);\n console.log(\n \"[User API] Metadata loaded:\",\n metadata ? \"found\" : \"not found\"\n );\n\n // Get AWS account alias if available (for better UX)\n let accountAlias = accountId;\n try {\n if (config.roleArn) {\n console.log(\"[User API] Attempting to fetch account alias via IAM\");\n const { assumeRole } = await import(\n \"../../utils/shared/assume-role.js\"\n );\n const { IAMClient, ListAccountAliasesCommand } = await import(\n \"@aws-sdk/client-iam\"\n );\n\n const credentials = await assumeRole(config.roleArn, region);\n const iamClient = new IAMClient({ region, credentials });\n\n const response = await iamClient.send(\n new ListAccountAliasesCommand({})\n );\n\n if (response.AccountAliases && response.AccountAliases.length > 0) {\n accountAlias = response.AccountAliases[0];\n console.log(\"[User API] Account alias found:\", accountAlias);\n } else {\n console.log(\"[User API] No account alias found, using account ID\");\n }\n } else {\n console.log(\"[User API] No roleArn, skipping account alias lookup\");\n }\n } catch (error) {\n // Silently fail if we can't get account alias\n console.error(\"[User API] Error fetching account alias:\", error);\n }\n\n const responseData = {\n accountId,\n accountAlias,\n region,\n provider: metadata?.provider || \"unknown\",\n domain: metadata?.services?.email?.config?.domain || null,\n preset: metadata?.services?.email?.preset || null,\n timestamp: metadata?.timestamp || null,\n };\n\n console.log(\"[User API] Sending response:\", responseData);\n res.json(responseData);\n } catch (error: unknown) {\n const errorMessage =\n error instanceof Error ? error.message : \"Unknown error\";\n console.error(\"[User API] Error fetching user info:\", error);\n res.status(500).json({ error: errorMessage });\n }\n });\n\n return router;\n}\n","import * as clack from \"@clack/prompts\";\nimport pc from \"picocolors\";\nimport type { DestroyOptions } from \"../../types/index.js\";\nimport {\n getAWSRegion,\n validateAWSCredentials,\n} from \"../../utils/shared/aws.js\";\nimport { loadConnectionMetadata } from \"../../utils/shared/metadata.js\";\nimport { emailDestroy } from \"../email/destroy.js\";\n\n/**\n * Global Destroy command - Show services and route to service-specific destroy\n */\nexport async function destroy(options: DestroyOptions): Promise<void> {\n clack.intro(pc.bold(\"Wraps Infrastructure Teardown\"));\n\n // 1. Validate AWS credentials\n const spinner = clack.spinner();\n spinner.start(\"Validating AWS credentials\");\n\n let identity;\n try {\n identity = await validateAWSCredentials();\n spinner.stop(\"AWS credentials validated\");\n } catch (error: any) {\n spinner.stop(\"AWS credentials validation failed\");\n throw error;\n }\n\n // 2. Get region\n const region = await getAWSRegion();\n\n // 3. Load connection metadata to see what services are deployed\n const metadata = await loadConnectionMetadata(identity.accountId, region);\n\n const deployedServices: string[] = [];\n\n if (metadata?.services?.email) {\n deployedServices.push(\"email\");\n }\n\n if (deployedServices.length === 0) {\n clack.log.warn(\"No Wraps services found in this region\");\n console.log(\n `\\nRun ${pc.cyan(\"wraps email init\")} to deploy infrastructure.\\n`\n );\n process.exit(0);\n }\n\n // 4. If only one service, destroy it directly\n if (deployedServices.length === 1) {\n const service = deployedServices[0];\n clack.log.info(`Found ${pc.cyan(service)} service deployed`);\n\n if (service === \"email\") {\n // Pass through to email destroy\n await emailDestroy(options);\n return;\n }\n }\n\n // 5. Multiple services - ask which to destroy\n const serviceToDestroy = await clack.select({\n message: \"Which service would you like to destroy?\",\n options: [\n ...deployedServices.map((s) => ({\n value: s,\n label: s.charAt(0).toUpperCase() + s.slice(1),\n hint: s === \"email\" ? \"AWS SES email infrastructure\" : undefined,\n })),\n {\n value: \"all\",\n label: \"All services\",\n hint: \"Destroy all Wraps infrastructure\",\n },\n ],\n });\n\n if (clack.isCancel(serviceToDestroy)) {\n clack.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n // 6. Route to appropriate destroy command\n if (serviceToDestroy === \"email\" || serviceToDestroy === \"all\") {\n if (deployedServices.includes(\"email\")) {\n await emailDestroy(options);\n }\n }\n\n if (serviceToDestroy === \"all\") {\n clack.outro(pc.green(\"All Wraps infrastructure has been removed\"));\n }\n}\n","import * as clack from \"@clack/prompts\";\nimport * as pulumi from \"@pulumi/pulumi\";\nimport pc from \"picocolors\";\nimport { trackCommand } from \"../../telemetry/events.js\";\nimport type { StatusOptions } from \"../../types/index.js\";\nimport {\n getAWSRegion,\n validateAWSCredentials,\n} from \"../../utils/shared/aws.js\";\nimport {\n ensurePulumiWorkDir,\n getPulumiWorkDir,\n} from \"../../utils/shared/fs.js\";\nimport { DeploymentProgress } from \"../../utils/shared/output.js\";\n\n/**\n * Global Status command - Show overview of all deployed infrastructure\n */\nexport async function status(_options: StatusOptions): Promise<void> {\n const startTime = Date.now();\n const progress = new DeploymentProgress();\n\n clack.intro(pc.bold(\"Wraps Infrastructure Status\"));\n\n // 1. Validate AWS credentials\n const identity = await progress.execute(\n \"Loading infrastructure status\",\n async () => validateAWSCredentials()\n );\n\n progress.info(`AWS Account: ${pc.cyan(identity.accountId)}`);\n\n // 2. Get region\n const region = await getAWSRegion();\n progress.info(`Region: ${pc.cyan(region)}`);\n\n // 3. Check for deployed services\n const services: Array<{\n name: string;\n status: \"deployed\" | \"not_deployed\";\n details?: string;\n }> = [];\n\n // Check Email infrastructure\n try {\n await ensurePulumiWorkDir();\n const stack = await pulumi.automation.LocalWorkspace.selectStack({\n stackName: `wraps-${identity.accountId}-${region}`,\n workDir: getPulumiWorkDir(),\n });\n const outputs = await stack.outputs();\n\n if (outputs.roleArn?.value) {\n const domainCount = outputs.domains?.value?.length || 0;\n services.push({\n name: \"Email\",\n status: \"deployed\",\n details: domainCount > 0 ? `${domainCount} domain(s)` : undefined,\n });\n } else {\n services.push({ name: \"Email\", status: \"not_deployed\" });\n }\n } catch (_error) {\n services.push({ name: \"Email\", status: \"not_deployed\" });\n }\n\n progress.stop();\n\n // 4. Display services overview\n console.log();\n clack.note(\n services\n .map((s) => {\n if (s.status === \"deployed\") {\n const details = s.details ? pc.dim(` (${s.details})`) : \"\";\n return ` ${pc.green(\"✓\")} ${s.name}${details}`;\n }\n return ` ${pc.dim(\"○\")} ${s.name} ${pc.dim(\"(not deployed)\")}`;\n })\n .join(\"\\n\"),\n \"Services\"\n );\n\n // 5. Show next steps\n const hasDeployedServices = services.some((s) => s.status === \"deployed\");\n\n if (hasDeployedServices) {\n console.log(`\\n${pc.bold(\"Details:\")}`);\n if (services.find((s) => s.name === \"Email\")?.status === \"deployed\") {\n console.log(\n ` ${pc.dim(\"Email:\")} ${pc.cyan(\"wraps email status\")}`\n );\n }\n } else {\n console.log(`\\n${pc.bold(\"Get started:\")}`);\n console.log(\n ` ${pc.dim(\"Deploy email:\")} ${pc.cyan(\"wraps email init\")}`\n );\n }\n\n console.log(`\\n${pc.bold(\"Dashboard:\")} ${pc.blue(\"https://app.wraps.dev\")}`);\n console.log(`${pc.bold(\"Docs:\")} ${pc.blue(\"https://wraps.dev/docs\")}\\n`);\n\n // 6. Track status command\n trackCommand(\"status\", {\n success: true,\n services_deployed: services.filter((s) => s.status === \"deployed\").length,\n duration_ms: Date.now() - startTime,\n });\n}\n","/**\n * Telemetry management commands\n * @module commands/telemetry\n */\n\nimport * as clack from \"@clack/prompts\";\nimport pc from \"picocolors\";\nimport { getTelemetryClient } from \"../telemetry/client.js\";\n\n/**\n * Enable telemetry\n */\nexport async function telemetryEnable(): Promise<void> {\n const client = getTelemetryClient();\n\n client.enable();\n\n clack.log.success(pc.green(\"Telemetry enabled\"));\n console.log(` Config: ${pc.dim(client.getConfigPath())}`);\n console.log(`\\n ${pc.dim(\"Thank you for helping improve Wraps!\")}\\n`);\n}\n\n/**\n * Disable telemetry\n */\nexport async function telemetryDisable(): Promise<void> {\n const client = getTelemetryClient();\n\n client.disable();\n\n clack.log.success(pc.green(\"Telemetry disabled\"));\n console.log(` Config: ${pc.dim(client.getConfigPath())}`);\n console.log(\n `\\n ${pc.dim(\"You can re-enable with:\")} wraps telemetry enable\\n`\n );\n}\n\n/**\n * Show telemetry status\n */\nexport async function telemetryStatus(): Promise<void> {\n const client = getTelemetryClient();\n\n clack.intro(pc.bold(\"Telemetry Status\"));\n\n const status = client.isEnabled() ? pc.green(\"Enabled\") : pc.red(\"Disabled\");\n\n console.log();\n console.log(` ${pc.bold(\"Status:\")} ${status}`);\n console.log(` ${pc.bold(\"Config file:\")} ${pc.dim(client.getConfigPath())}`);\n\n // Show opt-out methods\n if (client.isEnabled()) {\n console.log();\n console.log(pc.bold(\" How to opt-out:\"));\n console.log(` ${pc.cyan(\"wraps telemetry disable\")}`);\n console.log(\n ` ${pc.dim(\"Or set:\")} ${pc.cyan(\"WRAPS_TELEMETRY_DISABLED=1\")}`\n );\n console.log(` ${pc.dim(\"Or set:\")} ${pc.cyan(\"DO_NOT_TRACK=1\")}`);\n } else {\n console.log();\n console.log(pc.bold(\" How to opt-in:\"));\n console.log(` ${pc.cyan(\"wraps telemetry enable\")}`);\n }\n\n // Show debug mode info\n console.log();\n console.log(pc.bold(\" Debug mode:\"));\n console.log(\n ` ${pc.dim(\"See what would be sent:\")} ${pc.cyan(\"WRAPS_TELEMETRY_DEBUG=1 wraps <command>\")}`\n );\n\n // Show docs link\n console.log();\n console.log(\n ` ${pc.dim(\"Learn more:\")} ${pc.cyan(\"https://wraps.dev/docs/telemetry\")}`\n );\n console.log();\n}\n","/**\n * Setup tab completion for the Wraps CLI\n *\n * This is a placeholder for future tab completion support.\n * Will integrate with tabtab or similar completion library.\n */\nexport function setupTabCompletion() {\n // Placeholder for tab completion setup\n // Will be implemented in Phase 2\n}\n\n/**\n * Print completion script for the current shell\n */\nexport function printCompletionScript() {\n console.log(\"# Wraps CLI Tab Completion\");\n console.log(\"# ========================\\n\");\n console.log(\"# Tab completion will be available in a future release.\\n\");\n console.log(\"# For now, here are the available commands:\\n\");\n console.log(\"# Email Commands:\");\n console.log(\n \"# wraps email init [--provider vercel|aws|railway|other] [--region <region>] [--domain <domain>]\"\n );\n console.log(\"# wraps email connect [--region <region>]\");\n console.log(\"# wraps email status [--account <account-id>]\");\n console.log(\"# wraps email verify --domain <domain>\");\n console.log(\"# wraps email sync\");\n console.log(\"# wraps email upgrade\");\n console.log(\"# wraps email restore [--region <region>] [--force]\");\n console.log(\"# wraps email destroy [--force] [--preview]\");\n console.log(\"# wraps email domains add --domain <domain>\");\n console.log(\"# wraps email domains list\");\n console.log(\"# wraps email domains verify --domain <domain>\");\n console.log(\"# wraps email domains get-dkim --domain <domain>\");\n console.log(\"# wraps email domains remove --domain <domain> [--force]\\n\");\n console.log(\"# Global Commands:\");\n console.log(\"# wraps status\");\n console.log(\"# wraps destroy [--force] [--preview]\");\n console.log(\"# wraps console [--port <port>] [--no-open]\");\n console.log(\"# wraps completion\");\n console.log(\"# wraps telemetry [enable|disable|status]\\n\");\n console.log(\"# Dashboard Commands:\");\n console.log(\"# wraps dashboard update-role [--region <region>] [--force]\\n\");\n console.log(\"# Flags:\");\n console.log(\"# -p, --provider : vercel, aws, railway, other\");\n console.log(\n \"# -r, --region : us-east-1, us-east-2, us-west-1, us-west-2, eu-west-1, eu-west-2, etc.\"\n );\n console.log(\"# -d, --domain : Your domain name (e.g., myapp.com)\");\n console.log(\"# --account : AWS account ID or alias\");\n console.log(\"# --preset : starter, production, enterprise, custom\");\n console.log(\"# -y, --yes : Skip confirmation prompts\");\n console.log(\"# -f, --force : Force destructive operations\");\n console.log(\"# --preview : Preview changes without deploying\\n\");\n}\n"],"mappings":"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","names":["packageJson","acm","aws","config","aws","cloudfront","config","config","config","config","clack","pc","config","getAllPresetInfo","STSClient","MailManagerClient","DynamoDBClient","ScanCommand","unmarshall","dynamodb","dirname","join","fileURLToPath","clack","pc","intro","log","outro","pc","existsSync","join","join","existsSync","mkdir","clack","pc","status","intro","pc","log","iam","roleExists","outro","clack","pulumi","pc","aws","pulumi","DynamoDBClient","DescribeTableCommand","dynamodb","aws","config","aws","pulumi","IAMClient","GetRoleCommand","iam","config","existsSync","join","fileURLToPath","aws","pulumi","LambdaClient","lambda","config","aws","SESv2Client","GetConfigurationSetCommand","GetEmailIdentityCommand","config","aws","aws","IAMClient","iam","config","config","findHostedZone","createACMCertificate","createCloudFrontTracking","createMailManagerArchive","automation","installPulumiCli","pc","config","result","clack","pulumi","pc","IAMClient","GetIdentityVerificationAttributesCommand","ListIdentitiesCommand","SESClient","lambda","iam","pc","result","findHostedZone","createDNSRecords","clack","pulumi","pc","ChangeResourceRecordSetsCommand","ListHostedZonesByNameCommand","Route53Client","findHostedZone","Route53Client","ListHostedZonesByNameCommand","Route53Client","ChangeResourceRecordSetsCommand","SESv2Client","GetEmailIdentityCommand","pc","findHostedZone","SESv2Client","clack","pc","pc","SESv2Client","clack","pulumi","pc","pc","promptEmailArchiving","result","findHostedZone","createDNSRecords","clack","pulumi","pc","pc","clack","pulumi","pc","pc","SESv2Client","GetEmailIdentityCommand","identity","clack","pulumi","pc","pc","config","listSESDomains","findHostedZone","promptCustomConfig","result","createDNSRecords","clack","pulumi","pc","path","fileURLToPath","SESClient","GetEmailIdentityCommand","SESv2Client","sesv2","config","createRouter","DynamoDBClient","dynamodb","status","config","createRouter","fetchArchivedEmail","createRouter","cloudwatch","fetchDynamoDBMetrics","config","createRouter","createRouter","GetEmailIdentityCommand","SESv2Client","sesv2","config","createRouter","SESv2Client","assumeRole","createRouter","config","createRouter","assumeRole","IAMClient","__dirname","path","fileURLToPath","config","pc","clack","pc","pc","spinner","clack","pulumi","pc","pc","clack","pc","status","__filename","fileURLToPath","__dirname","dirname","join","pc"]}
|
|
1
|
+
{"version":3,"sources":["../../../node_modules/.pnpm/tsup@8.5.0_jiti@2.6.1_postcss@8.5.6_tsx@4.20.6_typescript@5.9.3/node_modules/tsup/assets/esm_shims.js","../src/utils/shared/ci-detection.ts","../src/telemetry/config.ts","../package.json","../src/telemetry/client.ts","../src/telemetry/events.ts","../src/utils/shared/errors.ts","../src/utils/shared/aws.ts","../src/utils/email/route53.ts","../src/infrastructure/resources/acm.ts","../src/infrastructure/resources/cloudfront.ts","../src/infrastructure/resources/mail-manager.ts","../src/utils/email/costs.ts","../src/utils/email/presets.ts","../src/utils/shared/prompts.ts","../src/utils/shared/assume-role.ts","../src/utils/archive.ts","../src/console/services/email-archive.ts","../src/console/services/dynamodb-metrics.ts","../src/cli.ts","../src/commands/dashboard/update-role.ts","../src/utils/shared/metadata.ts","../src/utils/shared/fs.ts","../src/utils/shared/output.ts","../src/commands/email/config.ts","../src/infrastructure/email-stack.ts","../src/infrastructure/resources/dynamodb.ts","../src/infrastructure/resources/eventbridge.ts","../src/infrastructure/resources/iam.ts","../src/infrastructure/resources/lambda.ts","../src/infrastructure/resources/ses.ts","../src/infrastructure/resources/sqs.ts","../src/infrastructure/vercel-oidc.ts","../src/utils/shared/pulumi.ts","../src/commands/email/connect.ts","../src/utils/shared/scanner.ts","../src/commands/email/destroy.ts","../src/utils/route53.ts","../src/commands/email/domains.ts","../src/commands/email/init.ts","../src/commands/email/restore.ts","../src/commands/email/status.ts","../src/commands/email/upgrade.ts","../src/commands/shared/dashboard.ts","../src/console/server.ts","../src/console/middleware/auth.ts","../src/console/middleware/error.ts","../src/console/routes/domains.ts","../src/console/services/ses-service.ts","../src/console/routes/emails.ts","../src/console/services/email-logs.ts","../src/console/routes/metrics.ts","../src/console/services/aws-metrics.ts","../src/console/routes/settings.ts","../src/console/services/settings-service.ts","../src/console/routes/user.ts","../src/commands/shared/destroy.ts","../src/commands/shared/status.ts","../src/commands/telemetry.ts","../src/utils/shared/completion.ts"],"sourcesContent":["// Shim globals in esm bundle\nimport path from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nconst getFilename = () => fileURLToPath(import.meta.url)\nconst getDirname = () => path.dirname(getFilename())\n\nexport const __dirname = /* @__PURE__ */ getDirname()\nexport const __filename = /* @__PURE__ */ getFilename()\n","/**\n * CI environment detection utilities\n * @module utils/shared/ci-detection\n */\n\n/**\n * Detect if running in CI environment\n *\n * Checks for common CI environment variables to determine\n * if the CLI is running in a continuous integration environment.\n *\n * @returns {boolean} True if running in CI, false otherwise\n *\n * @example\n * ```typescript\n * if (isCI()) {\n * console.log('Running in CI environment');\n * }\n * ```\n */\nexport function isCI(): boolean {\n // Universal CI indicator\n if (process.env.CI === \"true\" || process.env.CI === \"1\") {\n return true;\n }\n\n // Common CI providers\n const ciEnvVars = [\n \"GITHUB_ACTIONS\", // GitHub Actions\n \"GITLAB_CI\", // GitLab CI\n \"CIRCLECI\", // CircleCI\n \"TRAVIS\", // Travis CI\n \"JENKINS_URL\", // Jenkins\n \"BUILDKITE\", // Buildkite\n \"DRONE\", // Drone\n \"SEMAPHORE\", // Semaphore\n \"TEAMCITY_VERSION\", // TeamCity\n \"TF_BUILD\", // Azure Pipelines\n \"CODEBUILD_BUILD_ID\", // AWS CodeBuild\n \"NETLIFY\", // Netlify\n \"VERCEL\", // Vercel\n \"HEROKU_TEST_RUN_ID\", // Heroku CI\n \"BUDDY\", // Buddy\n \"BITBUCKET_BUILD_NUMBER\", // Bitbucket Pipelines\n ];\n\n return ciEnvVars.some((envVar) => process.env[envVar] !== undefined);\n}\n","/**\n * Telemetry configuration management\n * @module telemetry/config\n */\n\nimport Conf from \"conf\";\nimport { v4 as uuidv4 } from \"uuid\";\nimport type { TelemetryConfig } from \"./types.js\";\n\nconst CONFIG_DEFAULTS: TelemetryConfig = {\n enabled: true,\n anonymousId: uuidv4(),\n notificationShown: false,\n};\n\n/**\n * Manages telemetry configuration stored locally\n *\n * Configuration is stored in platform-specific locations:\n * - macOS: ~/Library/Preferences/wraps/telemetry.json\n * - Linux: ~/.config/wraps/telemetry.json\n * - Windows: %APPDATA%\\wraps\\Config\\telemetry.json\n *\n * @example\n * ```typescript\n * const config = new TelemetryConfigManager();\n *\n * if (config.isEnabled()) {\n * console.log('Telemetry is enabled');\n * }\n *\n * config.setEnabled(false);\n * ```\n */\nexport class TelemetryConfigManager {\n private readonly config: Conf<TelemetryConfig>;\n\n constructor() {\n this.config = new Conf<TelemetryConfig>({\n projectName: \"wraps\",\n configName: \"telemetry\",\n defaults: CONFIG_DEFAULTS,\n });\n }\n\n /**\n * Check if telemetry is enabled\n */\n isEnabled(): boolean {\n return this.config.get(\"enabled\");\n }\n\n /**\n * Enable or disable telemetry\n */\n setEnabled(enabled: boolean): void {\n this.config.set(\"enabled\", enabled);\n }\n\n /**\n * Get the anonymous user ID\n */\n getAnonymousId(): string {\n return this.config.get(\"anonymousId\");\n }\n\n /**\n * Check if the first-run notification has been shown\n */\n hasShownNotification(): boolean {\n return this.config.get(\"notificationShown\");\n }\n\n /**\n * Mark the first-run notification as shown\n */\n markNotificationShown(): void {\n this.config.set(\"notificationShown\", true);\n }\n\n /**\n * Get the full path to the configuration file\n */\n getConfigPath(): string {\n return this.config.path;\n }\n\n /**\n * Reset configuration to defaults\n */\n reset(): void {\n this.config.clear();\n // Set new defaults with fresh UUID\n this.config.set({\n ...CONFIG_DEFAULTS,\n anonymousId: uuidv4(),\n });\n }\n}\n","{\n \"name\": \"@wraps.dev/cli\",\n \"version\": \"1.5.3\",\n \"description\": \"CLI for deploying Wraps email infrastructure to your AWS account\",\n \"type\": \"module\",\n \"main\": \"./dist/cli.js\",\n \"bin\": {\n \"wraps\": \"./dist/cli.js\"\n },\n \"files\": [\n \"dist\",\n \"README.md\",\n \"LICENSE\"\n ],\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/wraps-team/wraps.git\",\n \"directory\": \"packages/cli\"\n },\n \"homepage\": \"https://wraps.dev\",\n \"bugs\": {\n \"url\": \"https://github.com/wraps-team/wraps/issues\"\n },\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"scripts\": {\n \"dev\": \"tsup --watch\",\n \"build\": \"pnpm build:console && pnpm build:lambda && tsup\",\n \"build:lambda\": \"tsx scripts/build-lambda.ts\",\n \"build:console\": \"pnpm --filter @wraps/console build\",\n \"test\": \"vitest run\",\n \"test:watch\": \"vitest --watch\",\n \"test:ui\": \"vitest --ui\",\n \"test:coverage\": \"vitest run --coverage\",\n \"typecheck\": \"tsc --noEmit\",\n \"lint\": \"eslint src\",\n \"prepublishOnly\": \"pnpm build\"\n },\n \"keywords\": [\n \"aws\",\n \"ses\",\n \"email\",\n \"infrastructure\",\n \"cli\"\n ],\n \"author\": \"Wraps\",\n \"license\": \"MIT\",\n \"dependencies\": {\n \"@aws-sdk/client-acm\": \"3.933.0\",\n \"@aws-sdk/client-cloudformation\": \"^3.490.0\",\n \"@aws-sdk/client-cloudfront\": \"3.933.0\",\n \"@aws-sdk/client-cloudwatch\": \"^3.490.0\",\n \"@aws-sdk/client-dynamodb\": \"^3.490.0\",\n \"@aws-sdk/client-iam\": \"3.932.0\",\n \"@aws-sdk/client-lambda\": \"3.925.0\",\n \"@aws-sdk/client-mailmanager\": \"3.925.0\",\n \"@aws-sdk/client-route-53\": \"3.925.0\",\n \"@aws-sdk/client-ses\": \"^3.490.0\",\n \"@aws-sdk/client-sesv2\": \"3.925.0\",\n \"@aws-sdk/client-sns\": \"^3.490.0\",\n \"@aws-sdk/client-sts\": \"^3.490.0\",\n \"@aws-sdk/util-dynamodb\": \"3.927.0\",\n \"@clack/prompts\": \"^0.11.0\",\n \"@pulumi/aws\": \"^7.11.1\",\n \"@pulumi/pulumi\": \"^3.207.0\",\n \"args\": \"^5.0.3\",\n \"conf\": \"^13.0.1\",\n \"cosmiconfig\": \"^9.0.0\",\n \"esbuild\": \"^0.25.12\",\n \"express\": \"^4.21.2\",\n \"get-port\": \"^7.1.0\",\n \"http-terminator\": \"^3.2.0\",\n \"isomorphic-dompurify\": \"2.32.0\",\n \"mailparser\": \"3.9.0\",\n \"open\": \"^10.1.0\",\n \"picocolors\": \"^1.1.1\",\n \"tabtab\": \"^3.0.2\",\n \"uuid\": \"^11.0.3\"\n },\n \"devDependencies\": {\n \"@types/args\": \"5.0.4\",\n \"@types/express\": \"^5.0.0\",\n \"@types/mailparser\": \"3.4.6\",\n \"@types/node\": \"^20.11.0\",\n \"@types/uuid\": \"^10.0.0\",\n \"@vitest/coverage-v8\": \"4.0.7\",\n \"aws-sdk-client-mock\": \"4.1.0\",\n \"aws-sdk-client-mock-vitest\": \"7.0.1\",\n \"eslint\": \"^8.56.0\",\n \"tsup\": \"^8.0.1\",\n \"tsx\": \"4.20.6\",\n \"typescript\": \"catalog:\",\n \"vitest\": \"^4.0.7\"\n },\n \"engines\": {\n \"node\": \">=20.0.0\"\n }\n}\n","/**\n * Telemetry client for Wraps CLI\n * @module telemetry/client\n */\n\nimport { isCI } from \"../utils/shared/ci-detection.js\";\nimport { TelemetryConfigManager } from \"./config.js\";\nimport type {\n TelemetryClientOptions,\n TelemetryEvent,\n TelemetryRequest,\n} from \"./types.js\";\n\nconst DEFAULT_ENDPOINT = \"https://wraps.dev/api/telemetry\";\nconst DEFAULT_TIMEOUT = 2000; // 2 seconds\n\n/**\n * Main telemetry client for tracking CLI usage\n *\n * Features:\n * - Non-blocking event tracking with automatic batching\n * - Respects DO_NOT_TRACK and WRAPS_TELEMETRY_DISABLED environment variables\n * - Auto-disabled in CI environments\n * - 2-second timeout with silent failure\n * - Debug mode for development\n *\n * @example\n * ```typescript\n * const client = getTelemetryClient();\n *\n * client.track('command:init', {\n * service: 'email',\n * success: true\n * });\n *\n * await client.shutdown(); // Flush remaining events\n * ```\n */\nexport class TelemetryClient {\n private readonly config: TelemetryConfigManager;\n private readonly endpoint: string;\n private readonly timeout: number;\n private readonly debug: boolean;\n private enabled: boolean;\n private eventQueue: TelemetryEvent[] = [];\n private flushTimer?: NodeJS.Timeout;\n\n constructor(options: TelemetryClientOptions = {}) {\n this.config = new TelemetryConfigManager();\n this.endpoint = options.endpoint || DEFAULT_ENDPOINT;\n this.timeout = options.timeout || DEFAULT_TIMEOUT;\n this.debug = options.debug || process.env.WRAPS_TELEMETRY_DEBUG === \"1\";\n\n // Check if telemetry should be enabled\n this.enabled = this.shouldBeEnabled();\n }\n\n /**\n * Determine if telemetry should be enabled based on environment and config\n */\n private shouldBeEnabled(): boolean {\n // Check DO_NOT_TRACK first (universal standard)\n if (process.env.DO_NOT_TRACK === \"1\") {\n return false;\n }\n\n // Check Wraps-specific env var\n if (process.env.WRAPS_TELEMETRY_DISABLED === \"1\") {\n return false;\n }\n\n // Check CI environment\n if (isCI()) {\n return false;\n }\n\n // Check config file\n if (!this.config.isEnabled()) {\n return false;\n }\n\n return true;\n }\n\n /**\n * Track an event\n *\n * @param event - Event name in format \"category:action\" (e.g., \"command:init\")\n * @param properties - Additional event properties (no PII)\n */\n track(event: string, properties?: Record<string, unknown>): void {\n const telemetryEvent: TelemetryEvent = {\n event,\n properties: {\n ...properties,\n cli_version: this.getCLIVersion(),\n os: process.platform,\n node_version: process.version,\n ci: isCI(),\n },\n anonymousId: this.config.getAnonymousId(),\n timestamp: new Date().toISOString(),\n };\n\n // Debug mode: show what would be sent (even if disabled)\n if (this.debug) {\n console.log(\n \"[Telemetry Debug] Event:\",\n JSON.stringify(telemetryEvent, null, 2)\n );\n return;\n }\n\n // Check if telemetry is enabled (after debug check)\n if (!this.enabled) {\n return;\n }\n\n // Add to queue\n this.eventQueue.push(telemetryEvent);\n\n // Flush after short delay (batch events from same command)\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n }\n this.flushTimer = setTimeout(() => this.flush(), 100);\n }\n\n /**\n * Flush queued events to server\n */\n private async flush(): Promise<void> {\n if (this.eventQueue.length === 0) {\n return;\n }\n\n const eventsToSend = [...this.eventQueue];\n this.eventQueue = [];\n\n try {\n const controller = new AbortController();\n const timeoutId = setTimeout(() => controller.abort(), this.timeout);\n\n const requestBody: TelemetryRequest = {\n events: eventsToSend,\n batch: true,\n };\n\n await fetch(this.endpoint, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(requestBody),\n signal: controller.signal,\n });\n\n clearTimeout(timeoutId);\n } catch (error) {\n // Silent failure - never break user's workflow\n if (this.debug) {\n console.error(\"[Telemetry Debug] Failed to send events:\", error);\n }\n }\n }\n\n /**\n * Flush and wait for all events to be sent\n * Should be called before CLI exits\n */\n async shutdown(): Promise<void> {\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n }\n await this.flush();\n }\n\n /**\n * Enable telemetry\n * Note: Won't override environment variable opt-outs (DO_NOT_TRACK, CI, etc.)\n */\n enable(): void {\n this.config.setEnabled(true);\n\n // Check if environment variables prevent telemetry\n if (\n process.env.DO_NOT_TRACK === \"1\" ||\n process.env.DO_NOT_TRACK === \"true\"\n ) {\n this.enabled = false;\n return;\n }\n\n if (process.env.WRAPS_TELEMETRY_DISABLED === \"1\") {\n this.enabled = false;\n return;\n }\n\n if (isCI()) {\n this.enabled = false;\n return;\n }\n\n // No env restrictions, enable telemetry\n this.enabled = true;\n }\n\n /**\n * Disable telemetry\n */\n disable(): void {\n this.config.setEnabled(false);\n this.enabled = false;\n this.eventQueue = [];\n }\n\n /**\n * Check if telemetry is enabled\n */\n isEnabled(): boolean {\n return this.enabled;\n }\n\n /**\n * Get config file path\n */\n getConfigPath(): string {\n return this.config.getConfigPath();\n }\n\n /**\n * Show first-run notification\n */\n shouldShowNotification(): boolean {\n return this.enabled && !this.config.hasShownNotification();\n }\n\n /**\n * Mark notification as shown\n */\n markNotificationShown(): void {\n this.config.markNotificationShown();\n }\n\n /**\n * Get CLI version from package.json\n */\n private getCLIVersion(): string {\n try {\n // Dynamic import would be better but requires async\n // Using require for now since this is CommonJS compatible\n const packageJson = require(\"../../package.json\");\n return packageJson.version;\n } catch {\n return \"unknown\";\n }\n }\n}\n\n// Singleton instance\nlet telemetryInstance: TelemetryClient | null = null;\n\n/**\n * Get the singleton telemetry client instance\n *\n * @example\n * ```typescript\n * const client = getTelemetryClient();\n * client.track('command:init', { success: true });\n * ```\n */\nexport function getTelemetryClient(): TelemetryClient {\n if (!telemetryInstance) {\n telemetryInstance = new TelemetryClient();\n }\n return telemetryInstance;\n}\n","/**\n * Event tracking helpers for Wraps CLI\n * @module telemetry/events\n */\n\nimport { getTelemetryClient } from \"./client.js\";\n\n/**\n * Track CLI command execution\n *\n * @param command - Command name (e.g., \"email:init\", \"status\")\n * @param metadata - Additional metadata about command execution\n *\n * @example\n * ```typescript\n * trackCommand('email:init', {\n * success: true,\n * duration_ms: 1500,\n * preset: 'production'\n * });\n * ```\n */\nexport function trackCommand(\n command: string,\n metadata?: {\n success?: boolean;\n duration_ms?: number;\n preset?: string;\n provider?: string;\n service?: string;\n [key: string]: unknown;\n }\n): void {\n const client = getTelemetryClient();\n\n // Sanitize metadata to ensure no PII\n const sanitized = metadata ? { ...metadata } : {};\n\n // Remove any potentially sensitive fields\n sanitized.domain = undefined;\n sanitized.accountId = undefined;\n sanitized.email = undefined;\n\n client.track(`command:${command}`, sanitized);\n}\n\n/**\n * Track service initialization\n *\n * @param service - Service type (email, sms, etc.)\n * @param success - Whether initialization succeeded\n * @param metadata - Additional metadata\n *\n * @example\n * ```typescript\n * trackServiceInit('email', true, {\n * preset: 'production',\n * provider: 'vercel'\n * });\n * ```\n */\nexport function trackServiceInit(\n service: string,\n success: boolean,\n metadata?: {\n preset?: string;\n provider?: string;\n features?: string[];\n duration_ms?: number;\n [key: string]: unknown;\n }\n): void {\n const client = getTelemetryClient();\n\n client.track(\"service:init\", {\n service,\n success,\n ...metadata,\n });\n}\n\n/**\n * Track service deployment\n *\n * @param service - Service type (email, sms, etc.)\n * @param metadata - Deployment metadata\n *\n * @example\n * ```typescript\n * trackServiceDeployed('email', {\n * duration_ms: 45000,\n * features: ['tracking', 'history']\n * });\n * ```\n */\nexport function trackServiceDeployed(\n service: string,\n metadata?: {\n duration_ms?: number;\n features?: string[];\n preset?: string;\n [key: string]: unknown;\n }\n): void {\n const client = getTelemetryClient();\n\n client.track(\"service:deployed\", {\n service,\n ...metadata,\n });\n}\n\n/**\n * Track dashboard/console start\n *\n * @param mode - Console mode (local or hosted)\n * @param metadata - Additional metadata\n *\n * @example\n * ```typescript\n * trackConsoleStart('local', { port: 3100 });\n * ```\n */\nexport function trackConsoleStart(\n mode: \"local\" | \"hosted\",\n metadata?: Record<string, unknown>\n): void {\n const client = getTelemetryClient();\n\n client.track(\"console:started\", {\n mode,\n ...metadata,\n });\n}\n\n/**\n * Track console stop\n *\n * @param metadata - Stop metadata (e.g., duration)\n *\n * @example\n * ```typescript\n * trackConsoleStop({ duration_s: 300 });\n * ```\n */\nexport function trackConsoleStop(metadata?: {\n duration_s?: number;\n [key: string]: unknown;\n}): void {\n const client = getTelemetryClient();\n\n client.track(\"console:stopped\", metadata || {});\n}\n\n/**\n * Track error occurrence\n *\n * IMPORTANT: Never include error messages, only error codes\n * Error messages may contain sensitive information\n *\n * @param errorCode - Error code (e.g., \"AWS_CREDENTIALS_INVALID\")\n * @param command - Command where error occurred\n * @param metadata - Additional metadata\n *\n * @example\n * ```typescript\n * trackError('AWS_CREDENTIALS_INVALID', 'email:init', {\n * step: 'credential_validation'\n * });\n * ```\n */\nexport function trackError(\n errorCode: string,\n command: string,\n metadata?: Record<string, unknown>\n): void {\n const client = getTelemetryClient();\n\n client.track(\"error:occurred\", {\n error_code: errorCode,\n command,\n ...metadata,\n });\n}\n\n/**\n * Track feature usage\n *\n * @param feature - Feature name\n * @param metadata - Feature metadata\n *\n * @example\n * ```typescript\n * trackFeature('domain_verified', {\n * dns_provider: 'route53',\n * auto_detected: true\n * });\n * ```\n */\nexport function trackFeature(\n feature: string,\n metadata?: Record<string, unknown>\n): void {\n const client = getTelemetryClient();\n\n client.track(`feature:${feature}`, metadata || {});\n}\n\n/**\n * Track service upgrade\n *\n * @param service - Service type\n * @param metadata - Upgrade metadata\n *\n * @example\n * ```typescript\n * trackServiceUpgrade('email', {\n * from_preset: 'starter',\n * to_preset: 'production',\n * added_features: ['history', 'dedicated_ip']\n * });\n * ```\n */\nexport function trackServiceUpgrade(\n service: string,\n metadata?: {\n from_preset?: string;\n to_preset?: string;\n added_features?: string[];\n [key: string]: unknown;\n }\n): void {\n const client = getTelemetryClient();\n\n client.track(\"service:upgraded\", {\n service,\n ...metadata,\n });\n}\n\n/**\n * Track service removal\n *\n * @param service - Service type\n * @param metadata - Removal metadata\n *\n * @example\n * ```typescript\n * trackServiceRemoved('email', { reason: 'user_initiated' });\n * ```\n */\nexport function trackServiceRemoved(\n service: string,\n metadata?: Record<string, unknown>\n): void {\n const client = getTelemetryClient();\n\n client.track(\"service:removed\", {\n service,\n ...metadata,\n });\n}\n","import * as clack from \"@clack/prompts\";\nimport pc from \"picocolors\";\nimport { trackError } from \"../../telemetry/events.js\";\n\n/**\n * Custom error class for Wraps CLI errors\n */\nexport class WrapsError extends Error {\n constructor(\n message: string,\n public code: string,\n public suggestion?: string,\n public docsUrl?: string\n ) {\n super(message);\n this.name = \"WrapsError\";\n }\n}\n\n/**\n * Global error handler for CLI errors\n * Formats and displays errors with suggestions and docs\n */\nexport function handleCLIError(error: unknown): never {\n console.error(\"\"); // Blank line\n\n if (error instanceof WrapsError) {\n // Track error (code only, never message)\n trackError(error.code, \"unknown\");\n\n clack.log.error(error.message);\n\n if (error.suggestion) {\n console.log(`\\n${pc.yellow(\"Suggestion:\")}`);\n console.log(` ${pc.white(error.suggestion)}\\n`);\n }\n\n if (error.docsUrl) {\n console.log(`${pc.dim(\"Documentation:\")}`);\n console.log(` ${pc.blue(error.docsUrl)}\\n`);\n }\n\n process.exit(1);\n }\n\n // Unknown error\n trackError(\"UNKNOWN_ERROR\", \"unknown\");\n\n clack.log.error(\"An unexpected error occurred\");\n console.error(error);\n console.log(`\\n${pc.dim(\"If this persists, please report at:\")}`);\n console.log(` ${pc.blue(\"https://github.com/wraps-team/wraps/issues\")}\\n`);\n process.exit(1);\n}\n\n/**\n * Common error factory functions\n */\nexport const errors = {\n noAWSCredentials: () =>\n new WrapsError(\n \"AWS credentials not found\",\n \"NO_AWS_CREDENTIALS\",\n \"Run: aws configure\\nOr set AWS_PROFILE environment variable\",\n \"https://wraps.dev/docs/setup/aws-credentials\"\n ),\n\n stackExists: (stackName: string) =>\n new WrapsError(\n `Stack \"${stackName}\" already exists`,\n \"STACK_EXISTS\",\n `To update: wraps email upgrade\\nTo remove: wraps destroy --stack ${stackName}`,\n \"https://wraps.dev/docs/cli/upgrade\"\n ),\n\n invalidRegion: (region: string) =>\n new WrapsError(\n `Invalid AWS region: ${region}`,\n \"INVALID_REGION\",\n \"Use a valid AWS region like: us-east-1, eu-west-1, ap-southeast-1\",\n \"https://docs.aws.amazon.com/general/latest/gr/rande.html\"\n ),\n\n pulumiError: (message: string) =>\n new WrapsError(\n `Infrastructure deployment failed: ${message}`,\n \"PULUMI_ERROR\",\n \"Check your AWS permissions and try again\",\n \"https://wraps.dev/docs/troubleshooting\"\n ),\n\n noStack: () =>\n new WrapsError(\n \"No Wraps infrastructure found in this AWS account\",\n \"NO_STACK\",\n \"Run: wraps email init\\nTo deploy new infrastructure\",\n \"https://wraps.dev/docs/cli/init\"\n ),\n\n pulumiNotInstalled: () =>\n new WrapsError(\n \"Pulumi CLI is not installed\",\n \"PULUMI_NOT_INSTALLED\",\n \"Install Pulumi:\\n macOS: brew install pulumi/tap/pulumi\\n Linux: curl -fsSL https://get.pulumi.com | sh\\n Windows: choco install pulumi\\n\\nOr download from: https://www.pulumi.com/docs/install/\",\n \"https://www.pulumi.com/docs/install/\"\n ),\n\n stackLocked: () =>\n new WrapsError(\n \"The Pulumi stack is locked from a previous run\",\n \"STACK_LOCKED\",\n \"This happens when a previous deployment was interrupted.\\n\\nTo unlock, run:\\n rm -rf ~/.wraps/pulumi/.pulumi/locks\\n\\nThen try your command again.\",\n \"https://wraps.dev/docs/troubleshooting\"\n ),\n};\n","import { ACMClient, DescribeCertificateCommand } from \"@aws-sdk/client-acm\";\nimport {\n GetIdentityVerificationAttributesCommand,\n ListIdentitiesCommand,\n SESClient,\n} from \"@aws-sdk/client-ses\";\nimport { GetCallerIdentityCommand, STSClient } from \"@aws-sdk/client-sts\";\nimport { errors } from \"./errors.js\";\n\n/**\n * AWS identity information\n */\nexport type AWSIdentity = {\n accountId: string;\n userId: string;\n arn: string;\n};\n\n/**\n * Validate AWS credentials by calling STS GetCallerIdentity\n */\nexport async function validateAWSCredentials(): Promise<AWSIdentity> {\n const sts = new STSClient({ region: \"us-east-1\" });\n\n try {\n const identity = await sts.send(new GetCallerIdentityCommand({}));\n return {\n accountId: identity.Account!,\n userId: identity.UserId!,\n arn: identity.Arn!,\n };\n } catch (_error) {\n throw errors.noAWSCredentials();\n }\n}\n\n/**\n * Check if a region is valid\n */\nexport async function checkRegion(region: string): Promise<boolean> {\n // List of valid AWS regions (as of 2025)\n const validRegions = [\n \"us-east-1\",\n \"us-east-2\",\n \"us-west-1\",\n \"us-west-2\",\n \"af-south-1\",\n \"ap-east-1\",\n \"ap-south-1\",\n \"ap-northeast-1\",\n \"ap-northeast-2\",\n \"ap-northeast-3\",\n \"ap-southeast-1\",\n \"ap-southeast-2\",\n \"ap-southeast-3\",\n \"ca-central-1\",\n \"eu-central-1\",\n \"eu-west-1\",\n \"eu-west-2\",\n \"eu-west-3\",\n \"eu-south-1\",\n \"eu-north-1\",\n \"me-south-1\",\n \"sa-east-1\",\n ];\n\n return validRegions.includes(region);\n}\n\n/**\n * Get AWS region from environment or config\n */\nexport async function getAWSRegion(): Promise<string> {\n // Try to detect region from various sources\n if (process.env.AWS_REGION) {\n return process.env.AWS_REGION;\n }\n if (process.env.AWS_DEFAULT_REGION) {\n return process.env.AWS_DEFAULT_REGION;\n }\n\n // Default fallback\n return \"us-east-1\";\n}\n\n/**\n * SES domain identity\n */\nexport type SESDomain = {\n domain: string;\n verified: boolean;\n};\n\n/**\n * List all SES identities (domains) in the account\n */\nexport async function listSESDomains(region: string): Promise<SESDomain[]> {\n const ses = new SESClient({ region });\n\n try {\n // Get all identities\n const identitiesResponse = await ses.send(\n new ListIdentitiesCommand({\n IdentityType: \"Domain\",\n })\n );\n\n const identities = identitiesResponse.Identities || [];\n\n if (identities.length === 0) {\n return [];\n }\n\n // Get verification attributes\n const attributesResponse = await ses.send(\n new GetIdentityVerificationAttributesCommand({\n Identities: identities,\n })\n );\n\n const attributes = attributesResponse.VerificationAttributes || {};\n\n // Map to SESDomain objects\n return identities.map((domain) => ({\n domain,\n verified: attributes[domain]?.VerificationStatus === \"Success\",\n }));\n } catch (error) {\n console.error(\"Error listing SES domains:\", error);\n return [];\n }\n}\n\n/**\n * Check if SES is in sandbox mode\n */\nexport async function isSESSandbox(region: string): Promise<boolean> {\n const ses = new SESClient({ region });\n\n try {\n // In sandbox mode, you can only send to verified addresses\n // This is a heuristic - we check if there are any identities\n await ses.send(\n new ListIdentitiesCommand({\n MaxItems: 1,\n })\n );\n\n // If we can call this API, SES is enabled\n // The actual sandbox check requires checking send quota\n // For now, we'll return false (not sandbox) if the API works\n return false;\n } catch (error: any) {\n // If we get an error about SES not being enabled, return true\n if (error.name === \"InvalidParameterValue\") {\n return true;\n }\n throw error;\n }\n}\n\n/**\n * ACM certificate status\n */\nexport type ACMCertificateStatus = {\n status: string;\n domainName: string;\n validationRecords: Array<{\n name: string;\n type: string;\n value: string;\n }>;\n};\n\n/**\n * Check ACM certificate validation status\n * Note: ACM certificates for CloudFront must be in us-east-1\n */\nexport async function getACMCertificateStatus(\n certificateArn: string\n): Promise<ACMCertificateStatus | null> {\n const acm = new ACMClient({ region: \"us-east-1\" });\n\n try {\n const response = await acm.send(\n new DescribeCertificateCommand({\n CertificateArn: certificateArn,\n })\n );\n\n const certificate = response.Certificate;\n if (!certificate) {\n return null;\n }\n\n // Extract validation records\n const validationRecords =\n certificate.DomainValidationOptions?.map((option) => ({\n name: option.ResourceRecord?.Name || \"\",\n type: option.ResourceRecord?.Type || \"\",\n value: option.ResourceRecord?.Value || \"\",\n })) || [];\n\n return {\n status: certificate.Status || \"UNKNOWN\",\n domainName: certificate.DomainName || \"\",\n validationRecords,\n };\n } catch (error) {\n console.error(\"Error getting ACM certificate status:\", error);\n return null;\n }\n}\n","import {\n type Change,\n ChangeResourceRecordSetsCommand,\n ListHostedZonesByNameCommand,\n Route53Client,\n} from \"@aws-sdk/client-route-53\";\n\n/**\n * Find Route53 hosted zone for a domain\n *\n * This function searches for a hosted zone that matches the given domain.\n * It will try the exact domain first, then fall back to parent domains.\n * For example, if given \"track.example.com\", it will check:\n * 1. track.example.com\n * 2. example.com\n */\nexport async function findHostedZone(\n domain: string,\n region: string\n): Promise<{ id: string; name: string } | null> {\n const client = new Route53Client({ region });\n\n // Try exact domain first\n try {\n const response = await client.send(\n new ListHostedZonesByNameCommand({\n DNSName: domain,\n MaxItems: 1,\n })\n );\n\n const zone = response.HostedZones?.[0];\n if (zone && zone.Name === `${domain}.` && zone.Id) {\n return {\n id: zone.Id.replace(\"/hostedzone/\", \"\"),\n name: zone.Name,\n };\n }\n } catch (_error) {\n // Continue to try parent domains\n }\n\n // Try parent domains (e.g., track.example.com -> example.com)\n const parts = domain.split(\".\");\n if (parts.length > 2) {\n const parentDomain = parts.slice(1).join(\".\");\n return findHostedZone(parentDomain, region);\n }\n\n return null;\n}\n\n/**\n * Create DNS records in Route53\n */\nexport async function createDNSRecords(\n hostedZoneId: string,\n domain: string,\n dkimTokens: string[],\n region: string,\n customTrackingDomain?: string,\n mailFromDomain?: string,\n cloudFrontDomain?: string\n): Promise<void> {\n const client = new Route53Client({ region });\n\n const changes: Change[] = [];\n\n // DKIM CNAME records\n for (const token of dkimTokens) {\n changes.push({\n Action: \"UPSERT\",\n ResourceRecordSet: {\n Name: `${token}._domainkey.${domain}`,\n Type: \"CNAME\",\n TTL: 1800,\n ResourceRecords: [{ Value: `${token}.dkim.amazonses.com` }],\n },\n });\n }\n\n // SPF TXT record\n changes.push({\n Action: \"UPSERT\",\n ResourceRecordSet: {\n Name: domain,\n Type: \"TXT\",\n TTL: 1800,\n ResourceRecords: [{ Value: '\"v=spf1 include:amazonses.com ~all\"' }],\n },\n });\n\n // DMARC TXT record\n // If MAIL FROM domain is provided, use it for the reporting address (rua)\n // Otherwise, use the main domain\n const dmarcReportEmail = mailFromDomain\n ? `postmaster@${mailFromDomain}`\n : `postmaster@${domain}`;\n\n changes.push({\n Action: \"UPSERT\",\n ResourceRecordSet: {\n Name: `_dmarc.${domain}`,\n Type: \"TXT\",\n TTL: 1800,\n ResourceRecords: [\n { Value: `\"v=DMARC1; p=quarantine; rua=mailto:${dmarcReportEmail}\"` },\n ],\n },\n });\n\n // Custom tracking domain CNAME (if provided)\n // This allows SES to rewrite links for open/click tracking using your custom domain\n if (customTrackingDomain) {\n // If CloudFront domain is provided, use it (HTTPS tracking)\n // Otherwise, use direct SES tracking endpoint (HTTP tracking)\n const targetDomain = cloudFrontDomain || `r.${region}.awstrack.me`;\n\n changes.push({\n Action: \"UPSERT\",\n ResourceRecordSet: {\n Name: customTrackingDomain,\n Type: \"CNAME\",\n TTL: 1800,\n ResourceRecords: [{ Value: targetDomain }],\n },\n });\n }\n\n // MAIL FROM domain records (if provided)\n // These records enable DMARC alignment by using a custom subdomain for the envelope sender\n if (mailFromDomain) {\n // MX record pointing to SES feedback server\n changes.push({\n Action: \"UPSERT\",\n ResourceRecordSet: {\n Name: mailFromDomain,\n Type: \"MX\",\n TTL: 1800,\n ResourceRecords: [\n { Value: `10 feedback-smtp.${region}.amazonses.com` },\n ],\n },\n });\n\n // SPF record for MAIL FROM domain\n changes.push({\n Action: \"UPSERT\",\n ResourceRecordSet: {\n Name: mailFromDomain,\n Type: \"TXT\",\n TTL: 1800,\n ResourceRecords: [{ Value: '\"v=spf1 include:amazonses.com ~all\"' }],\n },\n });\n }\n\n await client.send(\n new ChangeResourceRecordSetsCommand({\n HostedZoneId: hostedZoneId,\n ChangeBatch: {\n Changes: changes,\n },\n })\n );\n}\n","import * as aws from \"@pulumi/aws\";\nimport type * as pulumi from \"@pulumi/pulumi\";\n\n/**\n * ACM certificate configuration\n */\nexport type ACMCertificateConfig = {\n domain: string;\n hostedZoneId?: string; // Optional Route53 hosted zone for automatic DNS validation\n};\n\n/**\n * ACM certificate output\n */\nexport type ACMCertificateResources = {\n certificate: aws.acm.Certificate;\n certificateValidation?: aws.acm.CertificateValidation;\n validationRecords: pulumi.Output<\n Array<{\n name: string;\n type: string;\n value: string;\n }>\n >;\n};\n\n/**\n * Create ACM certificate for custom tracking domain\n *\n * IMPORTANT: CloudFront requires ACM certificates to be created in us-east-1 region.\n * This function creates the certificate in us-east-1 regardless of the SES region.\n *\n * If a Route53 hosted zone ID is provided, DNS validation records will be created\n * automatically and we'll wait for validation. Otherwise, validation records are\n * returned for manual creation.\n */\nexport async function createACMCertificate(\n config: ACMCertificateConfig\n): Promise<ACMCertificateResources> {\n const usEast1Provider = new aws.Provider(\"acm-us-east-1\", {\n region: \"us-east-1\",\n });\n\n // Create ACM certificate in us-east-1 (required for CloudFront)\n const certificate = new aws.acm.Certificate(\n \"wraps-email-tracking-cert\",\n {\n domainName: config.domain,\n validationMethod: \"DNS\",\n tags: {\n ManagedBy: \"wraps-cli\",\n Description: \"SSL certificate for Wraps email tracking domain\",\n },\n },\n {\n provider: usEast1Provider,\n }\n );\n\n // Extract validation records\n const validationRecords = certificate.domainValidationOptions.apply(\n (options) =>\n options.map((option) => ({\n name: option.resourceRecordName,\n type: option.resourceRecordType,\n value: option.resourceRecordValue,\n }))\n );\n\n // If Route53 hosted zone is provided, create validation records automatically\n let certificateValidation: aws.acm.CertificateValidation | undefined;\n\n if (config.hostedZoneId) {\n // Create validation record in Route53\n const validationRecord = new aws.route53.Record(\n \"wraps-email-tracking-cert-validation\",\n {\n zoneId: config.hostedZoneId,\n name: certificate.domainValidationOptions[0].resourceRecordName,\n type: certificate.domainValidationOptions[0].resourceRecordType,\n records: [certificate.domainValidationOptions[0].resourceRecordValue],\n ttl: 60,\n }\n );\n\n // Wait for certificate validation to complete\n certificateValidation = new aws.acm.CertificateValidation(\n \"wraps-email-tracking-cert-validation-waiter\",\n {\n certificateArn: certificate.arn,\n validationRecordFqdns: [validationRecord.fqdn],\n },\n {\n provider: usEast1Provider,\n }\n );\n }\n // For manual DNS: certificateValidation is undefined\n // User must add DNS records manually and run upgrade again\n\n return {\n certificate,\n certificateValidation,\n validationRecords,\n };\n}\n","import * as aws from \"@pulumi/aws\";\nimport type * as pulumi from \"@pulumi/pulumi\";\n\n/**\n * CloudFront resources configuration\n */\nexport type CloudFrontTrackingConfig = {\n customTrackingDomain: string;\n region: string; // SES region for the origin\n certificateArn: pulumi.Output<string>;\n hostedZoneId?: string; // Optional Route53 hosted zone for automatic DNS record creation\n};\n\n/**\n * Find existing CloudFront distribution by alias (CNAME)\n */\nasync function findDistributionByAlias(alias: string): Promise<string | null> {\n try {\n const { CloudFrontClient, ListDistributionsCommand } = await import(\n \"@aws-sdk/client-cloudfront\"\n );\n const cloudfront = new CloudFrontClient({ region: \"us-east-1\" }); // CloudFront is global but API is in us-east-1\n\n const response = await cloudfront.send(new ListDistributionsCommand({}));\n\n // Find distribution with matching alias\n const distribution = response.DistributionList?.Items?.find((dist) =>\n dist.Aliases?.Items?.includes(alias)\n );\n\n return distribution?.Id || null;\n } catch (error) {\n console.error(\"Error finding CloudFront distribution:\", error);\n return null;\n }\n}\n\n/**\n * CloudFront resources output\n */\nexport type CloudFrontResources = {\n distribution: aws.cloudfront.Distribution;\n domainName: pulumi.Output<string>;\n webAcl: aws.wafv2.WebAcl;\n};\n\n/**\n * Create WAF Web ACL with rate limiting for CloudFront\n *\n * This creates a WAFv2 Web ACL with rate limiting protection\n * to prevent abuse of tracking endpoints.\n */\nasync function createWAFWebACL(): Promise<aws.wafv2.WebAcl> {\n // WAF for CloudFront must be created in us-east-1\n const usEast1Provider = new aws.Provider(\"waf-us-east-1\", {\n region: \"us-east-1\",\n });\n\n const webAcl = new aws.wafv2.WebAcl(\n \"wraps-email-tracking-waf\",\n {\n scope: \"CLOUDFRONT\", // WAF for CloudFront must use CLOUDFRONT scope\n description: \"Rate limiting protection for Wraps email tracking\",\n\n defaultAction: {\n allow: {}, // Allow by default\n },\n\n rules: [\n {\n name: \"RateLimitRule\",\n priority: 1,\n action: {\n block: {}, // Block requests exceeding rate limit\n },\n statement: {\n rateBasedStatement: {\n limit: 2000, // 2000 requests per 5 minutes per IP\n aggregateKeyType: \"IP\",\n },\n },\n visibilityConfig: {\n sampledRequestsEnabled: true,\n cloudwatchMetricsEnabled: true,\n metricName: \"RateLimitRule\",\n },\n },\n ],\n\n visibilityConfig: {\n sampledRequestsEnabled: true,\n cloudwatchMetricsEnabled: true,\n metricName: \"wraps-email-tracking-waf\",\n },\n\n tags: {\n ManagedBy: \"wraps-cli\",\n Description: \"WAF for Wraps email tracking with rate limiting\",\n },\n },\n {\n provider: usEast1Provider,\n }\n );\n\n return webAcl;\n}\n\n/**\n * Create CloudFront distribution for HTTPS tracking domain\n *\n * This creates a CloudFront distribution that sits in front of AWS SES's tracking endpoint\n * (r.{region}.awstrack.me) and provides HTTPS support with a custom domain and SSL certificate.\n * Also creates a WAF Web ACL with rate limiting for security.\n */\nexport async function createCloudFrontTracking(\n config: CloudFrontTrackingConfig\n): Promise<CloudFrontResources> {\n const sesTrackingOrigin = `r.${config.region}.awstrack.me`;\n\n // Create WAF Web ACL with rate limiting protection\n const webAcl = await createWAFWebACL();\n\n // Check if CloudFront distribution already exists with this alias\n const existingDistributionId = await findDistributionByAlias(\n config.customTrackingDomain\n );\n\n // CloudFront distribution configuration\n const distributionConfig = {\n enabled: true,\n comment: \"Wraps email tracking with HTTPS support\",\n aliases: [config.customTrackingDomain],\n\n // Attach WAF Web ACL for rate limiting protection\n webAclId: webAcl.arn,\n\n // Origin: SES tracking endpoint\n origins: [\n {\n domainName: sesTrackingOrigin,\n originId: \"ses-tracking\",\n customOriginConfig: {\n httpPort: 80,\n httpsPort: 443,\n originProtocolPolicy: \"http-only\", // SES tracking endpoint is HTTP\n originSslProtocols: [\"TLSv1.2\"],\n },\n },\n ],\n\n // Default cache behavior\n defaultCacheBehavior: {\n targetOriginId: \"ses-tracking\",\n viewerProtocolPolicy: \"redirect-to-https\", // Force HTTPS\n allowedMethods: [\"GET\", \"HEAD\", \"OPTIONS\"],\n cachedMethods: [\"GET\", \"HEAD\"],\n\n // Forward all query strings and headers (tracking links use query params)\n forwardedValues: {\n queryString: true,\n cookies: {\n forward: \"all\",\n },\n headers: [\"*\"], // Forward all headers to preserve tracking functionality\n },\n\n // Minimal caching for tracking redirects\n minTtl: 0,\n defaultTtl: 0,\n maxTtl: 31_536_000,\n\n compress: true,\n },\n\n // Price class (use only North America & Europe for cost optimization)\n priceClass: \"PriceClass_100\",\n\n // Restrictions (none)\n restrictions: {\n geoRestriction: {\n restrictionType: \"none\",\n },\n },\n\n // SSL certificate from ACM\n viewerCertificate: {\n acmCertificateArn: config.certificateArn,\n sslSupportMethod: \"sni-only\",\n minimumProtocolVersion: \"TLSv1.2_2021\",\n },\n\n tags: {\n ManagedBy: \"wraps-cli\",\n Description: \"Wraps email tracking CloudFront distribution\",\n },\n };\n\n const distribution = existingDistributionId\n ? new aws.cloudfront.Distribution(\n \"wraps-email-tracking-cdn\",\n distributionConfig,\n {\n import: existingDistributionId, // Import existing distribution\n }\n )\n : new aws.cloudfront.Distribution(\n \"wraps-email-tracking-cdn\",\n distributionConfig\n );\n\n // Note: DNS CNAME record for custom tracking domain is created separately\n // via Route53 SDK in route53.ts (using UPSERT for idempotency)\n // This prevents Pulumi resource conflicts on subsequent deployments\n\n return {\n distribution,\n domainName: distribution.domainName,\n webAcl,\n };\n}\n","import {\n CreateArchiveCommand,\n GetArchiveCommand,\n ListArchivesCommand,\n MailManagerClient,\n type RetentionPeriod,\n} from \"@aws-sdk/client-mailmanager\";\nimport {\n PutConfigurationSetArchivingOptionsCommand,\n SESv2Client,\n} from \"@aws-sdk/client-sesv2\";\nimport type * as pulumi from \"@pulumi/pulumi\";\nimport type { ArchiveRetention } from \"../../types/index.js\";\n\n/**\n * Mail Manager archive configuration\n */\nexport type MailManagerArchiveConfig = {\n name: string;\n retention: ArchiveRetention;\n configSetName: pulumi.Output<string>;\n region?: string;\n kmsKeyArn?: string; // Optional: provide existing KMS key, otherwise AWS-managed key is used\n};\n\n/**\n * Mail Manager archive resources\n */\nexport type MailManagerArchiveResources = {\n archiveId: string;\n archiveArn: string;\n kmsKeyArn?: string;\n};\n\n/**\n * Convert our retention types to AWS SDK RetentionPeriod enum\n */\nfunction retentionToAWSPeriod(retention: ArchiveRetention): RetentionPeriod {\n switch (retention) {\n case \"3months\":\n return \"THREE_MONTHS\";\n case \"6months\":\n return \"SIX_MONTHS\";\n case \"9months\":\n return \"NINE_MONTHS\";\n case \"1year\":\n return \"ONE_YEAR\";\n case \"18months\":\n return \"EIGHTEEN_MONTHS\";\n case \"2years\":\n return \"TWO_YEARS\";\n case \"30months\":\n return \"THIRTY_MONTHS\";\n case \"3years\":\n return \"THREE_YEARS\";\n case \"4years\":\n return \"FOUR_YEARS\";\n case \"5years\":\n return \"FIVE_YEARS\";\n case \"6years\":\n return \"SIX_YEARS\";\n case \"7years\":\n return \"SEVEN_YEARS\";\n case \"8years\":\n return \"EIGHT_YEARS\";\n case \"9years\":\n return \"NINE_YEARS\";\n case \"10years\":\n return \"TEN_YEARS\";\n case \"permanent\":\n return \"PERMANENT\";\n default:\n return \"THREE_MONTHS\";\n }\n}\n\n/**\n * Create Mail Manager archive for storing full email content\n *\n * This creates:\n * 1. Mail Manager Archive - stores RFC 822/MIME formatted emails\n * 2. Links archive to SES Configuration Set\n *\n * Uses AWS SDK directly since Pulumi doesn't support Mail Manager yet.\n *\n * Cost: $2/GB ingestion + $0.19/GB/month storage\n * See: https://docs.aws.amazon.com/ses/latest/dg/eb-archiving.html\n *\n * Note: KMS encryption is optional. If not provided, AWS-managed encryption is used.\n */\nexport async function createMailManagerArchive(\n config: MailManagerArchiveConfig\n): Promise<MailManagerArchiveResources> {\n const region = config.region || process.env.AWS_REGION || \"us-east-1\";\n const archiveName = `wraps-${config.name}-archive`;\n\n // Initialize clients\n const mailManagerClient = new MailManagerClient({ region });\n const sesClient = new SESv2Client({ region });\n\n const kmsKeyArn = config.kmsKeyArn;\n\n // If no KMS key provided, create one for encryption\n // Note: User can also opt to not provide one and AWS will use service-managed keys\n if (!kmsKeyArn) {\n // For now, we'll let AWS use service-managed keys\n // In the future, we could create a customer-managed key here if needed:\n //\n // const kmsClient = new KMSClient({ region });\n // const createKeyResult = await kmsClient.send(\n // new CreateKeyCommand({\n // Description: `KMS key for Wraps email archive (${archiveName})`,\n // Tags: [\n // { TagKey: \"ManagedBy\", TagValue: \"wraps-cli\" },\n // { TagKey: \"Name\", TagValue: `wraps-${config.name}-archive-key` },\n // ],\n // })\n // );\n // kmsKeyArn = createKeyResult.KeyMetadata?.Arn;\n }\n\n // 1. Create Mail Manager Archive (or get existing)\n const awsRetention = retentionToAWSPeriod(config.retention);\n\n let archiveId: string | undefined;\n let archiveArn: string | undefined;\n\n // Check if archive already exists\n try {\n const listCommand = new ListArchivesCommand({});\n const listResult = await mailManagerClient.send(listCommand);\n\n const existingArchive = listResult.Archives?.find(\n (archive: { ArchiveName?: string; ArchiveId?: string }) =>\n archive.ArchiveName === archiveName\n );\n\n if (existingArchive?.ArchiveId) {\n // Archive exists, use it\n console.log(`Using existing Mail Manager archive: ${archiveName}`);\n archiveId = existingArchive.ArchiveId;\n\n // Get full archive details to get ARN\n const getCommand = new GetArchiveCommand({ ArchiveId: archiveId });\n const getResult = await mailManagerClient.send(getCommand);\n archiveArn = getResult.ArchiveArn;\n }\n } catch (error) {\n console.log(\"Error checking for existing archive:\", error);\n // Continue to create new archive\n }\n\n // Create archive if it doesn't exist\n if (!archiveId) {\n try {\n const createArchiveCommand = new CreateArchiveCommand({\n ArchiveName: archiveName,\n Retention: {\n RetentionPeriod: awsRetention,\n },\n ...(kmsKeyArn && { KmsKeyArn: kmsKeyArn }),\n Tags: [\n { Key: \"ManagedBy\", Value: \"wraps-cli\" },\n { Key: \"Name\", Value: archiveName },\n { Key: \"Retention\", Value: config.retention },\n ],\n });\n\n const archiveResult = await mailManagerClient.send(createArchiveCommand);\n archiveId = archiveResult.ArchiveId;\n\n if (!archiveId) {\n throw new Error(\n \"Failed to create Mail Manager Archive: No ArchiveId returned\"\n );\n }\n\n console.log(`Created new Mail Manager archive: ${archiveName}`);\n } catch (error) {\n // If it's a ConflictException, the archive was created between our check and now\n if (\n error instanceof Error &&\n error.name === \"ConflictException\" &&\n error.message.includes(\"Archive already exists\")\n ) {\n console.log(\n \"Archive was created concurrently, fetching existing archive...\"\n );\n\n // List again and find it\n const listCommand = new ListArchivesCommand({});\n const listResult = await mailManagerClient.send(listCommand);\n const existingArchive = listResult.Archives?.find(\n (archive: { ArchiveName?: string; ArchiveId?: string }) =>\n archive.ArchiveName === archiveName\n );\n\n if (!existingArchive?.ArchiveId) {\n throw new Error(\n `Archive exists but couldn't find it: ${archiveName}`\n );\n }\n\n archiveId = existingArchive.ArchiveId;\n\n // Get full archive details\n const getCommand = new GetArchiveCommand({ ArchiveId: archiveId });\n const getResult = await mailManagerClient.send(getCommand);\n archiveArn = getResult.ArchiveArn;\n } else {\n throw error;\n }\n }\n }\n\n // Construct the ARN if we don't have it yet\n if (!archiveArn) {\n // ARN format: arn:aws:ses:region:account-id:mailmanager-archive/archive-id\n const identity = await import(\"@aws-sdk/client-sts\").then((m) =>\n new m.STSClient({ region }).send(new m.GetCallerIdentityCommand({}))\n );\n const accountId = identity.Account;\n archiveArn = `arn:aws:ses:${region}:${accountId}:mailmanager-archive/${archiveId}`;\n }\n\n // 2. Link archive to SES Configuration Set\n // We need to wait for the configSetName to resolve from Pulumi Output\n const configSetName = await new Promise<string>((resolve) => {\n config.configSetName.apply((name) => {\n resolve(name);\n });\n });\n\n const putArchivingOptionsCommand =\n new PutConfigurationSetArchivingOptionsCommand({\n ConfigurationSetName: configSetName,\n ArchiveArn: archiveArn,\n });\n\n await sesClient.send(putArchivingOptionsCommand);\n\n if (!(archiveId && archiveArn)) {\n throw new Error(\"Failed to get archive ID or ARN\");\n }\n\n return {\n archiveId,\n archiveArn,\n kmsKeyArn,\n };\n}\n","import type {\n ArchiveRetention,\n FeatureCost,\n FeatureCostBreakdown,\n WrapsEmailConfig,\n} from \"../../types/index.js\";\n\n/**\n * AWS pricing constants (as of 2025)\n * All costs in USD (US East N. Virginia region)\n * Source: aws.amazon.com pricing pages verified January 2025\n */\nconst AWS_PRICING = {\n // SES pricing\n SES_PER_EMAIL: 0.0001, // $0.10 per 1,000 emails (outbound)\n SES_ATTACHMENT_PER_GB: 0.12, // $0.12 per GB of attachments\n\n // DynamoDB pricing (on-demand, Standard table class)\n DYNAMODB_WRITE_PER_MILLION: 1.25, // $1.25 per million write request units (US East Ohio)\n DYNAMODB_READ_PER_MILLION: 0.25, // $0.25 per million read request units (US East Ohio)\n DYNAMODB_STORAGE_PER_GB: 0.25, // $0.25 per GB-month\n\n // Lambda pricing (x86)\n LAMBDA_REQUESTS_PER_MILLION: 0.2, // $0.20 per 1M requests\n LAMBDA_COMPUTE_PER_GB_SECOND: 0.000_016_666_7, // $0.0000166667 per GB-second\n\n // SQS pricing (Standard queues)\n SQS_REQUESTS_PER_MILLION: 0.5, // $0.50 per million requests (after free tier)\n\n // EventBridge pricing\n EVENTBRIDGE_EVENTS_PER_MILLION: 1.0, // $1.00 per million custom events published\n\n // Dedicated IP\n DEDICATED_IP_PER_MONTH: 24.95, // $24.95 per dedicated IP per month\n\n // CloudWatch pricing\n CLOUDWATCH_LOGS_PER_GB: 0.5, // $0.50 per GB ingested\n CLOUDWATCH_LOGS_STORAGE_PER_GB: 0.03, // $0.03 per GB-month\n\n // SES Mail Manager Archiving\n MAIL_MANAGER_INGESTION_PER_GB: 2.0, // $2.00 per GB ingested\n MAIL_MANAGER_STORAGE_PER_GB: 0.19, // $0.19 per GB-month\n} as const;\n\n/**\n * AWS Free tier limits (monthly, always-free or first 12 months)\n * Note: Some limits are permanently free, others only for first 12 months\n */\nconst FREE_TIER = {\n // SES: 3,000 emails/month for first 12 months (new AWS accounts only)\n // After 12 months or for existing accounts: NO free tier\n SES_EMAILS: 0, // Conservative: assume no free tier (most users are past 12 months)\n\n // Lambda: Permanently free tier\n LAMBDA_REQUESTS: 1_000_000, // 1M requests per month (always free)\n LAMBDA_COMPUTE_GB_SECONDS: 400_000, // 400,000 GB-seconds per month (always free)\n\n // DynamoDB: Permanently free tier\n DYNAMODB_WRITES: 0, // No free tier for writes in on-demand mode\n DYNAMODB_READS: 0, // No free tier for reads in on-demand mode\n DYNAMODB_STORAGE_GB: 25, // 25 GB storage per month (always free)\n\n // SQS: Permanently free tier\n SQS_REQUESTS: 1_000_000, // 1M requests per month (always free)\n\n // CloudWatch: Permanently free tier\n CLOUDWATCH_LOGS_GB: 5, // 5 GB ingestion per month (always free)\n} as const;\n\n/**\n * Estimate storage size in GB based on retention period and email volume\n * Note: Each email generates multiple events (SEND, DELIVERY, OPEN, CLICK, etc.)\n *\n * This calculates STEADY-STATE storage (after retention period fills up).\n * Initial months will be cheaper as storage builds up gradually.\n *\n * Example for 90-day retention with 10k emails/month:\n * - Month 1: ~0.015 GB stored\n * - Month 2: ~0.031 GB stored\n * - Month 3: ~0.046 GB stored (steady state reached)\n * - Month 4+: ~0.046 GB stored (old data deleted, new data added)\n */\nfunction estimateStorageSize(\n emailsPerMonth: number,\n retention: ArchiveRetention,\n numEventTypes = 8\n): number {\n // Average email event record size: ~2 KB (including metadata)\n const avgRecordSizeKB = 2;\n\n // Calculate total months of retention\n const retentionMonths = {\n \"7days\": 0.25,\n \"30days\": 1,\n \"90days\": 3,\n \"3months\": 3,\n \"6months\": 6,\n \"9months\": 9,\n \"1year\": 12,\n \"18months\": 18,\n \"2years\": 24,\n \"30months\": 30,\n \"3years\": 36,\n \"4years\": 48,\n \"5years\": 60,\n \"6years\": 72,\n \"7years\": 84,\n \"8years\": 96,\n \"9years\": 108,\n \"10years\": 120,\n indefinite: 120,\n permanent: 120,\n }[retention];\n\n // Total steady-state storage = emails/month * event types * months * record size\n // This represents storage after retention period fills up\n const totalKB =\n emailsPerMonth * numEventTypes * (retentionMonths ?? 12) * avgRecordSizeKB;\n return totalKB / 1024 / 1024; // Convert to GB\n}\n\n/**\n * Estimate email archive storage size in GB (for full email content)\n * Different from event storage - stores complete RFC 822/MIME emails\n *\n * This calculates STEADY-STATE storage (after retention period fills up).\n */\nfunction estimateArchiveStorageSize(\n emailsPerMonth: number,\n retention: ArchiveRetention\n): number {\n // Average full email size: ~50 KB (HTML, headers, small attachments)\n const avgEmailSizeKB = 50;\n\n // Calculate total months of retention\n const retentionMonths = {\n \"7days\": 0.25,\n \"30days\": 1,\n \"90days\": 3,\n \"3months\": 3,\n \"6months\": 6,\n \"9months\": 9,\n \"1year\": 12,\n \"18months\": 18,\n \"2years\": 24,\n \"30months\": 30,\n \"3years\": 36,\n \"4years\": 48,\n \"5years\": 60,\n \"6years\": 72,\n \"7years\": 84,\n \"8years\": 96,\n \"9years\": 108,\n \"10years\": 120,\n indefinite: 120,\n permanent: 120,\n }[retention];\n\n // Total steady-state storage = emails/month * months * email size\n const totalKB = emailsPerMonth * (retentionMonths ?? 12) * avgEmailSizeKB;\n return totalKB / 1024 / 1024; // Convert to GB\n}\n\n/**\n * Calculate cost for event tracking feature\n * Architecture: SES → EventBridge → SQS → Lambda → DynamoDB\n */\nfunction calculateEventTrackingCost(\n config: WrapsEmailConfig,\n emailsPerMonth: number\n): FeatureCost | undefined {\n if (!config.eventTracking?.enabled) {\n return;\n }\n\n let monthlyCost = 0;\n const components: string[] = [];\n\n // Calculate number of events based on event types tracked\n const numEventTypes = config.eventTracking.events?.length || 8; // Default to 8 common events\n const totalEvents = emailsPerMonth * numEventTypes; // Each email can trigger multiple events\n\n // EventBridge custom events (SES → EventBridge)\n if (config.eventTracking.eventBridge) {\n const eventCost =\n (totalEvents / 1_000_000) * AWS_PRICING.EVENTBRIDGE_EVENTS_PER_MILLION;\n monthlyCost += eventCost;\n components.push(\"EventBridge\");\n }\n\n // SQS queue costs (EventBridge → SQS)\n // Each event: 1 send to SQS + 1 receive by Lambda + 1 delete = 3 requests\n const sqsRequests = totalEvents * 3;\n const sqsCost =\n (Math.max(0, sqsRequests - FREE_TIER.SQS_REQUESTS) / 1_000_000) *\n AWS_PRICING.SQS_REQUESTS_PER_MILLION;\n monthlyCost += sqsCost;\n components.push(\"SQS\");\n\n // Lambda processing costs (SQS → Lambda → DynamoDB)\n const lambdaInvocations = totalEvents; // One invocation per event\n const lambdaRequestCost =\n (Math.max(0, lambdaInvocations - FREE_TIER.LAMBDA_REQUESTS) / 1_000_000) *\n AWS_PRICING.LAMBDA_REQUESTS_PER_MILLION;\n\n // Compute cost: 512MB memory, 100ms average execution\n const memoryGB = 0.5; // 512MB = 0.5GB\n const avgDurationSeconds = 0.1; // 100ms\n const computeGBSeconds = lambdaInvocations * memoryGB * avgDurationSeconds;\n const lambdaComputeCost =\n Math.max(0, computeGBSeconds - FREE_TIER.LAMBDA_COMPUTE_GB_SECONDS) *\n AWS_PRICING.LAMBDA_COMPUTE_PER_GB_SECOND;\n\n monthlyCost += lambdaRequestCost + lambdaComputeCost;\n components.push(\"Lambda\");\n\n return {\n monthly: monthlyCost,\n description: `Event processing (${numEventTypes} event types: ${components.join(\" → \")})`,\n };\n}\n\n/**\n * Calculate cost for DynamoDB email history storage\n */\nfunction calculateDynamoDBCost(\n config: WrapsEmailConfig,\n emailsPerMonth: number\n): FeatureCost | undefined {\n if (!config.eventTracking?.dynamoDBHistory) {\n return;\n }\n\n const retention = config.eventTracking.archiveRetention || \"90days\";\n const numEventTypes = config.eventTracking.events?.length || 8;\n\n // Write costs: one write per event (each email generates multiple events)\n const totalEvents = emailsPerMonth * numEventTypes;\n const writeCost =\n (Math.max(0, totalEvents - FREE_TIER.DYNAMODB_WRITES) / 1_000_000) *\n AWS_PRICING.DYNAMODB_WRITE_PER_MILLION;\n\n // Storage costs\n const storageGB = estimateStorageSize(\n emailsPerMonth,\n retention,\n numEventTypes\n );\n const storageCost =\n Math.max(0, storageGB - FREE_TIER.DYNAMODB_STORAGE_GB) *\n AWS_PRICING.DYNAMODB_STORAGE_PER_GB;\n\n return {\n monthly: writeCost + storageCost,\n description: `Email history (${retention}, ~${storageGB.toFixed(2)} GB at steady-state, ${numEventTypes} event types)`,\n };\n}\n\n/**\n * Calculate cost for tracking features (opens/clicks)\n */\nfunction calculateTrackingCost(\n config: WrapsEmailConfig\n): FeatureCost | undefined {\n if (!config.tracking?.enabled) {\n return;\n }\n\n // Tracking is free - it's built into SES\n // Custom redirect domain DNS records are managed where user already manages DNS\n return {\n monthly: 0,\n description: \"Open/click tracking (no additional cost)\",\n };\n}\n\n/**\n * Calculate cost for reputation metrics\n */\nfunction calculateReputationMetricsCost(\n config: WrapsEmailConfig\n): FeatureCost | undefined {\n if (!config.reputationMetrics) {\n return;\n }\n\n // Reputation metrics are free in CloudWatch\n return {\n monthly: 0,\n description: \"Reputation metrics in CloudWatch (no additional cost)\",\n };\n}\n\n/**\n * Calculate cost for dedicated IP\n */\nfunction calculateDedicatedIpCost(\n config: WrapsEmailConfig\n): FeatureCost | undefined {\n if (!config.dedicatedIp) {\n return;\n }\n\n return {\n monthly: AWS_PRICING.DEDICATED_IP_PER_MONTH,\n description: \"Dedicated IP address (requires 100k+ emails/day for warmup)\",\n };\n}\n\n/**\n * Calculate cost for email archiving (full email content storage)\n * Architecture: SES → Mail Manager Archive (S3-backed)\n */\nfunction calculateEmailArchivingCost(\n config: WrapsEmailConfig,\n emailsPerMonth: number\n): FeatureCost | undefined {\n if (!config.emailArchiving?.enabled) {\n return;\n }\n\n const retention = config.emailArchiving.retention;\n const storageGB = estimateArchiveStorageSize(emailsPerMonth, retention);\n\n // Ingestion cost: one-time per email (charged monthly as new emails arrive)\n const monthlyDataGB = (emailsPerMonth * 50) / 1024 / 1024; // 50 KB average email\n const ingestionCost =\n monthlyDataGB * AWS_PRICING.MAIL_MANAGER_INGESTION_PER_GB;\n\n // Storage cost: steady-state storage after retention period fills\n const storageCost = storageGB * AWS_PRICING.MAIL_MANAGER_STORAGE_PER_GB;\n\n return {\n monthly: ingestionCost + storageCost,\n description: `Email archiving (${retention}, ~${storageGB.toFixed(2)} GB at steady-state)`,\n };\n}\n\n/**\n * Calculate total infrastructure costs\n *\n * @param config Email configuration\n * @param emailsPerMonth Estimated monthly email volume\n * @returns Detailed cost breakdown\n */\nexport function calculateCosts(\n config: WrapsEmailConfig,\n emailsPerMonth = 10_000\n): FeatureCostBreakdown {\n const tracking = calculateTrackingCost(config);\n const reputationMetrics = calculateReputationMetricsCost(config);\n const eventTracking = calculateEventTrackingCost(config, emailsPerMonth);\n const dynamoDBHistory = calculateDynamoDBCost(config, emailsPerMonth);\n const emailArchiving = calculateEmailArchivingCost(config, emailsPerMonth);\n const dedicatedIp = calculateDedicatedIpCost(config);\n\n // Calculate SES base costs (always present)\n const sesEmailCost =\n Math.max(0, emailsPerMonth - FREE_TIER.SES_EMAILS) *\n AWS_PRICING.SES_PER_EMAIL;\n\n // Sum all costs\n const totalMonthlyCost =\n sesEmailCost +\n (tracking?.monthly || 0) +\n (reputationMetrics?.monthly || 0) +\n (eventTracking?.monthly || 0) +\n (dynamoDBHistory?.monthly || 0) +\n (emailArchiving?.monthly || 0) +\n (dedicatedIp?.monthly || 0);\n\n return {\n tracking,\n reputationMetrics,\n eventTracking,\n dynamoDBHistory,\n emailArchiving,\n dedicatedIp,\n total: {\n monthly: totalMonthlyCost,\n perEmail: AWS_PRICING.SES_PER_EMAIL,\n description: `Total estimated cost for ${emailsPerMonth.toLocaleString()} emails/month`,\n },\n };\n}\n\n/**\n * Format cost for display\n */\nexport function formatCost(cost: number): string {\n if (cost === 0) {\n return \"Free\";\n }\n if (cost < 0.01) {\n return \"< $0.01\";\n }\n return `$${cost.toFixed(2)}`;\n}\n\n/**\n * Get cost estimate summary for display\n */\nexport function getCostSummary(\n config: WrapsEmailConfig,\n emailsPerMonth = 10_000\n): string {\n const costs = calculateCosts(config, emailsPerMonth);\n const lines: string[] = [];\n\n lines.push(\n `Estimated cost for ${emailsPerMonth.toLocaleString()} emails/month: ${formatCost(costs.total.monthly)}/mo`\n );\n lines.push(\n ` (${formatCost((costs.total.perEmail ?? 0) * 1000)}/1k emails + infrastructure)`\n );\n\n if (costs.tracking) {\n lines.push(\n ` - ${costs.tracking.description}: ${formatCost(costs.tracking.monthly)}`\n );\n }\n if (costs.reputationMetrics) {\n lines.push(\n ` - ${costs.reputationMetrics.description}: ${formatCost(costs.reputationMetrics.monthly)}`\n );\n }\n if (costs.eventTracking) {\n lines.push(\n ` - ${costs.eventTracking.description}: ${formatCost(costs.eventTracking.monthly)}`\n );\n }\n if (costs.dynamoDBHistory) {\n lines.push(\n ` - ${costs.dynamoDBHistory.description}: ${formatCost(costs.dynamoDBHistory.monthly)}`\n );\n }\n if (costs.emailArchiving) {\n lines.push(\n ` - ${costs.emailArchiving.description}: ${formatCost(costs.emailArchiving.monthly)}`\n );\n }\n if (costs.dedicatedIp) {\n lines.push(\n ` - ${costs.dedicatedIp.description}: ${formatCost(costs.dedicatedIp.monthly)}`\n );\n }\n\n return lines.join(\"\\n\");\n}\n","import type { ConfigPreset, WrapsEmailConfig } from \"../../types/index.js\";\nimport { calculateCosts, formatCost } from \"./costs.js\";\n\n/**\n * Preset configurations with recommended settings for different use cases\n */\n\n/**\n * Starter preset - minimal features for low-volume senders\n * Perfect for: Side projects, MVPs, development/staging\n * Volume: Up to 10k emails/month\n * Cost: ~$1-2/month (without archiving)\n */\nexport const STARTER_PRESET: WrapsEmailConfig = {\n tracking: {\n enabled: true,\n opens: true,\n clicks: true,\n },\n tlsRequired: true,\n reputationMetrics: false,\n suppressionList: {\n enabled: true,\n reasons: [\"BOUNCE\", \"COMPLAINT\"],\n },\n eventTracking: {\n enabled: false,\n },\n // Email archiving disabled by default\n emailArchiving: {\n enabled: false,\n retention: \"30days\",\n },\n sendingEnabled: true,\n};\n\n/**\n * Production preset - recommended for most production applications\n * Perfect for: SaaS apps, B2B products, moderate volume\n * Volume: 10k-500k emails/month\n * Cost: ~$10-50/month (scales with volume, add ~$5-15/mo for archiving)\n */\nexport const PRODUCTION_PRESET: WrapsEmailConfig = {\n tracking: {\n enabled: true,\n opens: true,\n clicks: true,\n },\n tlsRequired: true,\n reputationMetrics: true,\n suppressionList: {\n enabled: true,\n reasons: [\"BOUNCE\", \"COMPLAINT\"],\n },\n eventTracking: {\n enabled: true,\n eventBridge: true,\n events: [\n \"SEND\",\n \"DELIVERY\",\n \"OPEN\",\n \"CLICK\",\n \"BOUNCE\",\n \"COMPLAINT\",\n \"REJECT\",\n \"RENDERING_FAILURE\",\n ],\n dynamoDBHistory: true,\n archiveRetention: \"90days\",\n },\n // Email archiving with 90-day retention\n emailArchiving: {\n enabled: false, // User can opt-in\n retention: \"90days\",\n },\n sendingEnabled: true,\n};\n\n/**\n * Enterprise preset - full features for high-volume senders\n * Perfect for: Large platforms, high-volume transactional email\n * Volume: 500k+ emails/month\n * Cost: ~$100-200/month (includes $24.95 dedicated IP, add ~$50+/mo for archiving)\n */\nexport const ENTERPRISE_PRESET: WrapsEmailConfig = {\n tracking: {\n enabled: true,\n opens: true,\n clicks: true,\n },\n tlsRequired: true,\n reputationMetrics: true,\n suppressionList: {\n enabled: true,\n reasons: [\"BOUNCE\", \"COMPLAINT\"],\n },\n eventTracking: {\n enabled: true,\n eventBridge: true,\n events: [\n \"SEND\",\n \"DELIVERY\",\n \"OPEN\",\n \"CLICK\",\n \"BOUNCE\",\n \"COMPLAINT\",\n \"REJECT\",\n \"RENDERING_FAILURE\",\n \"DELIVERY_DELAY\",\n \"SUBSCRIPTION\",\n ],\n dynamoDBHistory: true,\n archiveRetention: \"1year\",\n },\n // Email archiving with 1-year retention\n emailArchiving: {\n enabled: false, // User can opt-in\n retention: \"1year\",\n },\n dedicatedIp: true,\n sendingEnabled: true,\n};\n\n/**\n * Get preset configuration by name\n */\nexport function getPreset(preset: ConfigPreset): WrapsEmailConfig | null {\n switch (preset) {\n case \"starter\":\n return STARTER_PRESET;\n case \"production\":\n return PRODUCTION_PRESET;\n case \"enterprise\":\n return ENTERPRISE_PRESET;\n case \"custom\":\n return null; // User will configure manually\n }\n}\n\n/**\n * Preset metadata for display\n */\nexport type PresetInfo = {\n name: string;\n description: string;\n recommended: string;\n volume: string;\n estimatedCost: string;\n features: string[];\n};\n\n/**\n * Get preset information for display\n */\nexport function getPresetInfo(preset: ConfigPreset): PresetInfo {\n const config = getPreset(preset);\n\n if (preset === \"custom\" || !config) {\n return {\n name: \"Custom\",\n description: \"Configure each feature individually\",\n recommended: \"Advanced users who need specific configuration\",\n volume: \"Any volume\",\n estimatedCost: \"Varies\",\n features: [\"Full control over all features\"],\n };\n }\n\n const costs = calculateCosts(\n config,\n preset === \"starter\"\n ? 10_000\n : preset === \"production\"\n ? 100_000\n : 1_000_000\n );\n\n const baseInfo = {\n starter: {\n name: \"Starter\",\n description: \"Minimal features for low-volume senders\",\n recommended: \"Side projects, MVPs, development/staging\",\n volume: \"Up to 10k emails/month\",\n features: [\n \"Open & click tracking\",\n \"TLS encryption required\",\n \"Automatic bounce/complaint suppression\",\n \"Optional: Email archiving (full content storage)\",\n ],\n },\n production: {\n name: \"Production\",\n description: \"Recommended for most production applications\",\n recommended: \"SaaS apps, B2B products, moderate volume (RECOMMENDED)\",\n volume: \"10k-500k emails/month\",\n features: [\n \"Everything in Starter\",\n \"Reputation tracking\",\n \"Real-time event tracking (EventBridge)\",\n \"90-day email history storage\",\n \"Optional: Email archiving with rendered viewer\",\n \"Complete event visibility\",\n ],\n },\n enterprise: {\n name: \"Enterprise\",\n description: \"Full features for high-volume senders\",\n recommended: \"Large platforms, high-volume transactional email\",\n volume: \"500k+ emails/month\",\n features: [\n \"Everything in Production\",\n \"Dedicated IP address\",\n \"1-year email history\",\n \"Optional: 1-year+ email archiving\",\n \"All event types tracked\",\n \"Priority support eligibility\",\n ],\n },\n }[preset];\n\n return {\n ...baseInfo,\n estimatedCost: formatCost(costs.total.monthly),\n } as PresetInfo;\n}\n\n/**\n * Get all preset options for CLI prompts\n */\nexport function getAllPresetInfo(): PresetInfo[] {\n return [\n getPresetInfo(\"starter\"),\n getPresetInfo(\"production\"),\n getPresetInfo(\"enterprise\"),\n getPresetInfo(\"custom\"),\n ];\n}\n\n/**\n * Compare two configurations to determine upgrade path\n */\nexport function getUpgradePath(\n current: WrapsEmailConfig,\n target: WrapsEmailConfig\n): string[] {\n const changes: string[] = [];\n\n // Check tracking changes\n if (!current.tracking?.enabled && target.tracking?.enabled) {\n changes.push(\"Enable email tracking (opens & clicks)\");\n }\n\n // Check reputation metrics\n if (!current.reputationMetrics && target.reputationMetrics) {\n changes.push(\"Enable reputation metrics\");\n }\n\n // Check event tracking\n if (!current.eventTracking?.enabled && target.eventTracking?.enabled) {\n changes.push(\"Enable real-time event tracking\");\n }\n\n // Check DynamoDB history\n if (\n !current.eventTracking?.dynamoDBHistory &&\n target.eventTracking?.dynamoDBHistory\n ) {\n changes.push(\"Enable email history storage\");\n }\n\n // Check retention upgrade\n if (\n current.eventTracking?.archiveRetention !==\n target.eventTracking?.archiveRetention &&\n target.eventTracking?.archiveRetention\n ) {\n changes.push(\n `Upgrade retention: ${current.eventTracking?.archiveRetention || \"none\"} → ${target.eventTracking.archiveRetention}`\n );\n }\n\n // Check dedicated IP\n if (!current.dedicatedIp && target.dedicatedIp) {\n changes.push(\"Add dedicated IP address\");\n }\n\n return changes;\n}\n\n/**\n * Validate configuration for common issues\n */\nexport function validateConfig(config: WrapsEmailConfig): string[] {\n const warnings: string[] = [];\n\n // Warn about dedicated IP without high volume\n if (config.dedicatedIp) {\n warnings.push(\n \"⚠️ Dedicated IPs require 100k+ emails/day for proper warmup. Consider starting with shared IPs.\"\n );\n }\n\n // Warn about event tracking without storage\n if (config.eventTracking?.enabled && !config.eventTracking?.dynamoDBHistory) {\n warnings.push(\n \"💡 Event tracking is enabled but history storage is disabled. Events will only be available in real-time.\"\n );\n }\n\n // Warn about long retention without need\n if (config.eventTracking?.archiveRetention === \"indefinite\") {\n warnings.push(\n \"⚠️ Indefinite retention can become expensive. Consider 90-day or 1-year retention.\"\n );\n }\n\n return warnings;\n}\n","import * as clack from \"@clack/prompts\";\nimport pc from \"picocolors\";\nimport type { ArchiveRetention } from \"../../types/index.js\";\n\n/**\n * Hosting provider type\n */\nexport type Provider = \"vercel\" | \"aws\" | \"railway\" | \"other\";\n\n/**\n * Prompt for hosting provider\n */\nexport async function promptProvider(): Promise<Provider> {\n const provider = await clack.select({\n message: \"Where is your app hosted?\",\n options: [\n {\n value: \"aws\",\n label: \"AWS (Lambda/ECS/EC2)\",\n hint: \"Uses IAM roles automatically\",\n },\n {\n value: \"vercel\",\n label: \"Vercel\",\n hint: \"Uses OIDC (no AWS credentials needed)\",\n },\n {\n value: \"railway\",\n label: \"Railway\",\n hint: \"Requires AWS credentials\",\n },\n {\n value: \"other\",\n label: \"Other\",\n hint: \"Will use AWS access keys\",\n },\n ],\n });\n\n if (clack.isCancel(provider)) {\n clack.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n return provider as Provider;\n}\n\n/**\n * Prompt for AWS region\n */\nexport async function promptRegion(defaultRegion: string): Promise<string> {\n const region = await clack.select({\n message: \"Select AWS region:\",\n options: [\n { value: \"us-east-1\", label: \"US East (N. Virginia)\", hint: \"us-east-1\" },\n { value: \"us-east-2\", label: \"US East (Ohio)\", hint: \"us-east-2\" },\n {\n value: \"us-west-1\",\n label: \"US West (N. California)\",\n hint: \"us-west-1\",\n },\n { value: \"us-west-2\", label: \"US West (Oregon)\", hint: \"us-west-2\" },\n { value: \"af-south-1\", label: \"Africa (Cape Town)\", hint: \"af-south-1\" },\n {\n value: \"ap-east-1\",\n label: \"Asia Pacific (Hong Kong)\",\n hint: \"ap-east-1\",\n },\n {\n value: \"ap-south-1\",\n label: \"Asia Pacific (Mumbai)\",\n hint: \"ap-south-1\",\n },\n {\n value: \"ap-northeast-1\",\n label: \"Asia Pacific (Tokyo)\",\n hint: \"ap-northeast-1\",\n },\n {\n value: \"ap-northeast-2\",\n label: \"Asia Pacific (Seoul)\",\n hint: \"ap-northeast-2\",\n },\n {\n value: \"ap-northeast-3\",\n label: \"Asia Pacific (Osaka)\",\n hint: \"ap-northeast-3\",\n },\n {\n value: \"ap-southeast-1\",\n label: \"Asia Pacific (Singapore)\",\n hint: \"ap-southeast-1\",\n },\n {\n value: \"ap-southeast-2\",\n label: \"Asia Pacific (Sydney)\",\n hint: \"ap-southeast-2\",\n },\n {\n value: \"ap-southeast-3\",\n label: \"Asia Pacific (Jakarta)\",\n hint: \"ap-southeast-3\",\n },\n {\n value: \"ca-central-1\",\n label: \"Canada (Central)\",\n hint: \"ca-central-1\",\n },\n {\n value: \"eu-central-1\",\n label: \"Europe (Frankfurt)\",\n hint: \"eu-central-1\",\n },\n { value: \"eu-west-1\", label: \"Europe (Ireland)\", hint: \"eu-west-1\" },\n { value: \"eu-west-2\", label: \"Europe (London)\", hint: \"eu-west-2\" },\n { value: \"eu-west-3\", label: \"Europe (Paris)\", hint: \"eu-west-3\" },\n { value: \"eu-south-1\", label: \"Europe (Milan)\", hint: \"eu-south-1\" },\n { value: \"eu-north-1\", label: \"Europe (Stockholm)\", hint: \"eu-north-1\" },\n {\n value: \"me-south-1\",\n label: \"Middle East (Bahrain)\",\n hint: \"me-south-1\",\n },\n {\n value: \"sa-east-1\",\n label: \"South America (São Paulo)\",\n hint: \"sa-east-1\",\n },\n ],\n initialValue: defaultRegion || \"us-east-1\",\n });\n\n if (clack.isCancel(region)) {\n clack.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n return region as string;\n}\n\n/**\n * Prompt for domain to verify (optional)\n */\nexport async function promptDomain(): Promise<string> {\n const domain = await clack.text({\n message: \"Domain to verify (optional):\",\n placeholder: \"myapp.com\",\n validate: (value) => {\n if (!value) {\n return; // Optional\n }\n if (!value.includes(\".\")) {\n return \"Please enter a valid domain (e.g., myapp.com)\";\n }\n },\n });\n\n if (clack.isCancel(domain)) {\n clack.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n return domain || \"\";\n}\n\n/**\n * Vercel configuration\n */\nexport type VercelConfig = {\n teamSlug: string;\n projectName: string;\n};\n\n/**\n * Prompt for Vercel configuration\n */\nexport async function promptVercelConfig(): Promise<VercelConfig> {\n const config = await clack.group(\n {\n teamSlug: () =>\n clack.text({\n message: \"Vercel team slug:\",\n placeholder: \"my-team\",\n validate: (value) => {\n if (!value) {\n return \"Team slug is required for Vercel integration\";\n }\n },\n }),\n projectName: () =>\n clack.text({\n message: \"Vercel project name:\",\n placeholder: \"my-project\",\n validate: (value) => {\n if (!value) {\n return \"Project name is required\";\n }\n },\n }),\n },\n {\n onCancel: () => {\n clack.cancel(\"Operation cancelled.\");\n process.exit(0);\n },\n }\n );\n\n return config as VercelConfig;\n}\n\n/**\n * Prompt for integration level\n */\nexport async function promptIntegrationLevel(): Promise<\n \"dashboard-only\" | \"enhanced\"\n> {\n const level = await clack.select({\n message: \"Integration level:\",\n options: [\n {\n value: \"enhanced\",\n label: \"Enhanced (full email tracking)\",\n hint: \"Creates SES config, DynamoDB, Lambda functions\",\n },\n {\n value: \"dashboard-only\",\n label: \"Dashboard-only (read-only)\",\n hint: \"Only creates IAM role for dashboard access\",\n },\n ],\n });\n\n if (clack.isCancel(level)) {\n clack.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n return level as \"dashboard-only\" | \"enhanced\";\n}\n\n/**\n * Confirm deployment\n */\nexport async function confirmDeploy(): Promise<boolean> {\n const confirmed = await clack.confirm({\n message: \"Deploy infrastructure to your AWS account?\",\n initialValue: true,\n });\n\n if (clack.isCancel(confirmed)) {\n clack.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n return confirmed;\n}\n\n/**\n * Feature definition for multi-select\n */\nexport type FeatureOption = {\n value: string;\n label: string;\n hint: string;\n};\n\n/**\n * Get available features\n */\nexport function getAvailableFeatures(): FeatureOption[] {\n return [\n {\n value: \"configSet\",\n label: \"Configuration Set\",\n hint: \"Track opens, clicks, bounces, and complaints\",\n },\n {\n value: \"bounceHandling\",\n label: \"Bounce Handling\",\n hint: \"Automatically process bounce notifications\",\n },\n {\n value: \"complaintHandling\",\n label: \"Complaint Handling\",\n hint: \"Automatically process spam complaints\",\n },\n {\n value: \"emailHistory\",\n label: \"Email History\",\n hint: \"Store sent emails in DynamoDB (90-day retention)\",\n },\n {\n value: \"eventProcessor\",\n label: \"Event Processor\",\n hint: \"Advanced analytics and webhook forwarding\",\n },\n {\n value: \"dashboardAccess\",\n label: \"Dashboard Access\",\n hint: \"Read-only IAM role for web dashboard\",\n },\n ];\n}\n\n/**\n * Prompt for feature selection (multi-select)\n */\nexport async function promptFeatureSelection(\n preselected?: string[]\n): Promise<string[]> {\n const features = getAvailableFeatures();\n\n const selected = await clack.multiselect({\n message: \"Select features to deploy:\",\n options: features,\n initialValues: preselected || [\n \"configSet\",\n \"bounceHandling\",\n \"complaintHandling\",\n \"dashboardAccess\",\n ],\n required: true,\n });\n\n if (clack.isCancel(selected)) {\n clack.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n return selected as string[];\n}\n\n/**\n * Conflict resolution action\n */\nexport type ConflictAction = \"deploy-alongside\" | \"replace\" | \"skip\";\n\n/**\n * Prompt for conflict resolution\n */\nexport async function promptConflictResolution(\n resourceType: string,\n existingResourceName: string\n): Promise<ConflictAction> {\n const action = await clack.select({\n message: `Found existing ${resourceType}: ${pc.cyan(existingResourceName)}. How should we handle this?`,\n options: [\n {\n value: \"deploy-alongside\",\n label: \"Deploy alongside (no changes)\",\n hint: \"Create our resources without modifying yours\",\n },\n {\n value: \"replace\",\n label: \"Replace with Wraps version\",\n hint: \"Save original for restore, use ours\",\n },\n {\n value: \"skip\",\n label: \"Skip this feature\",\n hint: \"Keep your setup, skip Wraps deployment\",\n },\n ],\n });\n\n if (clack.isCancel(action)) {\n clack.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n return action as ConflictAction;\n}\n\n/**\n * Prompt to select identities to track\n */\nexport async function promptSelectIdentities(\n identities: Array<{ name: string; verified: boolean }>\n): Promise<string[]> {\n const selected = await clack.multiselect({\n message: \"Select identities to connect with Wraps:\",\n options: identities.map((id) => ({\n value: id.name,\n label: id.name,\n hint: id.verified ? \"Verified\" : \"Pending verification\",\n })),\n required: false,\n });\n\n if (clack.isCancel(selected)) {\n clack.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n return selected as string[];\n}\n\n/**\n * Confirm connection deployment\n */\nexport async function confirmConnect(): Promise<boolean> {\n const confirmed = await clack.confirm({\n message: \"Connect to existing AWS infrastructure?\",\n initialValue: true,\n });\n\n if (clack.isCancel(confirmed)) {\n clack.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n return confirmed;\n}\n\n/**\n * Prompt for configuration preset\n */\nexport async function promptConfigPreset(): Promise<\n \"starter\" | \"production\" | \"enterprise\" | \"custom\"\n> {\n const { getAllPresetInfo } = await import(\"../email/presets.js\");\n const presets = getAllPresetInfo();\n\n const preset = await clack.select({\n message: \"Choose a configuration preset:\",\n options: presets.map((p: any) => ({\n value: p.name.toLowerCase() as\n | \"starter\"\n | \"production\"\n | \"enterprise\"\n | \"custom\",\n label: `${p.name} - ${p.description}`,\n hint: `${p.volume} | Est. ${p.estimatedCost}/mo`,\n })),\n });\n\n if (clack.isCancel(preset)) {\n clack.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n return preset as \"starter\" | \"production\" | \"enterprise\" | \"custom\";\n}\n\n/**\n * Prompt for estimated monthly email volume\n */\nexport async function promptEstimatedVolume(): Promise<number> {\n const volume = await clack.select({\n message: \"Estimated monthly email volume:\",\n options: [\n { value: 1000, label: \"< 1k emails/month\", hint: \"Hobby/Development\" },\n { value: 10_000, label: \"1k-10k emails/month\", hint: \"Side Project\" },\n {\n value: 50_000,\n label: \"10k-100k emails/month\",\n hint: \"Growing Startup\",\n },\n {\n value: 250_000,\n label: \"100k-500k emails/month\",\n hint: \"Production SaaS\",\n },\n { value: 1_000_000, label: \"500k+ emails/month\", hint: \"High Volume\" },\n ],\n });\n\n if (clack.isCancel(volume)) {\n clack.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n return volume as number;\n}\n\n/**\n * Prompt for custom configuration\n */\n/**\n * Prompt for email archiving configuration (for presets)\n */\nexport async function promptEmailArchiving(): Promise<{\n enabled: boolean;\n retention: ArchiveRetention;\n}> {\n const enabled = await clack.confirm({\n message:\n \"Enable email archiving? (Store full email content with HTML for viewing in dashboard)\",\n initialValue: false,\n });\n\n if (clack.isCancel(enabled)) {\n clack.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n if (!enabled) {\n return { enabled: false, retention: \"90days\" };\n }\n\n const retention = await clack.select({\n message: \"Email archive retention period:\",\n options: [\n { value: \"7days\", label: \"7 days\", hint: \"~$1-2/mo for 10k emails\" },\n { value: \"30days\", label: \"30 days\", hint: \"~$2-4/mo for 10k emails\" },\n {\n value: \"90days\",\n label: \"90 days (recommended)\",\n hint: \"~$5-10/mo for 10k emails\",\n },\n {\n value: \"6months\",\n label: \"6 months\",\n hint: \"~$15-25/mo for 10k emails\",\n },\n { value: \"1year\", label: \"1 year\", hint: \"~$25-40/mo for 10k emails\" },\n {\n value: \"18months\",\n label: \"18 months\",\n hint: \"~$35-60/mo for 10k emails\",\n },\n ],\n initialValue: \"90days\",\n });\n\n if (clack.isCancel(retention)) {\n clack.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n clack.log.info(\n pc.dim(\n \"Archiving stores full RFC 822 emails with HTML, attachments, and headers\"\n )\n );\n clack.log.info(\n pc.dim(\"Cost: $2/GB ingestion + $0.19/GB/month storage (~50KB per email)\")\n );\n\n return {\n enabled: true,\n retention: retention as ArchiveRetention,\n };\n}\n\nexport async function promptCustomConfig(existingConfig?: any): Promise<any> {\n clack.log.info(\"Custom configuration builder\");\n clack.log.info(\"Configure each feature individually\");\n\n // Reputation tracking (first, as it's recommended)\n const reputationMetrics = await clack.confirm({\n message: \"Enable reputation tracking (recommended)?\",\n initialValue: existingConfig?.reputationMetrics ?? true,\n });\n\n if (clack.isCancel(reputationMetrics)) {\n clack.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n // Tracking\n const trackingEnabled = await clack.confirm({\n message: \"Enable open & click tracking?\",\n initialValue: existingConfig?.tracking?.enabled ?? true,\n });\n\n if (clack.isCancel(trackingEnabled)) {\n clack.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n // Event tracking (combined - EventBridge + DynamoDB)\n const eventTrackingEnabled = await clack.confirm({\n message: \"Store email events in DynamoDB?\",\n initialValue: existingConfig?.eventTracking?.enabled ?? true,\n });\n\n if (clack.isCancel(eventTrackingEnabled)) {\n clack.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n let archiveRetention: string | symbol = \"90days\";\n\n if (eventTrackingEnabled) {\n archiveRetention = await clack.select({\n message: \"Event history retention period:\",\n options: [\n { value: \"7days\", label: \"7 days\", hint: \"Minimal storage cost\" },\n { value: \"30days\", label: \"30 days\", hint: \"Development/testing\" },\n {\n value: \"90days\",\n label: \"90 days (recommended)\",\n hint: \"Standard retention\",\n },\n { value: \"1year\", label: \"1 year\", hint: \"Compliance requirements\" },\n {\n value: \"indefinite\",\n label: \"Indefinite\",\n hint: \"Higher storage cost\",\n },\n ],\n initialValue:\n existingConfig?.eventTracking?.archiveRetention || \"90days\",\n });\n\n if (clack.isCancel(archiveRetention)) {\n clack.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n }\n\n // Security\n const tlsRequired = await clack.confirm({\n message: \"Require TLS encryption for all emails?\",\n initialValue: existingConfig?.tlsRequired ?? true,\n });\n\n if (clack.isCancel(tlsRequired)) {\n clack.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n // Custom MAIL FROM domain (for DMARC alignment)\n const customMailFrom = await clack.confirm({\n message: \"Configure custom MAIL FROM domain? (improves DMARC alignment)\",\n initialValue: existingConfig?.mailFromDomain !== undefined,\n });\n\n if (clack.isCancel(customMailFrom)) {\n clack.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n let mailFromSubdomain: string | symbol = \"mail\";\n\n if (customMailFrom) {\n mailFromSubdomain = await clack.text({\n message: \"MAIL FROM subdomain:\",\n placeholder: \"mail\",\n initialValue:\n existingConfig?.mailFromDomain?.split(\".\")[0] || \"mail\",\n validate: (value) => {\n if (!value || value.trim() === \"\") {\n return \"Subdomain is required\";\n }\n if (!/^[a-z0-9]([a-z0-9-]*[a-z0-9])?$/i.test(value)) {\n return \"Invalid subdomain format\";\n }\n return undefined;\n },\n });\n\n if (clack.isCancel(mailFromSubdomain)) {\n clack.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n clack.log.info(\n pc.dim(\n `MAIL FROM will be set to ${mailFromSubdomain}.yourdomain.com`\n )\n );\n }\n\n // Dedicated IP\n const dedicatedIp = await clack.confirm({\n message: \"Request dedicated IP address? (requires 100k+ emails/day)\",\n initialValue: existingConfig?.dedicatedIp ?? false,\n });\n\n if (clack.isCancel(dedicatedIp)) {\n clack.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n // Email Archiving\n const emailArchivingEnabled = await clack.confirm({\n message:\n \"Enable email archiving? (Store full email content with HTML for viewing)\",\n initialValue: existingConfig?.emailArchiving?.enabled ?? false,\n });\n\n if (clack.isCancel(emailArchivingEnabled)) {\n clack.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n let emailArchiveRetention: string | symbol = \"90days\";\n\n if (emailArchivingEnabled) {\n emailArchiveRetention = await clack.select({\n message: \"Email archive retention period:\",\n options: [\n { value: \"7days\", label: \"7 days\", hint: \"~$1-2/mo for 10k emails\" },\n { value: \"30days\", label: \"30 days\", hint: \"~$2-4/mo for 10k emails\" },\n {\n value: \"90days\",\n label: \"90 days (recommended)\",\n hint: \"~$5-10/mo for 10k emails\",\n },\n {\n value: \"6months\",\n label: \"6 months\",\n hint: \"~$15-25/mo for 10k emails\",\n },\n { value: \"1year\", label: \"1 year\", hint: \"~$25-40/mo for 10k emails\" },\n {\n value: \"18months\",\n label: \"18 months\",\n hint: \"~$35-60/mo for 10k emails\",\n },\n ],\n initialValue: existingConfig?.emailArchiving?.retention || \"90days\",\n });\n\n if (clack.isCancel(emailArchiveRetention)) {\n clack.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n clack.log.info(\n pc.dim(\n \"Note: Archiving stores full RFC 822 emails with HTML, attachments, and headers\"\n )\n );\n clack.log.info(\n pc.dim(\"Cost: $2/GB ingestion + $0.19/GB/month storage (~50KB per email)\")\n );\n }\n\n return {\n tracking: trackingEnabled\n ? {\n enabled: true,\n opens: true,\n clicks: true,\n }\n : { enabled: false },\n tlsRequired,\n reputationMetrics,\n mailFromSubdomain: customMailFrom\n ? typeof mailFromSubdomain === \"string\"\n ? mailFromSubdomain\n : \"mail\"\n : undefined,\n suppressionList: {\n enabled: true,\n reasons: [\"BOUNCE\", \"COMPLAINT\"],\n },\n eventTracking: eventTrackingEnabled\n ? {\n enabled: true,\n eventBridge: true,\n events: [\n \"SEND\",\n \"DELIVERY\",\n \"OPEN\",\n \"CLICK\",\n \"BOUNCE\",\n \"COMPLAINT\",\n \"REJECT\",\n \"RENDERING_FAILURE\",\n ],\n dynamoDBHistory: true,\n archiveRetention:\n typeof archiveRetention === \"string\" ? archiveRetention : \"90days\",\n }\n : { enabled: false },\n emailArchiving: emailArchivingEnabled\n ? {\n enabled: true,\n retention:\n typeof emailArchiveRetention === \"string\"\n ? emailArchiveRetention\n : \"90days\",\n }\n : { enabled: false, retention: \"90days\" },\n dedicatedIp,\n sendingEnabled: true,\n };\n}\n","import { AssumeRoleCommand, STSClient } from \"@aws-sdk/client-sts\";\n\n/**\n * AWS credential identity type\n */\nexport type AwsCredentialIdentity = {\n accessKeyId: string;\n secretAccessKey: string;\n sessionToken?: string;\n expiration?: Date;\n};\n\n/**\n * Assume IAM role and return temporary credentials\n */\nexport async function assumeRole(\n roleArn: string,\n region: string,\n sessionName = \"wraps-console\"\n): Promise<AwsCredentialIdentity> {\n const sts = new STSClient({ region });\n\n const response = await sts.send(\n new AssumeRoleCommand({\n RoleArn: roleArn,\n RoleSessionName: sessionName,\n DurationSeconds: 3600, // 1 hour\n })\n );\n\n if (!response.Credentials) {\n throw new Error(\"Failed to assume role: No credentials returned\");\n }\n\n return {\n accessKeyId: response.Credentials.AccessKeyId!,\n secretAccessKey: response.Credentials.SecretAccessKey!,\n sessionToken: response.Credentials.SessionToken!,\n expiration: response.Credentials.Expiration,\n };\n}\n","import {\n GetArchiveMessageCommand,\n type GetArchiveMessageCommandOutput,\n GetArchiveSearchResultsCommand,\n MailManagerClient,\n StartArchiveSearchCommand,\n} from \"@aws-sdk/client-mailmanager\";\nimport DOMPurify from \"isomorphic-dompurify\";\nimport { type ParsedMail, simpleParser } from \"mailparser\";\n\n/**\n * Parsed email from archive\n */\nexport type ParsedEmail = {\n messageId: string;\n from: string;\n to: string;\n subject: string;\n html?: string;\n text?: string;\n attachments: Array<{\n filename?: string;\n contentType: string;\n size: number;\n }>;\n headers: Record<string, string | string[] | undefined>;\n timestamp: Date;\n metadata?: {\n senderIp?: string;\n tlsProtocol?: string;\n tlsCipherSuite?: string;\n senderHostname?: string;\n };\n};\n\n/**\n * Extract archive ID from ARN\n * ARN format: arn:aws:ses:region:account-id:mailmanager-archive/archive-id\n * Returns just the archive-id part\n */\nfunction extractArchiveId(archiveArnOrId: string): string {\n if (archiveArnOrId.startsWith(\"arn:\")) {\n // Extract ID from ARN\n const parts = archiveArnOrId.split(\"/\");\n return parts.at(-1) as string;\n }\n // Already just an ID\n return archiveArnOrId;\n}\n\n/**\n * Search criteria for finding archived email\n */\nexport type ArchiveSearchCriteria = {\n from?: string;\n to?: string;\n subject?: string;\n timestamp?: Date;\n};\n\n/**\n * Get an archived email using search criteria\n *\n * This function performs a two-step process:\n * 1. Search the archive using FROM/TO/SUBJECT to find the ArchivedMessageId\n * 2. Retrieve the actual email content using the ArchivedMessageId\n *\n * Note: MailManager Archive doesn't support searching by SES Message-ID directly.\n * We search using FROM, TO, and SUBJECT which we get from DynamoDB events.\n *\n * @param archiveArnOrId Archive ARN or ID (will extract ID if ARN provided)\n * @param searchCriteria Email search criteria (from, to, subject, timestamp)\n * @param region AWS region\n * @returns Parsed email with full content\n */\nexport async function getArchivedEmail(\n archiveArnOrId: string,\n searchCriteria: ArchiveSearchCriteria,\n region: string\n): Promise<ParsedEmail> {\n const client = new MailManagerClient({ region });\n\n // Extract archive ID from ARN if needed\n const archiveId = extractArchiveId(archiveArnOrId);\n\n // Step 1: Search for the message to get the ArchivedMessageId\n // MailManager doesn't support MESSAGE_ID search, so we use FROM/TO/SUBJECT\n // Use timestamp to narrow the search window (±1 day)\n const searchTime = searchCriteria.timestamp || new Date();\n const dayBefore = new Date(searchTime.getTime() - 24 * 60 * 60 * 1000);\n const dayAfter = new Date(searchTime.getTime() + 24 * 60 * 60 * 1000);\n\n // Build search filters\n const filters: any[] = [];\n\n if (searchCriteria.from) {\n filters.push({\n StringExpression: {\n Evaluate: {\n Attribute: \"FROM\",\n },\n Operator: \"CONTAINS\",\n Values: [searchCriteria.from],\n },\n });\n }\n\n if (searchCriteria.to) {\n filters.push({\n StringExpression: {\n Evaluate: {\n Attribute: \"TO\",\n },\n Operator: \"CONTAINS\",\n Values: [searchCriteria.to],\n },\n });\n }\n\n if (searchCriteria.subject) {\n filters.push({\n StringExpression: {\n Evaluate: {\n Attribute: \"SUBJECT\",\n },\n Operator: \"CONTAINS\",\n Values: [searchCriteria.subject],\n },\n });\n }\n\n // If no filters provided, throw error\n if (filters.length === 0) {\n throw new Error(\n \"At least one search criterion (from, to, or subject) is required\"\n );\n }\n\n const searchCommand = new StartArchiveSearchCommand({\n ArchiveId: archiveId,\n FromTimestamp: dayBefore,\n ToTimestamp: dayAfter,\n Filters: {\n Include: filters,\n },\n MaxResults: 10, // Get a few results in case there are multiple matches\n });\n\n const searchResponse = await client.send(searchCommand);\n const searchId = searchResponse.SearchId;\n\n if (!searchId) {\n throw new Error(\"Failed to start archive search\");\n }\n\n // Step 2: Poll for search results (searches are async)\n let archivedMessageId: string | undefined;\n let attempts = 0;\n const maxAttempts = 20; // Increased max attempts\n const pollInterval = 1000; // 1 second between polls\n\n // Wait a bit before first poll to let search start\n await new Promise((resolve) => setTimeout(resolve, 1000));\n\n while (attempts < maxAttempts) {\n try {\n const resultsCommand = new GetArchiveSearchResultsCommand({\n SearchId: searchId,\n });\n\n const resultsResponse = await client.send(resultsCommand);\n\n if (resultsResponse.Rows && resultsResponse.Rows.length > 0) {\n // Found the message!\n archivedMessageId = resultsResponse.Rows[0].ArchivedMessageId;\n break;\n }\n\n // No results yet, but search completed - email not found\n if (resultsResponse.Rows && resultsResponse.Rows.length === 0) {\n // Search completed but no results\n break;\n }\n } catch (error: unknown) {\n // If search is still in progress, continue polling\n if (\n error instanceof Error &&\n error.name === \"ConflictException\" &&\n error.message.includes(\"still in progress\")\n ) {\n console.log(`Search still in progress, attempt ${attempts + 1}...`);\n } else {\n // Other errors should be thrown\n throw error;\n }\n }\n\n // Wait before next poll\n await new Promise((resolve) => setTimeout(resolve, pollInterval));\n attempts++;\n }\n\n if (!archivedMessageId) {\n throw new Error(\n \"Email not found in archive with the provided search criteria. It may have been sent before archiving was enabled.\"\n );\n }\n\n // Step 3: Get the actual archived message using the ArchivedMessageId\n const command = new GetArchiveMessageCommand({\n ArchivedMessageId: archivedMessageId,\n });\n\n const response: GetArchiveMessageCommandOutput = await client.send(command);\n\n if (!response.MessageDownloadLink) {\n throw new Error(\"No download link available for archived message\");\n }\n\n // Download raw email from presigned S3 URL\n const emailResponse = await fetch(response.MessageDownloadLink);\n if (!emailResponse.ok) {\n throw new Error(`Failed to download email: ${emailResponse.statusText}`);\n }\n\n const emailRaw = await emailResponse.text();\n\n // Parse RFC 822/MIME message\n const parsed: ParsedMail = await simpleParser(emailRaw);\n\n // Extract attachment metadata (don't include full content to save memory)\n const attachments =\n parsed.attachments?.map((att) => ({\n filename: att.filename,\n contentType: att.contentType,\n size: att.size,\n })) || [];\n\n // Convert headers Map to plain object\n const headers: Record<string, string | string[] | undefined> = {};\n if (parsed.headers) {\n for (const [key, value] of parsed.headers) {\n // Convert header values to string/string[]\n if (value instanceof Date) {\n headers[key] = value.toISOString();\n } else if (typeof value === \"string\") {\n headers[key] = value;\n } else if (\n Array.isArray(value) &&\n value.every((v) => typeof v === \"string\")\n ) {\n headers[key] = value as string[];\n } else {\n // Convert complex header types (AddressObject, StructuredHeader, etc.) to string\n headers[key] = JSON.stringify(value);\n }\n }\n }\n\n // Extract from/to as text\n const getAddressText = (\n addr: ParsedMail[\"from\"] | ParsedMail[\"to\"]\n ): string => {\n if (!addr) {\n return \"\";\n }\n if (Array.isArray(addr)) {\n return addr.map((a) => a.text).join(\", \");\n }\n return addr.text || \"\";\n };\n\n return {\n messageId: parsed.messageId || headers[\"message-id\"]?.toString() || \"\",\n from: getAddressText(parsed.from),\n to: getAddressText(parsed.to),\n subject: parsed.subject || \"\",\n html: parsed.html || undefined,\n text: parsed.text || undefined,\n attachments,\n headers,\n timestamp: parsed.date || new Date(),\n // Note: MessageMetadata is not available in GetArchiveMessageCommandOutput\n // These fields would need to be retrieved separately if needed\n metadata: {},\n };\n}\n\n/**\n * Search archived emails\n *\n * TODO: Update this to use the correct Mail Manager Archive Search API:\n * - StartArchiveSearchCommand to initiate search\n * - GetArchiveSearchResultsCommand to retrieve results\n * - Implement polling logic for async search completion\n *\n * @param archiveId Archive ARN or ID\n * @param params Search parameters\n * @param region AWS region\n * @returns Search results\n */\nexport async function searchArchivedEmails(\n archiveId: string,\n params: {\n from?: string;\n to?: string;\n subject?: string;\n startDate?: Date;\n endDate?: Date;\n maxResults?: number;\n },\n region: string\n): Promise<never> {\n // TODO: Implement proper search using StartArchiveSearchCommand\n // and GetArchiveSearchResultsCommand\n // Suppress unused variable warnings\n void archiveId;\n void params;\n void region;\n\n throw new Error(\n \"Archive search is not yet implemented. This requires:\\n\" +\n \"1. Start search with StartArchiveSearchCommand\\n\" +\n \"2. Poll for completion\\n\" +\n \"3. Get results with GetArchiveSearchResultsCommand\"\n );\n}\n\n/**\n * Sanitize HTML for safe rendering using DOMPurify\n * Removes potentially dangerous elements and attributes\n *\n * Uses isomorphic-dompurify which provides secure HTML sanitization\n * that works in both Node.js and browser environments.\n *\n * @param html HTML content to sanitize\n * @returns Sanitized HTML safe for rendering\n */\nexport function sanitizeEmailHtml(html: string): string {\n // DOMPurify provides comprehensive XSS protection including:\n // - Script tag removal (including nested bypass attacks like <scr<script>ipt>)\n // - Event handler removal (onclick, onload, etc.)\n // - Protocol sanitization (javascript:, data:text/html, etc.)\n // - Object/embed/iframe removal\n // - Style-based XSS protection\n return DOMPurify.sanitize(html, {\n // Allow common safe HTML elements for email rendering\n ALLOWED_TAGS: [\n \"p\",\n \"div\",\n \"span\",\n \"a\",\n \"img\",\n \"br\",\n \"strong\",\n \"em\",\n \"b\",\n \"i\",\n \"u\",\n \"h1\",\n \"h2\",\n \"h3\",\n \"h4\",\n \"h5\",\n \"h6\",\n \"ul\",\n \"ol\",\n \"li\",\n \"table\",\n \"thead\",\n \"tbody\",\n \"tr\",\n \"td\",\n \"th\",\n \"blockquote\",\n \"pre\",\n \"code\",\n ],\n ALLOWED_ATTR: [\"href\", \"src\", \"alt\", \"title\", \"class\", \"style\", \"target\"],\n // Keep safe URIs only (removes javascript:, data:text/html, etc.)\n ALLOW_DATA_ATTR: false,\n });\n}\n","import {\n type ArchiveSearchCriteria,\n getArchivedEmail,\n} from \"../../utils/archive.js\";\n\n/**\n * Archived email with full content\n */\nexport type ArchivedEmail = {\n messageId: string;\n from: string;\n to: string;\n subject: string;\n html?: string;\n text?: string;\n attachments: Array<{\n filename?: string;\n contentType: string;\n size: number;\n }>;\n headers: Record<string, string | string[] | undefined>;\n timestamp: Date;\n metadata?: {\n senderIp?: string;\n tlsProtocol?: string;\n tlsCipherSuite?: string;\n senderHostname?: string;\n };\n};\n\ntype FetchArchivedEmailOptions = {\n region: string;\n archiveArn: string;\n from?: string;\n to?: string;\n subject?: string;\n timestamp?: Date;\n};\n\n/**\n * Fetch archived email by message ID from AWS SES Mail Manager\n *\n * This function searches the archive using email metadata (from, to, subject)\n * to find the archived message, since MailManager doesn't support direct\n * search by SES Message-ID.\n *\n * @param messageId Email message ID (for logging/correlation only)\n * @param options Configuration options including search criteria\n * @returns Archived email with full content, or null if not found\n */\nexport async function fetchArchivedEmail(\n messageId: string,\n options: FetchArchivedEmailOptions\n): Promise<ArchivedEmail | null> {\n const { region, archiveArn, from, to, subject, timestamp } = options;\n\n try {\n console.log(\"Fetching archived email:\", {\n messageId,\n archiveArn,\n region,\n });\n\n // Build search criteria from email metadata\n const searchCriteria: ArchiveSearchCriteria = {\n from,\n to,\n subject,\n timestamp,\n };\n\n // Call the archive utility to get the email\n const email = await getArchivedEmail(archiveArn, searchCriteria, region);\n\n console.log(\"Archived email fetched successfully:\", {\n messageId: email.messageId,\n hasHtml: !!email.html,\n hasText: !!email.text,\n attachmentCount: email.attachments.length,\n });\n\n // Return the email data\n return email;\n } catch (error: unknown) {\n // If the email is not found, return null instead of throwing\n if (\n error instanceof Error &&\n (error.message.includes(\"not found\") ||\n error.message.includes(\"ResourceNotFoundException\"))\n ) {\n console.log(\"Archived email not found:\", messageId);\n return null;\n }\n\n // For other errors, log and rethrow\n console.error(\"Error fetching archived email:\", error);\n throw error;\n }\n}\n","import { DynamoDBClient, ScanCommand } from \"@aws-sdk/client-dynamodb\";\nimport { unmarshall } from \"@aws-sdk/util-dynamodb\";\n\nexport type DynamoDBMetrics = {\n opens: Array<{ timestamp: number; value: number }>;\n clicks: Array<{ timestamp: number; value: number }>;\n};\n\n/**\n * Fetch Open and Click metrics from DynamoDB\n */\nexport async function fetchDynamoDBMetrics(\n region: string,\n tableName: string,\n timeRange: { start: Date; end: Date }\n): Promise<DynamoDBMetrics> {\n const dynamodb = new DynamoDBClient({ region });\n\n try {\n const startTime = timeRange.start.getTime();\n const endTime = timeRange.end.getTime();\n\n // Scan the table for events in the time range\n // Note: In production, consider using accountId GSI for better performance\n const response = await dynamodb.send(\n new ScanCommand({\n TableName: tableName,\n FilterExpression:\n \"sentAt BETWEEN :startTime AND :endTime AND (eventType = :open OR eventType = :click)\",\n ExpressionAttributeValues: {\n \":startTime\": { N: startTime.toString() },\n \":endTime\": { N: endTime.toString() },\n \":open\": { S: \"Open\" },\n \":click\": { S: \"Click\" },\n },\n })\n );\n\n const items = (response.Items || []).map((item) => unmarshall(item));\n\n // Group events by 5-minute buckets (to match CloudWatch period)\n const period = 5 * 60 * 1000; // 5 minutes in milliseconds\n const openBuckets = new Map<number, number>();\n const clickBuckets = new Map<number, number>();\n\n for (const item of items) {\n const timestamp = Number(item.sentAt);\n const bucket = Math.floor(timestamp / period) * period;\n const eventType = item.eventType;\n\n if (eventType === \"Open\") {\n openBuckets.set(bucket, (openBuckets.get(bucket) || 0) + 1);\n } else if (eventType === \"Click\") {\n clickBuckets.set(bucket, (clickBuckets.get(bucket) || 0) + 1);\n }\n }\n\n // Convert to array format\n const opens = Array.from(openBuckets.entries()).map(\n ([timestamp, value]) => ({\n timestamp,\n value,\n })\n );\n\n const clicks = Array.from(clickBuckets.entries()).map(\n ([timestamp, value]) => ({\n timestamp,\n value,\n })\n );\n\n return {\n opens: opens.sort((a, b) => a.timestamp - b.timestamp),\n clicks: clicks.sort((a, b) => a.timestamp - b.timestamp),\n };\n } catch (error) {\n console.error(\"Error fetching DynamoDB metrics:\", error);\n // Return empty arrays on error instead of throwing\n return {\n opens: [],\n clicks: [],\n };\n }\n}\n","#!/usr/bin/env node\nimport { readFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport * as clack from \"@clack/prompts\";\nimport args from \"args\";\nimport pc from \"picocolors\";\n// Dashboard commands\nimport { updateRole } from \"./commands/dashboard/update-role.js\";\nimport { config } from \"./commands/email/config.js\";\n// Email commands\nimport { connect } from \"./commands/email/connect.js\";\nimport { emailDestroy } from \"./commands/email/destroy.js\";\nimport {\n addDomain,\n getDkim,\n listDomains,\n removeDomain,\n verifyDomain,\n} from \"./commands/email/domains.js\";\nimport { init } from \"./commands/email/init.js\";\nimport { restore } from \"./commands/email/restore.js\";\nimport { emailStatus } from \"./commands/email/status.js\";\nimport { upgrade } from \"./commands/email/upgrade.js\";\n// Shared commands\nimport { dashboard } from \"./commands/shared/dashboard.js\";\nimport { destroy } from \"./commands/shared/destroy.js\";\nimport { status } from \"./commands/shared/status.js\";\n// Telemetry commands\nimport {\n telemetryDisable,\n telemetryEnable,\n telemetryStatus,\n} from \"./commands/telemetry.js\";\nimport { getTelemetryClient } from \"./telemetry/client.js\";\nimport { trackCommand } from \"./telemetry/events.js\";\nimport {\n printCompletionScript,\n setupTabCompletion,\n} from \"./utils/shared/completion.js\";\nimport { handleCLIError } from \"./utils/shared/errors.js\";\n\n// Get package version\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\nconst packageJson = JSON.parse(\n readFileSync(join(__dirname, \"../package.json\"), \"utf-8\")\n);\nconst VERSION = packageJson.version;\n\n// Setup tab completion\nsetupTabCompletion();\n\n// Function to show version\nfunction showVersion() {\n console.log(`wraps v${VERSION}`);\n process.exit(0);\n}\n\n// Function to show help\nfunction showHelp() {\n clack.intro(pc.bold(`WRAPS CLI v${VERSION}`));\n console.log(\"Deploy AWS infrastructure to your account\\n\");\n console.log(\"Usage: wraps [service] <command> [options]\\n\");\n console.log(\"Services:\");\n console.log(` ${pc.cyan(\"email\")} Email infrastructure (AWS SES)\\n`);\n console.log(\"Email Commands:\");\n console.log(\n ` ${pc.cyan(\"email init\")} Deploy new email infrastructure`\n );\n console.log(\n ` ${pc.cyan(\"email connect\")} Connect to existing AWS SES`\n );\n console.log(` ${pc.cyan(\"email status\")} Show email infrastructure details`);\n console.log(` ${pc.cyan(\"email verify\")} Verify domain DNS records`);\n console.log(\n ` ${pc.cyan(\"email sync\")} Apply CLI updates to infrastructure`\n );\n console.log(` ${pc.cyan(\"email upgrade\")} Add features`);\n console.log(\n ` ${pc.cyan(\"email restore\")} Restore original configuration`\n );\n console.log(\n ` ${pc.cyan(\"email destroy\")} Remove email infrastructure`\n );\n console.log(` ${pc.cyan(\"email domains add\")} Add a domain to SES`);\n console.log(` ${pc.cyan(\"email domains list\")} List all domains`);\n console.log(` ${pc.cyan(\"email domains remove\")} Remove a domain\\n`);\n console.log(\"Console & Dashboard:\");\n console.log(` ${pc.cyan(\"console\")} Start local web console`);\n console.log(\n ` ${pc.cyan(\"dashboard update-role\")} Update hosted dashboard IAM permissions\\n`\n );\n console.log(\"Global Commands:\");\n console.log(` ${pc.cyan(\"status\")} Show overview of all services`);\n console.log(` ${pc.cyan(\"destroy\")} Remove deployed infrastructure`);\n console.log(` ${pc.cyan(\"completion\")} Generate shell completion script`);\n console.log(\n ` ${pc.cyan(\"telemetry\")} Manage anonymous telemetry settings\\n`\n );\n console.log(\"Options:\");\n console.log(\n ` ${pc.dim(\"-p, --provider\")} Hosting provider (vercel, aws, railway, other)`\n );\n console.log(` ${pc.dim(\"-r, --region\")} AWS region`);\n console.log(` ${pc.dim(\"-d, --domain\")} Domain name`);\n console.log(` ${pc.dim(\"--account\")} AWS account ID or alias`);\n console.log(` ${pc.dim(\"--preset\")} Configuration preset`);\n console.log(` ${pc.dim(\"-y, --yes\")} Skip confirmation prompts`);\n console.log(` ${pc.dim(\"-f, --force\")} Force destructive operations`);\n console.log(\n ` ${pc.dim(\"--preview\")} Preview changes without deploying`\n );\n console.log(` ${pc.dim(\"-v, --version\")} Show version number\\n`);\n console.log(\n `Run ${pc.cyan(\"wraps <service> <command> --help\")} for more information.\\n`\n );\n process.exit(0);\n}\n\n// Check for version before args parses\nif (process.argv.includes(\"--version\") || process.argv.includes(\"-v\")) {\n showVersion();\n}\n\n// Check for help before args parses (to override args' built-in help)\nif (process.argv.includes(\"--help\") || process.argv.includes(\"-h\")) {\n showHelp();\n}\n\n// Configure args\nargs.options([\n {\n name: [\"p\", \"provider\"],\n description: \"Hosting provider (vercel, aws, railway, other)\",\n defaultValue: undefined,\n },\n {\n name: [\"r\", \"region\"],\n description: \"AWS region\",\n defaultValue: undefined,\n },\n {\n name: [\"d\", \"domain\"],\n description: \"Domain name\",\n defaultValue: undefined,\n },\n {\n name: \"account\",\n description: \"AWS account ID or alias\",\n defaultValue: undefined,\n },\n {\n name: \"preset\",\n description:\n \"Configuration preset (starter, production, enterprise, custom)\",\n defaultValue: undefined,\n },\n {\n name: [\"y\", \"yes\"],\n description: \"Skip confirmation prompts (non-destructive operations)\",\n defaultValue: false,\n },\n {\n name: [\"f\", \"force\"],\n description:\n \"Force operation without confirmation (destructive operations)\",\n defaultValue: false,\n },\n {\n name: \"port\",\n description: \"Port for dashboard server\",\n defaultValue: undefined,\n },\n {\n name: \"noOpen\",\n description: \"Don't open browser automatically\",\n defaultValue: false,\n },\n {\n name: \"preview\",\n description: \"Preview changes without deploying\",\n defaultValue: false,\n },\n]);\n\n// Get command and flags\nconst flags = args.parse(process.argv);\nconst [primaryCommand, subCommand] = args.sub;\n\n// If no command provided, show interactive service selection\nif (!primaryCommand) {\n async function selectService() {\n clack.intro(pc.bold(`WRAPS CLI v${VERSION}`));\n console.log(\"Welcome! Let's get started deploying your email infrastructure.\\n\");\n\n // Ask what action they want to take\n const action = await clack.select({\n message: \"What would you like to do?\",\n options: [\n {\n value: \"init\",\n label: \"Deploy new infrastructure\",\n hint: \"Create new AWS SES infrastructure\",\n },\n {\n value: \"connect\",\n label: \"Connect existing infrastructure\",\n hint: \"Connect to existing AWS SES setup\",\n },\n ],\n });\n\n if (clack.isCancel(action)) {\n clack.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n // Run the appropriate command\n if (action === \"init\") {\n await init({\n provider: flags.provider,\n region: flags.region,\n domain: flags.domain,\n preset: flags.preset,\n yes: flags.yes,\n preview: flags.preview,\n });\n } else {\n await connect({\n provider: flags.provider,\n region: flags.region,\n yes: flags.yes,\n preview: flags.preview,\n });\n }\n }\n\n selectService().catch(handleCLIError);\n // Early exit - don't run the main run() function\n process.exit(0);\n}\n\n// Route to appropriate command\nasync function run() {\n const startTime = Date.now();\n const telemetry = getTelemetryClient();\n\n // Show first-run telemetry notification\n if (telemetry.shouldShowNotification()) {\n console.log();\n clack.log.info(pc.bold(\"Anonymous Telemetry\"));\n console.log(\n ` Wraps collects ${pc.cyan(\"anonymous usage data\")} to improve the CLI.`\n );\n console.log(\n ` We ${pc.bold(\"never\")} collect: domains, AWS credentials, email content, or PII.`\n );\n console.log(\n ` We ${pc.bold(\"only\")} collect: command names, success/failure, CLI version, OS.`\n );\n console.log();\n console.log(` Opt-out anytime: ${pc.cyan(\"wraps telemetry disable\")}`);\n console.log(` Or set: ${pc.cyan(\"WRAPS_TELEMETRY_DISABLED=1\")}`);\n console.log(` Learn more: ${pc.cyan(\"https://wraps.dev/docs/telemetry\")}`);\n console.log();\n\n telemetry.markNotificationShown();\n }\n\n try {\n // Handle service-specific subcommands (e.g., wraps email init)\n if (primaryCommand === \"email\" && subCommand) {\n switch (subCommand) {\n case \"init\":\n await init({\n provider: flags.provider,\n region: flags.region,\n domain: flags.domain,\n preset: flags.preset,\n yes: flags.yes,\n preview: flags.preview,\n });\n break;\n\n case \"connect\":\n await connect({\n provider: flags.provider,\n region: flags.region,\n yes: flags.yes,\n preview: flags.preview,\n });\n break;\n\n case \"config\":\n case \"sync\":\n await config({\n region: flags.region,\n yes: flags.yes,\n preview: flags.preview,\n });\n break;\n\n case \"upgrade\":\n await upgrade({\n region: flags.region,\n yes: flags.yes,\n preview: flags.preview,\n });\n break;\n\n case \"restore\":\n await restore({\n region: flags.region,\n force: flags.force,\n preview: flags.preview,\n });\n break;\n\n case \"status\":\n await emailStatus({\n account: flags.account,\n });\n break;\n\n case \"verify\": {\n if (!flags.domain) {\n clack.log.error(\"--domain flag is required\");\n console.log(\n `\\nUsage: ${pc.cyan(\"wraps email verify --domain yourapp.com\")}\\n`\n );\n process.exit(1);\n }\n await verifyDomain({ domain: flags.domain });\n break;\n }\n\n case \"domains\": {\n // Handle domains subcommands\n const domainsSubCommand = args.sub[2];\n\n switch (domainsSubCommand) {\n case \"add\": {\n if (!flags.domain) {\n clack.log.error(\"--domain flag is required\");\n console.log(\n `\\nUsage: ${pc.cyan(\"wraps email domains add --domain yourapp.com\")}\\n`\n );\n process.exit(1);\n }\n await addDomain({ domain: flags.domain });\n break;\n }\n\n case \"list\":\n await listDomains();\n break;\n\n case \"verify\": {\n if (!flags.domain) {\n clack.log.error(\"--domain flag is required\");\n console.log(\n `\\nUsage: ${pc.cyan(\"wraps email domains verify --domain yourapp.com\")}\\n`\n );\n process.exit(1);\n }\n await verifyDomain({ domain: flags.domain });\n break;\n }\n\n case \"get-dkim\": {\n if (!flags.domain) {\n clack.log.error(\"--domain flag is required\");\n console.log(\n `\\nUsage: ${pc.cyan(\"wraps email domains get-dkim --domain yourapp.com\")}\\n`\n );\n process.exit(1);\n }\n await getDkim({ domain: flags.domain });\n break;\n }\n\n case \"remove\": {\n if (!flags.domain) {\n clack.log.error(\"--domain flag is required\");\n console.log(\n `\\nUsage: ${pc.cyan(\"wraps email domains remove --domain yourapp.com --force\")}\\n`\n );\n process.exit(1);\n }\n await removeDomain({\n domain: flags.domain,\n force: flags.force,\n });\n break;\n }\n\n default:\n clack.log.error(\n `Unknown domains command: ${domainsSubCommand || \"(none)\"}`\n );\n console.log(\n `\\nAvailable commands: ${pc.cyan(\"add\")}, ${pc.cyan(\"list\")}, ${pc.cyan(\"verify\")}, ${pc.cyan(\"get-dkim\")}, ${pc.cyan(\"remove\")}\\n`\n );\n process.exit(1);\n }\n break;\n }\n\n case \"destroy\":\n await emailDestroy({\n force: flags.force,\n preview: flags.preview,\n });\n break;\n\n default:\n clack.log.error(`Unknown email command: ${subCommand}`);\n console.log(\n `\\nRun ${pc.cyan(\"wraps --help\")} for available commands.\\n`\n );\n process.exit(1);\n }\n // Track email commands (they return early, so track here)\n const emailDuration = Date.now() - startTime;\n const emailCommandName = `email:${subCommand}`;\n trackCommand(emailCommandName, {\n success: true,\n duration_ms: emailDuration,\n service: \"email\",\n });\n return;\n }\n\n // Handle Dashboard subcommands\n if (primaryCommand === \"dashboard\" && subCommand) {\n switch (subCommand) {\n case \"update-role\":\n await updateRole({\n region: flags.region,\n force: flags.force,\n });\n break;\n\n default:\n clack.log.error(`Unknown dashboard command: ${subCommand}`);\n console.log(`\\nAvailable commands: ${pc.cyan(\"update-role\")}\\n`);\n console.log(`Run ${pc.cyan(\"wraps --help\")} for more information.\\n`);\n process.exit(1);\n }\n // Track dashboard commands (they return early, so track here)\n const dashboardDuration = Date.now() - startTime;\n const dashboardCommandName = `dashboard:${subCommand}`;\n trackCommand(dashboardCommandName, {\n success: true,\n duration_ms: dashboardDuration,\n });\n return;\n }\n\n // Handle global commands\n switch (primaryCommand) {\n // Global commands (work across all services)\n case \"status\":\n await status({\n account: flags.account,\n });\n break;\n\n case \"console\":\n await dashboard({\n port: flags.port,\n noOpen: flags.noOpen,\n });\n break;\n\n case \"dashboard\":\n // Deprecated: 'wraps dashboard' without subcommand redirects to 'wraps console'\n if (!subCommand) {\n clack.log.warn(\n `'wraps dashboard' is deprecated. Use ${pc.cyan(\"wraps console\")} instead.`\n );\n await dashboard({\n port: flags.port,\n noOpen: flags.noOpen,\n });\n }\n break;\n\n case \"destroy\":\n await destroy({\n force: flags.force,\n preview: flags.preview,\n });\n break;\n\n case \"completion\":\n printCompletionScript();\n break;\n\n case \"telemetry\": {\n // Handle telemetry subcommands\n switch (subCommand) {\n case \"enable\":\n await telemetryEnable();\n break;\n\n case \"disable\":\n await telemetryDisable();\n break;\n\n case \"status\":\n case undefined:\n await telemetryStatus();\n break;\n\n default:\n clack.log.error(`Unknown telemetry command: ${subCommand}`);\n console.log(\n `\\nAvailable commands: ${pc.cyan(\"enable\")}, ${pc.cyan(\"disable\")}, ${pc.cyan(\"status\")}\\n`\n );\n process.exit(1);\n }\n break;\n }\n\n // Show help for service without subcommand\n case \"email\":\n console.log(\n `\\nPlease specify a command for ${primaryCommand} service.\\n`\n );\n showHelp();\n break;\n\n default:\n clack.log.error(`Unknown command: ${primaryCommand}`);\n console.log(\n `\\nRun ${pc.cyan(\"wraps --help\")} for available commands.\\n`\n );\n process.exit(1);\n }\n // Track successful command execution\n const duration = Date.now() - startTime;\n const commandName = subCommand\n ? `${primaryCommand}:${subCommand}`\n : primaryCommand;\n\n trackCommand(commandName, {\n success: true,\n duration_ms: duration,\n });\n } catch (error) {\n // Track failed command execution\n const duration = Date.now() - startTime;\n const commandName = subCommand\n ? `${primaryCommand}:${subCommand}`\n : primaryCommand;\n\n trackCommand(commandName, {\n success: false,\n duration_ms: duration,\n });\n\n handleCLIError(error);\n } finally {\n // Ensure telemetry events are sent before exit\n await telemetry.shutdown();\n }\n}\n\nrun();\n","import { GetRoleCommand, IAMClient } from \"@aws-sdk/client-iam\";\nimport { confirm, intro, isCancel, log, outro } from \"@clack/prompts\";\nimport pc from \"picocolors\";\nimport type { UpdateRoleOptions } from \"../../types/index.js\";\nimport {\n getAWSRegion,\n validateAWSCredentials,\n} from \"../../utils/shared/aws.js\";\nimport { loadConnectionMetadata } from \"../../utils/shared/metadata.js\";\nimport { DeploymentProgress } from \"../../utils/shared/output.js\";\n\n/**\n * Update hosted dashboard access role command\n *\n * Updates the wraps-console-access-role IAM role with the latest permissions\n * needed for feature detection in the hosted dashboard app (e.g., dynamodb:DescribeTable).\n *\n * This role is created when you connect AWS accounts through the hosted dashboard.\n * This command updates its permissions to match your current infrastructure setup.\n *\n * This command:\n * - Only updates the role if it exists (does not create it)\n * - Updates inline policies to match current feature requirements\n * - Preserves the trust policy (AssumeRole configuration)\n */\nexport async function updateRole(options: UpdateRoleOptions): Promise<void> {\n intro(pc.bold(\"Update Hosted Dashboard Access Role\"));\n\n const progress = new DeploymentProgress();\n\n // 1. Validate AWS credentials\n const identity = await progress.execute(\n \"Validating AWS credentials\",\n async () => validateAWSCredentials()\n );\n\n // 2. Get region\n const region = options.region || (await getAWSRegion());\n\n // 3. Load metadata to check if deployment exists\n const metadata = await loadConnectionMetadata(identity.accountId, region);\n if (!metadata) {\n progress.stop();\n log.error(\n `No Wraps deployment found for account ${pc.cyan(identity.accountId)} in region ${pc.cyan(region)}`\n );\n console.log(\n `\\nRun ${pc.cyan(\"wraps email init\")} to deploy infrastructure first.\\n`\n );\n process.exit(1);\n }\n\n // 4. Check if wraps-console-access-role exists\n const roleName = \"wraps-console-access-role\";\n const iam = new IAMClient({ region: \"us-east-1\" }); // IAM is global\n\n let roleExists = false;\n try {\n await iam.send(new GetRoleCommand({ RoleName: roleName }));\n roleExists = true;\n } catch (error) {\n if (\n error &&\n typeof error === \"object\" &&\n \"name\" in error &&\n error.name !== \"NoSuchEntity\"\n ) {\n throw error;\n }\n }\n\n if (!roleExists) {\n progress.stop();\n log.warn(`IAM role ${pc.cyan(roleName)} does not exist`);\n console.log(\n \"\\nThis role is created when you connect AWS accounts through the hosted dashboard.\"\n );\n console.log(\n \"If you haven't connected an AWS account to the hosted dashboard yet, there's nothing to update.\\n\"\n );\n process.exit(0);\n }\n\n progress.info(`Found IAM role: ${pc.cyan(roleName)}`);\n\n // 5. Confirm update (unless --force)\n if (!options.force) {\n progress.stop();\n const shouldContinue = await confirm({\n message: `Update IAM role ${pc.cyan(roleName)} with latest permissions?`,\n initialValue: true,\n });\n\n if (isCancel(shouldContinue) || !shouldContinue) {\n outro(\"Update cancelled\");\n process.exit(0);\n }\n }\n\n // 6. Build updated policy\n const emailConfig = metadata.services.email?.config;\n const policy = buildConsolePolicyDocument(emailConfig);\n\n // Extract config values for display\n const sendingEnabled =\n !emailConfig ||\n (emailConfig.sendingEnabled as boolean | undefined) !== false;\n const eventTracking = emailConfig?.eventTracking as\n | Record<string, unknown>\n | undefined;\n const emailArchiving = emailConfig?.emailArchiving as\n | Record<string, unknown>\n | undefined;\n\n // 7. Update role policy\n await progress.execute(\"Updating IAM role permissions\", async () => {\n const { PutRolePolicyCommand } = await import(\"@aws-sdk/client-iam\");\n\n await iam.send(\n new PutRolePolicyCommand({\n RoleName: roleName,\n PolicyName: \"wraps-console-access-policy\",\n PolicyDocument: JSON.stringify(policy, null, 2),\n })\n );\n });\n\n progress.stop();\n\n // Success\n outro(pc.green(\"✓ Hosted dashboard access role updated successfully\"));\n\n console.log(`\\n${pc.bold(\"Updated Permissions:\")}`);\n console.log(\n ` ${pc.green(\"✓\")} SES metrics and identity verification (always enabled)`\n );\n console.log(` ${pc.green(\"✓\")} SES template management (always enabled)`);\n\n if (sendingEnabled) {\n console.log(` ${pc.green(\"✓\")} Email sending via SES`);\n }\n\n if (eventTracking?.dynamoDBHistory) {\n console.log(\n ` ${pc.green(\"✓\")} DynamoDB read access (including DescribeTable)`\n );\n }\n\n if (eventTracking?.enabled) {\n console.log(` ${pc.green(\"✓\")} EventBridge and SQS access`);\n }\n\n if (emailArchiving?.enabled) {\n console.log(` ${pc.green(\"✓\")} Mail Manager Archive access`);\n }\n\n console.log(\n `\\n${pc.dim(\"The hosted dashboard will now have updated permissions for feature detection.\")}\\n`\n );\n}\n\n/**\n * Build IAM policy document for hosted dashboard access role\n *\n * This mirrors the permissions from the main wraps-email-role but is used\n * for the hosted dashboard app (not for SDK sending or local console).\n */\ntype PolicyStatement = {\n Effect: string;\n Action: string[];\n Resource: string | string[];\n};\n\ntype PolicyDocument = {\n Version: string;\n Statement: PolicyStatement[];\n};\n\nfunction buildConsolePolicyDocument(\n emailConfig: Record<string, unknown> | undefined\n): PolicyDocument {\n const statements: PolicyStatement[] = [];\n\n // Always allow reading SES metrics for dashboard\n statements.push({\n Effect: \"Allow\",\n Action: [\n \"ses:GetSendStatistics\",\n \"ses:ListIdentities\",\n \"ses:GetIdentityVerificationAttributes\",\n \"cloudwatch:GetMetricData\",\n \"cloudwatch:GetMetricStatistics\",\n ],\n Resource: \"*\",\n });\n\n // Always allow SES template management (for publishing email templates)\n statements.push({\n Effect: \"Allow\",\n Action: [\n \"ses:GetTemplate\",\n \"ses:ListTemplates\",\n \"ses:CreateTemplate\",\n \"ses:UpdateTemplate\",\n \"ses:DeleteTemplate\",\n \"ses:TestRenderTemplate\",\n ],\n Resource: \"*\",\n });\n\n // Allow sending if enabled\n const sendingEnabled = !emailConfig || emailConfig.sendingEnabled !== false;\n if (sendingEnabled) {\n statements.push({\n Effect: \"Allow\",\n Action: [\n \"ses:SendEmail\",\n \"ses:SendRawEmail\",\n \"ses:SendTemplatedEmail\",\n \"ses:SendBulkTemplatedEmail\",\n ],\n Resource: \"*\",\n });\n }\n\n // Allow DynamoDB access if history storage enabled\n const eventTracking = emailConfig?.eventTracking as\n | Record<string, unknown>\n | undefined;\n if (eventTracking?.dynamoDBHistory) {\n statements.push({\n Effect: \"Allow\",\n Action: [\n \"dynamodb:PutItem\",\n \"dynamodb:GetItem\",\n \"dynamodb:Query\",\n \"dynamodb:Scan\",\n \"dynamodb:BatchGetItem\",\n \"dynamodb:DescribeTable\",\n ],\n Resource: [\n \"arn:aws:dynamodb:*:*:table/wraps-email-*\",\n \"arn:aws:dynamodb:*:*:table/wraps-email-*/index/*\",\n ],\n });\n }\n\n // Allow EventBridge access if event tracking enabled\n if (eventTracking?.enabled) {\n statements.push({\n Effect: \"Allow\",\n Action: [\"events:PutEvents\", \"events:DescribeEventBus\"],\n Resource: \"arn:aws:events:*:*:event-bus/wraps-email-*\",\n });\n }\n\n // Allow SQS access if event tracking enabled\n if (eventTracking?.enabled) {\n statements.push({\n Effect: \"Allow\",\n Action: [\n \"sqs:SendMessage\",\n \"sqs:ReceiveMessage\",\n \"sqs:DeleteMessage\",\n \"sqs:GetQueueAttributes\",\n ],\n Resource: \"arn:aws:sqs:*:*:wraps-email-*\",\n });\n }\n\n // Allow Mail Manager Archive access if email archiving enabled\n const emailArchiving = emailConfig?.emailArchiving as\n | Record<string, unknown>\n | undefined;\n if (emailArchiving?.enabled) {\n statements.push({\n Effect: \"Allow\",\n Action: [\n \"ses:StartArchiveSearch\",\n \"ses:GetArchiveSearchResults\",\n \"ses:GetArchiveMessage\",\n \"ses:GetArchiveMessageContent\",\n \"ses:GetArchive\",\n \"ses:ListArchives\",\n \"ses:StartArchiveExport\",\n \"ses:GetArchiveExport\",\n ],\n Resource: \"arn:aws:ses:*:*:mailmanager-archive/*\",\n });\n }\n\n return {\n Version: \"2012-10-17\",\n Statement: statements,\n };\n}\n","import { existsSync } from \"node:fs\";\nimport { readFile, writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type {\n EmailConfigPreset,\n Provider,\n ServiceType,\n SMSConfigPreset,\n WrapsEmailConfig,\n WrapsSMSConfig,\n} from \"../../types/index.js\";\nimport { ensureWrapsDir, getWrapsDir } from \"./fs.js\";\n\n/**\n * Service-specific configuration with metadata\n */\nexport type ServiceConfig<TConfig, TPreset> = {\n preset?: TPreset;\n config: TConfig;\n pulumiStackName?: string;\n deployedAt: string;\n};\n\n/**\n * Connection metadata - supports multiple services per AWS account/region\n */\nexport type ConnectionMetadata = {\n version: string; // Metadata format version (e.g., \"1.0.0\")\n accountId: string;\n region: string;\n provider: Provider;\n timestamp: string; // Last updated timestamp\n vercel?: {\n teamSlug: string;\n projectName: string;\n };\n\n // Service-specific configurations\n services: {\n email?: ServiceConfig<WrapsEmailConfig, EmailConfigPreset>;\n sms?: ServiceConfig<WrapsSMSConfig, SMSConfigPreset>;\n };\n};\n\n/**\n * Legacy connection metadata (for backwards compatibility)\n * @deprecated Use ConnectionMetadata instead\n */\nexport type LegacyConnectionMetadata = {\n accountId: string;\n region: string;\n provider: Provider;\n timestamp: string;\n preset?: EmailConfigPreset;\n emailConfig: WrapsEmailConfig;\n vercel?: {\n teamSlug: string;\n projectName: string;\n };\n pulumiStackName?: string;\n};\n\n/**\n * Get the connections directory\n */\nfunction getConnectionsDir(): string {\n return join(getWrapsDir(), \"connections\");\n}\n\n/**\n * Get metadata file path for an account and region\n */\nfunction getMetadataPath(accountId: string, region: string): string {\n return join(getConnectionsDir(), `${accountId}-${region}.json`);\n}\n\n/**\n * Ensure the connections directory exists\n */\nasync function ensureConnectionsDir(): Promise<void> {\n await ensureWrapsDir();\n const connectionsDir = getConnectionsDir();\n if (!existsSync(connectionsDir)) {\n const { mkdir } = await import(\"node:fs/promises\");\n await mkdir(connectionsDir, { recursive: true });\n }\n}\n\n/**\n * Migrate legacy metadata to new multi-service format\n */\nfunction migrateLegacyMetadata(\n legacy: LegacyConnectionMetadata\n): ConnectionMetadata {\n return {\n version: \"1.0.0\",\n accountId: legacy.accountId,\n region: legacy.region,\n provider: legacy.provider,\n timestamp: legacy.timestamp,\n vercel: legacy.vercel,\n services: {\n email: {\n preset: legacy.preset,\n config: legacy.emailConfig,\n pulumiStackName: legacy.pulumiStackName,\n deployedAt: legacy.timestamp,\n },\n },\n };\n}\n\n/**\n * Check if metadata is in legacy format\n */\nfunction isLegacyMetadata(data: any): data is LegacyConnectionMetadata {\n return (\n \"emailConfig\" in data &&\n !(\"services\" in data) &&\n typeof data.emailConfig === \"object\"\n );\n}\n\n/**\n * Load connection metadata from disk\n * Automatically migrates legacy format to new multi-service format\n */\nexport async function loadConnectionMetadata(\n accountId: string,\n region: string\n): Promise<ConnectionMetadata | null> {\n const metadataPath = getMetadataPath(accountId, region);\n\n if (!existsSync(metadataPath)) {\n return null;\n }\n\n try {\n const content = await readFile(metadataPath, \"utf-8\");\n const data = JSON.parse(content);\n\n // Migrate legacy format if needed\n if (isLegacyMetadata(data)) {\n const migrated = migrateLegacyMetadata(data);\n // Save migrated version\n await saveConnectionMetadata(migrated);\n return migrated;\n }\n\n // Add version if missing (for backwards compatibility with early multi-service format)\n if (!data.version) {\n data.version = \"1.0.0\";\n await saveConnectionMetadata(data);\n }\n\n return data as ConnectionMetadata;\n } catch (error: any) {\n console.error(\"Error loading connection metadata:\", error.message);\n return null;\n }\n}\n\n/**\n * Save connection metadata to disk\n */\nexport async function saveConnectionMetadata(\n metadata: ConnectionMetadata\n): Promise<void> {\n await ensureConnectionsDir();\n const metadataPath = getMetadataPath(metadata.accountId, metadata.region);\n\n try {\n const content = JSON.stringify(metadata, null, 2);\n await writeFile(metadataPath, content, \"utf-8\");\n } catch (error: any) {\n console.error(\"Error saving connection metadata:\", error.message);\n throw error;\n }\n}\n\n/**\n * Delete connection metadata\n */\nexport async function deleteConnectionMetadata(\n accountId: string,\n region: string\n): Promise<void> {\n const metadataPath = getMetadataPath(accountId, region);\n\n if (existsSync(metadataPath)) {\n const { unlink } = await import(\"node:fs/promises\");\n await unlink(metadataPath);\n }\n}\n\n/**\n * List all connections\n */\nexport async function listConnections(): Promise<ConnectionMetadata[]> {\n const connectionsDir = getConnectionsDir();\n\n if (!existsSync(connectionsDir)) {\n return [];\n }\n\n try {\n const { readdir } = await import(\"node:fs/promises\");\n const files = await readdir(connectionsDir);\n const connections: ConnectionMetadata[] = [];\n\n for (const file of files) {\n if (file.endsWith(\".json\")) {\n const content = await readFile(join(connectionsDir, file), \"utf-8\");\n try {\n const metadata = JSON.parse(content) as ConnectionMetadata;\n connections.push(metadata);\n } catch (error) {\n console.error(`Error parsing ${file}:`, error);\n }\n }\n }\n\n return connections;\n } catch (error: any) {\n console.error(\"Error listing connections:\", error.message);\n return [];\n }\n}\n\n/**\n * Check if a connection exists\n */\nexport async function connectionExists(\n accountId: string,\n region: string\n): Promise<boolean> {\n const metadataPath = getMetadataPath(accountId, region);\n return existsSync(metadataPath);\n}\n\n/**\n * Create initial connection metadata\n * @deprecated Use addServiceToConnection instead\n */\nexport function createConnectionMetadata(\n accountId: string,\n region: string,\n provider: Provider,\n emailConfig: WrapsEmailConfig,\n preset?: EmailConfigPreset\n): ConnectionMetadata {\n return {\n version: \"1.0.0\",\n accountId,\n region,\n provider,\n timestamp: new Date().toISOString(),\n services: {\n email: {\n preset,\n config: emailConfig,\n deployedAt: new Date().toISOString(),\n },\n },\n };\n}\n\n/**\n * Apply config updates to existing config while preserving user-customized fields.\n *\n * This function starts with the existing config and applies updates,\n * while ensuring user-customized fields are never lost:\n * - domain (sending identity)\n * - mailFromDomain (custom MAIL FROM subdomain)\n * - tracking.customRedirectDomain (custom tracking domain)\n * - tracking.httpsEnabled (HTTPS tracking via CloudFront)\n */\nexport function applyConfigUpdates(\n existingConfig: WrapsEmailConfig,\n updates: Partial<WrapsEmailConfig>\n): WrapsEmailConfig {\n // Start with existing config (ensures all required fields are present)\n const result = { ...existingConfig };\n\n // Apply each update, with special handling for nested objects\n for (const [key, value] of Object.entries(updates)) {\n if (value === undefined) {\n continue;\n }\n\n if (key === \"tracking\" && typeof value === \"object\") {\n // Merge tracking updates while preserving user-customized fields\n const trackingUpdate = value as NonNullable<WrapsEmailConfig[\"tracking\"]>;\n result.tracking = {\n ...result.tracking,\n ...trackingUpdate,\n // Always preserve these if they exist in original\n customRedirectDomain:\n result.tracking?.customRedirectDomain ||\n trackingUpdate.customRedirectDomain,\n httpsEnabled:\n result.tracking?.httpsEnabled ?? trackingUpdate.httpsEnabled,\n };\n } else if (key === \"eventTracking\" && typeof value === \"object\") {\n // Deep merge eventTracking\n result.eventTracking = {\n ...result.eventTracking,\n ...(value as NonNullable<WrapsEmailConfig[\"eventTracking\"]>),\n } as NonNullable<WrapsEmailConfig[\"eventTracking\"]>;\n } else if (key === \"suppressionList\" && typeof value === \"object\") {\n // Deep merge suppressionList\n result.suppressionList = {\n ...result.suppressionList,\n ...(value as NonNullable<WrapsEmailConfig[\"suppressionList\"]>),\n } as NonNullable<WrapsEmailConfig[\"suppressionList\"]>;\n } else if (key === \"emailArchiving\" && typeof value === \"object\") {\n // Deep merge emailArchiving\n result.emailArchiving = {\n ...result.emailArchiving,\n ...(value as NonNullable<WrapsEmailConfig[\"emailArchiving\"]>),\n } as NonNullable<WrapsEmailConfig[\"emailArchiving\"]>;\n } else {\n // Direct assignment for primitives and other objects\n result[key as keyof WrapsEmailConfig] = value as any;\n }\n }\n\n return result;\n}\n\n/**\n * Update email configuration in metadata\n * @deprecated Use updateServiceConfig instead\n */\nexport function updateEmailConfig(\n metadata: ConnectionMetadata,\n emailConfig: Partial<WrapsEmailConfig>\n): void {\n if (!metadata.services.email) {\n throw new Error(\"Email service not configured in metadata\");\n }\n\n // Apply updates while preserving user-customized fields\n metadata.services.email.config = applyConfigUpdates(\n metadata.services.email.config,\n emailConfig\n );\n\n metadata.timestamp = new Date().toISOString();\n}\n\n/**\n * Add a service to an existing connection or create new connection metadata\n */\nexport function addServiceToConnection(\n accountId: string,\n region: string,\n provider: Provider,\n service: ServiceType,\n config: WrapsEmailConfig | WrapsSMSConfig,\n preset?: EmailConfigPreset | SMSConfigPreset,\n existingMetadata?: ConnectionMetadata\n): ConnectionMetadata {\n const timestamp = new Date().toISOString();\n\n if (existingMetadata) {\n // Add service to existing connection\n if (service === \"email\") {\n existingMetadata.services.email = {\n preset: preset as EmailConfigPreset,\n config: config as WrapsEmailConfig,\n deployedAt: timestamp,\n };\n } else if (service === \"sms\") {\n existingMetadata.services.sms = {\n preset: preset as SMSConfigPreset,\n config: config as WrapsSMSConfig,\n deployedAt: timestamp,\n };\n }\n existingMetadata.timestamp = timestamp;\n return existingMetadata;\n }\n\n // Create new connection metadata\n const metadata: ConnectionMetadata = {\n version: \"1.0.0\",\n accountId,\n region,\n provider,\n timestamp,\n services: {},\n };\n\n if (service === \"email\") {\n metadata.services.email = {\n preset: preset as EmailConfigPreset,\n config: config as WrapsEmailConfig,\n deployedAt: timestamp,\n };\n } else if (service === \"sms\") {\n metadata.services.sms = {\n preset: preset as SMSConfigPreset,\n config: config as WrapsSMSConfig,\n deployedAt: timestamp,\n };\n }\n\n return metadata;\n}\n\n/**\n * Update service configuration in metadata\n */\nexport function updateServiceConfig<T extends ServiceType>(\n metadata: ConnectionMetadata,\n service: T,\n config: T extends \"email\"\n ? Partial<WrapsEmailConfig>\n : T extends \"sms\"\n ? Partial<WrapsSMSConfig>\n : never\n): void {\n if (service === \"email\" && metadata.services.email) {\n metadata.services.email.config = {\n ...metadata.services.email.config,\n ...(config as Partial<WrapsEmailConfig>),\n };\n } else if (service === \"sms\" && metadata.services.sms) {\n metadata.services.sms.config = {\n ...metadata.services.sms.config,\n ...(config as Partial<WrapsSMSConfig>),\n };\n } else {\n throw new Error(`${service} service not configured in metadata`);\n }\n\n metadata.timestamp = new Date().toISOString();\n}\n\n/**\n * Remove a service from connection metadata\n */\nexport function removeServiceFromConnection(\n metadata: ConnectionMetadata,\n service: ServiceType\n): void {\n if (service === \"email\") {\n const { email, ...rest } = metadata.services;\n metadata.services = rest;\n } else if (service === \"sms\") {\n const { sms, ...rest } = metadata.services;\n metadata.services = rest;\n }\n metadata.timestamp = new Date().toISOString();\n}\n\n/**\n * Check if a service is configured in metadata\n */\nexport function hasService(\n metadata: ConnectionMetadata,\n service: ServiceType\n): boolean {\n if (service === \"email\") {\n return metadata.services.email !== undefined;\n }\n if (service === \"sms\") {\n return metadata.services.sms !== undefined;\n }\n return false;\n}\n\n/**\n * Get list of configured services in metadata\n */\nexport function getConfiguredServices(\n metadata: ConnectionMetadata\n): ServiceType[] {\n const services: ServiceType[] = [];\n if (metadata.services.email) {\n services.push(\"email\");\n }\n if (metadata.services.sms) {\n services.push(\"sms\");\n }\n return services;\n}\n","import { existsSync } from \"node:fs\";\nimport { mkdir } from \"node:fs/promises\";\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\n/**\n * Get the Wraps configuration directory\n */\nexport function getWrapsDir(): string {\n return join(homedir(), \".wraps\");\n}\n\n/**\n * Get the Pulumi workspace directory\n */\nexport function getPulumiWorkDir(): string {\n return join(getWrapsDir(), \"pulumi\");\n}\n\n/**\n * Ensure the Wraps configuration directory exists\n */\nexport async function ensureWrapsDir(): Promise<void> {\n const wrapsDir = getWrapsDir();\n if (!existsSync(wrapsDir)) {\n await mkdir(wrapsDir, { recursive: true });\n }\n}\n\n/**\n * Ensure the Pulumi workspace directory exists and configure local backend\n */\nexport async function ensurePulumiWorkDir(): Promise<void> {\n await ensureWrapsDir();\n const pulumiDir = getPulumiWorkDir();\n if (!existsSync(pulumiDir)) {\n await mkdir(pulumiDir, { recursive: true });\n }\n\n // Set Pulumi to use local backend (file-based state)\n // This avoids needing to login to Pulumi Cloud\n process.env.PULUMI_BACKEND_URL = `file://${pulumiDir}`;\n process.env.PULUMI_CONFIG_PASSPHRASE = \"\"; // Empty passphrase for simplicity\n}\n","import * as clack from \"@clack/prompts\";\nimport pc from \"picocolors\";\n\n/**\n * Deployment progress tracker with spinners using clack\n */\nexport class DeploymentProgress {\n private currentSpinner: ReturnType<typeof clack.spinner> | null = null;\n\n /**\n * Start a spinner with a message\n */\n start(message: string) {\n this.currentSpinner = clack.spinner();\n this.currentSpinner.start(message);\n }\n\n /**\n * Mark current step as succeeded\n */\n succeed(message: string) {\n if (this.currentSpinner) {\n this.currentSpinner.stop(message);\n }\n clack.log.success(message);\n }\n\n /**\n * Mark current step as failed\n */\n fail(message: string) {\n if (this.currentSpinner) {\n this.currentSpinner.stop(message);\n }\n clack.log.error(message);\n }\n\n /**\n * Show info message\n */\n info(message: string) {\n clack.log.info(message);\n }\n\n /**\n * Show step message\n */\n step(message: string) {\n clack.log.step(message);\n }\n\n /**\n * Execute a step with automatic spinner handling\n */\n async execute<T>(message: string, fn: () => Promise<T>): Promise<T> {\n this.start(message);\n try {\n const result = await fn();\n this.succeed(message);\n return result;\n } catch (error) {\n this.fail(message);\n throw error;\n }\n }\n\n /**\n * Stop the spinner\n */\n stop(message?: string) {\n if (this.currentSpinner) {\n this.currentSpinner.stop(message || \"\");\n }\n }\n}\n\n/**\n * DNS record type\n */\nexport type DNSRecord = {\n name: string;\n type: string;\n value: string;\n};\n\n/**\n * Success output configuration\n */\nexport type SuccessOutputs = {\n roleArn: string;\n configSetName?: string;\n region: string;\n dnsRecords?: DNSRecord[];\n trackingDomainDnsRecords?: DNSRecord[];\n acmValidationRecords?: DNSRecord[];\n tableName?: string;\n dnsAutoCreated?: boolean;\n domain?: string;\n customTrackingDomain?: string;\n httpsTrackingEnabled?: boolean;\n mailFromDomain?: string;\n};\n\n/**\n * Display success message with infrastructure outputs\n */\nexport function displaySuccess(outputs: SuccessOutputs) {\n const lines = [\n \"\",\n pc.bold(\"Role ARN:\"),\n ` ${pc.cyan(outputs.roleArn)}`,\n \"\",\n `${pc.bold(\"Region:\")} ${pc.cyan(outputs.region)}`,\n ];\n\n if (outputs.configSetName) {\n lines.push(`${pc.bold(\"Config Set:\")} ${pc.cyan(outputs.configSetName)}`);\n }\n\n if (outputs.tableName) {\n lines.push(`${pc.bold(\"DynamoDB Table:\")} ${pc.cyan(outputs.tableName)}`);\n }\n\n lines.push(\n \"\",\n pc.bold(\"Next steps:\"),\n ` 1. Install SDK: ${pc.yellow(\"npm install @wraps/sdk\")}`,\n ` 2. View dashboard: ${pc.blue(\"https://app.wraps.dev\")}`,\n \"\"\n );\n\n clack.outro(pc.green(\"Email infrastructure deployed successfully!\"));\n console.log(lines.join(\"\\n\"));\n\n // Show DNS auto-creation message\n if (outputs.dnsAutoCreated && outputs.domain) {\n clack.note(\n `DNS records (DKIM, SPF, DMARC) were automatically created in Route53 for ${pc.cyan(\n outputs.domain\n )}.\\n\\nVerification should complete within a few minutes.`,\n pc.green(\"✓ DNS Auto-Configured\")\n );\n }\n\n if (outputs.dnsRecords && outputs.dnsRecords.length > 0) {\n // Extract domain from first DKIM record\n const domain = outputs.dnsRecords[0]?.name.split(\"._domainkey.\")[1];\n\n const dnsLines = [\n pc.bold(\"DKIM Records (CNAME):\"),\n ...outputs.dnsRecords.map(\n (record) =>\n ` ${pc.cyan(record.name)} ${pc.dim(record.type)} \"${record.value}\"`\n ),\n ];\n\n if (domain) {\n // Use MAIL FROM domain for DMARC rua if configured, otherwise use main domain\n const dmarcRuaDomain = outputs.mailFromDomain || domain;\n dnsLines.push(\n \"\",\n pc.bold(\"SPF Record (TXT):\"),\n ` ${pc.cyan(domain)} ${pc.dim(\"TXT\")} \"v=spf1 include:amazonses.com ~all\"`,\n pc.dim(\" Note: If you have an existing SPF record, add 'include:amazonses.com' to it\"),\n \"\",\n pc.bold(\"DMARC Record (TXT):\"),\n ` ${pc.cyan(`_dmarc.${domain}`)} ${pc.dim(\"TXT\")} \"v=DMARC1; p=quarantine; rua=mailto:postmaster@${dmarcRuaDomain}\"`\n );\n\n // Add MAIL FROM domain DNS records if configured\n if (outputs.mailFromDomain) {\n dnsLines.push(\n \"\",\n pc.bold(\"MAIL FROM Domain Records (for DMARC alignment):\"),\n ` ${pc.cyan(outputs.mailFromDomain)} ${pc.dim(\"MX\")} \"10 feedback-smtp.${outputs.region}.amazonses.com\"`,\n ` ${pc.cyan(outputs.mailFromDomain)} ${pc.dim(\"TXT\")} \"v=spf1 include:amazonses.com ~all\"`\n );\n }\n }\n\n clack.note(dnsLines.join(\"\\n\"), \"DNS Records to add:\");\n }\n\n // Show ACM certificate validation records if HTTPS tracking is enabled\n if (outputs.acmValidationRecords && outputs.acmValidationRecords.length > 0) {\n const acmDnsLines = [\n pc.bold(\"SSL Certificate Validation (ACM):\"),\n ...outputs.acmValidationRecords.map(\n (record) =>\n ` ${pc.cyan(record.name)} ${pc.dim(record.type)} \"${record.value}\"`\n ),\n \"\",\n pc.dim(\n \"Note: These records are required to validate your SSL certificate.\"\n ),\n pc.dim(\n \"CloudFront will be enabled automatically after certificate validation.\"\n ),\n ];\n\n clack.note(\n acmDnsLines.join(\"\\n\"),\n \"SSL Certificate Validation DNS Records:\"\n );\n }\n\n // Show tracking domain DNS records if custom tracking domain is configured\n if (\n outputs.trackingDomainDnsRecords &&\n outputs.trackingDomainDnsRecords.length > 0\n ) {\n const trackingProtocol = outputs.httpsTrackingEnabled ? \"HTTPS\" : \"HTTP\";\n const trackingDnsLines = [\n pc.bold(`Custom Tracking Domain - ${trackingProtocol} Redirect CNAME:`),\n ...outputs.trackingDomainDnsRecords.map(\n (record) =>\n ` ${pc.cyan(record.name)} ${pc.dim(record.type)} \"${record.value}\"`\n ),\n \"\",\n pc.dim(\n \"Note: This CNAME allows SES to rewrite links in your emails to use\"\n ),\n pc.dim(\"your custom domain for open and click tracking.\"),\n ];\n\n if (outputs.httpsTrackingEnabled) {\n trackingDnsLines.push(\n \"\",\n pc.dim(\"HTTPS tracking is enabled via CloudFront with SSL certificate.\")\n );\n }\n\n clack.note(\n trackingDnsLines.join(\"\\n\"),\n \"Custom Tracking Domain DNS Records:\"\n );\n\n if (outputs.customTrackingDomain) {\n console.log(\n `\\n${pc.dim(\"Run:\")} ${pc.yellow(`wraps email verify --domain ${outputs.customTrackingDomain}`)} ${pc.dim(\n \"(after DNS propagates)\"\n )}\\n`\n );\n }\n }\n\n // Show tracking domain separately if we only have tracking domain (no other DNS records)\n // ONLY for HTTP tracking - HTTPS tracking DNS records are shown after CloudFront is created\n if (\n outputs.customTrackingDomain &&\n !outputs.httpsTrackingEnabled && // Only show for HTTP tracking\n !outputs.dnsAutoCreated &&\n (!outputs.dnsRecords || outputs.dnsRecords.length === 0) &&\n (!outputs.trackingDomainDnsRecords ||\n outputs.trackingDomainDnsRecords.length === 0)\n ) {\n const trackingLines = [\n pc.bold(\"Tracking Domain (CNAME):\"),\n ` ${pc.cyan(outputs.customTrackingDomain)} ${pc.dim(\"CNAME\")} \"r.${outputs.region}.awstrack.me\"`,\n \"\",\n pc.dim(\n \"Note: This CNAME allows SES to rewrite links in your emails to use\"\n ),\n pc.dim(\"your custom domain for open and click tracking.\"),\n ];\n\n clack.note(trackingLines.join(\"\\n\"), \"DNS Record to add:\");\n }\n}\n\n/**\n * Status output configuration\n */\nexport type StatusOutputs = {\n integrationLevel: \"dashboard-only\" | \"enhanced\";\n region: string;\n domains: Array<{\n domain: string;\n status: \"verified\" | \"pending\" | \"failed\";\n dkimTokens?: string[];\n mailFromDomain?: string;\n mailFromStatus?: string;\n }>;\n resources: {\n roleArn?: string;\n configSetName?: string;\n tableName?: string;\n lambdaFunctions?: number;\n snsTopics?: number;\n archiveArn?: string;\n archivingEnabled?: boolean;\n archiveRetention?: string;\n };\n tracking?: {\n customTrackingDomain?: string;\n httpsEnabled?: boolean;\n cloudFrontDomain?: string;\n };\n};\n\n/**\n * Display status information\n */\nexport function displayStatus(status: StatusOutputs) {\n clack.intro(pc.bold(\"Wraps Email Infrastructure\"));\n\n const infoLines = [\n `${pc.bold(\"Integration:\")} ${pc.cyan(status.integrationLevel)}`,\n `${pc.bold(\"Region:\")} ${pc.cyan(status.region)}`,\n ];\n\n if (status.domains.length > 0) {\n const domainStrings = status.domains.map((d) => {\n const statusIcon =\n d.status === \"verified\" ? \"✓\" : d.status === \"pending\" ? \"⏱\" : \"✗\";\n const statusColor =\n d.status === \"verified\"\n ? pc.green\n : d.status === \"pending\"\n ? pc.yellow\n : pc.red;\n\n let domainLine = ` ${d.domain} ${statusColor(`${statusIcon} ${d.status}`)}`;\n\n // Add MAIL FROM domain info if configured\n if (d.mailFromDomain) {\n const mailFromStatusIcon = d.mailFromStatus === \"SUCCESS\" ? \"✓\" : \"⏱\";\n const mailFromColor =\n d.mailFromStatus === \"SUCCESS\" ? pc.green : pc.yellow;\n domainLine += `\\n ${pc.dim(\"MAIL FROM:\")} ${d.mailFromDomain} ${mailFromColor(mailFromStatusIcon)}`;\n }\n\n return domainLine;\n });\n infoLines.push(`${pc.bold(\"Domains:\")}\\n${domainStrings.join(\"\\n\")}`);\n }\n\n clack.note(infoLines.join(\"\\n\"), \"Configuration\");\n\n // Features\n const featureLines = [];\n featureLines.push(` ${pc.green(\"✓\")} Email Sending ${pc.dim(\"(via SES)\")}`);\n\n if (status.resources.tableName) {\n featureLines.push(\n ` ${pc.green(\"✓\")} Email Tracking ${pc.dim(\"(DynamoDB logs)\")}`\n );\n } else {\n featureLines.push(\n ` ${pc.dim(\"○\")} Email Tracking ${pc.dim(\"(run 'wraps email upgrade' to enable)\")}`\n );\n }\n\n if (\n status.resources.lambdaFunctions &&\n status.resources.lambdaFunctions > 0\n ) {\n featureLines.push(\n ` ${pc.green(\"✓\")} Bounce/Complaint Handling ${pc.dim(\"(automated)\")}`\n );\n } else {\n featureLines.push(\n ` ${pc.dim(\"○\")} Bounce/Complaint Handling ${pc.dim(\"(run 'wraps email upgrade' to enable)\")}`\n );\n }\n\n // Email Archiving\n if (status.resources.archivingEnabled) {\n const retentionLabel =\n {\n \"7days\": \"7 days\",\n \"30days\": \"30 days\",\n \"90days\": \"90 days\",\n \"6months\": \"6 months\",\n \"1year\": \"1 year\",\n \"18months\": \"18 months\",\n }[status.resources.archiveRetention || \"90days\"] || \"90 days\";\n featureLines.push(\n ` ${pc.green(\"✓\")} Email Archiving ${pc.dim(`(${retentionLabel} retention)`)}`\n );\n } else {\n featureLines.push(\n ` ${pc.dim(\"○\")} Email Archiving ${pc.dim(\"(run 'wraps email upgrade' to enable)\")}`\n );\n }\n\n // Custom Tracking Domain\n if (status.tracking?.customTrackingDomain) {\n const protocol = status.tracking.httpsEnabled ? \"HTTPS\" : \"HTTP\";\n const cloudFrontStatus = status.tracking.httpsEnabled\n ? status.tracking.cloudFrontDomain\n ? pc.green(\"✓ Active\")\n : pc.yellow(\"⏱ Pending\")\n : \"\";\n const trackingLabel = status.tracking.httpsEnabled\n ? `${protocol} tracking ${cloudFrontStatus}`\n : `${protocol} tracking`;\n featureLines.push(\n ` ${pc.green(\"✓\")} Custom Tracking Domain ${pc.dim(`(${trackingLabel})`)}`\n );\n featureLines.push(` ${pc.cyan(status.tracking.customTrackingDomain)}`);\n } else {\n featureLines.push(\n ` ${pc.dim(\"○\")} Custom Tracking Domain ${pc.dim(\"(run 'wraps email upgrade' to enable)\")}`\n );\n }\n\n featureLines.push(\n ` ${pc.green(\"✓\")} Console Dashboard ${pc.dim(\"(run 'wraps console')\")}`\n );\n\n clack.note(featureLines.join(\"\\n\"), \"Features\");\n\n // Resources\n const resourceLines = [];\n\n if (status.resources.roleArn) {\n resourceLines.push(\n ` ${pc.green(\"✓\")} IAM Role: ${pc.cyan(status.resources.roleArn)}`\n );\n }\n\n if (status.resources.configSetName) {\n resourceLines.push(\n ` ${pc.green(\"✓\")} Configuration Set: ${pc.cyan(status.resources.configSetName)}`\n );\n }\n\n if (status.resources.tableName) {\n resourceLines.push(\n ` ${pc.green(\"✓\")} DynamoDB Table: ${pc.cyan(status.resources.tableName)}`\n );\n }\n\n if (status.resources.lambdaFunctions) {\n resourceLines.push(\n ` ${pc.green(\"✓\")} Lambda Functions: ${pc.cyan(\n `${status.resources.lambdaFunctions} deployed`\n )}`\n );\n }\n\n if (status.resources.snsTopics) {\n resourceLines.push(\n ` ${pc.green(\"✓\")} SNS Topics: ${pc.cyan(`${status.resources.snsTopics} configured`)}`\n );\n }\n\n if (status.resources.archiveArn) {\n resourceLines.push(\n ` ${pc.green(\"✓\")} Mail Manager Archive: ${pc.cyan(status.resources.archiveArn)}`\n );\n }\n\n clack.note(resourceLines.join(\"\\n\"), \"Resources\");\n\n // Show DNS records for pending domains OR domains with pending MAIL FROM\n const domainsNeedingDNS = status.domains.filter(\n (d) =>\n (d.status === \"pending\" && d.dkimTokens) ||\n (d.mailFromDomain && d.mailFromStatus !== \"SUCCESS\")\n );\n if (domainsNeedingDNS.length > 0) {\n for (const domain of domainsNeedingDNS) {\n const dnsLines = [];\n\n // DKIM records (only for pending domains)\n if (\n domain.status === \"pending\" &&\n domain.dkimTokens &&\n domain.dkimTokens.length > 0\n ) {\n // Use MAIL FROM domain for DMARC rua if configured, otherwise use main domain\n const dmarcRuaDomain = domain.mailFromDomain || domain.domain;\n dnsLines.push(\n pc.bold(\"DKIM Records (CNAME):\"),\n ...domain.dkimTokens.map(\n (token) =>\n ` ${pc.cyan(`${token}._domainkey.${domain.domain}`)} ${pc.dim(\"CNAME\")} \"${token}.dkim.amazonses.com\"`\n ),\n \"\",\n pc.bold(\"SPF Record (TXT):\"),\n ` ${pc.cyan(domain.domain)} ${pc.dim(\"TXT\")} \"v=spf1 include:amazonses.com ~all\"`,\n pc.dim(\" Note: If you have an existing SPF record, add 'include:amazonses.com' to it\"),\n \"\",\n pc.bold(\"DMARC Record (TXT):\"),\n ` ${pc.cyan(`_dmarc.${domain.domain}`)} ${pc.dim(\"TXT\")} \"v=DMARC1; p=quarantine; rua=mailto:postmaster@${dmarcRuaDomain}\"`\n );\n }\n\n // MAIL FROM records (if configured but not verified)\n if (domain.mailFromDomain && domain.mailFromStatus !== \"SUCCESS\") {\n if (dnsLines.length > 0) {\n dnsLines.push(\"\");\n }\n dnsLines.push(\n pc.bold(\"MAIL FROM Domain Records (for DMARC alignment):\"),\n ` ${pc.cyan(domain.mailFromDomain)} ${pc.dim(\"MX\")} \"10 feedback-smtp.${status.region}.amazonses.com\"`,\n ` ${pc.cyan(domain.mailFromDomain)} ${pc.dim(\"TXT\")} \"v=spf1 include:amazonses.com ~all\"`\n );\n }\n\n if (dnsLines.length > 0) {\n clack.note(dnsLines.join(\"\\n\"), `DNS Records for ${domain.domain}`);\n }\n }\n\n // Show verify command with first domain needing DNS as example\n const exampleDomain = domainsNeedingDNS[0].domain;\n console.log(\n `\\n${pc.dim(\"Run:\")} ${pc.yellow(`wraps email verify --domain ${exampleDomain}`)} ${pc.dim(\n \"(after DNS propagates)\"\n )}\\n`\n );\n }\n\n console.log(`\\n${pc.bold(\"Dashboard:\")} ${pc.blue(\"https://app.wraps.dev\")}`);\n console.log(`${pc.bold(\"Docs:\")} ${pc.blue(\"https://wraps.dev/docs\")}\\n`);\n}\n\n/**\n * Preview output configuration\n */\nexport type PreviewOutputs = {\n changeSummary: {\n create?: number;\n update?: number;\n delete?: number;\n same?: number;\n replace?: number;\n };\n costEstimate?: string;\n commandName: string;\n};\n\n/**\n * Display preview results with resource changes and cost estimate\n */\nexport function displayPreview(outputs: PreviewOutputs): void {\n console.log(pc.yellow(\"\\n--- PREVIEW MODE (no changes will be made) ---\\n\"));\n\n // Display change summary\n const changes = outputs.changeSummary;\n const summaryLines: string[] = [];\n\n if (changes.create && changes.create > 0) {\n summaryLines.push(` ${pc.green(\"+\")} ${changes.create} to create`);\n }\n if (changes.update && changes.update > 0) {\n summaryLines.push(` ${pc.yellow(\"~\")} ${changes.update} to update`);\n }\n if (changes.delete && changes.delete > 0) {\n summaryLines.push(` ${pc.red(\"-\")} ${changes.delete} to destroy`);\n }\n if (changes.same && changes.same > 0) {\n summaryLines.push(` ${pc.dim(\"=\")} ${changes.same} unchanged`);\n }\n if (changes.replace && changes.replace > 0) {\n summaryLines.push(` ${pc.magenta(\"+-\")} ${changes.replace} to replace`);\n }\n\n if (summaryLines.length > 0) {\n clack.note(summaryLines.join(\"\\n\"), \"Resource Changes\");\n } else {\n clack.note(\"No changes detected\", \"Resource Changes\");\n }\n\n // Display cost estimate\n if (outputs.costEstimate) {\n clack.note(outputs.costEstimate, \"Estimated Monthly Cost\");\n }\n\n console.log(pc.yellow(\"\\n--- END PREVIEW (no changes were made) ---\\n\"));\n}\n","import * as clack from \"@clack/prompts\";\nimport * as pulumi from \"@pulumi/pulumi\";\nimport pc from \"picocolors\";\nimport { deployEmailStack } from \"../../infrastructure/email-stack.js\";\nimport { trackCommand, trackError } from \"../../telemetry/events.js\";\nimport type {\n EmailConfigOptions,\n EmailStackConfig,\n} from \"../../types/index.js\";\nimport {\n getAWSRegion,\n validateAWSCredentials,\n} from \"../../utils/shared/aws.js\";\nimport { errors } from \"../../utils/shared/errors.js\";\nimport {\n ensurePulumiWorkDir,\n getPulumiWorkDir,\n} from \"../../utils/shared/fs.js\";\nimport {\n loadConnectionMetadata,\n saveConnectionMetadata,\n} from \"../../utils/shared/metadata.js\";\nimport {\n DeploymentProgress,\n displayPreview,\n displaySuccess,\n} from \"../../utils/shared/output.js\";\nimport { ensurePulumiInstalled } from \"../../utils/shared/pulumi.js\";\n\n/**\n * Config command - Redeploy infrastructure to apply CLI updates\n * This command updates Lambda functions and other managed resources\n * without requiring configuration changes from the user.\n */\nexport async function config(options: EmailConfigOptions): Promise<void> {\n const startTime = Date.now();\n\n clack.intro(\n pc.bold(\n options.preview\n ? \"Wraps Config Preview\"\n : \"Wraps Config - Apply CLI Updates to Infrastructure\"\n )\n );\n\n const progress = new DeploymentProgress();\n\n // 1. Check Pulumi CLI is installed\n const wasAutoInstalled = await progress.execute(\n \"Checking Pulumi CLI installation\",\n async () => await ensurePulumiInstalled()\n );\n\n if (wasAutoInstalled) {\n progress.info(\"Pulumi CLI was automatically installed\");\n }\n\n // 2. Validate AWS credentials\n const identity = await progress.execute(\n \"Validating AWS credentials\",\n async () => validateAWSCredentials()\n );\n\n progress.info(`Connected to AWS account: ${pc.cyan(identity.accountId)}`);\n\n // 3. Get region\n let region = options.region;\n if (!region) {\n const defaultRegion = await getAWSRegion();\n region = defaultRegion;\n }\n\n // 4. Load existing connection metadata\n const metadata = await loadConnectionMetadata(identity.accountId, region);\n\n if (!metadata) {\n clack.log.error(\n `No Wraps connection found for account ${pc.cyan(identity.accountId)} in region ${pc.cyan(region)}`\n );\n clack.log.info(\n `Use ${pc.cyan(\"wraps email init\")} to create new infrastructure or ${pc.cyan(\"wraps email connect\")} to connect existing.`\n );\n process.exit(1);\n }\n\n progress.info(`Found existing connection created: ${metadata.timestamp}`);\n\n // 5. Display current configuration\n console.log(`\\n${pc.bold(\"Current Configuration:\")}\\n`);\n\n if (metadata.services.email?.preset) {\n console.log(` Preset: ${pc.cyan(metadata.services.email?.preset)}`);\n } else {\n console.log(` Preset: ${pc.cyan(\"custom\")}`);\n }\n\n const config = metadata.services.email?.config;\n\n if (!config) {\n clack.log.error(\"No email configuration found in metadata\");\n clack.log.info(\n `Use ${pc.cyan(\"wraps email init\")} to create new infrastructure.`\n );\n process.exit(1);\n }\n\n // Show sending domain if configured\n if (config.domain) {\n console.log(` Sending Domain: ${pc.cyan(config.domain)}`);\n }\n\n if (config.tracking?.enabled) {\n console.log(` ${pc.green(\"✓\")} Open & Click Tracking`);\n }\n\n if (config.suppressionList?.enabled) {\n console.log(` ${pc.green(\"✓\")} Bounce/Complaint Suppression`);\n }\n\n if (config.eventTracking?.enabled) {\n console.log(` ${pc.green(\"✓\")} Event Tracking (EventBridge)`);\n }\n\n if (config.dedicatedIp) {\n console.log(` ${pc.green(\"✓\")} Dedicated IP Address`);\n }\n\n console.log(\"\");\n\n // 6. Show what will be updated\n console.log(`${pc.bold(\"What will be updated:\")}\\n`);\n console.log(\n ` ${pc.cyan(\"•\")} Lambda function code (if event tracking enabled)`\n );\n console.log(\n ` ${pc.cyan(\"•\")} EventBridge rules (if event tracking enabled)`\n );\n console.log(` ${pc.cyan(\"•\")} IAM policies (security improvements)`);\n console.log(` ${pc.cyan(\"•\")} SES configuration set (feature updates)`);\n console.log(\"\");\n\n progress.info(\n \"Your current configuration will be preserved - no features will be added or removed\"\n );\n console.log(\"\");\n\n // 7. Confirm update (skip if --yes or --preview)\n if (!(options.yes || options.preview)) {\n const confirmed = await clack.confirm({\n message: \"Proceed with update?\",\n initialValue: true,\n });\n\n if (clack.isCancel(confirmed) || !confirmed) {\n clack.cancel(\"Update cancelled.\");\n process.exit(0);\n }\n }\n\n // 8. Get Vercel config if needed\n let vercelConfig;\n if (metadata.provider === \"vercel\" && metadata.vercel) {\n vercelConfig = metadata.vercel;\n }\n\n // 9. Build stack configuration (reuse existing config)\n const stackConfig: EmailStackConfig = {\n provider: metadata.provider,\n region,\n vercel: vercelConfig,\n emailConfig: config,\n };\n\n // 10. Preview or Update Pulumi stack\n if (options.preview) {\n // PREVIEW MODE - show what would be updated without deploying\n try {\n const previewResult = await progress.execute(\n \"Generating update preview\",\n async () => {\n await ensurePulumiWorkDir();\n\n const stack =\n await pulumi.automation.LocalWorkspace.createOrSelectStack(\n {\n stackName:\n metadata.services.email?.pulumiStackName ||\n `wraps-${identity.accountId}-${region}`,\n projectName: \"wraps-email\",\n program: async () => {\n const result = await deployEmailStack(stackConfig);\n return {\n roleArn: result.roleArn,\n configSetName: result.configSetName,\n tableName: result.tableName,\n region: result.region,\n lambdaFunctions: result.lambdaFunctions,\n domain: result.domain,\n dkimTokens: result.dkimTokens,\n customTrackingDomain: result.customTrackingDomain,\n };\n },\n },\n {\n workDir: getPulumiWorkDir(),\n envVars: {\n PULUMI_CONFIG_PASSPHRASE: \"\",\n AWS_REGION: region,\n },\n secretsProvider: \"passphrase\",\n }\n );\n\n await stack.setConfig(\"aws:region\", { value: region });\n\n // Refresh state to sync with AWS before previewing\n await stack.refresh({ onOutput: () => {} });\n\n // Run preview instead of deployment\n const result = await stack.preview({ diff: true });\n return result;\n }\n );\n\n // Display preview results\n displayPreview({\n changeSummary: previewResult.changeSummary,\n commandName: \"wraps email config\",\n });\n\n clack.outro(\n pc.green(\"Preview complete. Run without --preview to update.\")\n );\n\n // Track preview completion\n trackCommand(\"email:config\", {\n success: true,\n preview: true,\n duration_ms: Date.now() - startTime,\n });\n return;\n } catch (error: any) {\n trackError(\"PREVIEW_FAILED\", \"email:config\", { step: \"preview\" });\n if (error.message?.includes(\"stack is currently locked\")) {\n throw errors.stackLocked();\n }\n throw new Error(`Preview failed: ${error.message}`);\n }\n }\n\n // UPDATE MODE - actually update infrastructure\n let outputs;\n try {\n outputs = await progress.execute(\n \"Updating Wraps infrastructure (this may take 2-3 minutes)\",\n async () => {\n await ensurePulumiWorkDir();\n\n const stack =\n await pulumi.automation.LocalWorkspace.createOrSelectStack(\n {\n stackName:\n metadata.services.email?.pulumiStackName ||\n `wraps-${identity.accountId}-${region}`,\n projectName: \"wraps-email\",\n program: async () => {\n const result = await deployEmailStack(stackConfig);\n\n return {\n roleArn: result.roleArn,\n configSetName: result.configSetName,\n tableName: result.tableName,\n region: result.region,\n lambdaFunctions: result.lambdaFunctions,\n domain: result.domain,\n dkimTokens: result.dkimTokens,\n customTrackingDomain: result.customTrackingDomain,\n };\n },\n },\n {\n workDir: getPulumiWorkDir(),\n envVars: {\n PULUMI_CONFIG_PASSPHRASE: \"\",\n AWS_REGION: region,\n },\n secretsProvider: \"passphrase\",\n }\n );\n\n await stack.workspace.selectStack(\n metadata.services.email?.pulumiStackName ||\n `wraps-${identity.accountId}-${region}`\n );\n await stack.setConfig(\"aws:region\", { value: region });\n\n // Refresh state to sync with actual AWS resources (prevents AlreadyExists errors)\n await stack.refresh({ onOutput: () => {} });\n\n // Pulumi will automatically detect changes and only update what's needed\n const upResult = await stack.up({ onOutput: () => {} });\n const pulumiOutputs = upResult.outputs;\n\n return {\n roleArn: pulumiOutputs.roleArn?.value as string,\n configSetName: pulumiOutputs.configSetName?.value as\n | string\n | undefined,\n tableName: pulumiOutputs.tableName?.value as string | undefined,\n region: pulumiOutputs.region?.value as string,\n lambdaFunctions: pulumiOutputs.lambdaFunctions?.value as\n | string[]\n | undefined,\n domain: pulumiOutputs.domain?.value as string | undefined,\n dkimTokens: pulumiOutputs.dkimTokens?.value as string[] | undefined,\n customTrackingDomain: pulumiOutputs.customTrackingDomain?.value as\n | string\n | undefined,\n };\n }\n );\n } catch (error: any) {\n // Track update failure\n trackCommand(\"email:config\", {\n success: false,\n duration_ms: Date.now() - startTime,\n });\n\n // Check if it's a lock file error\n if (error.message?.includes(\"stack is currently locked\")) {\n trackError(\"STACK_LOCKED\", \"email:config\", { step: \"update\" });\n throw errors.stackLocked();\n }\n\n trackError(\"UPDATE_FAILED\", \"email:config\", { step: \"update\" });\n throw new Error(`Pulumi update failed: ${error.message}`);\n }\n\n // 11. Update metadata timestamp (config stays the same)\n metadata.timestamp = new Date().toISOString();\n await saveConnectionMetadata(metadata);\n\n progress.info(\"Connection metadata updated\");\n\n // 12. Display success message\n displaySuccess({\n roleArn: outputs.roleArn,\n configSetName: outputs.configSetName,\n region: outputs.region!,\n tableName: outputs.tableName,\n customTrackingDomain: outputs.customTrackingDomain,\n });\n\n // Show what was updated\n console.log(`\\n${pc.green(\"✓\")} ${pc.bold(\"Update complete!\")}\\n`);\n console.log(\n \"Infrastructure has been updated with the latest CLI improvements.\\n\"\n );\n console.log(`${pc.bold(\"Next steps:\")}\\n`);\n console.log(\n ` ${pc.cyan(\"1.\")} No code changes needed - your existing SDK integration continues to work`\n );\n console.log(\n ` ${pc.cyan(\"2.\")} Check ${pc.cyan(\"wraps status\")} to verify all resources are healthy`\n );\n console.log(\n ` ${pc.cyan(\"3.\")} View analytics at ${pc.cyan(\"wraps console\")}\\n`\n );\n\n // 13. Track successful update\n trackCommand(\"email:config\", {\n success: true,\n duration_ms: Date.now() - startTime,\n });\n}\n","import * as aws from \"@pulumi/aws\";\nimport * as pulumi from \"@pulumi/pulumi\";\nimport type { EmailStackConfig, StackOutputs } from \"../types/index.js\";\nimport { createDynamoDBTables } from \"./resources/dynamodb.js\";\nimport { createEventBridgeResources } from \"./resources/eventbridge.js\";\nimport { createIAMRole } from \"./resources/iam.js\";\nimport { deployLambdaFunctions } from \"./resources/lambda.js\";\nimport { createSESResources, eventDestinationExists } from \"./resources/ses.js\";\nimport { createSQSResources } from \"./resources/sqs.js\";\nimport { createVercelOIDC } from \"./vercel-oidc.js\";\n\n/**\n * Deploy email infrastructure stack using Pulumi\n */\nexport async function deployEmailStack(\n config: EmailStackConfig\n): Promise<StackOutputs> {\n // Get current AWS account\n const identity = await aws.getCallerIdentity();\n const accountId = identity.accountId;\n\n let oidcProvider: aws.iam.OpenIdConnectProvider | undefined;\n\n // 1. Create OIDC provider if Vercel\n if (config.provider === \"vercel\" && config.vercel) {\n oidcProvider = await createVercelOIDC({\n teamSlug: config.vercel.teamSlug,\n accountId,\n });\n }\n\n const emailConfig = config.emailConfig;\n\n // 2. Create IAM role\n const role = await createIAMRole({\n provider: config.provider,\n oidcProvider,\n vercelTeamSlug: config.vercel?.teamSlug,\n vercelProjectName: config.vercel?.projectName,\n emailConfig,\n });\n\n // 3. CloudFront + ACM (if HTTPS tracking enabled)\n let cloudFrontResources;\n let acmResources;\n\n if (\n emailConfig.tracking?.enabled &&\n emailConfig.tracking.customRedirectDomain &&\n emailConfig.tracking.httpsEnabled\n ) {\n // Check for Route53 hosted zone (for automatic DNS validation)\n const { findHostedZone } = await import(\"../utils/email/route53.js\");\n const hostedZone = await findHostedZone(\n emailConfig.tracking.customRedirectDomain,\n config.region\n );\n\n // Create ACM certificate (in us-east-1 for CloudFront)\n const { createACMCertificate } = await import(\"./resources/acm.js\");\n acmResources = await createACMCertificate({\n domain: emailConfig.tracking.customRedirectDomain,\n hostedZoneId: hostedZone?.id,\n });\n\n // Create CloudFront distribution with SSL certificate\n // Import CloudFront creation function\n const { createCloudFrontTracking } = await import(\n \"./resources/cloudfront.js\"\n );\n\n // Determine which certificate ARN to use:\n // - Route53: Use certificateValidation.certificateArn (waits for validation)\n // - Manual DNS: Use certificate.arn directly (CloudFront will fail if not validated)\n const certificateArn = acmResources.certificateValidation\n ? acmResources.certificateValidation.certificateArn\n : acmResources.certificate.arn;\n\n cloudFrontResources = await createCloudFrontTracking({\n customTrackingDomain: emailConfig.tracking.customRedirectDomain,\n region: config.region,\n certificateArn,\n hostedZoneId: hostedZone?.id, // Pass hosted zone ID for automatic DNS record creation\n });\n }\n\n // 4. SES resources (if tracking or event tracking enabled)\n let sesResources;\n if (emailConfig.tracking?.enabled || emailConfig.eventTracking?.enabled) {\n // Check if the event destination already exists in AWS but not in Pulumi state\n // This can happen if resources were created outside Pulumi or state got out of sync\n const shouldImportEventDest =\n emailConfig.eventTracking?.enabled &&\n (await eventDestinationExists(\n \"wraps-email-tracking\",\n \"wraps-email-eventbridge\",\n config.region\n ));\n\n // Compute mailFromDomain from mailFromSubdomain if provided\n let mailFromDomain = emailConfig.mailFromDomain;\n if (!mailFromDomain && emailConfig.mailFromSubdomain && emailConfig.domain) {\n mailFromDomain = `${emailConfig.mailFromSubdomain}.${emailConfig.domain}`;\n }\n\n sesResources = await createSESResources({\n domain: emailConfig.domain,\n mailFromDomain,\n region: config.region,\n trackingConfig: emailConfig.tracking,\n eventTypes: emailConfig.eventTracking?.events,\n eventTrackingEnabled: emailConfig.eventTracking?.enabled, // Pass flag to create EventBridge destination\n tlsRequired: emailConfig.tlsRequired, // Require TLS encryption for all emails\n importExistingEventDestination: shouldImportEventDest, // Import if exists to avoid AlreadyExistsException\n });\n }\n\n // 5. DynamoDB tables (if history storage enabled)\n let dynamoTables;\n if (emailConfig.eventTracking?.dynamoDBHistory) {\n dynamoTables = await createDynamoDBTables({\n retention: emailConfig.eventTracking.archiveRetention,\n });\n }\n\n // 6. SQS queues (if event tracking enabled)\n let sqsResources;\n if (emailConfig.eventTracking?.enabled) {\n sqsResources = await createSQSResources();\n }\n\n // 7. EventBridge rule to route SES events to SQS (if event tracking enabled)\n if (emailConfig.eventTracking?.enabled && sesResources && sqsResources) {\n await createEventBridgeResources({\n eventBusArn: sesResources.eventBus.arn,\n queueArn: sqsResources.queue.arn,\n queueUrl: sqsResources.queue.url,\n });\n }\n\n // 8. Lambda functions (if event tracking and DynamoDB enabled)\n let lambdaFunctions;\n if (\n emailConfig.eventTracking?.dynamoDBHistory &&\n dynamoTables &&\n sqsResources\n ) {\n lambdaFunctions = await deployLambdaFunctions({\n roleArn: role.arn,\n tableName: dynamoTables.emailHistory.name,\n queueArn: sqsResources.queue.arn,\n accountId,\n region: config.region,\n });\n }\n\n // 9. Mail Manager Archive (if email archiving enabled)\n let archiveResources;\n if (emailConfig.emailArchiving?.enabled && sesResources) {\n const { createMailManagerArchive } = await import(\n \"./resources/mail-manager.js\"\n );\n archiveResources = await createMailManagerArchive({\n name: \"email\",\n retention: emailConfig.emailArchiving.retention,\n configSetName: sesResources.configSet.configurationSetName,\n region: config.region,\n });\n }\n\n // Return outputs\n return {\n roleArn: role.arn as any as string,\n configSetName: sesResources?.configSet.configurationSetName as any as\n | string\n | undefined,\n tableName: dynamoTables?.emailHistory.name as any as string | undefined,\n region: config.region,\n lambdaFunctions: lambdaFunctions\n ? [lambdaFunctions.eventProcessor.arn as any as string]\n : undefined,\n domain: emailConfig.domain,\n dkimTokens: sesResources?.dkimTokens as any as string[] | undefined,\n dnsAutoCreated: sesResources?.dnsAutoCreated,\n eventBusName: sesResources?.eventBus.name as any as string | undefined,\n queueUrl: sqsResources?.queue.url as any as string | undefined,\n dlqUrl: sqsResources?.dlq.url as any as string | undefined,\n customTrackingDomain: sesResources?.customTrackingDomain,\n httpsTrackingEnabled: emailConfig.tracking?.httpsEnabled,\n cloudFrontDomain: cloudFrontResources?.domainName as any as\n | string\n | undefined,\n acmCertificateValidationRecords: acmResources?.validationRecords as any as\n | Array<{ name: string; type: string; value: string }>\n | undefined,\n mailFromDomain: sesResources?.mailFromDomain,\n archiveArn: archiveResources?.archiveArn,\n archivingEnabled: emailConfig.emailArchiving?.enabled,\n archiveRetention: emailConfig.emailArchiving?.enabled\n ? emailConfig.emailArchiving.retention\n : undefined,\n };\n}\n\n/**\n * Run Pulumi program inline\n */\nexport async function runPulumiProgram(\n stackName: string,\n program: () => Promise<StackOutputs>\n): Promise<StackOutputs> {\n const stack = await pulumi.automation.LocalWorkspace.createOrSelectStack(\n {\n stackName,\n projectName: \"wraps-email\",\n program,\n },\n {\n workDir: `${process.env.HOME}/.wraps/pulumi`,\n }\n );\n\n // Set AWS region\n await stack.setConfig(\"aws:region\", { value: \"us-east-1\" });\n\n // Run the deployment\n const upResult = await stack.up({\n onOutput: (msg) => process.stdout.write(msg),\n });\n\n // Get outputs\n const outputs = upResult.outputs;\n\n return {\n roleArn: outputs.roleArn?.value as string,\n configSetName: outputs.configSetName?.value as string | undefined,\n tableName: outputs.tableName?.value as string | undefined,\n region: outputs.region?.value as string,\n };\n}\n","import * as aws from \"@pulumi/aws\";\nimport type { ArchiveRetention } from \"../../types/index.js\";\n\n/**\n * DynamoDB configuration\n */\nexport type DynamoDBConfig = {\n retention?: ArchiveRetention;\n};\n\n/**\n * DynamoDB tables output\n */\nexport type DynamoDBTables = {\n emailHistory: aws.dynamodb.Table;\n};\n\n/**\n * Check if DynamoDB table exists\n */\nasync function tableExists(tableName: string): Promise<boolean> {\n try {\n const { DynamoDBClient, DescribeTableCommand } = await import(\n \"@aws-sdk/client-dynamodb\"\n );\n const dynamodb = new DynamoDBClient({\n region: process.env.AWS_REGION || \"us-east-1\",\n });\n\n await dynamodb.send(new DescribeTableCommand({ TableName: tableName }));\n return true;\n } catch (error: any) {\n if (error.name === \"ResourceNotFoundException\") {\n return false;\n }\n console.error(\"Error checking for existing DynamoDB table:\", error);\n return false;\n }\n}\n\n/**\n * Create DynamoDB tables for email tracking\n */\nexport async function createDynamoDBTables(\n _config?: DynamoDBConfig\n): Promise<DynamoDBTables> {\n // Check if table already exists\n const tableName = \"wraps-email-history\";\n const exists = await tableExists(tableName);\n\n // Email history table (TTL is set based on retention in Lambda via expiresAt field)\n // Note: retention config is passed but TTL is actually managed by Lambda setting expiresAt\n const emailHistory = exists\n ? new aws.dynamodb.Table(\n tableName,\n {\n name: tableName,\n billingMode: \"PAY_PER_REQUEST\",\n hashKey: \"messageId\",\n rangeKey: \"sentAt\",\n attributes: [\n { name: \"messageId\", type: \"S\" },\n { name: \"sentAt\", type: \"N\" },\n { name: \"accountId\", type: \"S\" },\n ],\n globalSecondaryIndexes: [\n {\n name: \"accountId-sentAt-index\",\n hashKey: \"accountId\",\n rangeKey: \"sentAt\",\n projectionType: \"ALL\",\n },\n ],\n ttl: {\n enabled: true,\n attributeName: \"expiresAt\",\n },\n tags: {\n ManagedBy: \"wraps-cli\",\n },\n },\n {\n import: tableName, // Import existing table\n }\n )\n : new aws.dynamodb.Table(tableName, {\n name: tableName,\n billingMode: \"PAY_PER_REQUEST\",\n hashKey: \"messageId\",\n rangeKey: \"sentAt\",\n attributes: [\n { name: \"messageId\", type: \"S\" },\n { name: \"sentAt\", type: \"N\" },\n { name: \"accountId\", type: \"S\" },\n ],\n globalSecondaryIndexes: [\n {\n name: \"accountId-sentAt-index\",\n hashKey: \"accountId\",\n rangeKey: \"sentAt\",\n projectionType: \"ALL\",\n },\n ],\n ttl: {\n enabled: true,\n attributeName: \"expiresAt\",\n },\n tags: {\n ManagedBy: \"wraps-cli\",\n },\n });\n\n return {\n emailHistory,\n };\n}\n","import * as aws from \"@pulumi/aws\";\nimport * as pulumi from \"@pulumi/pulumi\";\n\n/**\n * EventBridge resources configuration\n */\nexport type EventBridgeConfig = {\n eventBusArn: pulumi.Output<string>;\n queueArn: pulumi.Output<string>;\n queueUrl: pulumi.Output<string>;\n};\n\n/**\n * EventBridge resources output\n */\nexport type EventBridgeResources = {\n rule: aws.cloudwatch.EventRule;\n target: aws.cloudwatch.EventTarget;\n};\n\n/**\n * Create EventBridge rule to route SES events to SQS queue\n *\n * This rule captures all SES events from the default event bus\n * and routes them to the SQS queue for processing.\n *\n * Note: SES can only send to the default EventBridge bus, not custom buses.\n */\nexport async function createEventBridgeResources(\n config: EventBridgeConfig\n): Promise<EventBridgeResources> {\n // Extract event bus name from ARN (will be \"default\" for SES)\n const eventBusName = config.eventBusArn.apply((arn) => arn.split(\"/\").pop()!);\n\n // EventBridge rule to capture all SES events on default bus\n const rule = new aws.cloudwatch.EventRule(\"wraps-email-events-rule\", {\n name: \"wraps-email-events-to-sqs\",\n description: \"Route all SES email events to SQS for processing\",\n eventBusName,\n eventPattern: JSON.stringify({\n source: [\"aws.ses\"],\n // SES sends events with various detail-types based on event type\n // We capture all by not filtering on detail-type\n }),\n tags: {\n ManagedBy: \"wraps-cli\",\n },\n });\n\n // SQS queue policy to allow EventBridge to send messages\n new aws.sqs.QueuePolicy(\"wraps-email-events-queue-policy\", {\n queueUrl: config.queueUrl,\n policy: pulumi\n .all([config.queueArn, rule.arn])\n .apply(([queueArn, ruleArn]) =>\n JSON.stringify({\n Version: \"2012-10-17\",\n Statement: [\n {\n Effect: \"Allow\",\n Principal: {\n Service: \"events.amazonaws.com\",\n },\n Action: \"sqs:SendMessage\",\n Resource: queueArn,\n Condition: {\n ArnEquals: {\n \"aws:SourceArn\": ruleArn,\n },\n },\n },\n ],\n })\n ),\n });\n\n // EventBridge target to send events to SQS\n const target = new aws.cloudwatch.EventTarget(\"wraps-email-events-target\", {\n rule: rule.name,\n eventBusName,\n arn: config.queueArn,\n });\n\n return {\n rule,\n target,\n };\n}\n","import * as aws from \"@pulumi/aws\";\nimport * as pulumi from \"@pulumi/pulumi\";\nimport type { Provider, WrapsEmailConfig } from \"../../types/index.js\";\n\n/**\n * IAM role configuration\n */\nexport type IAMRoleConfig = {\n provider: Provider;\n oidcProvider?: aws.iam.OpenIdConnectProvider;\n vercelTeamSlug?: string;\n vercelProjectName?: string;\n emailConfig: WrapsEmailConfig;\n};\n\n/**\n * Check if IAM role exists\n */\nasync function roleExists(roleName: string): Promise<boolean> {\n try {\n const { IAMClient, GetRoleCommand } = await import(\"@aws-sdk/client-iam\");\n // IAM is global but SDK still requires a region\n const iam = new IAMClient({\n region: process.env.AWS_REGION || \"us-east-1\",\n });\n\n await iam.send(new GetRoleCommand({ RoleName: roleName }));\n return true;\n } catch (error: any) {\n if (error.name === \"NoSuchEntityException\") {\n return false;\n }\n console.error(\"Error checking for existing IAM role:\", error);\n return false;\n }\n}\n\n/**\n * Create IAM role for email infrastructure\n */\nexport async function createIAMRole(\n config: IAMRoleConfig\n): Promise<aws.iam.Role> {\n // Build assume role policy based on provider\n let assumeRolePolicy: pulumi.Output<string>;\n\n if (config.provider === \"vercel\" && config.oidcProvider) {\n assumeRolePolicy = pulumi.interpolate`{\n \"Version\": \"2012-10-17\",\n \"Statement\": [{\n \"Effect\": \"Allow\",\n \"Principal\": {\n \"Federated\": \"${config.oidcProvider.arn}\"\n },\n \"Action\": \"sts:AssumeRoleWithWebIdentity\",\n \"Condition\": {\n \"StringEquals\": {\n \"oidc.vercel.com/${config.vercelTeamSlug}:aud\": \"https://vercel.com/${config.vercelTeamSlug}\"\n },\n \"StringLike\": {\n \"oidc.vercel.com/${config.vercelTeamSlug}:sub\": \"owner:${config.vercelTeamSlug}:project:${config.vercelProjectName}:environment:*\"\n }\n }\n }]\n }`;\n } else if (config.provider === \"aws\") {\n // Native AWS - EC2, Lambda, ECS can assume\n assumeRolePolicy = pulumi.output(`{\n \"Version\": \"2012-10-17\",\n \"Statement\": [{\n \"Effect\": \"Allow\",\n \"Principal\": {\n \"Service\": [\"lambda.amazonaws.com\", \"ec2.amazonaws.com\", \"ecs-tasks.amazonaws.com\"]\n },\n \"Action\": \"sts:AssumeRole\"\n }]\n }`);\n } else {\n // Other providers - will use access keys\n throw new Error(\"Other providers not yet implemented\");\n }\n\n // Check if role already exists\n const roleName = \"wraps-email-role\";\n const exists = await roleExists(roleName);\n\n const role = exists\n ? new aws.iam.Role(\n roleName,\n {\n name: roleName,\n assumeRolePolicy,\n tags: {\n ManagedBy: \"wraps-cli\",\n Provider: config.provider,\n },\n },\n {\n import: roleName, // Import existing role (use role name, not ARN)\n }\n )\n : new aws.iam.Role(roleName, {\n name: roleName,\n assumeRolePolicy,\n tags: {\n ManagedBy: \"wraps-cli\",\n Provider: config.provider,\n },\n });\n\n // Build policy statements based on enabled features\n const statements: any[] = [];\n\n // Always allow reading SES metrics for dashboard\n statements.push({\n Effect: \"Allow\",\n Action: [\n \"ses:GetSendStatistics\",\n \"ses:ListIdentities\",\n \"ses:GetIdentityVerificationAttributes\",\n \"cloudwatch:GetMetricData\",\n \"cloudwatch:GetMetricStatistics\",\n ],\n Resource: \"*\",\n });\n\n // Allow sending if enabled\n if (config.emailConfig.sendingEnabled !== false) {\n statements.push({\n Effect: \"Allow\",\n Action: [\n \"ses:SendEmail\",\n \"ses:SendRawEmail\",\n \"ses:SendTemplatedEmail\",\n \"ses:SendBulkTemplatedEmail\",\n ],\n Resource: \"*\",\n });\n }\n\n // Allow DynamoDB access if history storage enabled\n if (config.emailConfig.eventTracking?.dynamoDBHistory) {\n statements.push({\n Effect: \"Allow\",\n Action: [\n \"dynamodb:PutItem\",\n \"dynamodb:GetItem\",\n \"dynamodb:Query\",\n \"dynamodb:Scan\",\n \"dynamodb:BatchGetItem\",\n \"dynamodb:DescribeTable\",\n ],\n Resource: [\n \"arn:aws:dynamodb:*:*:table/wraps-email-*\",\n \"arn:aws:dynamodb:*:*:table/wraps-email-*/index/*\",\n ],\n });\n }\n\n // Allow EventBridge access if event tracking enabled\n if (config.emailConfig.eventTracking?.enabled) {\n statements.push({\n Effect: \"Allow\",\n Action: [\"events:PutEvents\", \"events:DescribeEventBus\"],\n Resource: \"arn:aws:events:*:*:event-bus/wraps-email-*\",\n });\n }\n\n // Allow SQS access if event tracking enabled\n if (config.emailConfig.eventTracking?.enabled) {\n statements.push({\n Effect: \"Allow\",\n Action: [\n \"sqs:SendMessage\",\n \"sqs:ReceiveMessage\",\n \"sqs:DeleteMessage\",\n \"sqs:GetQueueAttributes\",\n ],\n Resource: \"arn:aws:sqs:*:*:wraps-email-*\",\n });\n }\n\n // Allow Mail Manager Archive access if email archiving enabled\n if (config.emailConfig.emailArchiving?.enabled) {\n statements.push({\n Effect: \"Allow\",\n Action: [\n // Archive search operations\n \"ses:StartArchiveSearch\",\n \"ses:GetArchiveSearchResults\",\n // Archive message retrieval\n \"ses:GetArchiveMessage\",\n \"ses:GetArchiveMessageContent\",\n // Archive metadata\n \"ses:GetArchive\",\n \"ses:ListArchives\",\n // Archive export (for future use)\n \"ses:StartArchiveExport\",\n \"ses:GetArchiveExport\",\n ],\n Resource: \"arn:aws:ses:*:*:mailmanager-archive/*\",\n });\n }\n\n // Attach policy to role\n new aws.iam.RolePolicy(\"wraps-email-policy\", {\n role: role.name,\n policy: JSON.stringify({\n Version: \"2012-10-17\",\n Statement: statements,\n }),\n });\n\n return role;\n}\n","import { randomBytes } from \"node:crypto\";\nimport { existsSync, mkdirSync } from \"node:fs\";\nimport { tmpdir } from \"node:os\";\nimport { dirname, join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport * as aws from \"@pulumi/aws\";\nimport * as pulumi from \"@pulumi/pulumi\";\nimport { build } from \"esbuild\";\n\n/**\n * Get the package root directory (where package.json lives)\n * Works both in development (src/) and production (dist/)\n */\nfunction getPackageRoot(): string {\n const currentFile = fileURLToPath(import.meta.url);\n let dir = dirname(currentFile);\n\n // Walk up the directory tree until we find package.json\n while (dir !== dirname(dir)) {\n if (existsSync(join(dir, \"package.json\"))) {\n return dir;\n }\n dir = dirname(dir);\n }\n\n throw new Error(\"Could not find package.json\");\n}\n\n/**\n * Lambda configuration\n */\nexport type LambdaConfig = {\n roleArn: pulumi.Output<string>;\n tableName: pulumi.Output<string>;\n queueArn: pulumi.Output<string>;\n accountId: string;\n region: string;\n};\n\n/**\n * Lambda functions output\n */\nexport type LambdaFunctions = {\n eventProcessor: aws.lambda.Function;\n eventSourceMapping: aws.lambda.EventSourceMapping;\n};\n\n/**\n * Check if Lambda function exists\n */\nasync function lambdaFunctionExists(functionName: string): Promise<boolean> {\n try {\n const { LambdaClient, GetFunctionCommand } = await import(\n \"@aws-sdk/client-lambda\"\n );\n const lambda = new LambdaClient({\n region: process.env.AWS_REGION || \"us-east-1\",\n });\n\n await lambda.send(new GetFunctionCommand({ FunctionName: functionName }));\n return true;\n } catch (error: any) {\n if (error.name === \"ResourceNotFoundException\") {\n return false;\n }\n console.error(\"Error checking for existing Lambda function:\", error);\n return false;\n }\n}\n\n/**\n * Find existing event source mapping for Lambda function and SQS queue\n */\nasync function findEventSourceMapping(\n functionName: string,\n queueArn: string\n): Promise<string | null> {\n try {\n const { LambdaClient, ListEventSourceMappingsCommand } = await import(\n \"@aws-sdk/client-lambda\"\n );\n const lambda = new LambdaClient({\n region: process.env.AWS_REGION || \"us-east-1\",\n });\n\n const response = await lambda.send(\n new ListEventSourceMappingsCommand({\n FunctionName: functionName,\n EventSourceArn: queueArn,\n })\n );\n\n // Return UUID of the first matching event source mapping\n return response.EventSourceMappings?.[0]?.UUID || null;\n } catch (error: any) {\n console.error(\"Error finding event source mapping:\", error);\n return null;\n }\n}\n\n/**\n * Get the Lambda function code directory\n *\n * In production (published package), uses pre-bundled code from dist/lambda/\n * In development, bundles the TypeScript source on-the-fly\n */\nasync function getLambdaCode(functionName: string): Promise<string> {\n const packageRoot = getPackageRoot();\n\n // Check for pre-bundled Lambda code in dist/ (production - published package)\n const distLambdaPath = join(packageRoot, \"dist\", \"lambda\", functionName);\n const distBundleMarker = join(distLambdaPath, \".bundled\");\n\n if (existsSync(distBundleMarker)) {\n // Use pre-bundled code from dist/\n return distLambdaPath;\n }\n\n // Check for pre-bundled Lambda code in lambda/ (development build)\n const lambdaPath = join(packageRoot, \"lambda\", functionName);\n const lambdaBundleMarker = join(lambdaPath, \".bundled\");\n\n if (existsSync(lambdaBundleMarker)) {\n // Use pre-bundled code from lambda/\n return lambdaPath;\n }\n\n // Development mode: bundle on-the-fly from TypeScript source\n const sourcePath = join(lambdaPath, \"index.ts\");\n\n if (!existsSync(sourcePath)) {\n throw new Error(\n `Lambda source not found: ${sourcePath}\\n` +\n `This usually means the build process didn't complete successfully.\\n` +\n \"Try running: pnpm build\"\n );\n }\n\n const buildId = randomBytes(8).toString(\"hex\");\n const outdir = join(tmpdir(), `wraps-lambda-${buildId}`);\n\n if (!existsSync(outdir)) {\n mkdirSync(outdir, { recursive: true });\n }\n\n // Bundle with esbuild\n await build({\n entryPoints: [sourcePath],\n bundle: true,\n platform: \"node\",\n target: \"node24\",\n format: \"esm\",\n outfile: join(outdir, \"index.mjs\"),\n external: [\"@aws-sdk/*\"], // AWS SDK v3 is included in Lambda runtime\n minify: true,\n sourcemap: false,\n });\n\n return outdir;\n}\n\n/**\n * Deploy Lambda functions for email event processing\n *\n * Architecture:\n * SQS Queue -> Lambda (event-processor) -> DynamoDB\n *\n * The Lambda function is triggered by SQS via Event Source Mapping.\n * Failed messages are automatically sent to the DLQ after 3 retries.\n */\nexport async function deployLambdaFunctions(\n config: LambdaConfig\n): Promise<LambdaFunctions> {\n // Get Lambda code directory (pre-bundled in production, bundled on-the-fly in dev)\n const eventProcessorCode = await getLambdaCode(\"event-processor\");\n\n // IAM role for Lambda execution\n const lambdaRole = new aws.iam.Role(\"wraps-email-lambda-role\", {\n assumeRolePolicy: JSON.stringify({\n Version: \"2012-10-17\",\n Statement: [\n {\n Effect: \"Allow\",\n Principal: { Service: \"lambda.amazonaws.com\" },\n Action: \"sts:AssumeRole\",\n },\n ],\n }),\n tags: {\n ManagedBy: \"wraps-cli\",\n },\n });\n\n // Attach basic Lambda execution policy\n new aws.iam.RolePolicyAttachment(\"wraps-email-lambda-basic-execution\", {\n role: lambdaRole.name,\n policyArn:\n \"arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole\",\n });\n\n // Lambda policy for DynamoDB and SQS\n new aws.iam.RolePolicy(\"wraps-email-lambda-policy\", {\n role: lambdaRole.name,\n policy: pulumi\n .all([config.tableName, config.queueArn])\n .apply(([tableName, queueArn]) =>\n JSON.stringify({\n Version: \"2012-10-17\",\n Statement: [\n {\n // DynamoDB access\n Effect: \"Allow\",\n Action: [\n \"dynamodb:PutItem\",\n \"dynamodb:GetItem\",\n \"dynamodb:Query\",\n \"dynamodb:Scan\",\n \"dynamodb:UpdateItem\",\n ],\n Resource: [\n `arn:aws:dynamodb:*:*:table/${tableName}`,\n `arn:aws:dynamodb:*:*:table/${tableName}/index/*`,\n ],\n },\n {\n // SQS access for event source mapping\n Effect: \"Allow\",\n Action: [\n \"sqs:ReceiveMessage\",\n \"sqs:DeleteMessage\",\n \"sqs:GetQueueAttributes\",\n ],\n Resource: queueArn,\n },\n ],\n })\n ),\n });\n\n // Check if Lambda function already exists\n const functionName = \"wraps-email-event-processor\";\n const exists = await lambdaFunctionExists(functionName);\n\n // Create event-processor Lambda\n const eventProcessor = exists\n ? new aws.lambda.Function(\n functionName,\n {\n name: functionName,\n runtime: \"nodejs24.x\",\n handler: \"index.handler\",\n role: lambdaRole.arn,\n code: new pulumi.asset.FileArchive(eventProcessorCode),\n timeout: 300, // 5 minutes (matches SQS visibility timeout)\n memorySize: 512,\n environment: {\n variables: {\n TABLE_NAME: config.tableName,\n AWS_ACCOUNT_ID: config.accountId,\n },\n },\n tags: {\n ManagedBy: \"wraps-cli\",\n Description:\n \"Process SES email events from SQS and store in DynamoDB\",\n },\n },\n {\n import: functionName, // Import existing function\n }\n )\n : new aws.lambda.Function(functionName, {\n name: functionName,\n runtime: \"nodejs24.x\",\n handler: \"index.handler\",\n role: lambdaRole.arn,\n code: new pulumi.asset.FileArchive(eventProcessorCode),\n timeout: 300, // 5 minutes (matches SQS visibility timeout)\n memorySize: 512,\n environment: {\n variables: {\n TABLE_NAME: config.tableName,\n AWS_ACCOUNT_ID: config.accountId,\n },\n },\n tags: {\n ManagedBy: \"wraps-cli\",\n Description:\n \"Process SES email events from SQS and store in DynamoDB\",\n },\n });\n\n // Check if event source mapping already exists\n // Construct the queue ARN from the known queue name, region, and account ID\n const queueArnValue = `arn:aws:sqs:${config.region}:${config.accountId}:wraps-email-events`;\n const existingMappingUuid = await findEventSourceMapping(\n functionName,\n queueArnValue\n );\n\n // Create SQS event source mapping for Lambda\n // This automatically polls SQS and invokes the Lambda function\n const mappingConfig = {\n eventSourceArn: config.queueArn,\n functionName: eventProcessor.name,\n batchSize: 10, // Process up to 10 messages per invocation\n maximumBatchingWindowInSeconds: 5, // Wait up to 5 seconds to batch messages\n functionResponseTypes: [\"ReportBatchItemFailures\"], // Enable partial batch responses\n };\n\n const eventSourceMapping = existingMappingUuid\n ? new aws.lambda.EventSourceMapping(\n \"wraps-email-event-source-mapping\",\n mappingConfig,\n {\n import: existingMappingUuid, // Import with the UUID\n }\n )\n : new aws.lambda.EventSourceMapping(\n \"wraps-email-event-source-mapping\",\n mappingConfig\n );\n\n return {\n eventProcessor,\n eventSourceMapping,\n };\n}\n","import * as aws from \"@pulumi/aws\";\nimport type { SESEventType } from \"../../types/index.js\";\n\n/**\n * SES resources configuration\n */\nexport type SESResourcesConfig = {\n domain?: string;\n mailFromDomain?: string;\n region: string;\n trackingConfig?: {\n enabled: boolean;\n opens?: boolean;\n clicks?: boolean;\n customRedirectDomain?: string;\n httpsEnabled?: boolean;\n };\n eventTypes?: SESEventType[];\n eventTrackingEnabled?: boolean; // NEW: Whether to create EventBridge event destination\n tlsRequired?: boolean; // Require TLS encryption for all emails\n importExistingEventDestination?: boolean; // Import existing event destination if it exists\n};\n\n/**\n * Check if SES configuration set exists\n */\nasync function configurationSetExists(\n configSetName: string,\n region: string\n): Promise<boolean> {\n try {\n const { SESv2Client, GetConfigurationSetCommand } = await import(\n \"@aws-sdk/client-sesv2\"\n );\n const ses = new SESv2Client({ region });\n\n await ses.send(\n new GetConfigurationSetCommand({ ConfigurationSetName: configSetName })\n );\n return true;\n } catch (error: any) {\n if (error.name === \"NotFoundException\") {\n return false;\n }\n console.error(\"Error checking for existing configuration set:\", error);\n return false;\n }\n}\n\n/**\n * Check if event destination exists for a configuration set\n */\nexport async function eventDestinationExists(\n configSetName: string,\n eventDestName: string,\n region: string\n): Promise<boolean> {\n try {\n const { SESv2Client, GetConfigurationSetEventDestinationsCommand } =\n await import(\"@aws-sdk/client-sesv2\");\n const ses = new SESv2Client({ region });\n\n const response = await ses.send(\n new GetConfigurationSetEventDestinationsCommand({\n ConfigurationSetName: configSetName,\n })\n );\n\n return (\n response.EventDestinations?.some((dest) => dest.Name === eventDestName) ??\n false\n );\n } catch (error: any) {\n if (error.name === \"NotFoundException\") {\n return false;\n }\n // Silently return false on other errors - we'll try to create and handle errors there\n return false;\n }\n}\n\n/**\n * Check if email identity exists\n */\nasync function emailIdentityExists(\n emailIdentity: string,\n region: string\n): Promise<boolean> {\n try {\n const { SESv2Client, GetEmailIdentityCommand } = await import(\n \"@aws-sdk/client-sesv2\"\n );\n const ses = new SESv2Client({ region });\n\n await ses.send(\n new GetEmailIdentityCommand({ EmailIdentity: emailIdentity })\n );\n return true;\n } catch (error: any) {\n if (error.name === \"NotFoundException\") {\n return false;\n }\n console.error(\"Error checking for existing email identity:\", error);\n return false;\n }\n}\n\n/**\n * SES resources output\n */\nexport type SESResources = {\n configSet: aws.sesv2.ConfigurationSet;\n eventBus: aws.cloudwatch.EventBus;\n domainIdentity?: aws.sesv2.EmailIdentity;\n dkimTokens?: string[];\n dnsAutoCreated?: boolean;\n customTrackingDomain?: string;\n mailFromDomain?: string;\n};\n\n/**\n * Create SES resources (configuration set, EventBridge event bus, domain identity)\n */\nexport async function createSESResources(\n config: SESResourcesConfig\n): Promise<SESResources> {\n // Configuration set for tracking (using SESv2 which supports tags)\n const configSetOptions: aws.sesv2.ConfigurationSetArgs = {\n configurationSetName: \"wraps-email-tracking\",\n deliveryOptions: config.tlsRequired\n ? {\n tlsPolicy: \"REQUIRE\", // Require TLS 1.2+ for all emails\n }\n : undefined,\n suppressionOptions: {\n // Automatically suppress hard bounces and complaints at the configuration set level\n // This provides protection even if account-level suppression isn't configured\n suppressedReasons: [\"BOUNCE\", \"COMPLAINT\"],\n },\n tags: {\n ManagedBy: \"wraps-cli\",\n Description: \"Wraps email tracking configuration set\",\n },\n };\n\n // Add custom tracking domain if provided\n // Note: The tracking domain only needs a CNAME DNS record\n // - Without HTTPS: CNAME points to r.{region}.awstrack.me\n // - With HTTPS: CNAME points to CloudFront distribution domain\n if (config.trackingConfig?.customRedirectDomain) {\n configSetOptions.trackingOptions = {\n customRedirectDomain: config.trackingConfig.customRedirectDomain,\n // HTTPS policy depends on whether HTTPS tracking is enabled\n // - REQUIRE: When using CloudFront with SSL certificate\n // - OPTIONAL: When using direct SES tracking endpoint (no SSL)\n httpsPolicy: config.trackingConfig.httpsEnabled ? \"REQUIRE\" : \"OPTIONAL\",\n };\n }\n\n // Check if configuration set already exists\n const configSetName = \"wraps-email-tracking\";\n const exists = await configurationSetExists(configSetName, config.region);\n\n const configSet = exists\n ? new aws.sesv2.ConfigurationSet(configSetName, configSetOptions, {\n import: configSetName, // Import existing configuration set\n })\n : new aws.sesv2.ConfigurationSet(configSetName, configSetOptions);\n\n // SES can only send to the default EventBridge bus\n // We'll use EventBridge rules to route from default bus to SQS\n // Get the default event bus (it always exists)\n const defaultEventBus = aws.cloudwatch.getEventBusOutput({\n name: \"default\",\n });\n\n // Event destination for all SES events -> EventBridge (default bus)\n // Only create if event tracking is enabled\n if (config.eventTrackingEnabled) {\n const eventDestName = \"wraps-email-eventbridge\";\n\n new aws.sesv2.ConfigurationSetEventDestination(\n \"wraps-email-all-events\",\n {\n configurationSetName: configSet.configurationSetName,\n eventDestinationName: eventDestName,\n eventDestination: {\n enabled: true,\n matchingEventTypes: [\n \"SEND\",\n \"DELIVERY\",\n \"OPEN\",\n \"CLICK\",\n \"BOUNCE\",\n \"COMPLAINT\",\n \"REJECT\",\n \"RENDERING_FAILURE\",\n \"DELIVERY_DELAY\",\n \"SUBSCRIPTION\",\n ],\n eventBridgeDestination: {\n // SES requires default bus - cannot use custom bus\n eventBusArn: defaultEventBus.arn,\n },\n },\n },\n {\n // Import existing resource if it already exists in AWS\n // This prevents AlreadyExistsException when the resource exists but isn't in Pulumi state\n import: config.importExistingEventDestination\n ? `wraps-email-tracking|${eventDestName}`\n : undefined,\n }\n );\n }\n\n // Optional: Verify domain if provided\n let domainIdentity: aws.sesv2.EmailIdentity | undefined;\n let dkimTokens: string[] | undefined;\n let mailFromDomain: string | undefined;\n\n if (config.domain) {\n // Check if email identity already exists\n const identityExists = await emailIdentityExists(\n config.domain,\n config.region\n );\n\n // Use SES v2 API to create email identity with configuration set\n domainIdentity = identityExists\n ? new aws.sesv2.EmailIdentity(\n \"wraps-email-domain\",\n {\n emailIdentity: config.domain,\n configurationSetName: configSet.configurationSetName, // Link configuration set to domain\n dkimSigningAttributes: {\n nextSigningKeyLength: \"RSA_2048_BIT\",\n },\n tags: {\n ManagedBy: \"wraps-cli\",\n },\n },\n {\n import: config.domain, // Import existing identity\n }\n )\n : new aws.sesv2.EmailIdentity(\"wraps-email-domain\", {\n emailIdentity: config.domain,\n configurationSetName: configSet.configurationSetName, // Link configuration set to domain\n dkimSigningAttributes: {\n nextSigningKeyLength: \"RSA_2048_BIT\",\n },\n tags: {\n ManagedBy: \"wraps-cli\",\n },\n });\n\n // Extract DKIM tokens for DNS configuration\n dkimTokens = domainIdentity.dkimSigningAttributes.apply(\n (attrs) => attrs?.tokens || []\n ) as any;\n\n // Configure MAIL FROM domain for better DMARC alignment (only if explicitly configured)\n if (config.mailFromDomain) {\n mailFromDomain = config.mailFromDomain;\n\n // Create/update MAIL FROM attributes\n // Note: This resource doesn't support import, but it will update existing config\n new aws.sesv2.EmailIdentityMailFromAttributes(\n \"wraps-email-mail-from\",\n {\n emailIdentity: config.domain,\n mailFromDomain,\n behaviorOnMxFailure: \"USE_DEFAULT_VALUE\", // Fallback to amazonses.com if MX record fails\n },\n {\n dependsOn: [domainIdentity], // Ensure domain identity exists first\n }\n );\n }\n }\n\n return {\n configSet,\n eventBus: defaultEventBus as any, // Return default bus reference\n domainIdentity,\n dkimTokens,\n dnsAutoCreated: false, // Will be set after deployment\n customTrackingDomain: config.trackingConfig?.customRedirectDomain,\n mailFromDomain,\n };\n}\n","import * as aws from \"@pulumi/aws\";\n\n/**\n * SQS resources output\n */\nexport type SQSResources = {\n queue: aws.sqs.Queue;\n dlq: aws.sqs.Queue;\n};\n\n/**\n * Create SQS queue with Dead Letter Queue for event processing\n *\n * Architecture:\n * EventBridge -> SQS Queue -> Lambda (event-processor)\n * ↓\n * DLQ (failed messages after 3 retries)\n */\nexport async function createSQSResources(): Promise<SQSResources> {\n // Dead Letter Queue for failed event processing\n const dlq = new aws.sqs.Queue(\"wraps-email-events-dlq\", {\n name: \"wraps-email-events-dlq\",\n messageRetentionSeconds: 1_209_600, // 14 days\n tags: {\n ManagedBy: \"wraps-cli\",\n Description: \"Dead letter queue for failed SES event processing\",\n },\n });\n\n // Main queue for SES events\n const queue = new aws.sqs.Queue(\"wraps-email-events\", {\n name: \"wraps-email-events\",\n visibilityTimeoutSeconds: 300, // 5 minutes (Lambda timeout)\n messageRetentionSeconds: 345_600, // 4 days\n receiveWaitTimeSeconds: 20, // Long polling\n redrivePolicy: dlq.arn.apply((arn) =>\n JSON.stringify({\n deadLetterTargetArn: arn,\n maxReceiveCount: 3, // Retry 3 times before sending to DLQ\n })\n ),\n tags: {\n ManagedBy: \"wraps-cli\",\n Description: \"Queue for SES email events from EventBridge\",\n },\n });\n\n return {\n queue,\n dlq,\n };\n}\n","import * as aws from \"@pulumi/aws\";\n\n/**\n * Vercel OIDC configuration\n */\nexport type VercelOIDCConfig = {\n teamSlug: string;\n accountId: string;\n};\n\n/**\n * Get existing OIDC provider ARN by URL\n */\nasync function getExistingOIDCProviderArn(url: string): Promise<string | null> {\n try {\n const { IAMClient, ListOpenIDConnectProvidersCommand } = await import(\n \"@aws-sdk/client-iam\"\n );\n // IAM is global but SDK still requires a region\n const iam = new IAMClient({\n region: process.env.AWS_REGION || \"us-east-1\",\n });\n\n const response = await iam.send(new ListOpenIDConnectProvidersCommand({}));\n\n // Find provider by ARN pattern (ARN includes the URL)\n // Format: arn:aws:iam::ACCOUNT:oidc-provider/oidc.vercel.com/TEAM\n const expectedArnSuffix = url.replace(\"https://\", \"\");\n const provider = response.OpenIDConnectProviderList?.find((p) =>\n p.Arn?.endsWith(expectedArnSuffix)\n );\n\n return provider?.Arn || null;\n } catch (error) {\n console.error(\"Error checking for existing OIDC provider:\", error);\n return null;\n }\n}\n\n/**\n * Create or get existing Vercel OIDC provider for AssumeRoleWithWebIdentity\n */\nexport async function createVercelOIDC(\n config: VercelOIDCConfig\n): Promise<aws.iam.OpenIdConnectProvider> {\n const url = `https://oidc.vercel.com/${config.teamSlug}`;\n\n // Check if OIDC provider already exists\n const existingArn = await getExistingOIDCProviderArn(url);\n\n if (existingArn) {\n // Import existing OIDC provider instead of creating new one\n return new aws.iam.OpenIdConnectProvider(\n \"wraps-vercel-oidc\",\n {\n url,\n clientIdLists: [`https://vercel.com/${config.teamSlug}`],\n thumbprintLists: [\n // Vercel OIDC thumbprints\n \"20032e77eca0785eece16b56b42c9b330b906320\",\n \"696db3af0dffc17e65c6a20d925c5a7bd24dec7e\",\n ],\n tags: {\n ManagedBy: \"wraps-cli\",\n Provider: \"vercel\",\n },\n },\n {\n import: existingArn, // Import existing resource\n }\n );\n }\n\n // Create new OIDC provider if it doesn't exist\n return new aws.iam.OpenIdConnectProvider(\"wraps-vercel-oidc\", {\n url,\n clientIdLists: [`https://vercel.com/${config.teamSlug}`],\n thumbprintLists: [\n // Vercel OIDC thumbprints\n \"20032e77eca0785eece16b56b42c9b330b906320\",\n \"696db3af0dffc17e65c6a20d925c5a7bd24dec7e\",\n ],\n tags: {\n ManagedBy: \"wraps-cli\",\n Provider: \"vercel\",\n },\n });\n}\n","import { exec } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport * as automation from \"@pulumi/pulumi/automation/index.js\";\nimport { errors } from \"./errors.js\";\n\nconst execAsync = promisify(exec);\n\n// Extract installPulumiCli from the automation module\nconst installPulumiCli = (automation as any).installPulumiCli;\n\n/**\n * Check if Pulumi CLI is installed\n */\nexport async function checkPulumiInstalled(): Promise<boolean> {\n try {\n await execAsync(\"pulumi version\");\n return true;\n } catch (_error) {\n return false;\n }\n}\n\n/**\n * Ensure Pulumi CLI is installed, auto-install if missing\n * @returns true if Pulumi was auto-installed, false if it was already installed\n */\nexport async function ensurePulumiInstalled(): Promise<boolean> {\n const isInstalled = await checkPulumiInstalled();\n\n if (!isInstalled) {\n try {\n // Try to auto-install Pulumi CLI using Automation API\n await installPulumiCli();\n return true; // Was auto-installed\n } catch (_error) {\n // If auto-install fails, throw helpful error\n throw errors.pulumiNotInstalled();\n }\n }\n\n return false; // Was already installed\n}\n","import * as clack from \"@clack/prompts\";\nimport * as pulumi from \"@pulumi/pulumi\";\nimport pc from \"picocolors\";\nimport { deployEmailStack } from \"../../infrastructure/email-stack.js\";\nimport {\n trackError,\n trackServiceDeployed,\n trackServiceInit,\n} from \"../../telemetry/events.js\";\nimport type { ConnectOptions, EmailStackConfig } from \"../../types/index.js\";\nimport { getPreset } from \"../../utils/email/presets.js\";\nimport {\n getAWSRegion,\n validateAWSCredentials,\n} from \"../../utils/shared/aws.js\";\nimport { errors } from \"../../utils/shared/errors.js\";\nimport {\n ensurePulumiWorkDir,\n getPulumiWorkDir,\n} from \"../../utils/shared/fs.js\";\nimport {\n createConnectionMetadata,\n loadConnectionMetadata,\n saveConnectionMetadata,\n} from \"../../utils/shared/metadata.js\";\nimport {\n DeploymentProgress,\n displayPreview,\n displaySuccess,\n} from \"../../utils/shared/output.js\";\nimport {\n confirmConnect,\n promptConfigPreset,\n promptProvider,\n promptRegion,\n promptSelectIdentities,\n promptVercelConfig,\n} from \"../../utils/shared/prompts.js\";\nimport { ensurePulumiInstalled } from \"../../utils/shared/pulumi.js\";\nimport { scanAWSResources } from \"../../utils/shared/scanner.js\";\n\n/**\n * Connect command - Connect to existing AWS SES infrastructure\n */\nexport async function connect(options: ConnectOptions): Promise<void> {\n const startTime = Date.now();\n\n clack.intro(\n pc.bold(\n options.preview\n ? \"Wraps Connect Preview\"\n : \"Wraps Connect - Link Existing Infrastructure\"\n )\n );\n\n const progress = new DeploymentProgress();\n\n // 1. Check Pulumi CLI is installed\n const wasAutoInstalled = await progress.execute(\n \"Checking Pulumi CLI installation\",\n async () => await ensurePulumiInstalled()\n );\n\n if (wasAutoInstalled) {\n progress.info(\"Pulumi CLI was automatically installed\");\n }\n\n // 2. Validate AWS credentials\n const identity = await progress.execute(\n \"Validating AWS credentials\",\n async () => validateAWSCredentials()\n );\n\n progress.info(`Connected to AWS account: ${pc.cyan(identity.accountId)}`);\n\n // 3. Get region\n let region = options.region;\n if (!region) {\n const defaultRegion = await getAWSRegion();\n region = await promptRegion(defaultRegion);\n }\n\n // 4. Check if connection already exists\n const existingConnection = await loadConnectionMetadata(\n identity.accountId,\n region\n );\n if (existingConnection) {\n clack.log.warn(\n `Connection already exists for account ${pc.cyan(identity.accountId)} in region ${pc.cyan(region)}`\n );\n clack.log.info(`Created: ${existingConnection.timestamp}`);\n clack.log.info(`Use ${pc.cyan(\"wraps status\")} to view current setup`);\n clack.log.info(`Use ${pc.cyan(\"wraps upgrade\")} to add more features`);\n process.exit(0);\n }\n\n // 5. Scan existing AWS resources\n const scan = await progress.execute(\n \"Scanning existing AWS resources\",\n async () => scanAWSResources(region)\n );\n\n // Display what we found\n progress.info(\n `Found: ${scan.identities.length} identities, ${scan.configurationSets.length} config sets`\n );\n\n // Check if any identities exist\n if (scan.identities.length === 0) {\n clack.log.warn(\"No SES identities found in this region.\");\n clack.log.info(\n `Use ${pc.cyan(\"wraps email init\")} to create new email infrastructure instead.`\n );\n process.exit(0);\n }\n\n // Show verified identities\n const verifiedIdentities = scan.identities.filter((id) => id.verified);\n if (verifiedIdentities.length > 0) {\n progress.info(\n `Verified identities: ${verifiedIdentities.map((id) => pc.cyan(id.name)).join(\", \")}`\n );\n }\n\n // 6. Get provider configuration\n let provider = options.provider;\n if (!provider) {\n provider = await promptProvider();\n }\n\n // Get Vercel config if needed\n let vercelConfig;\n if (provider === \"vercel\") {\n vercelConfig = await promptVercelConfig();\n }\n\n // 7. Select identities to connect\n const selectedIdentities = await promptSelectIdentities(\n scan.identities.map((id) => ({\n name: id.name,\n verified: id.verified,\n }))\n );\n\n if (selectedIdentities.length === 0) {\n clack.log.warn(\"No identities selected. Nothing to connect.\");\n process.exit(0);\n }\n\n // 8. Select configuration preset\n const preset = await promptConfigPreset();\n const emailConfig =\n preset === \"custom\"\n ? await import(\"../../utils/shared/prompts.js\").then((m) =>\n m.promptCustomConfig()\n )\n : getPreset(preset)!;\n\n // 8a. Set the domain from the first selected identity (if it's a domain, not an email)\n // Filter to only domains (exclude email addresses)\n const domainIdentities = selectedIdentities.filter((id) => !id.includes(\"@\"));\n if (domainIdentities.length > 0) {\n emailConfig.domain = domainIdentities[0];\n }\n\n // 9. Confirm deployment (skip if --yes or --preview)\n if (!(options.yes || options.preview)) {\n const confirmed = await confirmConnect();\n if (!confirmed) {\n clack.cancel(\"Connection cancelled.\");\n process.exit(0);\n }\n }\n\n // 10. Build stack configuration\n const stackConfig: EmailStackConfig = {\n provider,\n region,\n vercel: vercelConfig,\n emailConfig,\n };\n\n // 11. Preview or Deploy infrastructure using Pulumi\n if (options.preview) {\n // PREVIEW MODE - show what would be created without deploying\n try {\n const previewResult = await progress.execute(\n \"Generating infrastructure preview\",\n async () => {\n await ensurePulumiWorkDir();\n\n const stack =\n await pulumi.automation.LocalWorkspace.createOrSelectStack(\n {\n stackName: `wraps-${identity.accountId}-${region}`,\n projectName: \"wraps-email\",\n program: async () => {\n const result = await deployEmailStack(stackConfig);\n return {\n roleArn: result.roleArn,\n configSetName: result.configSetName,\n tableName: result.tableName,\n region: result.region,\n lambdaFunctions: result.lambdaFunctions,\n domain: result.domain,\n dkimTokens: result.dkimTokens,\n customTrackingDomain: result.customTrackingDomain,\n };\n },\n },\n {\n workDir: getPulumiWorkDir(),\n envVars: {\n PULUMI_CONFIG_PASSPHRASE: \"\",\n AWS_REGION: region,\n },\n secretsProvider: \"passphrase\",\n }\n );\n\n await stack.setConfig(\"aws:region\", { value: region });\n\n // Run preview instead of deployment\n const result = await stack.preview({ diff: true });\n return result;\n }\n );\n\n // Display preview results\n displayPreview({\n changeSummary: previewResult.changeSummary,\n commandName: \"wraps email connect\",\n });\n\n clack.outro(\n pc.green(\"Preview complete. Run without --preview to connect.\")\n );\n\n // Track preview completion\n trackServiceInit(\"email\", true, {\n preset,\n provider,\n preview: true,\n duration_ms: Date.now() - startTime,\n existing_identities: selectedIdentities.length,\n });\n return;\n } catch (error: any) {\n trackError(\"PREVIEW_FAILED\", \"email:connect\", { step: \"preview\" });\n if (error.message?.includes(\"stack is currently locked\")) {\n throw errors.stackLocked();\n }\n throw new Error(`Preview failed: ${error.message}`);\n }\n }\n\n // DEPLOY MODE - actually create infrastructure\n let outputs;\n try {\n outputs = await progress.execute(\n \"Deploying Wraps infrastructure (this may take 2-3 minutes)\",\n async () => {\n await ensurePulumiWorkDir();\n\n const stack =\n await pulumi.automation.LocalWorkspace.createOrSelectStack(\n {\n stackName: `wraps-${identity.accountId}-${region}`,\n projectName: \"wraps-email\",\n program: async () => {\n const result = await deployEmailStack(stackConfig);\n\n return {\n roleArn: result.roleArn,\n configSetName: result.configSetName,\n tableName: result.tableName,\n region: result.region,\n lambdaFunctions: result.lambdaFunctions,\n domain: result.domain,\n dkimTokens: result.dkimTokens,\n customTrackingDomain: result.customTrackingDomain,\n };\n },\n },\n {\n workDir: getPulumiWorkDir(),\n envVars: {\n PULUMI_CONFIG_PASSPHRASE: \"\",\n AWS_REGION: region,\n },\n secretsProvider: \"passphrase\",\n }\n );\n\n await stack.workspace.selectStack(\n `wraps-${identity.accountId}-${region}`\n );\n await stack.setConfig(\"aws:region\", { value: region });\n\n const upResult = await stack.up({ onOutput: () => {} });\n const pulumiOutputs = upResult.outputs;\n\n return {\n roleArn: pulumiOutputs.roleArn?.value as string,\n configSetName: pulumiOutputs.configSetName?.value as\n | string\n | undefined,\n tableName: pulumiOutputs.tableName?.value as string | undefined,\n region: pulumiOutputs.region?.value as string,\n lambdaFunctions: pulumiOutputs.lambdaFunctions?.value as\n | string[]\n | undefined,\n domain: pulumiOutputs.domain?.value as string | undefined,\n dkimTokens: pulumiOutputs.dkimTokens?.value as string[] | undefined,\n customTrackingDomain: pulumiOutputs.customTrackingDomain?.value as\n | string\n | undefined,\n };\n }\n );\n } catch (error: any) {\n // Track deployment failure\n trackServiceInit(\"email\", false, {\n preset,\n provider,\n duration_ms: Date.now() - startTime,\n });\n\n // Check if it's a lock file error\n if (error.message?.includes(\"stack is currently locked\")) {\n trackError(\"STACK_LOCKED\", \"email:connect\", { step: \"deploy\" });\n throw errors.stackLocked();\n }\n\n trackError(\"DEPLOYMENT_FAILED\", \"email:connect\", { step: \"deploy\" });\n throw new Error(`Pulumi deployment failed: ${error.message}`);\n }\n\n // 12. Create DNS records in Route53 (if hosted zone exists)\n if (outputs.domain && outputs.dkimTokens && outputs.dkimTokens.length > 0) {\n const { findHostedZone, createDNSRecords } = await import(\n \"../../utils/email/route53.js\"\n );\n const hostedZone = await findHostedZone(outputs.domain, region);\n\n if (hostedZone) {\n try {\n progress.start(\"Creating DNS records in Route53\");\n\n // Determine mailFromDomain - use outputs if available, otherwise construct default\n const mailFromDomain =\n emailConfig.mailFromDomain || `mail.${outputs.domain}`;\n\n await createDNSRecords(\n hostedZone.id,\n outputs.domain,\n outputs.dkimTokens,\n region,\n outputs.customTrackingDomain,\n mailFromDomain\n );\n progress.succeed(\"DNS records created in Route53\");\n } catch (error: any) {\n progress.fail(\n `Failed to create DNS records automatically: ${error.message}`\n );\n progress.info(\n \"You can manually add the required DNS records shown below\"\n );\n }\n }\n }\n\n // 13. Save metadata\n const metadata = createConnectionMetadata(\n identity.accountId,\n region,\n provider,\n emailConfig,\n preset === \"custom\" ? undefined : preset\n );\n if (metadata.services.email) {\n metadata.services.email.pulumiStackName = `wraps-${identity.accountId}-${region}`;\n }\n if (vercelConfig) {\n metadata.vercel = vercelConfig;\n }\n await saveConnectionMetadata(metadata);\n\n progress.info(\"Connection metadata saved\");\n\n // 14. Display success message\n displaySuccess({\n roleArn: outputs.roleArn,\n configSetName: outputs.configSetName,\n region: outputs.region!,\n tableName: outputs.tableName,\n });\n\n // Show next steps\n if (selectedIdentities.length > 0 && emailConfig.tracking?.enabled) {\n console.log(`\\n${pc.bold(\"Next Steps:\")}\\n`);\n console.log(\n `Update your code to use configuration set: ${pc.cyan(\"wraps-email-tracking\")}`\n );\n console.log(`\\n${pc.dim(\"Example:\")}`);\n console.log(\n pc.gray(` await ses.sendEmail({\n ConfigurationSetName: 'wraps-email-tracking',\n // ... other parameters\n });`)\n );\n console.log(\"\");\n }\n\n // 15. Track successful connection\n const duration = Date.now() - startTime;\n const enabledFeatures: string[] = [];\n if (emailConfig.tracking?.enabled) enabledFeatures.push(\"tracking\");\n if (emailConfig.suppressionList?.enabled)\n enabledFeatures.push(\"suppression_list\");\n if (emailConfig.eventTracking?.enabled)\n enabledFeatures.push(\"event_tracking\");\n if (emailConfig.eventTracking?.dynamoDBHistory)\n enabledFeatures.push(\"dynamodb_history\");\n\n trackServiceInit(\"email\", true, {\n preset,\n provider,\n features: enabledFeatures,\n duration_ms: duration,\n existing_identities: selectedIdentities.length,\n });\n\n trackServiceDeployed(\"email\", {\n duration_ms: duration,\n features: enabledFeatures,\n preset,\n });\n}\n","import {\n DescribeTableCommand,\n DynamoDBClient,\n ListTablesCommand,\n} from \"@aws-sdk/client-dynamodb\";\nimport { IAMClient, ListRolesCommand } from \"@aws-sdk/client-iam\";\nimport { LambdaClient, ListFunctionsCommand } from \"@aws-sdk/client-lambda\";\nimport {\n DescribeConfigurationSetCommand,\n GetIdentityVerificationAttributesCommand,\n ListConfigurationSetsCommand,\n ListIdentitiesCommand,\n SESClient,\n} from \"@aws-sdk/client-ses\";\nimport {\n GetTopicAttributesCommand,\n ListTopicsCommand,\n SNSClient,\n} from \"@aws-sdk/client-sns\";\n\n/**\n * SES Identity with configuration\n */\nexport type SESIdentity = {\n name: string;\n type: \"Domain\" | \"EmailAddress\";\n verified: boolean;\n configurationSet?: string;\n dkimEnabled?: boolean;\n};\n\n/**\n * SES Configuration Set\n */\nexport type SESConfigurationSet = {\n name: string;\n eventDestinations: Array<{\n name: string;\n enabled: boolean;\n matchingEventTypes: string[];\n snsDestination?: string;\n cloudWatchDestination?: any;\n }>;\n};\n\n/**\n * SNS Topic\n */\nexport type SNSTopic = {\n arn: string;\n name: string;\n subscriptions?: number;\n};\n\n/**\n * DynamoDB Table\n */\nexport type DynamoTable = {\n name: string;\n status: string;\n itemCount?: number;\n sizeBytes?: number;\n};\n\n/**\n * Lambda Function\n */\nexport type LambdaFunction = {\n name: string;\n arn: string;\n runtime?: string;\n handler?: string;\n};\n\n/**\n * IAM Role\n */\nexport type IAMRole = {\n name: string;\n arn: string;\n assumeRolePolicyDocument: string;\n};\n\n/**\n * Complete scan of existing AWS resources\n */\nexport type AWSResourceScan = {\n identities: SESIdentity[];\n configurationSets: SESConfigurationSet[];\n snsTopics: SNSTopic[];\n dynamoTables: DynamoTable[];\n lambdaFunctions: LambdaFunction[];\n iamRoles: IAMRole[];\n};\n\n/**\n * Scan all existing SES identities (domains and email addresses)\n */\nexport async function scanSESIdentities(\n region: string\n): Promise<SESIdentity[]> {\n const ses = new SESClient({ region });\n const identities: SESIdentity[] = [];\n\n try {\n // List all identities\n const listResponse = await ses.send(new ListIdentitiesCommand({}));\n const identityNames = listResponse.Identities || [];\n\n if (identityNames.length === 0) {\n return [];\n }\n\n // Get verification attributes\n const attrsResponse = await ses.send(\n new GetIdentityVerificationAttributesCommand({\n Identities: identityNames,\n })\n );\n\n const attrs = attrsResponse.VerificationAttributes || {};\n\n // Build identity objects\n for (const name of identityNames) {\n const attr = attrs[name];\n identities.push({\n name,\n type: name.includes(\"@\") ? \"EmailAddress\" : \"Domain\",\n verified: attr?.VerificationStatus === \"Success\",\n });\n }\n\n return identities;\n } catch (error: any) {\n console.error(\"Error scanning SES identities:\", error.message);\n return [];\n }\n}\n\n/**\n * Scan all SES configuration sets\n */\nexport async function scanSESConfigurationSets(\n region: string\n): Promise<SESConfigurationSet[]> {\n const ses = new SESClient({ region });\n const configSets: SESConfigurationSet[] = [];\n\n try {\n // List configuration sets\n const listResponse = await ses.send(new ListConfigurationSetsCommand({}));\n const configSetNames =\n listResponse.ConfigurationSets?.map((cs) => cs.Name!).filter(Boolean) ||\n [];\n\n // Get details for each config set\n for (const name of configSetNames) {\n try {\n const describeResponse = await ses.send(\n new DescribeConfigurationSetCommand({ ConfigurationSetName: name })\n );\n\n const eventDestinations =\n describeResponse.EventDestinations?.map((ed) => ({\n name: ed.Name!,\n enabled: ed.Enabled ?? false,\n matchingEventTypes: ed.MatchingEventTypes || [],\n snsDestination: ed.SNSDestination?.TopicARN,\n cloudWatchDestination: ed.CloudWatchDestination,\n })) || [];\n\n configSets.push({\n name,\n eventDestinations,\n });\n } catch (error: any) {\n console.error(`Error describing config set ${name}:`, error.message);\n }\n }\n\n return configSets;\n } catch (error: any) {\n console.error(\"Error scanning SES configuration sets:\", error.message);\n return [];\n }\n}\n\n/**\n * Scan SNS topics (filter for email-related ones)\n */\nexport async function scanSNSTopics(region: string): Promise<SNSTopic[]> {\n const sns = new SNSClient({ region });\n const topics: SNSTopic[] = [];\n\n try {\n // List all topics\n const listResponse = await sns.send(new ListTopicsCommand({}));\n const topicArns =\n listResponse.Topics?.map((t) => t.TopicArn!).filter(Boolean) || [];\n\n // Get details for each topic\n for (const arn of topicArns) {\n try {\n const attrsResponse = await sns.send(\n new GetTopicAttributesCommand({ TopicArn: arn })\n );\n\n const name = arn.split(\":\").pop() || arn;\n const subscriptions = Number.parseInt(\n attrsResponse.Attributes?.SubscriptionsConfirmed || \"0\",\n 10\n );\n\n topics.push({\n arn,\n name,\n subscriptions,\n });\n } catch (error: any) {\n console.error(\n `Error getting topic attributes for ${arn}:`,\n error.message\n );\n }\n }\n\n return topics;\n } catch (error: any) {\n console.error(\"Error scanning SNS topics:\", error.message);\n return [];\n }\n}\n\n/**\n * Scan DynamoDB tables (filter for email-related ones)\n */\nexport async function scanDynamoTables(region: string): Promise<DynamoTable[]> {\n const dynamo = new DynamoDBClient({ region });\n const tables: DynamoTable[] = [];\n\n try {\n // List all tables\n const listResponse = await dynamo.send(new ListTablesCommand({}));\n const tableNames = listResponse.TableNames || [];\n\n // Get details for each table\n for (const name of tableNames) {\n try {\n const describeResponse = await dynamo.send(\n new DescribeTableCommand({ TableName: name })\n );\n\n const table = describeResponse.Table;\n if (table) {\n tables.push({\n name,\n status: table.TableStatus || \"UNKNOWN\",\n itemCount: table.ItemCount,\n sizeBytes: table.TableSizeBytes,\n });\n }\n } catch (error: any) {\n console.error(`Error describing table ${name}:`, error.message);\n }\n }\n\n return tables;\n } catch (error: any) {\n console.error(\"Error scanning DynamoDB tables:\", error.message);\n return [];\n }\n}\n\n/**\n * Scan Lambda functions (filter for email-related ones)\n */\nexport async function scanLambdaFunctions(\n region: string\n): Promise<LambdaFunction[]> {\n const lambda = new LambdaClient({ region });\n const functions: LambdaFunction[] = [];\n\n try {\n // List all functions\n const listResponse = await lambda.send(new ListFunctionsCommand({}));\n const functionConfigs = listResponse.Functions || [];\n\n for (const func of functionConfigs) {\n if (func.FunctionName && func.FunctionArn) {\n functions.push({\n name: func.FunctionName,\n arn: func.FunctionArn,\n runtime: func.Runtime,\n handler: func.Handler,\n });\n }\n }\n\n return functions;\n } catch (error: any) {\n console.error(\"Error scanning Lambda functions:\", error.message);\n return [];\n }\n}\n\n/**\n * Scan IAM roles (filter for Wraps or email-related ones)\n */\nexport async function scanIAMRoles(region: string): Promise<IAMRole[]> {\n const iam = new IAMClient({ region });\n const roles: IAMRole[] = [];\n\n try {\n // List all roles (with pagination support)\n let marker: string | undefined;\n let hasMore = true;\n\n while (hasMore) {\n const listResponse = await iam.send(\n new ListRolesCommand({\n Marker: marker,\n MaxItems: 100,\n })\n );\n\n const roleList = listResponse.Roles || [];\n\n for (const role of roleList) {\n if (role.RoleName && role.Arn) {\n roles.push({\n name: role.RoleName,\n arn: role.Arn,\n assumeRolePolicyDocument: role.AssumeRolePolicyDocument || \"\",\n });\n }\n }\n\n marker = listResponse.Marker;\n hasMore = listResponse.IsTruncated ?? false;\n }\n\n return roles;\n } catch (error: any) {\n console.error(\"Error scanning IAM roles:\", error.message);\n return [];\n }\n}\n\n/**\n * Scan all relevant AWS resources for email infrastructure\n */\nexport async function scanAWSResources(\n region: string\n): Promise<AWSResourceScan> {\n const [\n identities,\n configurationSets,\n snsTopics,\n dynamoTables,\n lambdaFunctions,\n iamRoles,\n ] = await Promise.all([\n scanSESIdentities(region),\n scanSESConfigurationSets(region),\n scanSNSTopics(region),\n scanDynamoTables(region),\n scanLambdaFunctions(region),\n scanIAMRoles(region),\n ]);\n\n return {\n identities,\n configurationSets,\n snsTopics,\n dynamoTables,\n lambdaFunctions,\n iamRoles,\n };\n}\n\n/**\n * Filter resources to only Wraps-managed ones (wraps-* prefix)\n */\nexport function filterWrapsResources(scan: AWSResourceScan): AWSResourceScan {\n return {\n identities: scan.identities, // All identities are relevant\n configurationSets: scan.configurationSets.filter((cs) =>\n cs.name.startsWith(\"wraps-\")\n ),\n snsTopics: scan.snsTopics.filter((t) => t.name.startsWith(\"wraps-\")),\n dynamoTables: scan.dynamoTables.filter((t) => t.name.startsWith(\"wraps-\")),\n lambdaFunctions: scan.lambdaFunctions.filter((f) =>\n f.name.startsWith(\"wraps-\")\n ),\n iamRoles: scan.iamRoles.filter((r) => r.name.startsWith(\"wraps-\")),\n };\n}\n\n/**\n * Check if specific Wraps resources exist\n */\nexport function checkWrapsResourcesExist(scan: AWSResourceScan): {\n hasConfigSet: boolean;\n hasSNSTopics: boolean;\n hasDynamoTable: boolean;\n hasLambdaFunctions: boolean;\n hasIAMRole: boolean;\n} {\n const filtered = filterWrapsResources(scan);\n\n return {\n hasConfigSet: filtered.configurationSets.length > 0,\n hasSNSTopics: filtered.snsTopics.length > 0,\n hasDynamoTable: filtered.dynamoTables.length > 0,\n hasLambdaFunctions: filtered.lambdaFunctions.length > 0,\n hasIAMRole: filtered.iamRoles.length > 0,\n };\n}\n","import * as clack from \"@clack/prompts\";\nimport * as pulumi from \"@pulumi/pulumi\";\nimport pc from \"picocolors\";\nimport {\n trackError,\n trackServiceRemoved,\n} from \"../../telemetry/events.js\";\nimport type { DestroyOptions } from \"../../types/index.js\";\nimport {\n getAWSRegion,\n validateAWSCredentials,\n} from \"../../utils/shared/aws.js\";\nimport { errors } from \"../../utils/shared/errors.js\";\nimport {\n ensurePulumiWorkDir,\n getPulumiWorkDir,\n} from \"../../utils/shared/fs.js\";\nimport {\n deleteConnectionMetadata,\n loadConnectionMetadata,\n} from \"../../utils/shared/metadata.js\";\nimport {\n DeploymentProgress,\n displayPreview,\n} from \"../../utils/shared/output.js\";\nimport { deleteDNSRecords, findHostedZone } from \"../../utils/route53.js\";\n\n/**\n * Get DKIM tokens and MAIL FROM domain for a domain from SES\n */\nasync function getEmailIdentityInfo(\n domain: string,\n region: string\n): Promise<{ dkimTokens: string[]; mailFromDomain?: string }> {\n try {\n const { SESv2Client, GetEmailIdentityCommand } = await import(\n \"@aws-sdk/client-sesv2\"\n );\n const ses = new SESv2Client({ region });\n\n const response = await ses.send(\n new GetEmailIdentityCommand({ EmailIdentity: domain })\n );\n\n return {\n dkimTokens: response.DkimAttributes?.Tokens || [],\n mailFromDomain: response.MailFromAttributes?.MailFromDomain,\n };\n } catch (_error) {\n return { dkimTokens: [] };\n }\n}\n\n/**\n * Email Destroy command - Remove email infrastructure\n */\nexport async function emailDestroy(options: DestroyOptions): Promise<void> {\n const startTime = Date.now();\n\n clack.intro(\n pc.bold(\n options.preview\n ? \"Email Infrastructure Destruction Preview\"\n : \"Email Infrastructure Teardown\"\n )\n );\n\n const progress = new DeploymentProgress();\n\n // 1. Validate AWS credentials\n const identity = await progress.execute(\n \"Validating AWS credentials\",\n async () => validateAWSCredentials()\n );\n\n // 2. Get region\n const region = await getAWSRegion();\n\n // 3. Load connection metadata to get domain info and stack name\n const metadata = await loadConnectionMetadata(identity.accountId, region);\n const emailService = metadata?.services?.email;\n const emailConfig = emailService?.config;\n const domain = emailConfig?.domain;\n const storedStackName = emailService?.pulumiStackName;\n\n // 4. Confirm destruction (skip if --force or --preview)\n if (!(options.force || options.preview)) {\n const confirmed = await clack.confirm({\n message: pc.red(\n \"Are you sure you want to destroy all email infrastructure?\"\n ),\n initialValue: false,\n });\n\n if (clack.isCancel(confirmed) || !confirmed) {\n clack.cancel(\"Destruction cancelled.\");\n process.exit(0);\n }\n }\n\n // 5. Check for Route53 hosted zone and offer to clean up DNS\n let shouldCleanDNS = false;\n let hostedZone: { id: string; name: string } | null = null;\n let dkimTokens: string[] = [];\n // Get mailFromDomain from metadata, or fall back to querying SES\n let mailFromDomain = emailConfig?.mailFromDomain;\n\n if (domain && !options.preview) {\n hostedZone = await findHostedZone(domain, region);\n\n if (hostedZone) {\n // Get DKIM tokens and MAIL FROM domain from SES before we destroy\n const identityInfo = await getEmailIdentityInfo(domain, region);\n dkimTokens = identityInfo.dkimTokens;\n // Use MAIL FROM from SES if not in metadata (handles legacy deployments)\n if (!mailFromDomain && identityInfo.mailFromDomain) {\n mailFromDomain = identityInfo.mailFromDomain;\n }\n\n if (!options.force) {\n const cleanDNS = await clack.confirm({\n message: `Found Route53 hosted zone for ${pc.cyan(domain)}. Delete DNS records (DKIM, DMARC, MAIL FROM)?`,\n initialValue: true,\n });\n\n if (clack.isCancel(cleanDNS)) {\n clack.cancel(\"Destruction cancelled.\");\n process.exit(0);\n }\n\n shouldCleanDNS = cleanDNS;\n } else {\n shouldCleanDNS = true; // Auto-clean with --force\n }\n }\n }\n\n // 6. Preview or Destroy infrastructure using Pulumi\n if (options.preview) {\n // PREVIEW MODE - show what would be destroyed without actually destroying\n try {\n const previewResult = await progress.execute(\n \"Generating destruction preview\",\n async () => {\n await ensurePulumiWorkDir();\n\n // Use stored stack name from metadata, fallback to generated name\n const stackName =\n storedStackName || `wraps-email-${identity.accountId}-${region}`;\n\n // Try to select the stack\n let stack;\n try {\n stack = await pulumi.automation.LocalWorkspace.selectStack({\n stackName,\n workDir: getPulumiWorkDir(),\n });\n } catch (_error) {\n throw new Error(\"No email infrastructure found to preview\");\n }\n\n // Run preview to see what would be destroyed\n const result = await stack.preview({ diff: true });\n return result;\n }\n );\n\n // Display preview results\n displayPreview({\n changeSummary: previewResult.changeSummary,\n costEstimate: \"Monthly cost after destruction: $0.00\",\n commandName: \"wraps email destroy\",\n });\n\n // Show DNS cleanup info\n if (domain) {\n const previewHostedZone = await findHostedZone(domain, region);\n if (previewHostedZone) {\n clack.log.info(\n `DNS records in Route53 for ${pc.cyan(domain)} will also be deleted`\n );\n }\n }\n\n clack.outro(\n pc.green(\"Preview complete. Run without --preview to destroy.\")\n );\n\n // Track preview completion\n trackServiceRemoved(\"email\", {\n preview: true,\n duration_ms: Date.now() - startTime,\n });\n return;\n } catch (error: any) {\n progress.stop();\n if (error.message.includes(\"No email infrastructure found\")) {\n clack.log.warn(\"No email infrastructure found to preview\");\n process.exit(0);\n }\n trackError(\"PREVIEW_FAILED\", \"email destroy\", { step: \"preview\" });\n throw new Error(`Preview failed: ${error.message}`);\n }\n }\n\n // DESTROY MODE - actually remove infrastructure\n\n // 7. Clean up DNS records first (before destroying SES identity)\n if (shouldCleanDNS && hostedZone && domain && dkimTokens.length > 0) {\n try {\n await progress.execute(\n `Deleting DNS records for ${domain}`,\n async () => {\n await deleteDNSRecords(\n hostedZone.id,\n domain,\n dkimTokens,\n region,\n emailConfig?.tracking?.customRedirectDomain,\n mailFromDomain\n );\n }\n );\n } catch (error: any) {\n clack.log.warn(`Could not delete DNS records: ${error.message}`);\n clack.log.info(\"You may need to delete them manually from Route53\");\n }\n }\n\n // 8. Destroy Pulumi infrastructure\n try {\n await progress.execute(\n \"Destroying email infrastructure (this may take 2-3 minutes)\",\n async () => {\n // Ensure Pulumi workspace directory exists\n await ensurePulumiWorkDir();\n\n // Use stored stack name from metadata, fallback to generated name\n const stackName =\n storedStackName || `wraps-email-${identity.accountId}-${region}`;\n\n // Try to select the stack\n let stack;\n try {\n stack = await pulumi.automation.LocalWorkspace.selectStack({\n stackName,\n workDir: getPulumiWorkDir(),\n });\n } catch (_error) {\n throw new Error(\"No email infrastructure found to destroy\");\n }\n\n // Run destroy\n await stack.destroy({ onOutput: () => {} }); // Suppress Pulumi output\n\n // Remove the stack from workspace\n await stack.workspace.removeStack(stackName);\n }\n );\n } catch (error: any) {\n progress.stop();\n if (error.message.includes(\"No email infrastructure found\")) {\n clack.log.warn(\"No email infrastructure found\");\n // Still delete metadata if it exists\n await deleteConnectionMetadata(identity.accountId, region);\n process.exit(0);\n }\n // Check if it's a lock file error\n if (error.message?.includes(\"stack is currently locked\")) {\n trackError(\"STACK_LOCKED\", \"email destroy\", { step: \"destroy\" });\n throw errors.stackLocked();\n }\n trackError(\"DESTROY_FAILED\", \"email destroy\", { step: \"destroy\" });\n clack.log.error(\"Email infrastructure destruction failed\");\n throw error;\n }\n\n // 9. Delete connection metadata\n await deleteConnectionMetadata(identity.accountId, region);\n\n // 10. Display success message\n progress.stop();\n\n const deletedItems = [\"AWS infrastructure\"];\n if (shouldCleanDNS && hostedZone) {\n deletedItems.push(\"Route53 DNS records\");\n }\n\n clack.outro(pc.green(`Email infrastructure has been removed`));\n\n if (domain) {\n console.log(`\\n${pc.bold(\"Cleaned up:\")}`);\n for (const item of deletedItems) {\n console.log(` ${pc.green(\"✓\")} ${item}`);\n }\n\n // Remind about SPF record\n console.log(\n `\\n${pc.dim(\"Note: SPF record was not deleted. Remove 'include:amazonses.com' manually if needed.\")}`\n );\n }\n\n console.log(\n `\\nRun ${pc.cyan(\"wraps email init\")} to deploy infrastructure again.\\n`\n );\n\n // 11. Track successful destruction\n trackServiceRemoved(\"email\", {\n reason: \"user_initiated\",\n duration_ms: Date.now() - startTime,\n dns_cleaned: shouldCleanDNS,\n });\n}\n","import {\n type Change,\n ChangeResourceRecordSetsCommand,\n ListHostedZonesByNameCommand,\n ListResourceRecordSetsCommand,\n Route53Client,\n} from \"@aws-sdk/client-route-53\";\n\n/**\n * Get existing TXT records for a domain\n * Returns all TXT record values and identifies which one is SPF\n */\nasync function getExistingTXTRecords(\n client: Route53Client,\n hostedZoneId: string,\n domain: string\n): Promise<{ allValues: string[]; spfValue: string | null; ttl: number }> {\n try {\n const response = await client.send(\n new ListResourceRecordSetsCommand({\n HostedZoneId: hostedZoneId,\n StartRecordName: domain,\n StartRecordType: \"TXT\",\n MaxItems: 100,\n })\n );\n\n // Find TXT records for the exact domain\n const txtRecordSet = response.ResourceRecordSets?.find(\n (rs) =>\n rs.Type === \"TXT\" &&\n (rs.Name === domain || rs.Name === `${domain}.`)\n );\n\n if (!txtRecordSet || !txtRecordSet.ResourceRecords) {\n return { allValues: [], spfValue: null, ttl: 1800 };\n }\n\n const allValues: string[] = [];\n let spfValue: string | null = null;\n\n for (const record of txtRecordSet.ResourceRecords) {\n const value = record.Value || \"\";\n allValues.push(value);\n\n // Check if this is the SPF record (strip quotes for comparison)\n const unquoted = value.replace(/^\"|\"$/g, \"\");\n if (unquoted.startsWith(\"v=spf1\")) {\n spfValue = unquoted;\n }\n }\n\n return {\n allValues,\n spfValue,\n ttl: txtRecordSet.TTL || 1800,\n };\n } catch (_error) {\n return { allValues: [], spfValue: null, ttl: 1800 };\n }\n}\n\n/**\n * Merge amazonses.com include into an existing SPF record\n * If the record already includes amazonses.com, returns unchanged\n */\nfunction mergeSPFRecord(existingSPF: string): string {\n const sesInclude = \"include:amazonses.com\";\n\n // Already includes SES\n if (existingSPF.includes(sesInclude)) {\n return existingSPF;\n }\n\n // Find the position before the \"all\" mechanism to insert our include\n // SPF format: v=spf1 [mechanisms...] [qualifier]all\n const allMatch = existingSPF.match(/\\s([~+?-]?all)$/);\n\n if (allMatch) {\n // Insert before the \"all\" mechanism\n const beforeAll = existingSPF.slice(0, allMatch.index);\n const allPart = allMatch[1];\n return `${beforeAll} ${sesInclude} ${allPart}`;\n }\n\n // No \"all\" mechanism found, append to end\n return `${existingSPF} ${sesInclude}`;\n}\n\n/**\n * Find Route53 hosted zone for a domain\n */\nexport async function findHostedZone(\n domain: string,\n region: string\n): Promise<{ id: string; name: string } | null> {\n const client = new Route53Client({ region });\n\n try {\n const response = await client.send(\n new ListHostedZonesByNameCommand({\n DNSName: domain,\n MaxItems: 1,\n })\n );\n\n const zone = response.HostedZones?.[0];\n if (zone && zone.Name === `${domain}.` && zone.Id) {\n return {\n id: zone.Id.replace(\"/hostedzone/\", \"\"),\n name: zone.Name,\n };\n }\n\n return null;\n } catch (_error) {\n return null;\n }\n}\n\n/**\n * Create DNS records in Route53\n */\nexport async function createDNSRecords(\n hostedZoneId: string,\n domain: string,\n dkimTokens: string[],\n region: string,\n customTrackingDomain?: string,\n mailFromDomain?: string\n): Promise<void> {\n const client = new Route53Client({ region });\n\n const changes: Change[] = [];\n\n // DKIM CNAME records\n for (const token of dkimTokens) {\n changes.push({\n Action: \"UPSERT\",\n ResourceRecordSet: {\n Name: `${token}._domainkey.${domain}`,\n Type: \"CNAME\",\n TTL: 1800,\n ResourceRecords: [{ Value: `${token}.dkim.amazonses.com` }],\n },\n });\n }\n\n // SPF TXT record - check for existing records and merge while preserving others\n const existingTXT = await getExistingTXTRecords(client, hostedZoneId, domain);\n\n // Build the new TXT record values, preserving all non-SPF records\n const newTXTValues: string[] = [];\n\n if (existingTXT.spfValue) {\n // Merge our include into the existing SPF record\n const mergedSPF = mergeSPFRecord(existingTXT.spfValue);\n newTXTValues.push(`\"${mergedSPF}\"`);\n\n // Add all other TXT values (non-SPF)\n for (const value of existingTXT.allValues) {\n const unquoted = value.replace(/^\"|\"$/g, \"\");\n if (!unquoted.startsWith(\"v=spf1\")) {\n newTXTValues.push(value);\n }\n }\n } else if (existingTXT.allValues.length > 0) {\n // No SPF exists, add new SPF and keep all existing values\n newTXTValues.push('\"v=spf1 include:amazonses.com ~all\"');\n newTXTValues.push(...existingTXT.allValues);\n } else {\n // No TXT records exist, create new SPF\n newTXTValues.push('\"v=spf1 include:amazonses.com ~all\"');\n }\n\n changes.push({\n Action: \"UPSERT\",\n ResourceRecordSet: {\n Name: domain,\n Type: \"TXT\",\n TTL: existingTXT.ttl,\n ResourceRecords: newTXTValues.map((v) => ({ Value: v })),\n },\n });\n\n // DMARC TXT record\n // Use MAIL FROM domain for rua if configured, otherwise use main domain\n const dmarcRuaDomain = mailFromDomain || domain;\n changes.push({\n Action: \"UPSERT\",\n ResourceRecordSet: {\n Name: `_dmarc.${domain}`,\n Type: \"TXT\",\n TTL: 1800,\n ResourceRecords: [\n { Value: `\"v=DMARC1; p=quarantine; rua=mailto:postmaster@${dmarcRuaDomain}\"` },\n ],\n },\n });\n\n // Custom tracking domain CNAME (if provided)\n // This allows SES to rewrite links for open/click tracking using your custom domain\n if (customTrackingDomain) {\n changes.push({\n Action: \"UPSERT\",\n ResourceRecordSet: {\n Name: customTrackingDomain,\n Type: \"CNAME\",\n TTL: 1800,\n ResourceRecords: [{ Value: `r.${region}.awstrack.me` }],\n },\n });\n }\n\n // MAIL FROM domain records (if provided)\n // These records enable DMARC alignment by using a custom subdomain for the envelope sender\n if (mailFromDomain) {\n // MX record pointing to SES feedback server\n changes.push({\n Action: \"UPSERT\",\n ResourceRecordSet: {\n Name: mailFromDomain,\n Type: \"MX\",\n TTL: 1800,\n ResourceRecords: [\n { Value: `10 feedback-smtp.${region}.amazonses.com` },\n ],\n },\n });\n\n // SPF record for MAIL FROM domain\n changes.push({\n Action: \"UPSERT\",\n ResourceRecordSet: {\n Name: mailFromDomain,\n Type: \"TXT\",\n TTL: 1800,\n ResourceRecords: [{ Value: '\"v=spf1 include:amazonses.com ~all\"' }],\n },\n });\n }\n\n await client.send(\n new ChangeResourceRecordSetsCommand({\n HostedZoneId: hostedZoneId,\n ChangeBatch: {\n Changes: changes,\n },\n })\n );\n}\n\n/**\n * Delete DNS records from Route53 that were created for SES\n */\nexport async function deleteDNSRecords(\n hostedZoneId: string,\n domain: string,\n dkimTokens: string[],\n region: string,\n customTrackingDomain?: string,\n mailFromDomain?: string\n): Promise<void> {\n const client = new Route53Client({ region });\n\n // First, we need to get the current record values to delete them\n // Route53 DELETE requires exact match of the record\n const response = await client.send(\n new ListResourceRecordSetsCommand({\n HostedZoneId: hostedZoneId,\n MaxItems: 500,\n })\n );\n\n const recordSets = response.ResourceRecordSets || [];\n const changes: Change[] = [];\n\n // Helper to find and add deletion for a record\n const addDeletionIfExists = (name: string, type: string) => {\n // Route53 names end with a dot\n const normalizedName = name.endsWith(\".\") ? name : `${name}.`;\n const record = recordSets.find(\n (rs) => rs.Name === normalizedName && rs.Type === type\n );\n if (record && record.ResourceRecords) {\n changes.push({\n Action: \"DELETE\",\n ResourceRecordSet: record,\n });\n }\n };\n\n // DKIM CNAME records\n for (const token of dkimTokens) {\n addDeletionIfExists(`${token}._domainkey.${domain}`, \"CNAME\");\n }\n\n // DMARC record\n addDeletionIfExists(`_dmarc.${domain}`, \"TXT\");\n\n // Custom tracking domain CNAME\n if (customTrackingDomain) {\n addDeletionIfExists(customTrackingDomain, \"CNAME\");\n }\n\n // MAIL FROM domain records\n if (mailFromDomain) {\n addDeletionIfExists(mailFromDomain, \"MX\");\n addDeletionIfExists(mailFromDomain, \"TXT\");\n }\n\n // Note: We don't delete the main domain SPF record as it might contain\n // other providers' includes. Users should manually remove amazonses.com\n // from their SPF if needed.\n\n if (changes.length === 0) {\n return; // Nothing to delete\n }\n\n await client.send(\n new ChangeResourceRecordSetsCommand({\n HostedZoneId: hostedZoneId,\n ChangeBatch: {\n Changes: changes,\n },\n })\n );\n}\n","import { Resolver } from \"node:dns/promises\";\nimport { GetEmailIdentityCommand, SESv2Client } from \"@aws-sdk/client-sesv2\";\nimport * as clack from \"@clack/prompts\";\nimport pc from \"picocolors\";\nimport { trackCommand, trackFeature } from \"../../telemetry/events.js\";\nimport type { EmailVerifyOptions } from \"../../types/index.js\";\nimport { getAWSRegion } from \"../../utils/shared/aws.js\";\nimport { DeploymentProgress } from \"../../utils/shared/output.js\";\n\n/**\n * Verify domain DNS records and verification status\n */\nexport async function verifyDomain(options: EmailVerifyOptions): Promise<void> {\n clack.intro(pc.bold(`Verifying ${options.domain}`));\n\n const progress = new DeploymentProgress();\n const region = await getAWSRegion();\n\n // 1. Check SES verification status\n const sesClient = new SESv2Client({ region });\n let identity;\n let dkimTokens: string[] = [];\n let mailFromDomain: string | undefined;\n\n try {\n identity = await progress.execute(\n \"Checking SES verification status\",\n async () => {\n const response = await sesClient.send(\n new GetEmailIdentityCommand({ EmailIdentity: options.domain })\n );\n return response;\n }\n );\n\n dkimTokens = identity.DkimAttributes?.Tokens || [];\n mailFromDomain = identity.MailFromAttributes?.MailFromDomain;\n } catch (_error: any) {\n progress.stop();\n clack.log.error(`Domain ${options.domain} not found in SES`);\n console.log(\n `\\nRun ${pc.cyan(`wraps email init --domain ${options.domain}`)} to add this domain.\\n`\n );\n process.exit(1);\n return; // Return after process.exit for testing\n }\n\n // 2. Check DNS records\n const resolver = new Resolver();\n // Use public DNS servers for more reliable results\n resolver.setServers([\"8.8.8.8\", \"1.1.1.1\"]);\n const dnsResults: Array<{\n name: string;\n type: string;\n status: string;\n records?: string[];\n }> = [];\n\n // Check DKIM records\n for (const token of dkimTokens) {\n const dkimRecord = `${token}._domainkey.${options.domain}`;\n try {\n const records = await resolver.resolveCname(dkimRecord);\n const expected = `${token}.dkim.amazonses.com`;\n const found = records.some((r) => r === expected || r === `${expected}.`);\n dnsResults.push({\n name: dkimRecord,\n type: \"CNAME\",\n status: found ? \"verified\" : \"incorrect\",\n records,\n });\n } catch (_error) {\n dnsResults.push({\n name: dkimRecord,\n type: \"CNAME\",\n status: \"missing\",\n });\n }\n }\n\n // Check SPF record\n try {\n const records = await resolver.resolveTxt(options.domain);\n const spfRecord = records.flat().find((r) => r.startsWith(\"v=spf1\"));\n const hasAmazonSES = spfRecord?.includes(\"include:amazonses.com\");\n dnsResults.push({\n name: options.domain,\n type: \"TXT (SPF)\",\n status: hasAmazonSES ? \"verified\" : spfRecord ? \"incorrect\" : \"missing\",\n records: spfRecord ? [spfRecord] : undefined,\n });\n } catch (_error) {\n dnsResults.push({\n name: options.domain,\n type: \"TXT (SPF)\",\n status: \"missing\",\n });\n }\n\n // Check DMARC record\n try {\n const records = await resolver.resolveTxt(`_dmarc.${options.domain}`);\n const dmarcRecord = records.flat().find((r) => r.startsWith(\"v=DMARC1\"));\n dnsResults.push({\n name: `_dmarc.${options.domain}`,\n type: \"TXT (DMARC)\",\n status: dmarcRecord ? \"verified\" : \"missing\",\n records: dmarcRecord ? [dmarcRecord] : undefined,\n });\n } catch (_error) {\n dnsResults.push({\n name: `_dmarc.${options.domain}`,\n type: \"TXT (DMARC)\",\n status: \"missing\",\n });\n }\n\n // Check MAIL FROM domain records (if configured)\n if (mailFromDomain) {\n // Check MX record for MAIL FROM domain\n try {\n const mxRecords = await resolver.resolveMx(mailFromDomain);\n const expectedMx = `feedback-smtp.${region}.amazonses.com`;\n const hasMx = mxRecords.some(\n (r) => r.exchange === expectedMx || r.exchange === `${expectedMx}.`\n );\n dnsResults.push({\n name: mailFromDomain,\n type: \"MX\",\n status: hasMx\n ? \"verified\"\n : mxRecords.length > 0\n ? \"incorrect\"\n : \"missing\",\n records: mxRecords.map((r) => `${r.priority} ${r.exchange}`),\n });\n } catch (_error) {\n dnsResults.push({\n name: mailFromDomain,\n type: \"MX\",\n status: \"missing\",\n });\n }\n\n // Check SPF record for MAIL FROM domain\n try {\n const records = await resolver.resolveTxt(mailFromDomain);\n const spfRecord = records.flat().find((r) => r.startsWith(\"v=spf1\"));\n const hasAmazonSES = spfRecord?.includes(\"include:amazonses.com\");\n dnsResults.push({\n name: mailFromDomain,\n type: \"TXT (SPF)\",\n status: hasAmazonSES ? \"verified\" : spfRecord ? \"incorrect\" : \"missing\",\n records: spfRecord ? [spfRecord] : undefined,\n });\n } catch (_error) {\n dnsResults.push({\n name: mailFromDomain,\n type: \"TXT (SPF)\",\n status: \"missing\",\n });\n }\n }\n\n progress.stop();\n\n // 3. Display results\n const verificationStatus = identity.VerifiedForSendingStatus\n ? \"verified\"\n : \"pending\";\n const dkimStatus = identity.DkimAttributes?.Status || \"PENDING\";\n const mailFromStatus =\n identity.MailFromAttributes?.MailFromDomainStatus || \"NOT_CONFIGURED\";\n\n const statusLines = [\n `${pc.bold(\"Domain:\")} ${options.domain}`,\n `${pc.bold(\"Verification Status:\")} ${\n verificationStatus === \"verified\"\n ? pc.green(\"✓ Verified\")\n : pc.yellow(\"⏱ Pending\")\n }`,\n `${pc.bold(\"DKIM Status:\")} ${\n dkimStatus === \"SUCCESS\"\n ? pc.green(\"✓ Success\")\n : pc.yellow(`⏱ ${dkimStatus}`)\n }`,\n ];\n\n if (mailFromDomain) {\n statusLines.push(\n `${pc.bold(\"MAIL FROM Domain:\")} ${mailFromDomain}`,\n `${pc.bold(\"MAIL FROM Status:\")} ${\n mailFromStatus === \"SUCCESS\"\n ? pc.green(\"✓ Success\")\n : mailFromStatus === \"NOT_CONFIGURED\"\n ? pc.yellow(\"⏱ Not Configured\")\n : pc.yellow(`⏱ ${mailFromStatus}`)\n }`\n );\n }\n\n clack.note(statusLines.join(\"\\n\"), \"SES Status\");\n\n // DNS Records\n const dnsLines = dnsResults.map((record) => {\n let statusIcon: string;\n let statusColor: (s: string) => string;\n\n if (record.status === \"verified\") {\n statusIcon = \"✓\";\n statusColor = pc.green;\n } else if (record.status === \"incorrect\") {\n statusIcon = \"✗\";\n statusColor = pc.red;\n } else {\n statusIcon = \"✗\";\n statusColor = pc.red;\n }\n\n const recordInfo = record.records ? ` → ${record.records.join(\", \")}` : \"\";\n return ` ${statusColor(statusIcon)} ${record.name} (${record.type}) ${statusColor(\n record.status\n )}${recordInfo}`;\n });\n\n clack.note(dnsLines.join(\"\\n\"), \"DNS Records\");\n\n // Summary\n const allVerified = dnsResults.every((r) => r.status === \"verified\");\n const someIncorrect = dnsResults.some((r) => r.status === \"incorrect\");\n\n if (verificationStatus === \"verified\" && allVerified) {\n clack.outro(\n pc.green(\"✓ Domain is fully verified and ready to send emails!\")\n );\n trackFeature(\"domain_verified\", { dns_auto_detected: true });\n } else if (someIncorrect) {\n clack.outro(\n pc.red(\"✗ Some DNS records are incorrect. Please update them.\")\n );\n console.log(\n `\\nRun ${pc.cyan(\"wraps email status\")} to see the correct DNS records.\\n`\n );\n } else {\n clack.outro(\n pc.yellow(\"⏱ Waiting for DNS propagation and SES verification\")\n );\n console.log(\"\\nDNS records can take up to 48 hours to propagate.\");\n console.log(\n \"SES verification usually completes within 72 hours after DNS propagation.\\n\"\n );\n }\n\n // Track verify command\n trackCommand(\"email:domains:verify\", {\n success: true,\n verified: verificationStatus === \"verified\" && allVerified,\n dkim_status: dkimStatus,\n });\n}\n\n/**\n * Add a domain to SES for email sending\n */\nexport async function addDomain(options: { domain: string }): Promise<void> {\n clack.intro(pc.bold(`Adding domain ${options.domain} to SES`));\n\n const progress = new DeploymentProgress();\n const region = await getAWSRegion();\n const sesClient = new SESv2Client({ region });\n\n try {\n // Check if domain already exists\n try {\n await sesClient.send(\n new GetEmailIdentityCommand({ EmailIdentity: options.domain })\n );\n progress.stop();\n clack.log.warn(`Domain ${options.domain} already exists in SES`);\n console.log(\n `\\nRun ${pc.cyan(`wraps email domains verify --domain ${options.domain}`)} to check verification status.\\n`\n );\n return;\n } catch (error: any) {\n // Domain doesn't exist, continue with creation\n if (error.name !== \"NotFoundException\") {\n throw error;\n }\n }\n\n // Create the email identity\n const { CreateEmailIdentityCommand } = await import(\n \"@aws-sdk/client-sesv2\"\n );\n await progress.execute(\"Adding domain to SES\", async () => {\n await sesClient.send(\n new CreateEmailIdentityCommand({\n EmailIdentity: options.domain,\n DkimSigningAttributes: {\n NextSigningKeyLength: \"RSA_2048_BIT\",\n },\n })\n );\n });\n\n // Get the DKIM tokens\n const identity = await sesClient.send(\n new GetEmailIdentityCommand({ EmailIdentity: options.domain })\n );\n const dkimTokens = identity.DkimAttributes?.Tokens || [];\n\n progress.stop();\n\n clack.outro(pc.green(`✓ Domain ${options.domain} added successfully!`));\n\n // Show next steps\n console.log(`\\n${pc.bold(\"Next steps:\")}\\n`);\n console.log(\"1. Add the following DKIM records to your DNS:\\n\");\n\n for (const token of dkimTokens) {\n console.log(` ${pc.cyan(`${token}._domainkey.${options.domain}`)}`);\n console.log(\n ` ${pc.dim(\"Type:\")} CNAME ${pc.dim(\"Value:\")} ${token}.dkim.amazonses.com\\n`\n );\n }\n\n console.log(\n `2. Verify DNS propagation: ${pc.cyan(`wraps email domains verify --domain ${options.domain}`)}`\n );\n console.log(`3. Check status: ${pc.cyan(\"wraps email status\")}\\n`);\n\n // Track add domain success\n trackCommand(\"email:domains:add\", {\n success: true,\n });\n trackFeature(\"domain_added\", {});\n } catch (error: any) {\n progress.stop();\n trackCommand(\"email:domains:add\", {\n success: false,\n });\n throw error;\n }\n}\n\n/**\n * List all domains configured in SES\n */\nexport async function listDomains(): Promise<void> {\n clack.intro(pc.bold(\"SES Email Domains\"));\n\n const progress = new DeploymentProgress();\n const region = await getAWSRegion();\n const sesClient = new SESv2Client({ region });\n\n try {\n const { ListEmailIdentitiesCommand } = await import(\n \"@aws-sdk/client-sesv2\"\n );\n\n const identities = await progress.execute(\n \"Loading domains from SES\",\n async () => {\n const response = await sesClient.send(\n new ListEmailIdentitiesCommand({})\n );\n return response.EmailIdentities || [];\n }\n );\n\n // Filter to only domains (not email addresses)\n const domains = identities.filter(\n (identity) =>\n identity.IdentityType === \"DOMAIN\" ||\n (identity.IdentityName && !identity.IdentityName.includes(\"@\"))\n );\n\n progress.stop();\n\n if (domains.length === 0) {\n clack.outro(\"No domains found in SES\");\n console.log(\n `\\nRun ${pc.cyan(\"wraps email domains add <domain>\")} to add a domain.\\n`\n );\n return;\n }\n\n // Get detailed info for each domain\n const domainDetails = await Promise.all(\n domains.map(async (domain) => {\n try {\n const details = await sesClient.send(\n new GetEmailIdentityCommand({\n EmailIdentity: domain.IdentityName!,\n })\n );\n return {\n name: domain.IdentityName!,\n verified: details.VerifiedForSendingStatus,\n dkimStatus: details.DkimAttributes?.Status || \"PENDING\",\n };\n } catch {\n return {\n name: domain.IdentityName!,\n verified: false,\n dkimStatus: \"UNKNOWN\",\n };\n }\n })\n );\n\n // Display domains in a formatted table\n const domainLines = domainDetails.map((domain) => {\n const statusIcon = domain.verified ? pc.green(\"✓\") : pc.yellow(\"⏱\");\n const dkimIcon =\n domain.dkimStatus === \"SUCCESS\" ? pc.green(\"✓\") : pc.yellow(\"⏱\");\n return ` ${statusIcon} ${pc.bold(domain.name)} DKIM: ${dkimIcon} ${domain.dkimStatus}`;\n });\n\n clack.note(\n domainLines.join(\"\\n\"),\n `${domains.length} domain(s) in ${region}`\n );\n clack.outro(\n pc.dim(\n `Run ${pc.cyan(\"wraps email domains verify --domain <domain>\")} for details`\n )\n );\n\n // Track list domains success\n trackCommand(\"email:domains:list\", {\n success: true,\n domain_count: domains.length,\n });\n } catch (error: any) {\n progress.stop();\n trackCommand(\"email:domains:list\", { success: false });\n throw error;\n }\n}\n\n/**\n * Get DKIM tokens for a domain\n */\nexport async function getDkim(options: { domain: string }): Promise<void> {\n clack.intro(pc.bold(`DKIM Tokens for ${options.domain}`));\n\n const progress = new DeploymentProgress();\n const region = await getAWSRegion();\n const sesClient = new SESv2Client({ region });\n\n try {\n const identity = await progress.execute(\n \"Fetching DKIM configuration\",\n async () => {\n const response = await sesClient.send(\n new GetEmailIdentityCommand({ EmailIdentity: options.domain })\n );\n return response;\n }\n );\n\n const dkimTokens = identity.DkimAttributes?.Tokens || [];\n const dkimStatus = identity.DkimAttributes?.Status || \"PENDING\";\n\n progress.stop();\n\n if (dkimTokens.length === 0) {\n clack.outro(pc.yellow(\"No DKIM tokens found for this domain\"));\n return;\n }\n\n // Display DKIM status\n const statusLine = `${pc.bold(\"DKIM Status:\")} ${\n dkimStatus === \"SUCCESS\"\n ? pc.green(\"✓ Verified\")\n : pc.yellow(`⏱ ${dkimStatus}`)\n }`;\n clack.note(statusLine, \"Status\");\n\n // Display DKIM records\n console.log(`\\n${pc.bold(\"DNS Records to add:\")}\\n`);\n for (const token of dkimTokens) {\n console.log(`${pc.cyan(`${token}._domainkey.${options.domain}`)}`);\n console.log(` ${pc.dim(\"Type:\")} CNAME`);\n console.log(` ${pc.dim(\"Value:\")} ${token}.dkim.amazonses.com\\n`);\n }\n\n if (dkimStatus !== \"SUCCESS\") {\n console.log(\n `${pc.dim(\"After adding these records, run:\")} ${pc.cyan(`wraps email domains verify --domain ${options.domain}`)}\\n`\n );\n }\n\n // Track get-dkim success\n trackCommand(\"email:domains:get-dkim\", {\n success: true,\n dkim_status: dkimStatus,\n });\n } catch (error: any) {\n progress.stop();\n trackCommand(\"email:domains:get-dkim\", { success: false });\n if (error.name === \"NotFoundException\") {\n clack.log.error(`Domain ${options.domain} not found in SES`);\n console.log(\n `\\nRun ${pc.cyan(`wraps email domains add ${options.domain}`)} to add this domain.\\n`\n );\n process.exit(1);\n return; // Return after process.exit for testing\n }\n throw error;\n }\n}\n\n/**\n * Remove a domain from SES\n */\nexport async function removeDomain(options: {\n domain: string;\n force?: boolean; // Destructive operation\n}): Promise<void> {\n clack.intro(pc.bold(`Remove domain ${options.domain} from SES`));\n\n const progress = new DeploymentProgress();\n const region = await getAWSRegion();\n const sesClient = new SESv2Client({ region });\n\n try {\n // Check if domain exists\n await progress.execute(\"Checking if domain exists\", async () => {\n await sesClient.send(\n new GetEmailIdentityCommand({ EmailIdentity: options.domain })\n );\n });\n\n progress.stop();\n\n // Confirm deletion\n if (!options.force) {\n const shouldContinue = await clack.confirm({\n message: `Are you sure you want to remove ${pc.red(options.domain)} from SES?`,\n initialValue: false,\n });\n\n if (clack.isCancel(shouldContinue) || !shouldContinue) {\n clack.cancel(\"Operation cancelled\");\n process.exit(0);\n }\n }\n\n // Delete the identity\n const { DeleteEmailIdentityCommand } = await import(\n \"@aws-sdk/client-sesv2\"\n );\n await progress.execute(\"Removing domain from SES\", async () => {\n await sesClient.send(\n new DeleteEmailIdentityCommand({\n EmailIdentity: options.domain,\n })\n );\n });\n\n progress.stop();\n clack.outro(pc.green(`✓ Domain ${options.domain} removed successfully`));\n\n // Track remove domain success\n trackCommand(\"email:domains:remove\", {\n success: true,\n });\n trackFeature(\"domain_removed\", {});\n } catch (error: any) {\n progress.stop();\n trackCommand(\"email:domains:remove\", { success: false });\n if (error.name === \"NotFoundException\") {\n clack.log.error(`Domain ${options.domain} not found in SES`);\n process.exit(1);\n return; // Return after process.exit for testing\n }\n throw error;\n }\n}\n","import * as clack from \"@clack/prompts\";\nimport * as pulumi from \"@pulumi/pulumi\";\nimport pc from \"picocolors\";\nimport { deployEmailStack } from \"../../infrastructure/email-stack.js\";\nimport {\n trackError,\n trackServiceDeployed,\n trackServiceInit,\n} from \"../../telemetry/events.js\";\nimport type {\n EmailStackConfig,\n InitOptions,\n WrapsEmailConfig,\n} from \"../../types/index.js\";\nimport { getCostSummary } from \"../../utils/email/costs.js\";\nimport { getPreset, validateConfig } from \"../../utils/email/presets.js\";\nimport {\n getAWSRegion,\n validateAWSCredentials,\n} from \"../../utils/shared/aws.js\";\nimport { errors } from \"../../utils/shared/errors.js\";\nimport {\n ensurePulumiWorkDir,\n getPulumiWorkDir,\n} from \"../../utils/shared/fs.js\";\nimport {\n createConnectionMetadata,\n loadConnectionMetadata,\n saveConnectionMetadata,\n} from \"../../utils/shared/metadata.js\";\nimport {\n DeploymentProgress,\n displayPreview,\n displaySuccess,\n} from \"../../utils/shared/output.js\";\nimport {\n confirmDeploy,\n promptConfigPreset,\n promptCustomConfig,\n promptDomain,\n promptEstimatedVolume,\n promptProvider,\n promptRegion,\n promptVercelConfig,\n} from \"../../utils/shared/prompts.js\";\nimport { ensurePulumiInstalled } from \"../../utils/shared/pulumi.js\";\n\n/**\n * Init command - Deploy new email infrastructure\n */\nexport async function init(options: InitOptions): Promise<void> {\n const startTime = Date.now();\n\n clack.intro(\n pc.bold(\n options.preview\n ? \"Wraps Email Infrastructure Preview\"\n : \"Wraps Email Infrastructure Setup\"\n )\n );\n\n const progress = new DeploymentProgress();\n\n // 1. Check Pulumi CLI is installed (auto-install if missing)\n const wasAutoInstalled = await progress.execute(\n \"Checking Pulumi CLI installation\",\n async () => await ensurePulumiInstalled()\n );\n\n if (wasAutoInstalled) {\n progress.info(\"Pulumi CLI was automatically installed\");\n }\n\n // 2. Validate AWS credentials\n const identity = await progress.execute(\n \"Validating AWS credentials\",\n async () => validateAWSCredentials()\n );\n\n progress.info(`Connected to AWS account: ${pc.cyan(identity.accountId)}`);\n\n // 3. Get configuration (from options or prompts)\n let provider = options.provider;\n if (!provider) {\n provider = await promptProvider();\n }\n\n let region = options.region;\n if (!region) {\n const defaultRegion = await getAWSRegion();\n region = await promptRegion(defaultRegion);\n }\n\n let domain = options.domain;\n if (!domain) {\n domain = await promptDomain();\n }\n\n // Get Vercel config if needed\n let vercelConfig;\n if (provider === \"vercel\") {\n vercelConfig = await promptVercelConfig();\n }\n\n // 4. Check if connection already exists\n const existingConnection = await loadConnectionMetadata(\n identity.accountId,\n region\n );\n if (existingConnection) {\n clack.log.warn(\n `Connection already exists for account ${pc.cyan(identity.accountId)} in region ${pc.cyan(region)}`\n );\n clack.log.info(`Created: ${existingConnection.timestamp}`);\n clack.log.info(`Use ${pc.cyan(\"wraps status\")} to view current setup`);\n clack.log.info(`Use ${pc.cyan(\"wraps upgrade\")} to add more features`);\n process.exit(0);\n }\n\n // 5. Configuration selection\n let preset = options.preset;\n if (!preset) {\n preset = await promptConfigPreset();\n }\n\n let emailConfig: WrapsEmailConfig;\n if (preset === \"custom\") {\n emailConfig = await promptCustomConfig();\n } else {\n emailConfig = getPreset(preset)!;\n\n // Prompt for email archiving (optional feature for presets)\n const { promptEmailArchiving } = await import(\n \"../../utils/shared/prompts.js\"\n );\n const archivingConfig = await promptEmailArchiving();\n emailConfig.emailArchiving = archivingConfig;\n }\n\n // Set domain if provided\n if (domain) {\n emailConfig.domain = domain;\n }\n\n // Get estimated volume for cost calculation\n const estimatedVolume = await promptEstimatedVolume();\n\n // Display cost summary\n progress.info(`\\n${pc.bold(\"Cost Estimate:\")}`);\n const costSummary = getCostSummary(emailConfig, estimatedVolume);\n clack.log.info(costSummary);\n\n // Validate configuration and show warnings\n const warnings = validateConfig(emailConfig);\n if (warnings.length > 0) {\n progress.info(`\\n${pc.yellow(pc.bold(\"Configuration Warnings:\"))}`);\n for (const warning of warnings) {\n clack.log.warn(warning);\n }\n }\n\n // 6. Create metadata to track deployment\n const metadata = createConnectionMetadata(\n identity.accountId,\n region,\n provider,\n emailConfig,\n preset === \"custom\" ? undefined : preset\n );\n if (vercelConfig) {\n metadata.vercel = vercelConfig;\n }\n\n // Confirm deployment (skip if --yes flag or --preview flag)\n if (!(options.yes || options.preview)) {\n const confirmed = await confirmDeploy();\n if (!confirmed) {\n clack.cancel(\"Deployment cancelled.\");\n process.exit(0);\n }\n }\n\n // 7. Build stack configuration\n const stackConfig: EmailStackConfig = {\n provider,\n region,\n vercel: vercelConfig,\n emailConfig,\n };\n\n // 8. Preview or Deploy infrastructure using Pulumi\n if (options.preview) {\n // PREVIEW MODE - show what would be created without deploying\n try {\n const previewResult = await progress.execute(\n \"Generating infrastructure preview\",\n async () => {\n await ensurePulumiWorkDir();\n\n const stack =\n await pulumi.automation.LocalWorkspace.createOrSelectStack(\n {\n stackName: `wraps-${identity.accountId}-${region}`,\n projectName: \"wraps-email\",\n program: async () => {\n const result = await deployEmailStack(stackConfig);\n return {\n roleArn: result.roleArn,\n configSetName: result.configSetName,\n tableName: result.tableName,\n region: result.region,\n lambdaFunctions: result.lambdaFunctions,\n domain: result.domain,\n dkimTokens: result.dkimTokens,\n customTrackingDomain: result.customTrackingDomain,\n mailFromDomain: result.mailFromDomain,\n archiveArn: result.archiveArn,\n archivingEnabled: result.archivingEnabled,\n archiveRetention: result.archiveRetention,\n };\n },\n },\n {\n workDir: getPulumiWorkDir(),\n envVars: {\n PULUMI_CONFIG_PASSPHRASE: \"\",\n AWS_REGION: region,\n },\n secretsProvider: \"passphrase\",\n }\n );\n\n await stack.setConfig(\"aws:region\", { value: region });\n\n // Run preview instead of deployment\n const result = await stack.preview({ diff: true });\n return result;\n }\n );\n\n // Display preview results\n displayPreview({\n changeSummary: previewResult.changeSummary,\n costEstimate: costSummary,\n commandName: \"wraps email init\",\n });\n\n clack.outro(\n pc.green(\"Preview complete. Run without --preview to deploy.\")\n );\n\n // Track preview completion\n trackServiceInit(\"email\", true, {\n preset,\n provider,\n preview: true,\n duration_ms: Date.now() - startTime,\n });\n return;\n } catch (error: any) {\n trackError(\"PREVIEW_FAILED\", \"email:init\", { step: \"preview\" });\n if (error.message?.includes(\"stack is currently locked\")) {\n throw errors.stackLocked();\n }\n throw new Error(`Preview failed: ${error.message}`);\n }\n }\n\n // DEPLOY MODE - actually create infrastructure\n let outputs;\n try {\n outputs = await progress.execute(\n \"Deploying infrastructure (this may take 2-3 minutes)\",\n async () => {\n // Ensure Pulumi workspace directory exists\n await ensurePulumiWorkDir();\n\n // Run Pulumi inline program with local backend (no cloud required)\n const stack =\n await pulumi.automation.LocalWorkspace.createOrSelectStack(\n {\n stackName: `wraps-${identity.accountId}-${region}`,\n projectName: \"wraps-email\",\n program: async () => {\n const result = await deployEmailStack(stackConfig);\n\n // Export outputs\n return {\n roleArn: result.roleArn,\n configSetName: result.configSetName,\n tableName: result.tableName,\n region: result.region,\n lambdaFunctions: result.lambdaFunctions,\n domain: result.domain,\n dkimTokens: result.dkimTokens,\n customTrackingDomain: result.customTrackingDomain,\n mailFromDomain: result.mailFromDomain,\n archiveArn: result.archiveArn,\n archivingEnabled: result.archivingEnabled,\n archiveRetention: result.archiveRetention,\n };\n },\n },\n {\n workDir: getPulumiWorkDir(),\n // Use local file-based backend (no Pulumi Cloud login required)\n envVars: {\n PULUMI_CONFIG_PASSPHRASE: \"\", // Use empty passphrase for local state\n AWS_REGION: region,\n },\n secretsProvider: \"passphrase\",\n }\n );\n\n // Set backend to local file system\n await stack.workspace.selectStack(\n `wraps-${identity.accountId}-${region}`\n );\n\n // Set AWS region\n await stack.setConfig(\"aws:region\", { value: region });\n\n // Run the deployment\n const upResult = await stack.up({ onOutput: () => {} }); // Suppress Pulumi output\n\n // Get outputs\n const pulumiOutputs = upResult.outputs;\n\n return {\n roleArn: pulumiOutputs.roleArn?.value as string,\n configSetName: pulumiOutputs.configSetName?.value as\n | string\n | undefined,\n tableName: pulumiOutputs.tableName?.value as string | undefined,\n region: pulumiOutputs.region?.value as string,\n lambdaFunctions: pulumiOutputs.lambdaFunctions?.value as\n | string[]\n | undefined,\n domain: pulumiOutputs.domain?.value as string | undefined,\n dkimTokens: pulumiOutputs.dkimTokens?.value as string[] | undefined,\n customTrackingDomain: pulumiOutputs.customTrackingDomain?.value as\n | string\n | undefined,\n mailFromDomain: pulumiOutputs.mailFromDomain?.value as\n | string\n | undefined,\n archiveArn: pulumiOutputs.archiveArn?.value as string | undefined,\n archivingEnabled: pulumiOutputs.archivingEnabled?.value as\n | boolean\n | undefined,\n archiveRetention: pulumiOutputs.archiveRetention?.value as\n | string\n | undefined,\n };\n }\n );\n } catch (error: any) {\n // Track deployment failure\n trackServiceInit(\"email\", false, {\n preset,\n provider,\n duration_ms: Date.now() - startTime,\n });\n\n // Check if it's a lock file error\n if (error.message?.includes(\"stack is currently locked\")) {\n trackError(\"STACK_LOCKED\", \"email:init\", { step: \"deploy\" });\n throw errors.stackLocked();\n }\n\n trackError(\"DEPLOYMENT_FAILED\", \"email:init\", { step: \"deploy\" });\n throw new Error(`Pulumi deployment failed: ${error.message}`);\n }\n\n // 9. Save metadata for future upgrades and restore\n if (metadata.services.email) {\n metadata.services.email.pulumiStackName = `wraps-${identity.accountId}-${region}`;\n // Save computed values from Pulumi outputs back to config\n // These may have been computed during deployment (e.g., mailFromDomain from mailFromSubdomain)\n if (outputs.mailFromDomain) {\n metadata.services.email.config.mailFromDomain = outputs.mailFromDomain;\n }\n if (outputs.customTrackingDomain && metadata.services.email.config.tracking) {\n metadata.services.email.config.tracking.customRedirectDomain =\n outputs.customTrackingDomain;\n }\n }\n await saveConnectionMetadata(metadata);\n\n progress.info(\"Connection metadata saved for upgrade and restore capability\");\n\n // 10. Check if Route53 hosted zone exists and create DNS records automatically\n let dnsAutoCreated = false;\n if (outputs.domain && outputs.dkimTokens && outputs.dkimTokens.length > 0) {\n const { findHostedZone, createDNSRecords } = await import(\n \"../../utils/email/route53.js\"\n );\n const hostedZone = await findHostedZone(outputs.domain, region);\n\n if (hostedZone) {\n try {\n progress.start(\"Creating DNS records in Route53\");\n await createDNSRecords(\n hostedZone.id,\n outputs.domain,\n outputs.dkimTokens,\n region,\n outputs.customTrackingDomain,\n outputs.mailFromDomain\n );\n progress.succeed(\"DNS records created in Route53\");\n dnsAutoCreated = true;\n } catch (error: any) {\n progress.fail(\"Failed to create DNS records in Route53\");\n clack.log.warn(`Could not auto-create DNS records: ${error.message}`);\n }\n }\n }\n\n // 11. Format DNS records if domain was provided and DNS wasn't auto-created\n const dnsRecords = [];\n if (\n outputs.domain &&\n outputs.dkimTokens &&\n outputs.dkimTokens.length > 0 &&\n !dnsAutoCreated\n ) {\n // Add DKIM CNAME records\n for (const token of outputs.dkimTokens) {\n dnsRecords.push({\n name: `${token}._domainkey.${outputs.domain}`,\n type: \"CNAME\",\n value: `${token}.dkim.amazonses.com`,\n });\n }\n }\n\n // 12. Display success message\n displaySuccess({\n roleArn: outputs.roleArn,\n configSetName: outputs.configSetName,\n region: outputs.region!,\n tableName: outputs.tableName,\n dnsRecords: dnsRecords.length > 0 ? dnsRecords : undefined,\n dnsAutoCreated,\n domain: outputs.domain,\n mailFromDomain: outputs.mailFromDomain,\n });\n\n // 13. Track successful deployment\n const duration = Date.now() - startTime;\n const enabledFeatures: string[] = [];\n if (emailConfig.tracking?.enabled) enabledFeatures.push(\"tracking\");\n if (emailConfig.suppressionList?.enabled)\n enabledFeatures.push(\"suppression_list\");\n if (emailConfig.eventTracking?.enabled)\n enabledFeatures.push(\"event_tracking\");\n if (emailConfig.eventTracking?.dynamoDBHistory)\n enabledFeatures.push(\"dynamodb_history\");\n if (emailConfig.dedicatedIp) enabledFeatures.push(\"dedicated_ip\");\n if (emailConfig.emailArchiving?.enabled)\n enabledFeatures.push(\"email_archiving\");\n\n trackServiceInit(\"email\", true, {\n preset,\n provider,\n features: enabledFeatures,\n duration_ms: duration,\n });\n\n trackServiceDeployed(\"email\", {\n duration_ms: duration,\n features: enabledFeatures,\n preset,\n });\n}\n","import * as clack from \"@clack/prompts\";\nimport * as pulumi from \"@pulumi/pulumi\";\nimport pc from \"picocolors\";\nimport {\n trackError,\n trackServiceRemoved,\n} from \"../../telemetry/events.js\";\nimport type { EmailRestoreOptions } from \"../../types/index.js\";\nimport {\n getAWSRegion,\n validateAWSCredentials,\n} from \"../../utils/shared/aws.js\";\nimport { getPulumiWorkDir } from \"../../utils/shared/fs.js\";\nimport {\n deleteConnectionMetadata,\n loadConnectionMetadata,\n} from \"../../utils/shared/metadata.js\";\nimport {\n DeploymentProgress,\n displayPreview,\n} from \"../../utils/shared/output.js\";\n\n/**\n * Restore command - Remove Wraps infrastructure (alias for destroy)\n *\n * Note: This command removes all Wraps-managed resources.\n * Since Wraps always creates NEW resources (wraps- prefix) and never modifies\n * existing infrastructure, there's nothing to \"restore\" - only to remove.\n */\nexport async function restore(options: EmailRestoreOptions): Promise<void> {\n const startTime = Date.now();\n\n clack.intro(\n pc.bold(\n options.preview\n ? \"Wraps Restore Preview\"\n : \"Wraps Restore - Remove Wraps Infrastructure\"\n )\n );\n\n clack.log.info(\n `${pc.yellow(\"Note:\")} This will remove all Wraps-managed infrastructure.`\n );\n clack.log.info(\n \"Your original AWS resources remain untouched (Wraps never modifies them).\\n\"\n );\n\n const progress = new DeploymentProgress();\n\n // 1. Validate AWS credentials\n const identity = await progress.execute(\n \"Validating AWS credentials\",\n async () => validateAWSCredentials()\n );\n\n progress.info(`Connected to AWS account: ${pc.cyan(identity.accountId)}`);\n\n // 2. Get region\n let region = options.region;\n if (!region) {\n const defaultRegion = await getAWSRegion();\n region = defaultRegion;\n }\n\n // 3. Load connection metadata\n const metadata = await loadConnectionMetadata(identity.accountId, region);\n\n if (!metadata) {\n clack.log.error(\n `No Wraps connection found for account ${pc.cyan(identity.accountId)} in region ${pc.cyan(region)}`\n );\n clack.log.info(\n `Use ${pc.cyan(\"wraps email init\")} or ${pc.cyan(\"wraps email connect\")} to create a connection first.`\n );\n process.exit(1);\n }\n\n progress.info(`Found connection created: ${metadata.timestamp}`);\n\n // 4. Display what will be removed\n console.log(\n `\\n${pc.bold(\"The following Wraps resources will be removed:\")}\\n`\n );\n\n if (metadata.services.email?.config.tracking?.enabled) {\n console.log(` ${pc.cyan(\"✓\")} Configuration Set (wraps-email-tracking)`);\n }\n if (metadata.services.email?.config.eventTracking?.dynamoDBHistory) {\n console.log(` ${pc.cyan(\"✓\")} DynamoDB Table (wraps-email-history)`);\n }\n if (metadata.services.email?.config.eventTracking?.enabled) {\n console.log(` ${pc.cyan(\"✓\")} EventBridge Rules`);\n console.log(` ${pc.cyan(\"✓\")} SQS Queues`);\n console.log(` ${pc.cyan(\"✓\")} Lambda Functions`);\n }\n console.log(` ${pc.cyan(\"✓\")} IAM Role (wraps-email-role)`);\n console.log(\"\");\n\n // 5. Confirm removal (skip if --force or --preview)\n if (!(options.force || options.preview)) {\n const confirmed = await clack.confirm({\n message: \"Proceed with removal? This cannot be undone.\",\n initialValue: false,\n });\n\n if (clack.isCancel(confirmed) || !confirmed) {\n clack.cancel(\"Removal cancelled.\");\n process.exit(0);\n }\n }\n\n // 6. Preview or Destroy Pulumi stack\n if (options.preview) {\n // PREVIEW MODE - show what would be destroyed without actually destroying\n if (metadata.services.email?.pulumiStackName) {\n try {\n const previewResult = await progress.execute(\n \"Generating removal preview\",\n async () => {\n const stack = await pulumi.automation.LocalWorkspace.selectStack(\n {\n stackName: metadata.services.email!.pulumiStackName!,\n projectName: \"wraps-email\",\n program: async () => {}, // Empty program for destroy\n },\n {\n workDir: getPulumiWorkDir(),\n envVars: {\n PULUMI_CONFIG_PASSPHRASE: \"\",\n AWS_REGION: region,\n },\n secretsProvider: \"passphrase\",\n }\n );\n\n // Run preview to see what would be destroyed\n const result = await stack.preview({ diff: true });\n return result;\n }\n );\n\n // Display preview results\n displayPreview({\n changeSummary: previewResult.changeSummary,\n costEstimate: \"Monthly cost after removal: $0.00\",\n commandName: \"wraps email restore\",\n });\n\n clack.outro(\n pc.green(\n \"Preview complete. Run without --preview to remove infrastructure.\"\n )\n );\n\n // Track preview completion\n trackServiceRemoved(\"email\", {\n preview: true,\n duration_ms: Date.now() - startTime,\n });\n return;\n } catch (error: any) {\n trackError(\"PREVIEW_FAILED\", \"email:restore\", { step: \"preview\" });\n throw new Error(`Preview failed: ${error.message}`);\n }\n }\n return;\n }\n\n // DESTROY MODE - actually remove infrastructure\n if (metadata.services.email?.pulumiStackName) {\n await progress.execute(\"Removing Wraps infrastructure\", async () => {\n try {\n if (!metadata.services.email?.pulumiStackName) {\n throw new Error(\"No Pulumi stack name found in metadata\");\n }\n\n const stack = await pulumi.automation.LocalWorkspace.selectStack(\n {\n stackName: metadata.services.email.pulumiStackName,\n projectName: \"wraps-email\",\n program: async () => {}, // Empty program\n },\n {\n workDir: getPulumiWorkDir(),\n envVars: {\n PULUMI_CONFIG_PASSPHRASE: \"\",\n AWS_REGION: region,\n },\n secretsProvider: \"passphrase\",\n }\n );\n\n // Destroy the stack\n await stack.destroy({ onOutput: () => {} });\n\n // Remove the stack\n await stack.workspace.removeStack(\n metadata.services.email.pulumiStackName\n );\n } catch (error: any) {\n trackError(\"DESTROY_FAILED\", \"email:restore\", { step: \"destroy\" });\n throw new Error(`Failed to destroy Pulumi stack: ${error.message}`);\n }\n });\n }\n\n // 7. Delete connection metadata\n await deleteConnectionMetadata(identity.accountId, region);\n\n progress.info(\"Connection metadata deleted\");\n\n // 8. Success message\n console.log(\n `\\n${pc.green(\"✓\")} ${pc.bold(\"Infrastructure removed successfully!\")}\\n`\n );\n console.log(\n `${pc.dim(\"All Wraps resources have been deleted from your AWS account.\")}`\n );\n console.log(`${pc.dim(\"Your original AWS resources remain unchanged.\")}\\n`);\n\n // 9. Track successful removal\n trackServiceRemoved(\"email\", {\n reason: \"user_initiated\",\n duration_ms: Date.now() - startTime,\n });\n}\n","import * as clack from \"@clack/prompts\";\nimport * as pulumi from \"@pulumi/pulumi\";\nimport pc from \"picocolors\";\nimport { trackCommand } from \"../../telemetry/events.js\";\nimport type { StatusOptions } from \"../../types/index.js\";\nimport {\n getAWSRegion,\n listSESDomains,\n validateAWSCredentials,\n} from \"../../utils/shared/aws.js\";\nimport {\n ensurePulumiWorkDir,\n getPulumiWorkDir,\n} from \"../../utils/shared/fs.js\";\nimport {\n DeploymentProgress,\n displayStatus,\n} from \"../../utils/shared/output.js\";\n\n/**\n * Email Status command - Show current email infrastructure setup\n */\nexport async function emailStatus(_options: StatusOptions): Promise<void> {\n const startTime = Date.now();\n const progress = new DeploymentProgress();\n\n clack.intro(pc.bold(\"Wraps Email Status\"));\n\n // 1. Validate AWS credentials\n const identity = await progress.execute(\n \"Loading email infrastructure status\",\n async () => validateAWSCredentials()\n );\n\n // 2. Get region\n const region = await getAWSRegion();\n\n // 3. Try to load Pulumi stack\n let stackOutputs: any = {};\n try {\n // Ensure Pulumi workspace is configured (sets backend URL)\n await ensurePulumiWorkDir();\n\n const stack = await pulumi.automation.LocalWorkspace.selectStack({\n stackName: `wraps-${identity.accountId}-${region}`,\n workDir: getPulumiWorkDir(),\n });\n\n stackOutputs = await stack.outputs();\n } catch (_error: any) {\n progress.stop();\n clack.log.error(\"No email infrastructure found\");\n console.log(\n `\\nRun ${pc.cyan(\"wraps email init\")} to deploy email infrastructure.\\n`\n );\n process.exit(1);\n }\n\n // 4. Get SES domains with DKIM tokens\n const domains = await listSESDomains(region);\n\n // 4a. Fetch DKIM tokens for each domain\n const { SESv2Client, GetEmailIdentityCommand } = await import(\n \"@aws-sdk/client-sesv2\"\n );\n const sesv2Client = new SESv2Client({ region });\n\n const domainsWithTokens = await Promise.all(\n domains.map(async (d) => {\n try {\n const identity = await sesv2Client.send(\n new GetEmailIdentityCommand({ EmailIdentity: d.domain })\n );\n return {\n domain: d.domain,\n status: d.verified ? (\"verified\" as const) : (\"pending\" as const),\n dkimTokens: identity.DkimAttributes?.Tokens || [],\n mailFromDomain: identity.MailFromAttributes?.MailFromDomain,\n mailFromStatus: identity.MailFromAttributes?.MailFromDomainStatus,\n };\n } catch (_error) {\n return {\n domain: d.domain,\n status: d.verified ? (\"verified\" as const) : (\"pending\" as const),\n dkimTokens: undefined,\n mailFromDomain: undefined,\n mailFromStatus: undefined,\n };\n }\n })\n );\n\n // 5. Determine integration level\n const integrationLevel = stackOutputs.configSetName\n ? \"enhanced\"\n : \"dashboard-only\";\n\n // 6. Display status\n progress.stop();\n displayStatus({\n integrationLevel: integrationLevel as \"dashboard-only\" | \"enhanced\",\n region,\n domains: domainsWithTokens,\n resources: {\n roleArn: stackOutputs.roleArn?.value,\n configSetName: stackOutputs.configSetName?.value,\n tableName: stackOutputs.tableName?.value,\n lambdaFunctions: stackOutputs.lambdaFunctions?.value?.length || 0,\n snsTopics: integrationLevel === \"enhanced\" ? 1 : 0,\n archiveArn: stackOutputs.archiveArn?.value,\n archivingEnabled: stackOutputs.archivingEnabled?.value,\n archiveRetention: stackOutputs.archiveRetention?.value,\n },\n tracking: stackOutputs.customTrackingDomain?.value\n ? {\n customTrackingDomain: stackOutputs.customTrackingDomain?.value,\n httpsEnabled: stackOutputs.httpsTrackingEnabled?.value,\n cloudFrontDomain: stackOutputs.cloudFrontDomain?.value,\n }\n : undefined,\n });\n\n // 7. Track status command\n trackCommand(\"email:status\", {\n success: true,\n domain_count: domainsWithTokens.length,\n integration_level: integrationLevel,\n duration_ms: Date.now() - startTime,\n });\n}\n","import * as clack from \"@clack/prompts\";\nimport * as pulumi from \"@pulumi/pulumi\";\nimport pc from \"picocolors\";\nimport { deployEmailStack } from \"../../infrastructure/email-stack.js\";\nimport {\n trackError,\n trackServiceUpgrade,\n} from \"../../telemetry/events.js\";\nimport type {\n EmailStackConfig,\n UpgradeOptions,\n WrapsEmailConfig,\n} from \"../../types/index.js\";\nimport { calculateCosts, formatCost } from \"../../utils/email/costs.js\";\nimport { getAllPresetInfo, getPreset } from \"../../utils/email/presets.js\";\nimport {\n getAWSRegion,\n validateAWSCredentials,\n} from \"../../utils/shared/aws.js\";\nimport { errors } from \"../../utils/shared/errors.js\";\nimport {\n ensurePulumiWorkDir,\n getPulumiWorkDir,\n} from \"../../utils/shared/fs.js\";\nimport {\n applyConfigUpdates,\n loadConnectionMetadata,\n saveConnectionMetadata,\n updateEmailConfig,\n} from \"../../utils/shared/metadata.js\";\nimport {\n DeploymentProgress,\n displayPreview,\n displaySuccess,\n} from \"../../utils/shared/output.js\";\nimport { promptVercelConfig } from \"../../utils/shared/prompts.js\";\nimport { ensurePulumiInstalled } from \"../../utils/shared/pulumi.js\";\n\n/**\n * Upgrade command - Enhance existing Wraps infrastructure\n */\nexport async function upgrade(options: UpgradeOptions): Promise<void> {\n const startTime = Date.now();\n let upgradeAction: string | symbol = \"\";\n\n clack.intro(\n pc.bold(\n options.preview\n ? \"Wraps Upgrade Preview\"\n : \"Wraps Upgrade - Enhance Your Email Infrastructure\"\n )\n );\n\n const progress = new DeploymentProgress();\n\n // 1. Check Pulumi CLI is installed\n const wasAutoInstalled = await progress.execute(\n \"Checking Pulumi CLI installation\",\n async () => await ensurePulumiInstalled()\n );\n\n if (wasAutoInstalled) {\n progress.info(\"Pulumi CLI was automatically installed\");\n }\n\n // 2. Validate AWS credentials\n const identity = await progress.execute(\n \"Validating AWS credentials\",\n async () => validateAWSCredentials()\n );\n\n progress.info(`Connected to AWS account: ${pc.cyan(identity.accountId)}`);\n\n // 3. Get region\n let region = options.region;\n if (!region) {\n const defaultRegion = await getAWSRegion();\n region = defaultRegion;\n }\n\n // 4. Load existing connection metadata\n const metadata = await loadConnectionMetadata(identity.accountId, region);\n\n if (!metadata) {\n clack.log.error(\n `No Wraps connection found for account ${pc.cyan(identity.accountId)} in region ${pc.cyan(region)}`\n );\n clack.log.info(\n `Use ${pc.cyan(\"wraps email init\")} to create new infrastructure or ${pc.cyan(\"wraps email connect\")} to connect existing.`\n );\n process.exit(1);\n }\n\n progress.info(`Found existing connection created: ${metadata.timestamp}`);\n\n // 5. Display current configuration\n console.log(`\\n${pc.bold(\"Current Configuration:\")}\\n`);\n\n if (metadata.services.email?.preset) {\n console.log(` Preset: ${pc.cyan(metadata.services.email?.preset)}`);\n } else {\n console.log(` Preset: ${pc.cyan(\"custom\")}`);\n }\n\n const config = metadata.services.email?.config;\n\n if (!config) {\n clack.log.error(\"No email configuration found in metadata\");\n clack.log.info(\n `Use ${pc.cyan(\"wraps email init\")} to create new infrastructure.`\n );\n process.exit(1);\n }\n\n // Show sending domain if configured\n if (config.domain) {\n console.log(` Sending Domain: ${pc.cyan(config.domain)}`);\n }\n\n if (config.tracking?.enabled) {\n console.log(` ${pc.green(\"✓\")} Open & Click Tracking`);\n if (config.tracking.customRedirectDomain) {\n console.log(\n ` ${pc.dim(\"└─\")} Custom domain: ${pc.cyan(config.tracking.customRedirectDomain)}`\n );\n }\n }\n\n if (config.suppressionList?.enabled) {\n console.log(` ${pc.green(\"✓\")} Bounce/Complaint Suppression`);\n }\n\n if (config.eventTracking?.enabled) {\n console.log(` ${pc.green(\"✓\")} Event Tracking (EventBridge)`);\n if (config.eventTracking.dynamoDBHistory) {\n console.log(\n ` ${pc.dim(\"└─\")} Email History: ${pc.cyan(config.eventTracking.archiveRetention || \"90days\")}`\n );\n }\n }\n\n if (config.dedicatedIp) {\n console.log(` ${pc.green(\"✓\")} Dedicated IP Address`);\n }\n\n if (config.emailArchiving?.enabled) {\n const retentionLabel =\n {\n \"7days\": \"7 days\",\n \"30days\": \"30 days\",\n \"90days\": \"90 days\",\n \"3months\": \"3 months\",\n \"6months\": \"6 months\",\n \"9months\": \"9 months\",\n \"1year\": \"1 year\",\n \"18months\": \"18 months\",\n \"2years\": \"2 years\",\n \"30months\": \"30 months\",\n \"3years\": \"3 years\",\n \"4years\": \"4 years\",\n \"5years\": \"5 years\",\n \"6years\": \"6 years\",\n \"7years\": \"7 years\",\n \"8years\": \"8 years\",\n \"9years\": \"9 years\",\n \"10years\": \"10 years\",\n indefinite: \"indefinite\",\n permanent: \"permanent\",\n }[config.emailArchiving.retention] || \"90 days\";\n console.log(` ${pc.green(\"✓\")} Email Archiving (${retentionLabel})`);\n }\n\n // Calculate current cost\n const currentCostData = calculateCosts(config, 50_000); // Assume 50k emails/mo for estimate\n console.log(\n `\\n Estimated Cost: ${pc.cyan(`~${formatCost(currentCostData.total.monthly)}/mo`)}`\n );\n\n console.log(\"\");\n\n // 6. Prompt for upgrade action\n upgradeAction = await clack.select({\n message: \"What would you like to do?\",\n options: [\n {\n value: \"preset\",\n label: \"Upgrade to a different preset\",\n hint: \"Starter → Production → Enterprise\",\n },\n {\n value: \"archiving\",\n label: config.emailArchiving?.enabled\n ? \"Change email archiving settings\"\n : \"Enable email archiving\",\n hint: config.emailArchiving?.enabled\n ? \"Update retention or disable\"\n : \"Store full email content with HTML\",\n },\n {\n value: \"tracking-domain\",\n label: \"Add/change custom tracking domain\",\n hint: \"Use your own domain for email links\",\n },\n {\n value: \"retention\",\n label: \"Change email history retention\",\n hint: \"7 days, 30 days, 90 days, 6 months, 1 year, 18 months\",\n },\n {\n value: \"events\",\n label: \"Customize tracked event types\",\n hint: \"Choose which SES events to track\",\n },\n {\n value: \"dedicated-ip\",\n label: \"Enable dedicated IP address\",\n hint: \"Requires 100k+ emails/day ($50-100/mo)\",\n },\n {\n value: \"custom\",\n label: \"Custom configuration\",\n hint: \"Modify multiple settings at once\",\n },\n ],\n });\n\n if (clack.isCancel(upgradeAction)) {\n clack.cancel(\"Upgrade cancelled.\");\n process.exit(0);\n }\n\n let updatedConfig: WrapsEmailConfig = { ...config };\n let newPreset: string | undefined = metadata.services.email?.preset;\n\n // 7. Handle upgrade action\n switch (upgradeAction) {\n case \"preset\": {\n // Show available presets\n const presets = getAllPresetInfo();\n const currentPresetIdx = presets.findIndex(\n (p) => p.name.toLowerCase() === metadata.services.email?.preset\n );\n\n const availablePresets = presets\n .map((p, idx) => ({\n value: p.name.toLowerCase(),\n label: `${p.name} - ${p.description}`,\n hint: `${p.volume} | Est. ${p.estimatedCost}/mo`,\n disabled:\n currentPresetIdx >= 0 && idx <= currentPresetIdx\n ? \"Current or lower tier\"\n : undefined,\n }))\n .filter((p) => !p.disabled);\n\n if (availablePresets.length === 0) {\n clack.log.warn(\"Already on highest preset (Enterprise)\");\n process.exit(0);\n }\n\n const selectedPreset = await clack.select({\n message: \"Select new preset:\",\n options: availablePresets,\n });\n\n if (clack.isCancel(selectedPreset)) {\n clack.cancel(\"Upgrade cancelled.\");\n process.exit(0);\n }\n\n // Get preset config but preserve user-customized fields from existing config\n const presetConfig = getPreset(selectedPreset as any)!;\n\n // Apply preset updates to existing config (preserves user customizations)\n updatedConfig = applyConfigUpdates(config, presetConfig);\n newPreset = selectedPreset as string;\n break;\n }\n\n case \"archiving\": {\n if (config.emailArchiving?.enabled) {\n // Already enabled - allow changing retention or disabling\n const archivingAction = await clack.select({\n message: \"What would you like to do with email archiving?\",\n options: [\n {\n value: \"change-retention\",\n label: \"Change retention period\",\n hint: `Current: ${config.emailArchiving.retention}`,\n },\n {\n value: \"disable\",\n label: \"Disable email archiving\",\n hint: \"Stop storing full email content\",\n },\n ],\n });\n\n if (clack.isCancel(archivingAction)) {\n clack.cancel(\"Upgrade cancelled.\");\n process.exit(0);\n }\n\n if (archivingAction === \"disable\") {\n const confirmDisable = await clack.confirm({\n message:\n \"Are you sure? Existing archived emails will remain, but new emails won't be archived.\",\n initialValue: false,\n });\n\n if (clack.isCancel(confirmDisable) || !confirmDisable) {\n clack.cancel(\"Archiving not disabled.\");\n process.exit(0);\n }\n\n updatedConfig = {\n ...config,\n emailArchiving: {\n enabled: false,\n retention: config.emailArchiving.retention,\n },\n };\n } else {\n // Change retention\n const retention = await clack.select({\n message: \"Email archive retention period:\",\n options: [\n {\n value: \"7days\",\n label: \"7 days\",\n hint: \"~$1-2/mo for 10k emails\",\n },\n {\n value: \"30days\",\n label: \"30 days\",\n hint: \"~$2-4/mo for 10k emails\",\n },\n {\n value: \"90days\",\n label: \"90 days (recommended)\",\n hint: \"~$5-10/mo for 10k emails\",\n },\n {\n value: \"6months\",\n label: \"6 months\",\n hint: \"~$15-25/mo for 10k emails\",\n },\n {\n value: \"1year\",\n label: \"1 year\",\n hint: \"~$25-40/mo for 10k emails\",\n },\n {\n value: \"18months\",\n label: \"18 months\",\n hint: \"~$35-60/mo for 10k emails\",\n },\n ],\n initialValue: config.emailArchiving.retention,\n });\n\n if (clack.isCancel(retention)) {\n clack.cancel(\"Upgrade cancelled.\");\n process.exit(0);\n }\n\n updatedConfig = {\n ...config,\n emailArchiving: {\n enabled: true,\n retention: retention as any,\n },\n };\n }\n } else {\n // Not enabled - prompt to enable with retention selection\n const enableArchiving = await clack.confirm({\n message:\n \"Enable email archiving? (Store full email content with HTML for viewing)\",\n initialValue: true,\n });\n\n if (clack.isCancel(enableArchiving)) {\n clack.cancel(\"Upgrade cancelled.\");\n process.exit(0);\n }\n\n if (!enableArchiving) {\n clack.log.info(\"Email archiving not enabled.\");\n process.exit(0);\n }\n\n const retention = await clack.select({\n message: \"Email archive retention period:\",\n options: [\n {\n value: \"7days\",\n label: \"7 days\",\n hint: \"~$1-2/mo for 10k emails\",\n },\n {\n value: \"30days\",\n label: \"30 days\",\n hint: \"~$2-4/mo for 10k emails\",\n },\n {\n value: \"90days\",\n label: \"90 days (recommended)\",\n hint: \"~$5-10/mo for 10k emails\",\n },\n {\n value: \"6months\",\n label: \"6 months\",\n hint: \"~$15-25/mo for 10k emails\",\n },\n {\n value: \"1year\",\n label: \"1 year\",\n hint: \"~$25-40/mo for 10k emails\",\n },\n {\n value: \"18months\",\n label: \"18 months\",\n hint: \"~$35-60/mo for 10k emails\",\n },\n ],\n initialValue: \"90days\",\n });\n\n if (clack.isCancel(retention)) {\n clack.cancel(\"Upgrade cancelled.\");\n process.exit(0);\n }\n\n clack.log.info(\n pc.dim(\n \"Archiving stores full RFC 822 emails with HTML, attachments, and headers\"\n )\n );\n clack.log.info(\n pc.dim(\n \"Cost: $2/GB ingestion + $0.19/GB/month storage (~50KB per email)\"\n )\n );\n\n updatedConfig = {\n ...config,\n emailArchiving: {\n enabled: true,\n retention: retention as any,\n },\n };\n }\n newPreset = undefined; // Custom config\n break;\n }\n\n case \"tracking-domain\": {\n // First, check if a sending identity (domain) is configured and verified\n if (!config.domain) {\n clack.log.error(\n \"No sending domain configured. You must configure a sending domain before adding a custom tracking domain.\"\n );\n clack.log.info(\n `Use ${pc.cyan(\"wraps email init\")} to set up a sending domain first.`\n );\n process.exit(1);\n }\n\n // Verify that the sending identity is verified\n const { listSESDomains } = await import(\"../../utils/shared/aws.js\");\n const domains = await progress.execute(\n \"Checking domain verification status\",\n async () => await listSESDomains(region)\n );\n\n const sendingDomain = domains.find((d) => d.domain === config.domain);\n\n if (!sendingDomain?.verified) {\n clack.log.error(\n `Sending domain ${pc.cyan(config.domain)} is not verified.`\n );\n clack.log.info(\n \"You must verify your sending domain before adding a custom tracking domain.\"\n );\n clack.log.info(\n `Use ${pc.cyan(\"wraps email verify\")} to check DNS records and complete verification.`\n );\n process.exit(1);\n }\n\n progress.info(\n `Sending domain ${pc.cyan(config.domain)} is verified ${pc.green(\"✓\")}`\n );\n\n const trackingDomain = await clack.text({\n message: \"Custom tracking redirect domain:\",\n placeholder: \"track.yourdomain.com\",\n initialValue: config.tracking?.customRedirectDomain || \"\",\n validate: (value) => {\n if (value && !/^[a-z0-9.-]+\\.[a-z]{2,}$/.test(value)) {\n return \"Please enter a valid domain\";\n }\n },\n });\n\n if (clack.isCancel(trackingDomain)) {\n clack.cancel(\"Upgrade cancelled.\");\n process.exit(0);\n }\n\n // Ask if HTTPS tracking should be enabled\n const enableHttps = await clack.confirm({\n message: \"Enable HTTPS tracking with CloudFront + SSL certificate?\",\n initialValue: true,\n });\n\n if (clack.isCancel(enableHttps)) {\n clack.cancel(\"Upgrade cancelled.\");\n process.exit(0);\n }\n\n if (enableHttps) {\n clack.log.info(\n pc.dim(\n \"HTTPS tracking creates a CloudFront distribution with an SSL certificate.\"\n )\n );\n clack.log.info(\n pc.dim(\n \"This ensures all tracking links use secure HTTPS connections.\"\n )\n );\n\n // Check if domain has Route53 hosted zone\n const { findHostedZone } = await import(\"../../utils/email/route53.js\");\n const hostedZone = await progress.execute(\n \"Checking for Route53 hosted zone\",\n async () =>\n await findHostedZone(trackingDomain || config.domain!, region)\n );\n\n if (hostedZone) {\n progress.info(\n `Found Route53 hosted zone: ${pc.cyan(hostedZone.name)} ${pc.green(\"✓\")}`\n );\n clack.log.info(\n pc.dim(\n \"DNS records (SSL certificate validation + CloudFront) will be created automatically.\"\n )\n );\n } else {\n clack.log.warn(\n `No Route53 hosted zone found for ${pc.cyan(trackingDomain || config.domain!)}`\n );\n clack.log.info(\n pc.dim(\n \"You'll need to manually create DNS records for SSL certificate validation and CloudFront.\"\n )\n );\n clack.log.info(\n pc.dim(\"DNS record details will be shown after deployment.\")\n );\n }\n\n const confirmHttps = await clack.confirm({\n message: hostedZone\n ? \"Proceed with automatic HTTPS setup?\"\n : \"Proceed with manual HTTPS setup (requires DNS configuration)?\",\n initialValue: true,\n });\n\n if (clack.isCancel(confirmHttps) || !confirmHttps) {\n clack.log.info(\"HTTPS tracking not enabled. Using HTTP tracking.\");\n updatedConfig = {\n ...config,\n tracking: {\n ...config.tracking,\n enabled: true,\n customRedirectDomain: trackingDomain || undefined,\n httpsEnabled: false,\n },\n };\n } else {\n updatedConfig = {\n ...config,\n tracking: {\n ...config.tracking,\n enabled: true,\n customRedirectDomain: trackingDomain || undefined,\n httpsEnabled: true,\n },\n };\n }\n } else {\n clack.log.info(\n pc.dim(\n \"Using HTTP tracking (standard). Links will use http:// protocol.\"\n )\n );\n updatedConfig = {\n ...config,\n tracking: {\n ...config.tracking,\n enabled: true,\n customRedirectDomain: trackingDomain || undefined,\n httpsEnabled: false,\n },\n };\n }\n\n newPreset = undefined; // Custom config\n break;\n }\n\n case \"retention\": {\n const retention = await clack.select({\n message: \"Email history retention period (event data in DynamoDB):\",\n options: [\n { value: \"7days\", label: \"7 days\", hint: \"Minimal storage cost\" },\n { value: \"30days\", label: \"30 days\", hint: \"Development/testing\" },\n {\n value: \"90days\",\n label: \"90 days (recommended)\",\n hint: \"Standard retention\",\n },\n {\n value: \"6months\",\n label: \"6 months\",\n hint: \"Extended retention\",\n },\n { value: \"1year\", label: \"1 year\", hint: \"Compliance requirements\" },\n {\n value: \"18months\",\n label: \"18 months\",\n hint: \"Long-term retention\",\n },\n ],\n initialValue: config.eventTracking?.archiveRetention || \"90days\",\n });\n\n if (clack.isCancel(retention)) {\n clack.cancel(\"Upgrade cancelled.\");\n process.exit(0);\n }\n\n clack.log.info(\n pc.dim(\n \"Note: This is for event data (sent, delivered, opened, etc.) stored in DynamoDB.\"\n )\n );\n clack.log.info(\n pc.dim(\n \"For full email content storage, use 'Enable email archiving' option.\"\n )\n );\n\n updatedConfig = {\n ...config,\n eventTracking: {\n ...config.eventTracking,\n enabled: true,\n dynamoDBHistory: true,\n archiveRetention: retention as any,\n },\n };\n newPreset = undefined; // Custom config\n break;\n }\n\n case \"events\": {\n const selectedEvents = await clack.multiselect({\n message: \"Select SES event types to track:\",\n options: [\n { value: \"SEND\", label: \"Send\", hint: \"Email sent to SES\" },\n {\n value: \"DELIVERY\",\n label: \"Delivery\",\n hint: \"Email delivered successfully\",\n },\n { value: \"OPEN\", label: \"Open\", hint: \"Recipient opened email\" },\n { value: \"CLICK\", label: \"Click\", hint: \"Recipient clicked link\" },\n { value: \"BOUNCE\", label: \"Bounce\", hint: \"Email bounced\" },\n {\n value: \"COMPLAINT\",\n label: \"Complaint\",\n hint: \"Spam complaint received\",\n },\n { value: \"REJECT\", label: \"Reject\", hint: \"Email rejected by SES\" },\n {\n value: \"RENDERING_FAILURE\",\n label: \"Rendering Failure\",\n hint: \"Template rendering failed\",\n },\n {\n value: \"DELIVERY_DELAY\",\n label: \"Delivery Delay\",\n hint: \"Temporary delivery delay\",\n },\n {\n value: \"SUBSCRIPTION\",\n label: \"Subscription\",\n hint: \"List subscription event\",\n },\n ],\n initialValues: config.eventTracking?.events || [\n \"SEND\",\n \"DELIVERY\",\n \"OPEN\",\n \"CLICK\",\n \"BOUNCE\",\n \"COMPLAINT\",\n ],\n required: true,\n });\n\n if (clack.isCancel(selectedEvents)) {\n clack.cancel(\"Upgrade cancelled.\");\n process.exit(0);\n }\n\n updatedConfig = {\n ...config,\n eventTracking: {\n ...config.eventTracking,\n enabled: true,\n events: selectedEvents as any,\n },\n };\n newPreset = undefined; // Custom config\n break;\n }\n\n case \"dedicated-ip\": {\n const confirmed = await clack.confirm({\n message:\n \"Enable dedicated IP? (Requires 100k+ emails/day, adds ~$50-100/mo)\",\n initialValue: false,\n });\n\n if (clack.isCancel(confirmed)) {\n clack.cancel(\"Upgrade cancelled.\");\n process.exit(0);\n }\n\n if (!confirmed) {\n clack.log.info(\"Dedicated IP not enabled.\");\n process.exit(0);\n }\n\n updatedConfig = {\n ...config,\n dedicatedIp: true,\n };\n newPreset = undefined; // Custom config\n break;\n }\n\n case \"custom\": {\n // Full custom configuration\n const { promptCustomConfig } = await import(\n \"../../utils/shared/prompts.js\"\n );\n\n // Pass existing config to preserve values\n const customConfig = await promptCustomConfig(config);\n\n // Apply custom config updates to existing config (preserves user-customized fields)\n updatedConfig = applyConfigUpdates(config, customConfig);\n newPreset = undefined;\n break;\n }\n }\n\n // 8. Show cost comparison\n const newCostData = calculateCosts(updatedConfig, 50_000);\n const costDiff = newCostData.total.monthly - currentCostData.total.monthly;\n\n console.log(`\\n${pc.bold(\"Cost Impact:\")}`);\n console.log(\n ` Current: ${pc.cyan(`${formatCost(currentCostData.total.monthly)}/mo`)}`\n );\n console.log(\n ` New: ${pc.cyan(`${formatCost(newCostData.total.monthly)}/mo`)}`\n );\n if (costDiff > 0) {\n console.log(` Change: ${pc.yellow(`+${formatCost(costDiff)}/mo`)}`);\n } else if (costDiff < 0) {\n console.log(\n ` Change: ${pc.green(`${formatCost(Math.abs(costDiff))}/mo`)}`\n );\n }\n console.log(\"\");\n\n // 9. Confirm upgrade (skip if --yes or --preview)\n if (!(options.yes || options.preview)) {\n const confirmed = await clack.confirm({\n message: \"Proceed with upgrade?\",\n initialValue: true,\n });\n\n if (clack.isCancel(confirmed) || !confirmed) {\n clack.cancel(\"Upgrade cancelled.\");\n process.exit(0);\n }\n }\n\n // 10. Get Vercel config if needed and not already stored\n let vercelConfig;\n if (metadata.provider === \"vercel\" && !metadata.vercel) {\n vercelConfig = await promptVercelConfig();\n } else if (metadata.provider === \"vercel\") {\n vercelConfig = metadata.vercel;\n }\n\n // 11. Build stack configuration\n const stackConfig: EmailStackConfig = {\n provider: metadata.provider,\n region,\n vercel: vercelConfig,\n emailConfig: updatedConfig,\n };\n\n // 12. Preview or Update Pulumi stack\n if (options.preview) {\n // PREVIEW MODE - show what would be changed without deploying\n try {\n const previewResult = await progress.execute(\n \"Generating upgrade preview\",\n async () => {\n await ensurePulumiWorkDir();\n\n const stack =\n await pulumi.automation.LocalWorkspace.createOrSelectStack(\n {\n stackName:\n metadata.services.email?.pulumiStackName ||\n `wraps-${identity.accountId}-${region}`,\n projectName: \"wraps-email\",\n program: async () => {\n const result = await deployEmailStack(stackConfig);\n return {\n roleArn: result.roleArn,\n configSetName: result.configSetName,\n tableName: result.tableName,\n region: result.region,\n lambdaFunctions: result.lambdaFunctions,\n domain: result.domain,\n dkimTokens: result.dkimTokens,\n customTrackingDomain: result.customTrackingDomain,\n httpsTrackingEnabled: result.httpsTrackingEnabled,\n cloudFrontDomain: result.cloudFrontDomain,\n acmCertificateValidationRecords:\n result.acmCertificateValidationRecords,\n archiveArn: result.archiveArn,\n archivingEnabled: result.archivingEnabled,\n archiveRetention: result.archiveRetention,\n };\n },\n },\n {\n workDir: getPulumiWorkDir(),\n envVars: {\n PULUMI_CONFIG_PASSPHRASE: \"\",\n AWS_REGION: region,\n },\n secretsProvider: \"passphrase\",\n }\n );\n\n await stack.setConfig(\"aws:region\", { value: region });\n\n // Refresh state to sync with AWS before previewing\n await stack.refresh({ onOutput: () => {} });\n\n // Run preview instead of deployment\n const result = await stack.preview({ diff: true });\n return result;\n }\n );\n\n // Build cost comparison string\n const costComparison = [\n `Current: ${formatCost(currentCostData.total.monthly)}/mo`,\n `After upgrade: ${formatCost(newCostData.total.monthly)}/mo`,\n costDiff > 0\n ? `Change: +${formatCost(costDiff)}/mo`\n : costDiff < 0\n ? `Change: -${formatCost(Math.abs(costDiff))}/mo`\n : \"Change: No cost difference\",\n ].join(\"\\n\");\n\n // Display preview results\n displayPreview({\n changeSummary: previewResult.changeSummary,\n costEstimate: costComparison,\n commandName: \"wraps email upgrade\",\n });\n\n clack.outro(\n pc.green(\"Preview complete. Run without --preview to upgrade.\")\n );\n\n // Track preview completion\n trackServiceUpgrade(\"email\", {\n from_preset: metadata.services.email?.preset,\n to_preset: newPreset,\n preview: true,\n action: typeof upgradeAction === \"string\" ? upgradeAction : undefined,\n duration_ms: Date.now() - startTime,\n });\n return;\n } catch (error: any) {\n trackError(\"PREVIEW_FAILED\", \"email:upgrade\", { step: \"preview\" });\n if (error.message?.includes(\"stack is currently locked\")) {\n throw errors.stackLocked();\n }\n throw new Error(`Preview failed: ${error.message}`);\n }\n }\n\n // DEPLOY MODE - actually update infrastructure\n let outputs;\n try {\n outputs = await progress.execute(\n \"Updating Wraps infrastructure (this may take 2-3 minutes)\",\n async () => {\n await ensurePulumiWorkDir();\n\n const stack =\n await pulumi.automation.LocalWorkspace.createOrSelectStack(\n {\n stackName:\n metadata.services.email?.pulumiStackName ||\n `wraps-${identity.accountId}-${region}`,\n projectName: \"wraps-email\",\n program: async () => {\n const result = await deployEmailStack(stackConfig);\n\n return {\n roleArn: result.roleArn,\n configSetName: result.configSetName,\n tableName: result.tableName,\n region: result.region,\n lambdaFunctions: result.lambdaFunctions,\n domain: result.domain,\n dkimTokens: result.dkimTokens,\n customTrackingDomain: result.customTrackingDomain,\n httpsTrackingEnabled: result.httpsTrackingEnabled,\n cloudFrontDomain: result.cloudFrontDomain,\n acmCertificateValidationRecords:\n result.acmCertificateValidationRecords,\n archiveArn: result.archiveArn,\n archivingEnabled: result.archivingEnabled,\n archiveRetention: result.archiveRetention,\n };\n },\n },\n {\n workDir: getPulumiWorkDir(),\n envVars: {\n PULUMI_CONFIG_PASSPHRASE: \"\",\n AWS_REGION: region,\n },\n secretsProvider: \"passphrase\",\n }\n );\n\n await stack.workspace.selectStack(\n metadata.services.email?.pulumiStackName ||\n `wraps-${identity.accountId}-${region}`\n );\n await stack.setConfig(\"aws:region\", { value: region });\n\n // Refresh state to sync with AWS before upgrading\n // This ensures Pulumi knows about resources that already exist\n await stack.refresh({ onOutput: () => {} });\n\n // Pulumi will automatically detect changes and only update what's needed\n const upResult = await stack.up({ onOutput: () => {} });\n const pulumiOutputs = upResult.outputs;\n\n return {\n roleArn: pulumiOutputs.roleArn?.value as string,\n configSetName: pulumiOutputs.configSetName?.value as\n | string\n | undefined,\n tableName: pulumiOutputs.tableName?.value as string | undefined,\n region: pulumiOutputs.region?.value as string,\n lambdaFunctions: pulumiOutputs.lambdaFunctions?.value as\n | string[]\n | undefined,\n domain: pulumiOutputs.domain?.value as string | undefined,\n dkimTokens: pulumiOutputs.dkimTokens?.value as string[] | undefined,\n customTrackingDomain: pulumiOutputs.customTrackingDomain?.value as\n | string\n | undefined,\n httpsTrackingEnabled: pulumiOutputs.httpsTrackingEnabled?.value as\n | boolean\n | undefined,\n cloudFrontDomain: pulumiOutputs.cloudFrontDomain?.value as\n | string\n | undefined,\n acmCertificateValidationRecords: pulumiOutputs\n .acmCertificateValidationRecords?.value as\n | Array<{ name: string; type: string; value: string }>\n | undefined,\n archiveArn: pulumiOutputs.archiveArn?.value as string | undefined,\n archivingEnabled: pulumiOutputs.archivingEnabled?.value as\n | boolean\n | undefined,\n archiveRetention: pulumiOutputs.archiveRetention?.value as\n | string\n | undefined,\n };\n }\n );\n } catch (error: any) {\n // Track upgrade failure\n trackServiceUpgrade(\"email\", {\n from_preset: metadata.services.email?.preset,\n to_preset: newPreset,\n action: typeof upgradeAction === \"string\" ? upgradeAction : undefined,\n duration_ms: Date.now() - startTime,\n });\n\n // Check if it's a lock file error\n if (error.message?.includes(\"stack is currently locked\")) {\n trackError(\"STACK_LOCKED\", \"email:upgrade\", { step: \"deploy\" });\n throw errors.stackLocked();\n }\n\n trackError(\"UPGRADE_FAILED\", \"email:upgrade\", { step: \"deploy\" });\n throw new Error(`Pulumi upgrade failed: ${error.message}`);\n }\n\n // 13. Create DNS records in Route53 (if hosted zone exists)\n if (outputs.domain && outputs.dkimTokens && outputs.dkimTokens.length > 0) {\n const { findHostedZone, createDNSRecords } = await import(\n \"../../utils/email/route53.js\"\n );\n const hostedZone = await findHostedZone(outputs.domain, region);\n\n if (hostedZone) {\n try {\n progress.start(\"Creating DNS records in Route53\");\n\n // Determine mailFromDomain - use updatedConfig if available, otherwise construct default\n const mailFromDomain =\n updatedConfig.mailFromDomain || `mail.${outputs.domain}`;\n\n await createDNSRecords(\n hostedZone.id,\n outputs.domain,\n outputs.dkimTokens,\n region,\n outputs.customTrackingDomain,\n mailFromDomain,\n outputs.cloudFrontDomain\n );\n progress.succeed(\"DNS records created in Route53\");\n } catch (error: any) {\n progress.fail(\n `Failed to create DNS records automatically: ${error.message}`\n );\n progress.info(\n \"You can manually add the required DNS records shown below\"\n );\n }\n }\n }\n\n // 14. Update metadata\n updateEmailConfig(metadata, updatedConfig);\n if (metadata.services.email) {\n metadata.services.email.preset = newPreset as any;\n }\n await saveConnectionMetadata(metadata);\n\n progress.info(\"Connection metadata updated\");\n\n // 15. Format tracking domain DNS records if custom tracking domain was added\n const trackingDomainDnsRecords = [];\n const acmValidationRecords = [];\n\n if (outputs.customTrackingDomain) {\n // For HTTPS tracking, only show CNAME if CloudFront exists\n // For HTTP tracking, point to SES tracking endpoint\n if (outputs.httpsTrackingEnabled) {\n // Only add tracking domain CNAME if CloudFront is created\n if (outputs.cloudFrontDomain) {\n trackingDomainDnsRecords.push({\n name: outputs.customTrackingDomain,\n type: \"CNAME\",\n value: outputs.cloudFrontDomain,\n });\n }\n } else {\n // HTTP tracking - use SES tracking endpoint\n trackingDomainDnsRecords.push({\n name: outputs.customTrackingDomain,\n type: \"CNAME\",\n value: `r.${outputs.region}.awstrack.me`,\n });\n }\n }\n\n // Add ACM certificate validation records if HTTPS tracking is enabled\n if (outputs.httpsTrackingEnabled && outputs.acmCertificateValidationRecords) {\n acmValidationRecords.push(...outputs.acmCertificateValidationRecords);\n }\n\n // Check if HTTPS tracking was enabled but CloudFront wasn't created (manual DNS validation needed)\n const needsCertificateValidation =\n outputs.httpsTrackingEnabled &&\n acmValidationRecords.length > 0 &&\n !outputs.cloudFrontDomain;\n\n // 15. Display success message\n displaySuccess({\n roleArn: outputs.roleArn,\n configSetName: outputs.configSetName,\n region: outputs.region!,\n tableName: outputs.tableName,\n trackingDomainDnsRecords:\n trackingDomainDnsRecords.length > 0\n ? trackingDomainDnsRecords\n : undefined,\n acmValidationRecords:\n acmValidationRecords.length > 0 ? acmValidationRecords : undefined,\n customTrackingDomain: outputs.customTrackingDomain,\n httpsTrackingEnabled: outputs.httpsTrackingEnabled,\n });\n\n // Show what was upgraded\n console.log(`\\n${pc.green(\"✓\")} ${pc.bold(\"Upgrade complete!\")}\\n`);\n\n if (upgradeAction === \"preset\" && newPreset) {\n console.log(\n `Upgraded to ${pc.cyan(newPreset)} preset (${pc.green(`${formatCost(newCostData.total.monthly)}/mo`)})\\n`\n );\n } else {\n console.log(\n `Updated configuration (${pc.green(`${formatCost(newCostData.total.monthly)}/mo`)})\\n`\n );\n }\n\n // Show next steps for HTTPS tracking if certificate validation is pending\n if (needsCertificateValidation) {\n console.log(pc.bold(\"⚠️ HTTPS Tracking - Next Steps:\\n\"));\n console.log(\n \" 1. Add the SSL certificate validation DNS record shown above to your DNS provider\"\n );\n console.log(\n \" 2. Wait for DNS propagation and certificate validation (5-30 minutes)\"\n );\n console.log(\n ` 3. Run ${pc.cyan(\"wraps email upgrade\")} again to complete CloudFront setup\\n`\n );\n console.log(\n pc.dim(\n \" Note: CloudFront distribution will be created once the certificate is validated.\\n\"\n )\n );\n } else if (outputs.httpsTrackingEnabled && outputs.cloudFrontDomain) {\n console.log(\n pc.green(\"✓\") +\n \" \" +\n pc.bold(\"HTTPS tracking is fully configured and ready to use!\\n\")\n );\n }\n\n // 16. Track successful upgrade\n const enabledFeatures: string[] = [];\n if (updatedConfig.tracking?.enabled) enabledFeatures.push(\"tracking\");\n if (updatedConfig.suppressionList?.enabled)\n enabledFeatures.push(\"suppression_list\");\n if (updatedConfig.eventTracking?.enabled)\n enabledFeatures.push(\"event_tracking\");\n if (updatedConfig.eventTracking?.dynamoDBHistory)\n enabledFeatures.push(\"dynamodb_history\");\n if (updatedConfig.dedicatedIp) enabledFeatures.push(\"dedicated_ip\");\n if (updatedConfig.emailArchiving?.enabled)\n enabledFeatures.push(\"email_archiving\");\n\n trackServiceUpgrade(\"email\", {\n from_preset: metadata.services.email?.preset,\n to_preset: newPreset,\n added_features: enabledFeatures,\n action: typeof upgradeAction === \"string\" ? upgradeAction : undefined,\n duration_ms: Date.now() - startTime,\n });\n}\n","import * as clack from \"@clack/prompts\";\nimport * as pulumi from \"@pulumi/pulumi\";\nimport getPort from \"get-port\";\nimport open from \"open\";\nimport pc from \"picocolors\";\nimport { startConsoleServer } from \"../../console/server.js\";\nimport { trackCommand } from \"../../telemetry/events.js\";\nimport type { DashboardOptions } from \"../../types/index.js\";\nimport {\n getAWSRegion,\n validateAWSCredentials,\n} from \"../../utils/shared/aws.js\";\nimport {\n ensurePulumiWorkDir,\n getPulumiWorkDir,\n} from \"../../utils/shared/fs.js\";\nimport { DeploymentProgress } from \"../../utils/shared/output.js\";\n\n/**\n * Dashboard command - Start local web dashboard\n */\nexport async function dashboard(options: DashboardOptions): Promise<void> {\n clack.intro(pc.bold(\"Wraps Dashboard\"));\n\n const progress = new DeploymentProgress();\n\n // 1. Validate AWS credentials\n const identity = await progress.execute(\n \"Validating AWS credentials\",\n async () => validateAWSCredentials()\n );\n\n // 2. Get region\n const region = await getAWSRegion();\n\n // 3. Load stack outputs to get IAM role ARN\n let stackOutputs: any = {};\n try {\n // Ensure Pulumi workspace is configured (sets backend URL)\n await ensurePulumiWorkDir();\n\n const stack = await pulumi.automation.LocalWorkspace.selectStack({\n stackName: `wraps-${identity.accountId}-${region}`,\n workDir: getPulumiWorkDir(),\n });\n\n stackOutputs = await stack.outputs();\n } catch (_error: unknown) {\n progress.stop();\n clack.log.error(\"No Wraps infrastructure found\");\n console.log(\n `\\\\nRun ${pc.cyan(\"wraps email init\")} to deploy infrastructure first.\\\\n`\n );\n process.exit(1);\n }\n\n // Extract outputs from stack (optional - console uses current AWS credentials)\n const tableName = stackOutputs.tableName?.value;\n const archiveArn = stackOutputs.archiveArn?.value;\n const archivingEnabled = stackOutputs.archivingEnabled?.value ?? false;\n\n // 4. Find available port\n const port =\n options.port || (await getPort({ port: [5555, 5556, 5557, 5558, 5559] }));\n\n // 5. Start server\n progress.stop();\n clack.log.success(\"Starting dashboard server...\");\n console.log(\n `${pc.dim(\"Using current AWS credentials (no role assumption)\")}\\\\n`\n );\n\n const { url } = await startConsoleServer({\n port,\n roleArn: undefined, // Use current credentials instead of assuming role\n region,\n tableName,\n accountId: identity.accountId,\n noOpen: options.noOpen ?? false,\n archiveArn,\n archivingEnabled,\n });\n\n console.log(`\\\\n${pc.bold(\"Dashboard:\")} ${pc.cyan(url)}`);\n console.log(`${pc.dim(\"Press Ctrl+C to stop\")}\\\\n`);\n\n // 6. Open browser (unless --no-open)\n if (!options.noOpen) {\n await open(url);\n }\n\n // 7. Track console launch\n trackCommand(\"console\", {\n success: true,\n port,\n no_open: options.noOpen ?? false,\n });\n\n // Keep process alive\n await new Promise(() => {});\n}\n","import crypto from \"node:crypto\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport express from \"express\";\nimport { createHttpTerminator } from \"http-terminator\";\nimport { authenticateToken } from \"./middleware/auth.js\";\nimport { errorHandler } from \"./middleware/error.js\";\nimport { createDomainsRouter } from \"./routes/domains.js\";\nimport { createEmailsRouter } from \"./routes/emails.js\";\nimport { createMetricsRouter } from \"./routes/metrics.js\";\nimport { createSettingsRouter } from \"./routes/settings.js\";\nimport { createUserRouter } from \"./routes/user.js\";\n\nconst __dirname = path.dirname(fileURLToPath(import.meta.url));\n\nexport type ServerConfig = {\n port: number;\n roleArn: string | undefined;\n region: string;\n tableName?: string;\n accountId?: string;\n noOpen: boolean;\n archiveArn?: string;\n archivingEnabled?: boolean;\n};\n\nexport type ServerInfo = {\n url: string;\n token: string;\n};\n\n/**\n * Start console server\n */\nexport async function startConsoleServer(\n config: ServerConfig\n): Promise<ServerInfo> {\n const app = express();\n\n // Generate auth token\n const authToken = crypto.randomBytes(32).toString(\"hex\");\n\n // Middleware\n app.use(express.json());\n\n // Simple rate limiting for static file requests (defense-in-depth)\n // Note: This is a localhost-only dev server with token auth, so this is just\n // a safeguard against accidental abuse or runaway scripts\n const requestCounts = new Map<string, { count: number; resetTime: number }>();\n const RATE_LIMIT_WINDOW = 60 * 1000; // 1 minute\n const RATE_LIMIT_MAX_REQUESTS = 1000; // 1000 requests per minute per IP\n\n app.use((req, res, next) => {\n const ip = req.ip || req.socket.remoteAddress || \"unknown\";\n const now = Date.now();\n const record = requestCounts.get(ip);\n\n if (!record || now > record.resetTime) {\n // New window\n requestCounts.set(ip, { count: 1, resetTime: now + RATE_LIMIT_WINDOW });\n next();\n } else if (record.count < RATE_LIMIT_MAX_REQUESTS) {\n // Within limit\n record.count++;\n next();\n } else {\n // Rate limit exceeded\n res.status(429).json({\n error: \"Too many requests, please slow down\",\n retryAfter: Math.ceil((record.resetTime - now) / 1000),\n });\n }\n });\n\n // Security headers\n app.use((_req, res, next) => {\n res.setHeader(\"X-Frame-Options\", \"DENY\");\n res.setHeader(\"X-Content-Type-Options\", \"nosniff\");\n res.setHeader(\n \"Content-Security-Policy\",\n \"default-src 'self' 'unsafe-inline' 'unsafe-eval'; \" +\n \"style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; \" +\n \"font-src 'self' https://fonts.gstatic.com; \" +\n \"connect-src 'self'\"\n );\n next();\n });\n\n // Request logging middleware\n app.use((req, _res, next) => {\n console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);\n next();\n });\n\n // API routes (with authentication)\n app.use(\n \"/api/metrics\",\n authenticateToken(authToken),\n createMetricsRouter(config)\n );\n app.use(\n \"/api/domains\",\n authenticateToken(authToken),\n createDomainsRouter(config)\n );\n app.use(\n \"/api/emails\",\n authenticateToken(authToken),\n createEmailsRouter(config)\n );\n app.use(\n \"/api/settings\",\n authenticateToken(authToken),\n createSettingsRouter(config)\n );\n app.use(\"/api/user\", authenticateToken(authToken), createUserRouter(config));\n\n // Serve static files from console-ui build\n // __dirname will be dist/ after compilation, console UI is in dist/console/\n const staticDir = path.join(__dirname, \"console\");\n app.use(express.static(staticDir));\n\n // SPA fallback\n app.get(\"*\", (_req, res) => {\n res.sendFile(path.join(staticDir, \"index.html\"));\n });\n\n // Error handler\n app.use(errorHandler);\n\n // Start server\n const server = app.listen(config.port, \"127.0.0.1\");\n\n // Setup graceful shutdown\n const httpTerminator = createHttpTerminator({ server });\n\n process.on(\"SIGTERM\", async () => {\n console.log(\"\\\\nShutting down gracefully...\");\n await httpTerminator.terminate();\n process.exit(0);\n });\n\n process.on(\"SIGINT\", async () => {\n console.log(\"\\\\nShutting down gracefully...\");\n await httpTerminator.terminate();\n process.exit(0);\n });\n\n const url = `http://localhost:${config.port}?token=${authToken}`;\n\n return { url, token: authToken };\n}\n","import type { NextFunction, Request, Response } from \"express\";\n\n/**\n * Token-based authentication middleware\n */\nexport function authenticateToken(expectedToken: string) {\n return (req: Request, res: Response, next: NextFunction) => {\n // Get token from query param or header\n const token = req.query.token || req.headers[\"x-auth-token\"];\n\n if (!token || token !== expectedToken) {\n return res.status(401).json({ error: \"Unauthorized\" });\n }\n\n next();\n };\n}\n","import type { NextFunction, Request, Response } from \"express\";\n\n/**\n * Error handling middleware\n */\nexport function errorHandler(\n err: Error,\n _req: Request,\n res: Response,\n _next: NextFunction\n) {\n console.error(\"Server error:\", err);\n\n res.status(500).json({\n error: \"Internal server error\",\n message: err.message,\n });\n}\n","import type { Request, Response, Router } from \"express\";\nimport { Router as createRouter } from \"express\";\nimport type { ServerConfig } from \"../server.js\";\nimport { fetchDomainInfo } from \"../services/ses-service.js\";\n\nexport function createDomainsRouter(config: ServerConfig): Router {\n const router = createRouter();\n\n /**\n * Get domain verification status\n */\n router.get(\"/:domain\", async (req: Request, res: Response) => {\n try {\n const { domain } = req.params;\n\n if (!domain) {\n return res.status(400).json({ error: \"Domain parameter required\" });\n }\n\n const domainInfo = await fetchDomainInfo(\n config.roleArn,\n config.region,\n domain\n );\n\n res.json(domainInfo);\n } catch (error: unknown) {\n const errorMessage =\n error instanceof Error ? error.message : \"Unknown error\";\n res.status(500).json({ error: errorMessage });\n }\n });\n\n return router;\n}\n","import { GetSendQuotaCommand, SESClient } from \"@aws-sdk/client-ses\";\nimport { GetEmailIdentityCommand, SESv2Client } from \"@aws-sdk/client-sesv2\";\nimport { assumeRole } from \"../../utils/shared/assume-role.js\";\n\nexport type SendQuota = {\n max24HourSend: number;\n maxSendRate: number;\n sentLast24Hours: number;\n};\n\nexport type DomainInfo = {\n domain: string;\n verified: boolean;\n dkimStatus: string;\n dkimTokens: string[];\n};\n\n/**\n * Fetch SES send quota\n */\nexport async function fetchSendQuota(\n roleArn: string | undefined,\n region: string\n): Promise<SendQuota> {\n // For console usage, use current credentials instead of assuming role\n const credentials = roleArn ? await assumeRole(roleArn, region) : undefined;\n const ses = new SESClient({ region, credentials });\n\n const response = await ses.send(new GetSendQuotaCommand({}));\n\n return {\n max24HourSend: response.Max24HourSend || 0,\n maxSendRate: response.MaxSendRate || 0,\n sentLast24Hours: response.SentLast24Hours || 0,\n };\n}\n\n/**\n * Fetch domain verification status\n */\nexport async function fetchDomainInfo(\n roleArn: string | undefined,\n region: string,\n domain: string\n): Promise<DomainInfo> {\n // For console usage, use current credentials instead of assuming role\n const credentials = roleArn ? await assumeRole(roleArn, region) : undefined;\n const sesv2 = new SESv2Client({ region, credentials });\n\n const response = await sesv2.send(\n new GetEmailIdentityCommand({\n EmailIdentity: domain,\n })\n );\n\n return {\n domain,\n verified: response.VerifiedForSendingStatus ?? false,\n dkimStatus: response.DkimAttributes?.Status || \"PENDING\",\n dkimTokens: response.DkimAttributes?.Tokens || [],\n };\n}\n","import type { Request, Response, Router } from \"express\";\nimport { Router as createRouter } from \"express\";\nimport type { ServerConfig } from \"../server.js\";\nimport { fetchEmailById, fetchEmailLogs } from \"../services/email-logs.js\";\n\nexport function createEmailsRouter(config: ServerConfig): Router {\n const router = createRouter();\n\n /**\n * Get email logs\n */\n router.get(\"/\", async (req: Request, res: Response) => {\n try {\n console.log(\"Email logs request received\");\n console.log(\"Query params:\", req.query);\n console.log(\"Config:\", {\n tableName: config.tableName,\n region: config.region,\n accountId: config.accountId,\n });\n\n // Parse query parameters\n const limit = req.query.limit\n ? Number.parseInt(req.query.limit as string, 10)\n : 100;\n const startTime = req.query.startTime\n ? Number.parseInt(req.query.startTime as string, 10)\n : undefined;\n const endTime = req.query.endTime\n ? Number.parseInt(req.query.endTime as string, 10)\n : undefined;\n\n if (!config.tableName) {\n console.log(\"No table name configured\");\n return res.status(400).json({\n error:\n \"Email tracking not enabled. Deploy with enhanced integration to enable email logs.\",\n });\n }\n\n console.log(\"Fetching email logs from DynamoDB...\");\n const logs = await fetchEmailLogs({\n region: config.region,\n tableName: config.tableName,\n accountId: config.accountId,\n limit,\n startTime,\n endTime,\n });\n\n console.log(`Found ${logs.length} email logs`);\n res.json({ logs });\n } catch (error: unknown) {\n const errorMessage =\n error instanceof Error ? error.message : \"Unknown error\";\n console.error(\"Error fetching email logs:\", error);\n res.status(500).json({ error: errorMessage });\n }\n });\n\n /**\n * Get email details by ID\n */\n router.get(\"/:id\", async (req: Request, res: Response) => {\n try {\n const { id } = req.params;\n console.log(\"Email detail request received for ID:\", id);\n console.log(\"Request headers:\", req.headers);\n console.log(\"Request query:\", req.query);\n\n if (!config.tableName) {\n console.log(\"No table name configured\");\n return res.status(400).json({\n error:\n \"Email tracking not enabled. Deploy with enhanced integration to enable email logs.\",\n });\n }\n\n console.log(\"Fetching email details from DynamoDB...\");\n const email = await fetchEmailById(id, {\n region: config.region,\n tableName: config.tableName,\n });\n\n if (!email) {\n console.log(\"Email not found for ID:\", id);\n return res.status(404).json({ error: \"Email not found\" });\n }\n\n console.log(\"Email details found:\", email.messageId);\n console.log(\"Sending response with\", email.events.length, \"events\");\n res.json(email);\n } catch (error: unknown) {\n const errorMessage =\n error instanceof Error ? error.message : \"Unknown error\";\n console.error(\"Error fetching email details:\", error);\n console.error(\n \"Stack trace:\",\n error instanceof Error ? error.stack : \"N/A\"\n );\n res.status(500).json({ error: errorMessage });\n }\n });\n\n /**\n * Get archived email content by message ID\n */\n router.get(\"/:id/archive\", async (req: Request, res: Response) => {\n try {\n const { id } = req.params;\n console.log(\"Archived email request received for message ID:\", id);\n\n if (!config.archivingEnabled) {\n console.log(\"Email archiving not enabled\");\n return res.status(400).json({\n error: \"Email archiving not enabled for this deployment.\",\n });\n }\n\n if (!config.archiveArn) {\n console.log(\"No archive ARN configured\");\n return res.status(400).json({\n error: \"Archive ARN not configured.\",\n });\n }\n\n if (!config.tableName) {\n console.log(\"No table name configured\");\n return res.status(400).json({\n error:\n \"Email tracking not enabled. Need email metadata to search archive.\",\n });\n }\n\n // First, fetch email details from DynamoDB to get search metadata\n console.log(\"Fetching email metadata from DynamoDB...\");\n const emailDetails = await fetchEmailById(id, {\n region: config.region,\n tableName: config.tableName,\n });\n\n if (!emailDetails) {\n console.log(\"Email metadata not found in DynamoDB for ID:\", id);\n return res.status(404).json({\n error: \"Email metadata not found. Cannot search archive.\",\n });\n }\n\n console.log(\"Fetching archived email from Mail Manager...\");\n const { fetchArchivedEmail } = await import(\n \"../services/email-archive.js\"\n );\n const archivedEmail = await fetchArchivedEmail(id, {\n region: config.region,\n archiveArn: config.archiveArn,\n from: emailDetails.from,\n to: emailDetails.to[0], // Use first recipient for search\n subject: emailDetails.subject,\n timestamp: new Date(emailDetails.sentAt),\n });\n\n if (!archivedEmail) {\n console.log(\"Archived email not found for message ID:\", id);\n return res.status(404).json({\n error:\n \"Archived email not found. It may have been sent before archiving was enabled.\",\n });\n }\n\n console.log(\"Archived email found:\", archivedEmail.messageId);\n res.json(archivedEmail);\n } catch (error: unknown) {\n const errorMessage =\n error instanceof Error ? error.message : \"Unknown error\";\n console.error(\"Error fetching archived email:\", error);\n console.error(\n \"Stack trace:\",\n error instanceof Error ? error.stack : \"N/A\"\n );\n res.status(500).json({ error: errorMessage });\n }\n });\n\n return router;\n}\n","import {\n DynamoDBClient,\n QueryCommand,\n ScanCommand,\n} from \"@aws-sdk/client-dynamodb\";\nimport { unmarshall } from \"@aws-sdk/util-dynamodb\";\n\nexport type EmailLog = {\n messageId: string;\n to: string[]; // Array of recipients\n from: string;\n subject: string;\n status:\n | \"delivered\"\n | \"bounced\"\n | \"complained\"\n | \"sent\"\n | \"failed\"\n | \"opened\"\n | \"clicked\";\n sentAt: number;\n accountId?: string;\n errorMessage?: string;\n};\n\nexport type EmailEvent = {\n type:\n | \"sent\"\n | \"delivered\"\n | \"bounced\"\n | \"complained\"\n | \"opened\"\n | \"clicked\"\n | \"failed\";\n timestamp: number;\n metadata?: Record<string, any>;\n};\n\nexport type EmailDetails = {\n id: string;\n messageId: string;\n from: string;\n to: string[];\n replyTo?: string;\n subject: string;\n htmlBody?: string;\n textBody?: string;\n status:\n | \"delivered\"\n | \"bounced\"\n | \"complained\"\n | \"sent\"\n | \"failed\"\n | \"opened\"\n | \"clicked\";\n sentAt: number;\n events: EmailEvent[];\n};\n\ntype FetchEmailLogsOptions = {\n region: string;\n tableName: string;\n accountId?: string;\n limit?: number;\n startTime?: number;\n endTime?: number;\n};\n\n/**\n * Fetch email logs from DynamoDB\n */\nexport async function fetchEmailLogs(\n options: FetchEmailLogsOptions\n): Promise<EmailLog[]> {\n const {\n region,\n tableName,\n accountId,\n limit = 100,\n startTime,\n endTime,\n } = options;\n\n const dynamodb = new DynamoDBClient({ region });\n\n try {\n // If we have accountId, use GSI for better performance\n let items: any[] = [];\n if (accountId) {\n let keyConditionExpression = \"accountId = :accountId\";\n const expressionAttributeValues: Record<string, any> = {\n \":accountId\": { S: accountId },\n };\n\n // Add time range if provided\n if (startTime && endTime) {\n keyConditionExpression += \" AND sentAt BETWEEN :startTime AND :endTime\";\n expressionAttributeValues[\":startTime\"] = { N: startTime.toString() };\n expressionAttributeValues[\":endTime\"] = { N: endTime.toString() };\n } else if (startTime) {\n keyConditionExpression += \" AND sentAt >= :startTime\";\n expressionAttributeValues[\":startTime\"] = { N: startTime.toString() };\n }\n\n const response = await dynamodb.send(\n new QueryCommand({\n TableName: tableName,\n IndexName: \"accountId-sentAt-index\",\n KeyConditionExpression: keyConditionExpression,\n ExpressionAttributeValues: expressionAttributeValues,\n ScanIndexForward: false, // Sort by sentAt descending (newest first)\n })\n );\n\n items = response.Items || [];\n } else {\n // Otherwise, scan the table (less efficient but works without accountId)\n const response = await dynamodb.send(\n new ScanCommand({\n TableName: tableName,\n })\n );\n\n items = response.Items || [];\n }\n\n // Unmarshall all items\n const unmarshalled = items.map((item) => unmarshall(item));\n\n // Group events by messageId to get the latest status for each email\n const emailMap = new Map<string, any>();\n\n for (const item of unmarshalled) {\n const messageId = item.messageId;\n const existing = emailMap.get(messageId);\n\n if (existing) {\n // Keep the event with the most important status\n // Priority: Complaint > Permanent Bounce > Delivery > Transient Bounce > Send\n const currentPriority = getEventPriority(item);\n const existingPriority = getEventPriority(existing);\n\n if (currentPriority > existingPriority) {\n emailMap.set(messageId, item);\n }\n } else {\n emailMap.set(messageId, item);\n }\n }\n\n // Convert map to array and normalize\n const logs = Array.from(emailMap.values())\n .map(normalizeEmailLog)\n .sort((a, b) => b.sentAt - a.sentAt);\n\n // Apply limit\n return logs.slice(0, limit);\n } catch (error) {\n console.error(\"Error fetching email logs:\", error);\n throw error;\n }\n}\n\n/**\n * Get priority for event type (higher = more important to display)\n * Priority: Complaint > Permanent Bounce > Click > Open > Delivery > Transient Bounce > Send\n */\nfunction getEventPriority(item: any): number {\n const type = item.eventType?.toLowerCase();\n\n switch (type) {\n case \"complaint\":\n return 7;\n case \"bounce\": {\n // Permanent bounces (hard bounces) are more important than delivery\n // Transient bounces (OOTO, mailbox full) are less important than delivery\n const bounceType = item.bounceType?.toLowerCase();\n return bounceType === \"permanent\" ? 6 : 2;\n }\n case \"click\":\n return 5;\n case \"open\":\n return 4;\n case \"delivery\":\n return 3;\n case \"send\":\n return 1;\n default:\n return 0;\n }\n}\n\n/**\n * Normalize email log data from DynamoDB\n */\nfunction normalizeEmailLog(data: any): EmailLog {\n // Determine status based on eventType\n let status: EmailLog[\"status\"] = \"sent\";\n const eventType = data.eventType?.toLowerCase();\n\n if (eventType === \"complaint\") {\n status = \"complained\";\n } else if (eventType === \"bounce\") {\n status = \"bounced\";\n } else if (eventType === \"click\") {\n status = \"clicked\";\n } else if (eventType === \"open\") {\n status = \"opened\";\n } else if (eventType === \"delivery\") {\n status = \"delivered\";\n } else if (eventType === \"send\") {\n status = \"sent\";\n } else if (data.errorMessage) {\n status = \"failed\";\n }\n\n // Handle 'to' field - it's stored as a String Set in DynamoDB\n // DynamoDB String Sets get unmarshalled as JavaScript Set objects\n let toAddresses: string[] = [];\n const toField = data.to || data.destination; // CSV export might show as 'destination'\n\n if (toField) {\n console.log(\n \"Raw 'to' field:\",\n toField,\n \"Type:\",\n typeof toField,\n \"Constructor:\",\n toField.constructor?.name\n );\n\n if (toField instanceof Set) {\n // DynamoDB String Set -> JavaScript Set\n toAddresses = Array.from(toField);\n } else if (Array.isArray(toField)) {\n toAddresses = toField;\n } else if (typeof toField === \"string\") {\n toAddresses = [toField];\n }\n }\n\n console.log(\"Normalized toAddresses:\", toAddresses);\n\n return {\n messageId: data.messageId,\n to: toAddresses,\n from: data.from || \"unknown\",\n subject: data.subject || \"(no subject)\",\n status,\n sentAt: Number(data.sentAt),\n accountId: data.accountId,\n errorMessage: data.errorMessage,\n };\n}\n\n/**\n * Fetch email details by message ID (with all events)\n */\nexport async function fetchEmailById(\n messageId: string,\n options: { region: string; tableName: string }\n): Promise<EmailDetails | null> {\n const { region, tableName } = options;\n const dynamodb = new DynamoDBClient({ region });\n\n try {\n // Query all events for this messageId\n const response = await dynamodb.send(\n new QueryCommand({\n TableName: tableName,\n KeyConditionExpression: \"messageId = :messageId\",\n ExpressionAttributeValues: {\n \":messageId\": { S: messageId },\n },\n })\n );\n\n const items = response.Items || [];\n\n if (items.length === 0) {\n return null;\n }\n\n // Unmarshall all events\n const events = items.map((item) => unmarshall(item));\n\n // Get the send event (has the email content)\n const sendEvent = events.find((e) => e.eventType?.toLowerCase() === \"send\");\n\n if (!sendEvent) {\n return null;\n }\n\n console.log(\"Send event fields:\", {\n from: sendEvent.from,\n source: sendEvent.source,\n subject: sendEvent.subject,\n to: sendEvent.to,\n destination: sendEvent.destination,\n availableKeys: Object.keys(sendEvent),\n });\n\n // Try to extract email content from eventData\n let htmlBody: string | undefined;\n let textBody: string | undefined;\n\n if (sendEvent.eventData) {\n try {\n const eventData = JSON.parse(sendEvent.eventData);\n console.log(\"Send event data keys:\", Object.keys(eventData));\n\n // SES doesn't include email content in events by default\n // Check if content was somehow included\n if (eventData.content) {\n htmlBody = eventData.content.html;\n textBody = eventData.content.text;\n }\n\n // Check mail.content (unlikely but worth trying)\n if (eventData.mail?.content) {\n htmlBody = eventData.mail.content.html;\n textBody = eventData.mail.content.text;\n }\n } catch (e) {\n console.error(\"Failed to parse eventData:\", e);\n }\n }\n\n // Parse to addresses\n let toAddresses: string[] = [];\n const toField = sendEvent.to || sendEvent.destination;\n\n if (toField) {\n if (toField instanceof Set) {\n toAddresses = Array.from(toField);\n } else if (Array.isArray(toField)) {\n toAddresses = toField;\n } else if (typeof toField === \"string\") {\n toAddresses = [toField];\n }\n }\n\n // Determine final status (priority order: complaint > bounce > click > open > delivery > sent)\n let status: EmailDetails[\"status\"] = \"sent\";\n const hasDelivery = events.some(\n (e) => e.eventType?.toLowerCase() === \"delivery\"\n );\n const hasBounce = events.some(\n (e) => e.eventType?.toLowerCase() === \"bounce\"\n );\n const hasComplaint = events.some(\n (e) => e.eventType?.toLowerCase() === \"complaint\"\n );\n const hasOpen = events.some((e) => e.eventType?.toLowerCase() === \"open\");\n const hasClick = events.some((e) => e.eventType?.toLowerCase() === \"click\");\n\n if (hasComplaint) {\n status = \"complained\";\n } else if (hasBounce) {\n status = \"bounced\";\n } else if (hasClick) {\n status = \"clicked\";\n } else if (hasOpen) {\n status = \"opened\";\n } else if (hasDelivery) {\n status = \"delivered\";\n }\n\n // Map events to simplified timeline\n const timeline: EmailEvent[] = events\n .map((event) => {\n const eventType = event.eventType?.toLowerCase();\n let type: EmailEvent[\"type\"] = \"sent\";\n\n switch (eventType) {\n case \"send\":\n type = \"sent\";\n break;\n case \"delivery\":\n type = \"delivered\";\n break;\n case \"bounce\":\n type = \"bounced\";\n break;\n case \"complaint\":\n type = \"complained\";\n break;\n case \"open\":\n type = \"opened\";\n break;\n case \"click\":\n type = \"clicked\";\n break;\n default:\n type = \"sent\";\n }\n\n const metadata: Record<string, any> = {};\n\n // Add relevant metadata based on event type\n if (eventType === \"bounce\" && event.bounceType) {\n metadata.bounceType = event.bounceType;\n metadata.bounceSubType = event.bounceSubType;\n }\n\n if (eventType === \"complaint\" && event.complaintFeedbackType) {\n metadata.feedbackType = event.complaintFeedbackType;\n }\n\n if (eventType === \"click\" && event.link) {\n metadata.link = event.link;\n }\n\n if (event.userAgent) {\n metadata.userAgent = event.userAgent;\n }\n\n return {\n type,\n timestamp: Number(event.sentAt || event.timestamp),\n metadata: Object.keys(metadata).length > 0 ? metadata : undefined,\n };\n })\n .sort((a, b) => a.timestamp - b.timestamp);\n\n return {\n id: messageId,\n messageId,\n from: sendEvent.from || \"unknown\",\n to: toAddresses,\n replyTo: sendEvent.replyTo,\n subject: sendEvent.subject || \"(no subject)\",\n htmlBody: htmlBody || sendEvent.htmlBody,\n textBody: textBody || sendEvent.textBody,\n status,\n sentAt: Number(sendEvent.sentAt),\n events: timeline,\n };\n } catch (error) {\n console.error(\"Error fetching email by ID:\", error);\n throw error;\n }\n}\n","import type { Request, Response, Router } from \"express\";\nimport { Router as createRouter } from \"express\";\nimport type { ServerConfig } from \"../server.js\";\nimport { fetchSESMetrics } from \"../services/aws-metrics.js\";\nimport { fetchSendQuota } from \"../services/ses-service.js\";\n\nexport function createMetricsRouter(config: ServerConfig): Router {\n const router = createRouter();\n\n /**\n * SSE endpoint for real-time metrics\n */\n router.get(\"/stream\", async (req: Request, res: Response) => {\n // Set SSE headers\n res.setHeader(\"Content-Type\", \"text/event-stream\");\n res.setHeader(\"Cache-Control\", \"no-cache\");\n res.setHeader(\"Connection\", \"keep-alive\");\n\n // Send initial connection event\n res.write('data: {\"type\":\"connected\"}\\n\\n');\n\n // Get time range from query params, default to last 24 hours\n const { startTime, endTime } = req.query;\n const getTimeRange = () => ({\n start: startTime\n ? new Date(Number.parseInt(startTime as string, 10))\n : new Date(Date.now() - 24 * 60 * 60 * 1000),\n end: endTime\n ? new Date(Number.parseInt(endTime as string, 10))\n : new Date(),\n });\n\n // Function to fetch and send metrics\n const sendMetrics = async () => {\n try {\n console.log(\"Fetching metrics from AWS...\");\n\n const timeRange = getTimeRange();\n\n console.log(\"Time range:\", timeRange);\n console.log(\"Config:\", {\n region: config.region,\n roleArn: config.roleArn\n ? `${config.roleArn.substring(0, 30)}...`\n : \"using current credentials\",\n });\n\n const [metrics, quota] = await Promise.all([\n fetchSESMetrics(\n config.roleArn,\n config.region,\n timeRange,\n config.tableName\n ),\n fetchSendQuota(config.roleArn, config.region),\n ]);\n\n console.log(\"Metrics fetched successfully\");\n\n const data = {\n type: \"metrics\",\n timestamp: Date.now(),\n metrics,\n quota,\n };\n\n res.write(`data: ${JSON.stringify(data)}\\n\\n`);\n } catch (error: unknown) {\n const errorMessage =\n error instanceof Error ? error.message : \"Unknown error\";\n console.error(\"Error fetching metrics:\", error);\n res.write(\n `data: ${JSON.stringify({ type: \"error\", error: errorMessage })}\\n\\n`\n );\n }\n };\n\n // Send immediately on connect\n await sendMetrics();\n\n // Poll every 60 seconds\n const interval = setInterval(sendMetrics, 60_000);\n\n // Clean up on disconnect\n req.on(\"close\", () => {\n clearInterval(interval);\n });\n });\n\n /**\n * Get current metrics snapshot (REST endpoint)\n */\n router.get(\"/snapshot\", async (_req: Request, res: Response) => {\n try {\n const timeRange = {\n start: new Date(Date.now() - 24 * 60 * 60 * 1000),\n end: new Date(),\n };\n\n const [metrics, quota] = await Promise.all([\n fetchSESMetrics(\n config.roleArn,\n config.region,\n timeRange,\n config.tableName\n ),\n fetchSendQuota(config.roleArn, config.region),\n ]);\n\n res.json({ metrics, quota });\n } catch (error: unknown) {\n const errorMessage =\n error instanceof Error ? error.message : \"Unknown error\";\n res.status(500).json({ error: errorMessage });\n }\n });\n\n /**\n * Get metrics for a specific time range\n */\n router.get(\"/\", async (req: Request, res: Response) => {\n try {\n const { startTime, endTime } = req.query;\n\n // Default to last 24 hours if no time range provided\n const timeRange = {\n start: startTime\n ? new Date(Number.parseInt(startTime as string, 10))\n : new Date(Date.now() - 24 * 60 * 60 * 1000),\n end: endTime\n ? new Date(Number.parseInt(endTime as string, 10))\n : new Date(),\n };\n\n const [metrics, quota] = await Promise.all([\n fetchSESMetrics(\n config.roleArn,\n config.region,\n timeRange,\n config.tableName\n ),\n fetchSendQuota(config.roleArn, config.region),\n ]);\n\n res.json({\n metrics,\n quota,\n timestamp: Date.now(),\n });\n } catch (error: unknown) {\n const errorMessage =\n error instanceof Error ? error.message : \"Unknown error\";\n console.error(\"Error fetching metrics:\", error);\n res.status(500).json({ error: errorMessage });\n }\n });\n\n return router;\n}\n","import {\n CloudWatchClient,\n GetMetricDataCommand,\n type MetricDataQuery,\n} from \"@aws-sdk/client-cloudwatch\";\nimport { assumeRole } from \"../../utils/shared/assume-role.js\";\n\nexport type MetricsData = {\n sends: Array<{ timestamp: number; value: number }>;\n bounces: Array<{ timestamp: number; value: number }>;\n complaints: Array<{ timestamp: number; value: number }>;\n deliveries: Array<{ timestamp: number; value: number }>;\n opens: Array<{ timestamp: number; value: number }>;\n clicks: Array<{ timestamp: number; value: number }>;\n};\n\n/**\n * Fetch SES metrics from CloudWatch\n */\nexport async function fetchSESMetrics(\n roleArn: string | undefined,\n region: string,\n timeRange: { start: Date; end: Date },\n tableName?: string\n): Promise<MetricsData> {\n // For console usage, use current credentials instead of assuming role\n const credentials = roleArn ? await assumeRole(roleArn, region) : undefined;\n\n // Create CloudWatch client\n const cloudwatch = new CloudWatchClient({ region, credentials });\n\n // Define metric queries\n const queries: MetricDataQuery[] = [\n {\n Id: \"sends\",\n MetricStat: {\n Metric: {\n Namespace: \"AWS/SES\",\n MetricName: \"Send\",\n },\n Period: 300, // 5 minutes\n Stat: \"Sum\",\n },\n },\n {\n Id: \"bounces\",\n MetricStat: {\n Metric: {\n Namespace: \"AWS/SES\",\n MetricName: \"Bounce\",\n },\n Period: 300,\n Stat: \"Sum\",\n },\n },\n {\n Id: \"complaints\",\n MetricStat: {\n Metric: {\n Namespace: \"AWS/SES\",\n MetricName: \"Complaint\",\n },\n Period: 300,\n Stat: \"Sum\",\n },\n },\n {\n Id: \"deliveries\",\n MetricStat: {\n Metric: {\n Namespace: \"AWS/SES\",\n MetricName: \"Delivery\",\n },\n Period: 300,\n Stat: \"Sum\",\n },\n },\n ];\n\n // Fetch metrics\n const response = await cloudwatch.send(\n new GetMetricDataCommand({\n MetricDataQueries: queries,\n StartTime: timeRange.start,\n EndTime: timeRange.end,\n })\n );\n\n // Parse results\n const results = response.MetricDataResults || [];\n\n const parseMetric = (id: string) => {\n const metric = results.find((r) => r.Id === id);\n if (!(metric?.Timestamps && metric.Values)) {\n return [];\n }\n\n return metric.Timestamps.map((timestamp, i) => ({\n timestamp: timestamp.getTime(),\n value: metric.Values?.[i] || 0,\n }));\n };\n\n // Fetch Opens and Clicks from DynamoDB if table name is provided\n let opens: Array<{ timestamp: number; value: number }> = [];\n let clicks: Array<{ timestamp: number; value: number }> = [];\n\n if (tableName) {\n try {\n const { fetchDynamoDBMetrics } = await import(\"./dynamodb-metrics.js\");\n const dynamoMetrics = await fetchDynamoDBMetrics(\n region,\n tableName,\n timeRange\n );\n opens = dynamoMetrics.opens;\n clicks = dynamoMetrics.clicks;\n } catch (error) {\n console.error(\"Error fetching DynamoDB metrics:\", error);\n // Continue with empty arrays\n }\n }\n\n return {\n sends: parseMetric(\"sends\"),\n bounces: parseMetric(\"bounces\"),\n complaints: parseMetric(\"complaints\"),\n deliveries: parseMetric(\"deliveries\"),\n opens,\n clicks,\n };\n}\n","import dns from \"node:dns/promises\";\nimport type { Request, Response, Router } from \"express\";\nimport { Router as createRouter } from \"express\";\nimport { loadConnectionMetadata } from \"../../utils/shared/metadata.js\";\nimport type { ServerConfig } from \"../server.js\";\nimport { fetchEmailSettings } from \"../services/settings-service.js\";\n\nexport function createSettingsRouter(config: ServerConfig): Router {\n const router = createRouter();\n\n /**\n * Get deployment configuration\n */\n router.get(\"/deployment\", async (_req: Request, res: Response) => {\n try {\n res.json({\n archivingEnabled: config.archivingEnabled ?? false,\n archiveArn: config.archiveArn,\n tableName: config.tableName,\n region: config.region,\n });\n } catch (error: unknown) {\n const errorMessage =\n error instanceof Error ? error.message : \"Unknown error\";\n console.error(\"Error fetching deployment config:\", error);\n res.status(500).json({ error: errorMessage });\n }\n });\n\n /**\n * Get email settings (configuration set + identity)\n */\n router.get(\"/\", async (_req: Request, res: Response) => {\n try {\n // Load metadata to get configuration\n const metadata = await loadConnectionMetadata(\n config.accountId || \"\",\n config.region\n );\n\n if (!metadata) {\n return res.status(404).json({\n error: \"No Wraps infrastructure found for this account and region\",\n });\n }\n\n // Get configuration set name and domain from metadata\n const configSetName = \"wraps-email-tracking\"; // Always use this name\n const domain = metadata.services.email?.config.domain;\n\n // Fetch settings from AWS\n const settings = await fetchEmailSettings(\n config.roleArn,\n config.region,\n configSetName,\n domain\n );\n\n // Add region to response\n res.json({\n ...settings,\n region: config.region,\n });\n } catch (error: unknown) {\n const errorMessage =\n error instanceof Error ? error.message : \"Unknown error\";\n console.error(\"Error fetching settings:\", error);\n res.status(500).json({ error: errorMessage });\n }\n });\n\n /**\n * Verify tracking domain CNAME\n */\n router.get(\"/verify-tracking-domain\", async (req: Request, res: Response) => {\n try {\n const { domain, expectedTarget } = req.query;\n\n if (!domain || typeof domain !== \"string\") {\n return res.status(400).json({ error: \"Domain parameter required\" });\n }\n\n if (!expectedTarget || typeof expectedTarget !== \"string\") {\n return res\n .status(400)\n .json({ error: \"Expected target parameter required\" });\n }\n\n console.log(`[Verify] Checking CNAME for: ${domain}`);\n console.log(`[Verify] Expected target: ${expectedTarget}`);\n\n // Check CNAME record using DNS\n const records = await dns.resolveCname(domain);\n\n console.log(\"[Verify] CNAME records found:\", records);\n\n // Check if any CNAME points to the expected target\n const verified = records.some((record) =>\n record.toLowerCase().includes(expectedTarget.toLowerCase())\n );\n\n console.log(`[Verify] Verified: ${verified}`);\n\n res.json({\n verified,\n error: verified\n ? undefined\n : `CNAME not pointing to ${expectedTarget}. Found: ${records.join(\", \")}`,\n });\n } catch (error: any) {\n console.error(\"[Verify] Error verifying tracking domain:\", error);\n\n // If no CNAME record exists, DNS will throw ENODATA or ENOTFOUND\n if (error.code === \"ENODATA\" || error.code === \"ENOTFOUND\") {\n return res.json({\n verified: false,\n error: \"No CNAME record found for this domain\",\n });\n }\n\n const errorMessage =\n error instanceof Error ? error.message : \"Failed to verify\";\n res.json({\n verified: false,\n error: errorMessage,\n });\n }\n });\n\n /**\n * Verify DMARC TXT record\n */\n router.get(\"/verify-dmarc\", async (req: Request, res: Response) => {\n try {\n const { domain } = req.query;\n\n if (!domain || typeof domain !== \"string\") {\n return res.status(400).json({ error: \"Domain parameter required\" });\n }\n\n const dmarcDomain = `_dmarc.${domain}`;\n\n console.log(`[Verify] Checking DMARC for: ${dmarcDomain}`);\n\n // Use Node.js DNS to resolve TXT records\n const records = await dns.resolveTxt(dmarcDomain);\n\n console.log(\"[Verify] TXT records found:\", records);\n\n // Check if there's a TXT record that starts with \"v=DMARC1\"\n // TXT records are arrays of strings, so we need to join them\n const hasDmarc = records.some((record) => {\n const value = record.join(\"\");\n return value.startsWith(\"v=DMARC1\");\n });\n\n console.log(`[Verify] DMARC verified: ${hasDmarc}`);\n\n res.json({\n verified: hasDmarc,\n error: hasDmarc ? undefined : \"DMARC record not found\",\n });\n } catch (error: any) {\n console.error(\"[Verify] Error verifying DMARC:\", error);\n\n // If no TXT record exists, DNS will throw ENODATA or ENOTFOUND\n if (error.code === \"ENODATA\" || error.code === \"ENOTFOUND\") {\n return res.json({\n verified: false,\n error: \"No DMARC record found for this domain\",\n });\n }\n\n const errorMessage =\n error instanceof Error ? error.message : \"Failed to verify\";\n res.json({\n verified: false,\n error: errorMessage,\n });\n }\n });\n\n /**\n * Update configuration set sending options\n */\n router.put(\"/config-set/sending\", async (req: Request, res: Response) => {\n try {\n const { enabled } = req.body;\n\n if (typeof enabled !== \"boolean\") {\n return res.status(400).json({ error: \"enabled must be a boolean\" });\n }\n\n // Load metadata to get configuration set name\n const metadata = await loadConnectionMetadata(\n config.accountId || \"\",\n config.region\n );\n\n if (!metadata) {\n return res.status(404).json({\n error: \"No Wraps infrastructure found for this account and region\",\n });\n }\n\n const configSetName = \"wraps-email-tracking\";\n\n console.log(\n `[Settings] Updating sending options for ${configSetName}: ${enabled}`\n );\n\n // Update sending options via AWS SDK\n const { SESv2Client, PutConfigurationSetSendingOptionsCommand } =\n await import(\"@aws-sdk/client-sesv2\");\n const { assumeRole } = await import(\"../../utils/shared/assume-role.js\");\n\n const credentials = config.roleArn\n ? await assumeRole(config.roleArn, config.region)\n : undefined;\n const sesClient = new SESv2Client({ region: config.region, credentials });\n\n await sesClient.send(\n new PutConfigurationSetSendingOptionsCommand({\n ConfigurationSetName: configSetName,\n SendingEnabled: enabled,\n })\n );\n\n console.log(\"[Settings] Successfully updated sending options\");\n\n res.json({ success: true });\n } catch (error: unknown) {\n const errorMessage =\n error instanceof Error ? error.message : \"Unknown error\";\n console.error(\"[Settings] Error updating sending options:\", error);\n res.status(500).json({ error: errorMessage });\n }\n });\n\n /**\n * Update configuration set reputation options\n */\n router.put(\"/config-set/reputation\", async (req: Request, res: Response) => {\n try {\n const { enabled } = req.body;\n\n if (typeof enabled !== \"boolean\") {\n return res.status(400).json({ error: \"enabled must be a boolean\" });\n }\n\n // Load metadata to get configuration set name\n const metadata = await loadConnectionMetadata(\n config.accountId || \"\",\n config.region\n );\n\n if (!metadata) {\n return res.status(404).json({\n error: \"No Wraps infrastructure found for this account and region\",\n });\n }\n\n const configSetName = \"wraps-email-tracking\";\n\n console.log(\n `[Settings] Updating reputation options for ${configSetName}: ${enabled}`\n );\n\n // Update reputation options via AWS SDK\n const { SESv2Client, PutConfigurationSetReputationOptionsCommand } =\n await import(\"@aws-sdk/client-sesv2\");\n const { assumeRole } = await import(\"../../utils/shared/assume-role.js\");\n\n const credentials = config.roleArn\n ? await assumeRole(config.roleArn, config.region)\n : undefined;\n const sesClient = new SESv2Client({ region: config.region, credentials });\n\n await sesClient.send(\n new PutConfigurationSetReputationOptionsCommand({\n ConfigurationSetName: configSetName,\n ReputationMetricsEnabled: enabled,\n })\n );\n\n console.log(\"[Settings] Successfully updated reputation options\");\n\n res.json({ success: true });\n } catch (error: unknown) {\n const errorMessage =\n error instanceof Error ? error.message : \"Unknown error\";\n console.error(\"[Settings] Error updating reputation options:\", error);\n res.status(500).json({ error: errorMessage });\n }\n });\n\n /**\n * Update tracking domain\n */\n router.put(\n \"/config-set/tracking-domain\",\n async (req: Request, res: Response) => {\n try {\n const { domain } = req.body;\n\n if (!domain || typeof domain !== \"string\") {\n return res.status(400).json({ error: \"domain must be a string\" });\n }\n\n // Validate domain format (basic check)\n const domainRegex = /^[a-zA-Z0-9][a-zA-Z0-9-_.]+[a-zA-Z0-9]$/;\n if (!domainRegex.test(domain)) {\n return res.status(400).json({ error: \"Invalid domain format\" });\n }\n\n // Load metadata to get configuration set name\n const metadata = await loadConnectionMetadata(\n config.accountId || \"\",\n config.region\n );\n\n if (!metadata) {\n return res.status(404).json({\n error: \"No Wraps infrastructure found for this account and region\",\n });\n }\n\n const configSetName = \"wraps-email-tracking\";\n\n console.log(\n `[Settings] Updating tracking domain for ${configSetName}: ${domain}`\n );\n\n // Update tracking options via AWS SDK\n const { SESv2Client, PutConfigurationSetTrackingOptionsCommand } =\n await import(\"@aws-sdk/client-sesv2\");\n const { assumeRole } = await import(\n \"../../utils/shared/assume-role.js\"\n );\n\n const credentials = config.roleArn\n ? await assumeRole(config.roleArn, config.region)\n : undefined;\n const sesClient = new SESv2Client({\n region: config.region,\n credentials,\n });\n\n await sesClient.send(\n new PutConfigurationSetTrackingOptionsCommand({\n ConfigurationSetName: configSetName,\n CustomRedirectDomain: domain,\n })\n );\n\n console.log(\"[Settings] Successfully updated tracking domain\");\n\n res.json({ success: true });\n } catch (error: unknown) {\n const errorMessage =\n error instanceof Error ? error.message : \"Unknown error\";\n console.error(\"[Settings] Error updating tracking domain:\", error);\n res.status(500).json({ error: errorMessage });\n }\n }\n );\n\n return router;\n}\n","import {\n GetConfigurationSetCommand,\n GetEmailIdentityCommand,\n SESv2Client,\n} from \"@aws-sdk/client-sesv2\";\nimport { assumeRole } from \"../../utils/shared/assume-role.js\";\n\nexport type EmailSettings = {\n configurationSet?: ConfigurationSetDetails;\n identity?: EmailIdentityDetails;\n};\n\nexport type ConfigurationSetDetails = {\n name: string;\n trackingOptions?: {\n customRedirectDomain?: string;\n httpsPolicy?: \"REQUIRE\" | \"OPTIONAL\";\n };\n deliveryOptions?: {\n tlsPolicy?: \"REQUIRE\" | \"OPTIONAL\";\n sendingPoolName?: string;\n };\n reputationOptions?: {\n reputationMetricsEnabled: boolean;\n lastFreshStart?: Date;\n };\n sendingOptions?: {\n sendingEnabled: boolean;\n };\n suppressionOptions?: {\n suppressedReasons?: (\"BOUNCE\" | \"COMPLAINT\")[];\n };\n};\n\nexport type EmailIdentityDetails = {\n identityType: \"EMAIL_ADDRESS\" | \"DOMAIN\";\n identityName: string;\n verificationStatus:\n | \"PENDING\"\n | \"SUCCESS\"\n | \"FAILED\"\n | \"TEMPORARY_FAILURE\"\n | \"NOT_STARTED\";\n dkimAttributes?: {\n status: \"SUCCESS\" | \"PENDING\" | \"FAILED\" | \"NOT_STARTED\";\n tokens?: string[];\n signingEnabled: boolean;\n signingKeyLength?: \"RSA_1024_BIT\" | \"RSA_2048_BIT\";\n };\n mailFromAttributes?: {\n mailFromDomain?: string;\n mailFromDomainStatus?: \"PENDING\" | \"SUCCESS\" | \"FAILED\";\n behaviorOnMxFailure?: \"USE_DEFAULT_VALUE\" | \"REJECT_MESSAGE\";\n };\n configurationSetName?: string;\n verifiedForSendingStatus: boolean;\n tags?: Record<string, string>;\n};\n\n/**\n * Fetch configuration set details\n */\nexport async function fetchConfigurationSet(\n roleArn: string | undefined,\n region: string,\n configSetName: string\n): Promise<ConfigurationSetDetails> {\n const credentials = roleArn ? await assumeRole(roleArn, region) : undefined;\n const sesv2 = new SESv2Client({ region, credentials });\n\n const response = await sesv2.send(\n new GetConfigurationSetCommand({\n ConfigurationSetName: configSetName,\n })\n );\n\n return {\n name: configSetName,\n trackingOptions: response.TrackingOptions\n ? {\n customRedirectDomain: response.TrackingOptions.CustomRedirectDomain,\n httpsPolicy: response.TrackingOptions.HttpsPolicy as\n | \"REQUIRE\"\n | \"OPTIONAL\",\n }\n : undefined,\n deliveryOptions: response.DeliveryOptions\n ? {\n tlsPolicy: response.DeliveryOptions.TlsPolicy as\n | \"REQUIRE\"\n | \"OPTIONAL\",\n sendingPoolName: response.DeliveryOptions.SendingPoolName,\n }\n : undefined,\n reputationOptions: response.ReputationOptions\n ? {\n reputationMetricsEnabled:\n response.ReputationOptions.ReputationMetricsEnabled ?? false,\n lastFreshStart: response.ReputationOptions.LastFreshStart,\n }\n : undefined,\n sendingOptions: response.SendingOptions\n ? {\n sendingEnabled: response.SendingOptions.SendingEnabled ?? true,\n }\n : undefined,\n suppressionOptions: response.SuppressionOptions\n ? {\n suppressedReasons: response.SuppressionOptions.SuppressedReasons as\n | (\"BOUNCE\" | \"COMPLAINT\")[]\n | undefined,\n }\n : undefined,\n };\n}\n\n/**\n * Fetch email identity details\n */\nexport async function fetchEmailIdentity(\n roleArn: string | undefined,\n region: string,\n identityName: string\n): Promise<EmailIdentityDetails> {\n const credentials = roleArn ? await assumeRole(roleArn, region) : undefined;\n const sesv2 = new SESv2Client({ region, credentials });\n\n const response = await sesv2.send(\n new GetEmailIdentityCommand({\n EmailIdentity: identityName,\n })\n );\n\n return {\n identityType: response.IdentityType as \"EMAIL_ADDRESS\" | \"DOMAIN\",\n identityName,\n verificationStatus:\n response.VerificationStatus as EmailIdentityDetails[\"verificationStatus\"],\n dkimAttributes: response.DkimAttributes\n ? {\n status: response.DkimAttributes.Status as\n | \"SUCCESS\"\n | \"PENDING\"\n | \"FAILED\"\n | \"NOT_STARTED\",\n tokens: response.DkimAttributes.Tokens,\n signingEnabled: response.DkimAttributes.SigningEnabled ?? false,\n signingKeyLength: response.DkimAttributes\n .NextSigningKeyLength as NonNullable<\n EmailIdentityDetails[\"dkimAttributes\"]\n >[\"signingKeyLength\"],\n }\n : undefined,\n mailFromAttributes: response.MailFromAttributes\n ? {\n mailFromDomain: response.MailFromAttributes.MailFromDomain,\n mailFromDomainStatus: response.MailFromAttributes\n .MailFromDomainStatus as NonNullable<\n EmailIdentityDetails[\"mailFromAttributes\"]\n >[\"mailFromDomainStatus\"],\n behaviorOnMxFailure: response.MailFromAttributes\n .BehaviorOnMxFailure as NonNullable<\n EmailIdentityDetails[\"mailFromAttributes\"]\n >[\"behaviorOnMxFailure\"],\n }\n : undefined,\n configurationSetName: response.ConfigurationSetName,\n verifiedForSendingStatus: response.VerifiedForSendingStatus ?? false,\n tags: response.Tags?.reduce(\n (acc, tag) => {\n if (tag.Key) {\n acc[tag.Key] = tag.Value || \"\";\n }\n return acc;\n },\n {} as Record<string, string>\n ),\n };\n}\n\n/**\n * Fetch complete email settings\n */\nexport async function fetchEmailSettings(\n roleArn: string | undefined,\n region: string,\n configSetName?: string,\n domain?: string\n): Promise<EmailSettings> {\n const settings: EmailSettings = {};\n\n if (configSetName) {\n try {\n settings.configurationSet = await fetchConfigurationSet(\n roleArn,\n region,\n configSetName\n );\n } catch (error) {\n console.error(\"Failed to fetch configuration set:\", error);\n }\n }\n\n if (domain) {\n try {\n settings.identity = await fetchEmailIdentity(roleArn, region, domain);\n } catch (error) {\n console.error(\"Failed to fetch email identity:\", error);\n }\n }\n\n return settings;\n}\n","import type { Request, Response, Router } from \"express\";\nimport { Router as createRouter } from \"express\";\nimport { loadConnectionMetadata } from \"../../utils/shared/metadata.js\";\nimport type { ServerConfig } from \"../server.js\";\n\nexport function createUserRouter(config: ServerConfig): Router {\n const router = createRouter();\n\n /**\n * Get current AWS user/account information\n */\n router.get(\"/\", async (_req: Request, res: Response) => {\n try {\n const accountId = config.accountId || \"Unknown\";\n const region = config.region;\n\n console.log(\n \"[User API] Fetching user info for account:\",\n accountId,\n \"region:\",\n region\n );\n\n // Load metadata to get additional details\n const metadata = await loadConnectionMetadata(accountId, region);\n console.log(\n \"[User API] Metadata loaded:\",\n metadata ? \"found\" : \"not found\"\n );\n\n // Get AWS account alias if available (for better UX)\n let accountAlias = accountId;\n try {\n if (config.roleArn) {\n console.log(\"[User API] Attempting to fetch account alias via IAM\");\n const { assumeRole } = await import(\n \"../../utils/shared/assume-role.js\"\n );\n const { IAMClient, ListAccountAliasesCommand } = await import(\n \"@aws-sdk/client-iam\"\n );\n\n const credentials = await assumeRole(config.roleArn, region);\n const iamClient = new IAMClient({ region, credentials });\n\n const response = await iamClient.send(\n new ListAccountAliasesCommand({})\n );\n\n if (response.AccountAliases && response.AccountAliases.length > 0) {\n accountAlias = response.AccountAliases[0];\n console.log(\"[User API] Account alias found:\", accountAlias);\n } else {\n console.log(\"[User API] No account alias found, using account ID\");\n }\n } else {\n console.log(\"[User API] No roleArn, skipping account alias lookup\");\n }\n } catch (error) {\n // Silently fail if we can't get account alias\n console.error(\"[User API] Error fetching account alias:\", error);\n }\n\n const responseData = {\n accountId,\n accountAlias,\n region,\n provider: metadata?.provider || \"unknown\",\n domain: metadata?.services?.email?.config?.domain || null,\n preset: metadata?.services?.email?.preset || null,\n timestamp: metadata?.timestamp || null,\n };\n\n console.log(\"[User API] Sending response:\", responseData);\n res.json(responseData);\n } catch (error: unknown) {\n const errorMessage =\n error instanceof Error ? error.message : \"Unknown error\";\n console.error(\"[User API] Error fetching user info:\", error);\n res.status(500).json({ error: errorMessage });\n }\n });\n\n return router;\n}\n","import * as clack from \"@clack/prompts\";\nimport pc from \"picocolors\";\nimport type { DestroyOptions } from \"../../types/index.js\";\nimport {\n getAWSRegion,\n validateAWSCredentials,\n} from \"../../utils/shared/aws.js\";\nimport { loadConnectionMetadata } from \"../../utils/shared/metadata.js\";\nimport { emailDestroy } from \"../email/destroy.js\";\n\n/**\n * Global Destroy command - Show services and route to service-specific destroy\n */\nexport async function destroy(options: DestroyOptions): Promise<void> {\n clack.intro(pc.bold(\"Wraps Infrastructure Teardown\"));\n\n // 1. Validate AWS credentials\n const spinner = clack.spinner();\n spinner.start(\"Validating AWS credentials\");\n\n let identity;\n try {\n identity = await validateAWSCredentials();\n spinner.stop(\"AWS credentials validated\");\n } catch (error: any) {\n spinner.stop(\"AWS credentials validation failed\");\n throw error;\n }\n\n // 2. Get region\n const region = await getAWSRegion();\n\n // 3. Load connection metadata to see what services are deployed\n const metadata = await loadConnectionMetadata(identity.accountId, region);\n\n const deployedServices: string[] = [];\n\n if (metadata?.services?.email) {\n deployedServices.push(\"email\");\n }\n\n if (deployedServices.length === 0) {\n clack.log.warn(\"No Wraps services found in this region\");\n console.log(\n `\\nRun ${pc.cyan(\"wraps email init\")} to deploy infrastructure.\\n`\n );\n process.exit(0);\n }\n\n // 4. If only one service, destroy it directly\n if (deployedServices.length === 1) {\n const service = deployedServices[0];\n clack.log.info(`Found ${pc.cyan(service)} service deployed`);\n\n if (service === \"email\") {\n // Pass through to email destroy\n await emailDestroy(options);\n return;\n }\n }\n\n // 5. Multiple services - ask which to destroy\n const serviceToDestroy = await clack.select({\n message: \"Which service would you like to destroy?\",\n options: [\n ...deployedServices.map((s) => ({\n value: s,\n label: s.charAt(0).toUpperCase() + s.slice(1),\n hint: s === \"email\" ? \"AWS SES email infrastructure\" : undefined,\n })),\n {\n value: \"all\",\n label: \"All services\",\n hint: \"Destroy all Wraps infrastructure\",\n },\n ],\n });\n\n if (clack.isCancel(serviceToDestroy)) {\n clack.cancel(\"Operation cancelled.\");\n process.exit(0);\n }\n\n // 6. Route to appropriate destroy command\n if (serviceToDestroy === \"email\" || serviceToDestroy === \"all\") {\n if (deployedServices.includes(\"email\")) {\n await emailDestroy(options);\n }\n }\n\n if (serviceToDestroy === \"all\") {\n clack.outro(pc.green(\"All Wraps infrastructure has been removed\"));\n }\n}\n","import * as clack from \"@clack/prompts\";\nimport * as pulumi from \"@pulumi/pulumi\";\nimport pc from \"picocolors\";\nimport { trackCommand } from \"../../telemetry/events.js\";\nimport type { StatusOptions } from \"../../types/index.js\";\nimport {\n getAWSRegion,\n validateAWSCredentials,\n} from \"../../utils/shared/aws.js\";\nimport {\n ensurePulumiWorkDir,\n getPulumiWorkDir,\n} from \"../../utils/shared/fs.js\";\nimport { DeploymentProgress } from \"../../utils/shared/output.js\";\n\n/**\n * Global Status command - Show overview of all deployed infrastructure\n */\nexport async function status(_options: StatusOptions): Promise<void> {\n const startTime = Date.now();\n const progress = new DeploymentProgress();\n\n clack.intro(pc.bold(\"Wraps Infrastructure Status\"));\n\n // 1. Validate AWS credentials\n const identity = await progress.execute(\n \"Loading infrastructure status\",\n async () => validateAWSCredentials()\n );\n\n progress.info(`AWS Account: ${pc.cyan(identity.accountId)}`);\n\n // 2. Get region\n const region = await getAWSRegion();\n progress.info(`Region: ${pc.cyan(region)}`);\n\n // 3. Check for deployed services\n const services: Array<{\n name: string;\n status: \"deployed\" | \"not_deployed\";\n details?: string;\n }> = [];\n\n // Check Email infrastructure\n try {\n await ensurePulumiWorkDir();\n const stack = await pulumi.automation.LocalWorkspace.selectStack({\n stackName: `wraps-${identity.accountId}-${region}`,\n workDir: getPulumiWorkDir(),\n });\n const outputs = await stack.outputs();\n\n if (outputs.roleArn?.value) {\n const domainCount = outputs.domains?.value?.length || 0;\n services.push({\n name: \"Email\",\n status: \"deployed\",\n details: domainCount > 0 ? `${domainCount} domain(s)` : undefined,\n });\n } else {\n services.push({ name: \"Email\", status: \"not_deployed\" });\n }\n } catch (_error) {\n services.push({ name: \"Email\", status: \"not_deployed\" });\n }\n\n progress.stop();\n\n // 4. Display services overview\n console.log();\n clack.note(\n services\n .map((s) => {\n if (s.status === \"deployed\") {\n const details = s.details ? pc.dim(` (${s.details})`) : \"\";\n return ` ${pc.green(\"✓\")} ${s.name}${details}`;\n }\n return ` ${pc.dim(\"○\")} ${s.name} ${pc.dim(\"(not deployed)\")}`;\n })\n .join(\"\\n\"),\n \"Services\"\n );\n\n // 5. Show next steps\n const hasDeployedServices = services.some((s) => s.status === \"deployed\");\n\n if (hasDeployedServices) {\n console.log(`\\n${pc.bold(\"Details:\")}`);\n if (services.find((s) => s.name === \"Email\")?.status === \"deployed\") {\n console.log(\n ` ${pc.dim(\"Email:\")} ${pc.cyan(\"wraps email status\")}`\n );\n }\n } else {\n console.log(`\\n${pc.bold(\"Get started:\")}`);\n console.log(\n ` ${pc.dim(\"Deploy email:\")} ${pc.cyan(\"wraps email init\")}`\n );\n }\n\n console.log(`\\n${pc.bold(\"Dashboard:\")} ${pc.blue(\"https://app.wraps.dev\")}`);\n console.log(`${pc.bold(\"Docs:\")} ${pc.blue(\"https://wraps.dev/docs\")}\\n`);\n\n // 6. Track status command\n trackCommand(\"status\", {\n success: true,\n services_deployed: services.filter((s) => s.status === \"deployed\").length,\n duration_ms: Date.now() - startTime,\n });\n}\n","/**\n * Telemetry management commands\n * @module commands/telemetry\n */\n\nimport * as clack from \"@clack/prompts\";\nimport pc from \"picocolors\";\nimport { getTelemetryClient } from \"../telemetry/client.js\";\n\n/**\n * Enable telemetry\n */\nexport async function telemetryEnable(): Promise<void> {\n const client = getTelemetryClient();\n\n client.enable();\n\n clack.log.success(pc.green(\"Telemetry enabled\"));\n console.log(` Config: ${pc.dim(client.getConfigPath())}`);\n console.log(`\\n ${pc.dim(\"Thank you for helping improve Wraps!\")}\\n`);\n}\n\n/**\n * Disable telemetry\n */\nexport async function telemetryDisable(): Promise<void> {\n const client = getTelemetryClient();\n\n client.disable();\n\n clack.log.success(pc.green(\"Telemetry disabled\"));\n console.log(` Config: ${pc.dim(client.getConfigPath())}`);\n console.log(\n `\\n ${pc.dim(\"You can re-enable with:\")} wraps telemetry enable\\n`\n );\n}\n\n/**\n * Show telemetry status\n */\nexport async function telemetryStatus(): Promise<void> {\n const client = getTelemetryClient();\n\n clack.intro(pc.bold(\"Telemetry Status\"));\n\n const status = client.isEnabled() ? pc.green(\"Enabled\") : pc.red(\"Disabled\");\n\n console.log();\n console.log(` ${pc.bold(\"Status:\")} ${status}`);\n console.log(` ${pc.bold(\"Config file:\")} ${pc.dim(client.getConfigPath())}`);\n\n // Show opt-out methods\n if (client.isEnabled()) {\n console.log();\n console.log(pc.bold(\" How to opt-out:\"));\n console.log(` ${pc.cyan(\"wraps telemetry disable\")}`);\n console.log(\n ` ${pc.dim(\"Or set:\")} ${pc.cyan(\"WRAPS_TELEMETRY_DISABLED=1\")}`\n );\n console.log(` ${pc.dim(\"Or set:\")} ${pc.cyan(\"DO_NOT_TRACK=1\")}`);\n } else {\n console.log();\n console.log(pc.bold(\" How to opt-in:\"));\n console.log(` ${pc.cyan(\"wraps telemetry enable\")}`);\n }\n\n // Show debug mode info\n console.log();\n console.log(pc.bold(\" Debug mode:\"));\n console.log(\n ` ${pc.dim(\"See what would be sent:\")} ${pc.cyan(\"WRAPS_TELEMETRY_DEBUG=1 wraps <command>\")}`\n );\n\n // Show docs link\n console.log();\n console.log(\n ` ${pc.dim(\"Learn more:\")} ${pc.cyan(\"https://wraps.dev/docs/telemetry\")}`\n );\n console.log();\n}\n","/**\n * Setup tab completion for the Wraps CLI\n *\n * This is a placeholder for future tab completion support.\n * Will integrate with tabtab or similar completion library.\n */\nexport function setupTabCompletion() {\n // Placeholder for tab completion setup\n // Will be implemented in Phase 2\n}\n\n/**\n * Print completion script for the current shell\n */\nexport function printCompletionScript() {\n console.log(\"# Wraps CLI Tab Completion\");\n console.log(\"# ========================\\n\");\n console.log(\"# Tab completion will be available in a future release.\\n\");\n console.log(\"# For now, here are the available commands:\\n\");\n console.log(\"# Email Commands:\");\n console.log(\n \"# wraps email init [--provider vercel|aws|railway|other] [--region <region>] [--domain <domain>]\"\n );\n console.log(\"# wraps email connect [--region <region>]\");\n console.log(\"# wraps email status [--account <account-id>]\");\n console.log(\"# wraps email verify --domain <domain>\");\n console.log(\"# wraps email sync\");\n console.log(\"# wraps email upgrade\");\n console.log(\"# wraps email restore [--region <region>] [--force]\");\n console.log(\"# wraps email destroy [--force] [--preview]\");\n console.log(\"# wraps email domains add --domain <domain>\");\n console.log(\"# wraps email domains list\");\n console.log(\"# wraps email domains verify --domain <domain>\");\n console.log(\"# wraps email domains get-dkim --domain <domain>\");\n console.log(\"# wraps email domains remove --domain <domain> [--force]\\n\");\n console.log(\"# Global Commands:\");\n console.log(\"# wraps status\");\n console.log(\"# wraps destroy [--force] [--preview]\");\n console.log(\"# wraps console [--port <port>] [--no-open]\");\n console.log(\"# wraps completion\");\n console.log(\"# wraps telemetry [enable|disable|status]\\n\");\n console.log(\"# Dashboard Commands:\");\n console.log(\"# wraps dashboard update-role [--region <region>] [--force]\\n\");\n console.log(\"# Flags:\");\n console.log(\"# -p, --provider : vercel, aws, railway, other\");\n console.log(\n \"# -r, --region : us-east-1, us-east-2, us-west-1, us-west-2, eu-west-1, eu-west-2, etc.\"\n );\n console.log(\"# -d, --domain : Your domain name (e.g., myapp.com)\");\n console.log(\"# --account : AWS account ID or alias\");\n console.log(\"# --preset : starter, production, enterprise, custom\");\n console.log(\"# -y, --yes : Skip confirmation prompts\");\n console.log(\"# -f, --force : Force destructive operations\");\n console.log(\"# --preview : Preview changes without deploying\\n\");\n}\n"],"mappings":"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","names":["packageJson","acm","aws","config","aws","cloudfront","config","config","config","config","clack","pc","config","getAllPresetInfo","STSClient","MailManagerClient","DynamoDBClient","ScanCommand","unmarshall","dynamodb","dirname","join","fileURLToPath","clack","pc","intro","log","outro","pc","existsSync","join","join","existsSync","mkdir","clack","pc","status","intro","pc","log","iam","roleExists","outro","clack","pulumi","pc","aws","pulumi","DynamoDBClient","DescribeTableCommand","dynamodb","aws","config","aws","pulumi","IAMClient","GetRoleCommand","iam","config","existsSync","join","fileURLToPath","aws","pulumi","LambdaClient","lambda","config","aws","SESv2Client","GetConfigurationSetCommand","GetEmailIdentityCommand","config","aws","aws","IAMClient","iam","config","config","findHostedZone","createACMCertificate","createCloudFrontTracking","createMailManagerArchive","automation","installPulumiCli","pc","config","result","clack","pulumi","pc","IAMClient","GetIdentityVerificationAttributesCommand","ListIdentitiesCommand","SESClient","lambda","iam","pc","result","findHostedZone","createDNSRecords","clack","pulumi","pc","ChangeResourceRecordSetsCommand","ListHostedZonesByNameCommand","Route53Client","findHostedZone","Route53Client","ListHostedZonesByNameCommand","Route53Client","ChangeResourceRecordSetsCommand","SESv2Client","GetEmailIdentityCommand","pc","findHostedZone","SESv2Client","clack","pc","pc","SESv2Client","clack","pulumi","pc","pc","promptEmailArchiving","result","findHostedZone","createDNSRecords","clack","pulumi","pc","pc","clack","pulumi","pc","pc","SESv2Client","GetEmailIdentityCommand","identity","clack","pulumi","pc","pc","config","listSESDomains","findHostedZone","promptCustomConfig","result","createDNSRecords","clack","pulumi","pc","path","fileURLToPath","SESClient","GetEmailIdentityCommand","SESv2Client","sesv2","config","createRouter","DynamoDBClient","dynamodb","status","config","createRouter","fetchArchivedEmail","createRouter","cloudwatch","fetchDynamoDBMetrics","config","createRouter","createRouter","GetEmailIdentityCommand","SESv2Client","sesv2","config","createRouter","SESv2Client","assumeRole","createRouter","config","createRouter","assumeRole","IAMClient","__dirname","path","fileURLToPath","config","pc","clack","pc","pc","spinner","clack","pulumi","pc","pc","clack","pc","status","__filename","fileURLToPath","__dirname","dirname","join","pc"]}
|