@wraps.dev/cli 0.1.4 → 0.2.0

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 CHANGED
@@ -1285,7 +1285,7 @@ async function findHostedZone(domain, region) {
1285
1285
  return null;
1286
1286
  }
1287
1287
  }
1288
- async function createDNSRecords(hostedZoneId, domain, dkimTokens, region, customTrackingDomain) {
1288
+ async function createDNSRecords(hostedZoneId, domain, dkimTokens, region, customTrackingDomain, mailFromDomain) {
1289
1289
  const client = new Route53Client({ region });
1290
1290
  const changes = [];
1291
1291
  for (const token of dkimTokens) {
@@ -1330,6 +1330,28 @@ async function createDNSRecords(hostedZoneId, domain, dkimTokens, region, custom
1330
1330
  }
1331
1331
  });
1332
1332
  }
1333
+ if (mailFromDomain) {
1334
+ changes.push({
1335
+ Action: "UPSERT",
1336
+ ResourceRecordSet: {
1337
+ Name: mailFromDomain,
1338
+ Type: "MX",
1339
+ TTL: 1800,
1340
+ ResourceRecords: [
1341
+ { Value: `10 feedback-smtp.${region}.amazonses.com` }
1342
+ ]
1343
+ }
1344
+ });
1345
+ changes.push({
1346
+ Action: "UPSERT",
1347
+ ResourceRecordSet: {
1348
+ Name: mailFromDomain,
1349
+ Type: "TXT",
1350
+ TTL: 1800,
1351
+ ResourceRecords: [{ Value: '"v=spf1 include:amazonses.com ~all"' }]
1352
+ }
1353
+ });
1354
+ }
1333
1355
  await client.send(
1334
1356
  new ChangeResourceRecordSetsCommand({
1335
1357
  HostedZoneId: hostedZoneId,
@@ -1348,9 +1370,12 @@ var init_route53 = __esm({
1348
1370
 
1349
1371
  // src/cli.ts
1350
1372
  init_esm_shims();
1351
- import * as clack12 from "@clack/prompts";
1373
+ import { readFileSync } from "fs";
1374
+ import { dirname as dirname2, join as join4 } from "path";
1375
+ import { fileURLToPath as fileURLToPath4 } from "url";
1376
+ import * as clack13 from "@clack/prompts";
1352
1377
  import args from "args";
1353
- import pc12 from "picocolors";
1378
+ import pc13 from "picocolors";
1354
1379
 
1355
1380
  // src/commands/connect.ts
1356
1381
  init_esm_shims();
@@ -1775,6 +1800,7 @@ async function createSESResources(config) {
1775
1800
  });
1776
1801
  let domainIdentity;
1777
1802
  let dkimTokens;
1803
+ let mailFromDomain;
1778
1804
  if (config.domain) {
1779
1805
  domainIdentity = new aws5.sesv2.EmailIdentity("wraps-email-domain", {
1780
1806
  emailIdentity: config.domain,
@@ -1790,6 +1816,20 @@ async function createSESResources(config) {
1790
1816
  dkimTokens = domainIdentity.dkimSigningAttributes.apply(
1791
1817
  (attrs) => attrs?.tokens || []
1792
1818
  );
1819
+ mailFromDomain = config.mailFromDomain || `mail.${config.domain}`;
1820
+ new aws5.sesv2.EmailIdentityMailFromAttributes(
1821
+ "wraps-email-mail-from",
1822
+ {
1823
+ emailIdentity: config.domain,
1824
+ mailFromDomain,
1825
+ behaviorOnMxFailure: "USE_DEFAULT_VALUE"
1826
+ // Fallback to amazonses.com if MX record fails
1827
+ },
1828
+ {
1829
+ dependsOn: [domainIdentity]
1830
+ // Ensure domain identity exists first
1831
+ }
1832
+ );
1793
1833
  }
1794
1834
  return {
1795
1835
  configSet,
@@ -1799,7 +1839,8 @@ async function createSESResources(config) {
1799
1839
  dkimTokens,
1800
1840
  dnsAutoCreated: false,
1801
1841
  // Will be set after deployment
1802
- customTrackingDomain: config.trackingConfig?.customRedirectDomain
1842
+ customTrackingDomain: config.trackingConfig?.customRedirectDomain,
1843
+ mailFromDomain
1803
1844
  };
1804
1845
  }
1805
1846
 
@@ -1884,6 +1925,7 @@ async function deployEmailStack(config) {
1884
1925
  if (emailConfig.tracking?.enabled || emailConfig.eventTracking?.enabled) {
1885
1926
  sesResources = await createSESResources({
1886
1927
  domain: emailConfig.domain,
1928
+ mailFromDomain: emailConfig.mailFromDomain,
1887
1929
  region: config.region,
1888
1930
  trackingConfig: emailConfig.tracking,
1889
1931
  eventTypes: emailConfig.eventTracking?.events
@@ -1927,7 +1969,8 @@ async function deployEmailStack(config) {
1927
1969
  eventBusName: sesResources?.eventBus.name,
1928
1970
  queueUrl: sqsResources?.queue.url,
1929
1971
  dlqUrl: sqsResources?.dlq.url,
1930
- customTrackingDomain: sesResources?.customTrackingDomain
1972
+ customTrackingDomain: sesResources?.customTrackingDomain,
1973
+ mailFromDomain: sesResources?.mailFromDomain
1931
1974
  };
1932
1975
  }
1933
1976
 
@@ -2146,6 +2189,14 @@ Verification should complete within a few minutes.`,
2146
2189
  pc2.bold("DMARC Record (TXT):"),
2147
2190
  ` ${pc2.cyan(`_dmarc.${domain}`)} ${pc2.dim("TXT")} "v=DMARC1; p=quarantine; rua=mailto:postmaster@${domain}"`
2148
2191
  );
2192
+ if (outputs.mailFromDomain) {
2193
+ dnsLines.push(
2194
+ "",
2195
+ pc2.bold("MAIL FROM Domain Records (for DMARC alignment):"),
2196
+ ` ${pc2.cyan(outputs.mailFromDomain)} ${pc2.dim("MX")} "10 feedback-smtp.${outputs.region}.amazonses.com"`,
2197
+ ` ${pc2.cyan(outputs.mailFromDomain)} ${pc2.dim("TXT")} "v=spf1 include:amazonses.com ~all"`
2198
+ );
2199
+ }
2149
2200
  }
2150
2201
  clack2.note(dnsLines.join("\n"), "DNS Records to add:");
2151
2202
  }
@@ -2198,7 +2249,14 @@ function displayStatus(status2) {
2198
2249
  const domainStrings = status2.domains.map((d) => {
2199
2250
  const statusIcon = d.status === "verified" ? "\u2713" : d.status === "pending" ? "\u23F1" : "\u2717";
2200
2251
  const statusColor = d.status === "verified" ? pc2.green : d.status === "pending" ? pc2.yellow : pc2.red;
2201
- return ` ${d.domain} ${statusColor(`${statusIcon} ${d.status}`)}`;
2252
+ let domainLine = ` ${d.domain} ${statusColor(`${statusIcon} ${d.status}`)}`;
2253
+ if (d.mailFromDomain) {
2254
+ const mailFromStatusIcon = d.mailFromStatus === "SUCCESS" ? "\u2713" : "\u23F1";
2255
+ const mailFromColor = d.mailFromStatus === "SUCCESS" ? pc2.green : pc2.yellow;
2256
+ domainLine += `
2257
+ ${pc2.dim("MAIL FROM:")} ${d.mailFromDomain} ${mailFromColor(mailFromStatusIcon)}`;
2258
+ }
2259
+ return domainLine;
2202
2260
  });
2203
2261
  infoLines.push(`${pc2.bold("Domains:")}
2204
2262
  ${domainStrings.join("\n")}`);
@@ -2257,13 +2315,14 @@ ${domainStrings.join("\n")}`);
2257
2315
  );
2258
2316
  }
2259
2317
  clack2.note(resourceLines.join("\n"), "Resources");
2260
- const pendingDomains = status2.domains.filter(
2261
- (d) => d.status === "pending" && d.dkimTokens
2318
+ const domainsNeedingDNS = status2.domains.filter(
2319
+ (d) => d.status === "pending" && d.dkimTokens || d.mailFromDomain && d.mailFromStatus !== "SUCCESS"
2262
2320
  );
2263
- if (pendingDomains.length > 0) {
2264
- for (const domain of pendingDomains) {
2265
- if (domain.dkimTokens && domain.dkimTokens.length > 0) {
2266
- const dnsLines = [
2321
+ if (domainsNeedingDNS.length > 0) {
2322
+ for (const domain of domainsNeedingDNS) {
2323
+ const dnsLines = [];
2324
+ if (domain.status === "pending" && domain.dkimTokens && domain.dkimTokens.length > 0) {
2325
+ dnsLines.push(
2267
2326
  pc2.bold("DKIM Records (CNAME):"),
2268
2327
  ...domain.dkimTokens.map(
2269
2328
  (token) => ` ${pc2.cyan(`${token}._domainkey.${domain.domain}`)} ${pc2.dim("CNAME")} "${token}.dkim.amazonses.com"`
@@ -2274,11 +2333,21 @@ ${domainStrings.join("\n")}`);
2274
2333
  "",
2275
2334
  pc2.bold("DMARC Record (TXT):"),
2276
2335
  ` ${pc2.cyan(`_dmarc.${domain.domain}`)} ${pc2.dim("TXT")} "v=DMARC1; p=quarantine; rua=mailto:postmaster@${domain.domain}"`
2277
- ];
2336
+ );
2337
+ }
2338
+ if (domain.mailFromDomain && domain.mailFromStatus !== "SUCCESS") {
2339
+ if (dnsLines.length > 0) dnsLines.push("");
2340
+ dnsLines.push(
2341
+ pc2.bold("MAIL FROM Domain Records (for DMARC alignment):"),
2342
+ ` ${pc2.cyan(domain.mailFromDomain)} ${pc2.dim("MX")} "10 feedback-smtp.${status2.region}.amazonses.com"`,
2343
+ ` ${pc2.cyan(domain.mailFromDomain)} ${pc2.dim("TXT")} "v=spf1 include:amazonses.com ~all"`
2344
+ );
2345
+ }
2346
+ if (dnsLines.length > 0) {
2278
2347
  clack2.note(dnsLines.join("\n"), `DNS Records for ${domain.domain}`);
2279
2348
  }
2280
2349
  }
2281
- const exampleDomain = pendingDomains[0].domain;
2350
+ const exampleDomain = domainsNeedingDNS[0].domain;
2282
2351
  console.log(
2283
2352
  `
2284
2353
  ${pc2.dim("Run:")} ${pc2.yellow(`wraps verify --domain ${exampleDomain}`)} ${pc2.dim(
@@ -4165,7 +4234,8 @@ async function init(options) {
4165
4234
  outputs.domain,
4166
4235
  outputs.dkimTokens,
4167
4236
  region,
4168
- outputs.customTrackingDomain
4237
+ outputs.customTrackingDomain,
4238
+ outputs.mailFromDomain
4169
4239
  );
4170
4240
  progress.succeed("DNS records created in Route53");
4171
4241
  dnsAutoCreated = true;
@@ -4192,7 +4262,8 @@ async function init(options) {
4192
4262
  tableName: outputs.tableName,
4193
4263
  dnsRecords: dnsRecords.length > 0 ? dnsRecords : void 0,
4194
4264
  dnsAutoCreated,
4195
- domain: outputs.domain
4265
+ domain: outputs.domain,
4266
+ mailFromDomain: outputs.mailFromDomain
4196
4267
  });
4197
4268
  }
4198
4269
 
@@ -4342,13 +4413,17 @@ Run ${pc9.cyan("wraps init")} to deploy infrastructure.
4342
4413
  return {
4343
4414
  domain: d.domain,
4344
4415
  status: d.verified ? "verified" : "pending",
4345
- dkimTokens: identity2.DkimAttributes?.Tokens || []
4416
+ dkimTokens: identity2.DkimAttributes?.Tokens || [],
4417
+ mailFromDomain: identity2.MailFromAttributes?.MailFromDomain,
4418
+ mailFromStatus: identity2.MailFromAttributes?.MailFromDomainStatus
4346
4419
  };
4347
4420
  } catch (_error) {
4348
4421
  return {
4349
4422
  domain: d.domain,
4350
4423
  status: d.verified ? "verified" : "pending",
4351
- dkimTokens: void 0
4424
+ dkimTokens: void 0,
4425
+ mailFromDomain: void 0,
4426
+ mailFromStatus: void 0
4352
4427
  };
4353
4428
  }
4354
4429
  })
@@ -4369,17 +4444,14 @@ Run ${pc9.cyan("wraps init")} to deploy infrastructure.
4369
4444
  });
4370
4445
  }
4371
4446
 
4372
- // src/commands/upgrade.ts
4447
+ // src/commands/update.ts
4373
4448
  init_esm_shims();
4374
4449
  import * as clack10 from "@clack/prompts";
4375
4450
  import * as pulumi11 from "@pulumi/pulumi";
4376
4451
  import pc10 from "picocolors";
4377
4452
  init_aws();
4378
- init_costs();
4379
- init_presets();
4380
- init_prompts();
4381
- async function upgrade(options) {
4382
- clack10.intro(pc10.bold("Wraps Upgrade - Enhance Your Email Infrastructure"));
4453
+ async function update(options) {
4454
+ clack10.intro(pc10.bold("Wraps Update - Apply CLI Updates to Infrastructure"));
4383
4455
  const progress = new DeploymentProgress();
4384
4456
  const wasAutoInstalled = await progress.execute(
4385
4457
  "Checking Pulumi CLI installation",
@@ -4423,33 +4495,224 @@ ${pc10.bold("Current Configuration:")}
4423
4495
  }
4424
4496
  if (config.tracking?.enabled) {
4425
4497
  console.log(` ${pc10.green("\u2713")} Open & Click Tracking`);
4498
+ }
4499
+ if (config.suppressionList?.enabled) {
4500
+ console.log(` ${pc10.green("\u2713")} Bounce/Complaint Suppression`);
4501
+ }
4502
+ if (config.eventTracking?.enabled) {
4503
+ console.log(` ${pc10.green("\u2713")} Event Tracking (EventBridge)`);
4504
+ }
4505
+ if (config.dedicatedIp) {
4506
+ console.log(` ${pc10.green("\u2713")} Dedicated IP Address`);
4507
+ }
4508
+ console.log("");
4509
+ console.log(`${pc10.bold("What will be updated:")}
4510
+ `);
4511
+ console.log(
4512
+ ` ${pc10.cyan("\u2022")} Lambda function code (if event tracking enabled)`
4513
+ );
4514
+ console.log(
4515
+ ` ${pc10.cyan("\u2022")} EventBridge rules (if event tracking enabled)`
4516
+ );
4517
+ console.log(` ${pc10.cyan("\u2022")} IAM policies (security improvements)`);
4518
+ console.log(` ${pc10.cyan("\u2022")} SES configuration set (feature updates)`);
4519
+ console.log("");
4520
+ progress.info(
4521
+ "Your current configuration will be preserved - no features will be added or removed"
4522
+ );
4523
+ console.log("");
4524
+ if (!options.yes) {
4525
+ const confirmed = await clack10.confirm({
4526
+ message: "Proceed with update?",
4527
+ initialValue: true
4528
+ });
4529
+ if (clack10.isCancel(confirmed) || !confirmed) {
4530
+ clack10.cancel("Update cancelled.");
4531
+ process.exit(0);
4532
+ }
4533
+ }
4534
+ let vercelConfig;
4535
+ if (metadata.provider === "vercel" && metadata.vercel) {
4536
+ vercelConfig = metadata.vercel;
4537
+ }
4538
+ const stackConfig = {
4539
+ provider: metadata.provider,
4540
+ region,
4541
+ vercel: vercelConfig,
4542
+ emailConfig: config
4543
+ };
4544
+ let outputs;
4545
+ try {
4546
+ outputs = await progress.execute(
4547
+ "Updating Wraps infrastructure (this may take 2-3 minutes)",
4548
+ async () => {
4549
+ await ensurePulumiWorkDir();
4550
+ const stack = await pulumi11.automation.LocalWorkspace.createOrSelectStack(
4551
+ {
4552
+ stackName: metadata.pulumiStackName || `wraps-${identity.accountId}-${region}`,
4553
+ projectName: "wraps-email",
4554
+ program: async () => {
4555
+ const result = await deployEmailStack(stackConfig);
4556
+ return {
4557
+ roleArn: result.roleArn,
4558
+ configSetName: result.configSetName,
4559
+ tableName: result.tableName,
4560
+ region: result.region,
4561
+ lambdaFunctions: result.lambdaFunctions,
4562
+ domain: result.domain,
4563
+ dkimTokens: result.dkimTokens,
4564
+ customTrackingDomain: result.customTrackingDomain
4565
+ };
4566
+ }
4567
+ },
4568
+ {
4569
+ workDir: getPulumiWorkDir(),
4570
+ envVars: {
4571
+ PULUMI_CONFIG_PASSPHRASE: ""
4572
+ },
4573
+ secretsProvider: "passphrase"
4574
+ }
4575
+ );
4576
+ await stack.workspace.selectStack(
4577
+ metadata.pulumiStackName || `wraps-${identity.accountId}-${region}`
4578
+ );
4579
+ await stack.setConfig("aws:region", { value: region });
4580
+ const upResult = await stack.up({ onOutput: () => {
4581
+ } });
4582
+ const pulumiOutputs = upResult.outputs;
4583
+ return {
4584
+ roleArn: pulumiOutputs.roleArn?.value,
4585
+ configSetName: pulumiOutputs.configSetName?.value,
4586
+ tableName: pulumiOutputs.tableName?.value,
4587
+ region: pulumiOutputs.region?.value,
4588
+ lambdaFunctions: pulumiOutputs.lambdaFunctions?.value,
4589
+ domain: pulumiOutputs.domain?.value,
4590
+ dkimTokens: pulumiOutputs.dkimTokens?.value,
4591
+ customTrackingDomain: pulumiOutputs.customTrackingDomain?.value
4592
+ };
4593
+ }
4594
+ );
4595
+ } catch (error) {
4596
+ clack10.log.error("Infrastructure update failed");
4597
+ if (error.message?.includes("stack is currently locked")) {
4598
+ clack10.log.warn("\nThe Pulumi stack is locked from a previous run.");
4599
+ clack10.log.info("To fix this, run:");
4600
+ clack10.log.info(` ${pc10.cyan("rm -rf ~/.wraps/pulumi/.pulumi/locks")}`);
4601
+ clack10.log.info("\nThen try running wraps update again.");
4602
+ }
4603
+ throw new Error(`Pulumi update failed: ${error.message}`);
4604
+ }
4605
+ metadata.timestamp = (/* @__PURE__ */ new Date()).toISOString();
4606
+ await saveConnectionMetadata(metadata);
4607
+ progress.info("Connection metadata updated");
4608
+ displaySuccess({
4609
+ roleArn: outputs.roleArn,
4610
+ configSetName: outputs.configSetName,
4611
+ region: outputs.region,
4612
+ tableName: outputs.tableName,
4613
+ customTrackingDomain: outputs.customTrackingDomain
4614
+ });
4615
+ console.log(`
4616
+ ${pc10.green("\u2713")} ${pc10.bold("Update complete!")}
4617
+ `);
4618
+ console.log(
4619
+ "Infrastructure has been updated with the latest CLI improvements.\n"
4620
+ );
4621
+ console.log(`${pc10.bold("Next steps:")}
4622
+ `);
4623
+ console.log(
4624
+ ` ${pc10.cyan("1.")} No code changes needed - your existing SDK integration continues to work`
4625
+ );
4626
+ console.log(
4627
+ ` ${pc10.cyan("2.")} Check ${pc10.cyan("wraps status")} to verify all resources are healthy`
4628
+ );
4629
+ console.log(
4630
+ ` ${pc10.cyan("3.")} View analytics at ${pc10.cyan("wraps console")}
4631
+ `
4632
+ );
4633
+ }
4634
+
4635
+ // src/commands/upgrade.ts
4636
+ init_esm_shims();
4637
+ import * as clack11 from "@clack/prompts";
4638
+ import * as pulumi12 from "@pulumi/pulumi";
4639
+ import pc11 from "picocolors";
4640
+ init_aws();
4641
+ init_costs();
4642
+ init_presets();
4643
+ init_prompts();
4644
+ async function upgrade(options) {
4645
+ clack11.intro(pc11.bold("Wraps Upgrade - Enhance Your Email Infrastructure"));
4646
+ const progress = new DeploymentProgress();
4647
+ const wasAutoInstalled = await progress.execute(
4648
+ "Checking Pulumi CLI installation",
4649
+ async () => await ensurePulumiInstalled()
4650
+ );
4651
+ if (wasAutoInstalled) {
4652
+ progress.info("Pulumi CLI was automatically installed");
4653
+ }
4654
+ const identity = await progress.execute(
4655
+ "Validating AWS credentials",
4656
+ async () => validateAWSCredentials()
4657
+ );
4658
+ progress.info(`Connected to AWS account: ${pc11.cyan(identity.accountId)}`);
4659
+ let region = options.region;
4660
+ if (!region) {
4661
+ const defaultRegion = await getAWSRegion();
4662
+ region = defaultRegion;
4663
+ }
4664
+ const metadata = await loadConnectionMetadata(identity.accountId, region);
4665
+ if (!metadata) {
4666
+ clack11.log.error(
4667
+ `No Wraps connection found for account ${pc11.cyan(identity.accountId)} in region ${pc11.cyan(region)}`
4668
+ );
4669
+ clack11.log.info(
4670
+ `Use ${pc11.cyan("wraps init")} to create new infrastructure or ${pc11.cyan("wraps connect")} to connect existing.`
4671
+ );
4672
+ process.exit(1);
4673
+ }
4674
+ progress.info(`Found existing connection created: ${metadata.timestamp}`);
4675
+ console.log(`
4676
+ ${pc11.bold("Current Configuration:")}
4677
+ `);
4678
+ if (metadata.preset) {
4679
+ console.log(` Preset: ${pc11.cyan(metadata.preset)}`);
4680
+ } else {
4681
+ console.log(` Preset: ${pc11.cyan("custom")}`);
4682
+ }
4683
+ const config = metadata.emailConfig;
4684
+ if (config.domain) {
4685
+ console.log(` Sending Domain: ${pc11.cyan(config.domain)}`);
4686
+ }
4687
+ if (config.tracking?.enabled) {
4688
+ console.log(` ${pc11.green("\u2713")} Open & Click Tracking`);
4426
4689
  if (config.tracking.customRedirectDomain) {
4427
4690
  console.log(
4428
- ` ${pc10.dim("\u2514\u2500")} Custom domain: ${pc10.cyan(config.tracking.customRedirectDomain)}`
4691
+ ` ${pc11.dim("\u2514\u2500")} Custom domain: ${pc11.cyan(config.tracking.customRedirectDomain)}`
4429
4692
  );
4430
4693
  }
4431
4694
  }
4432
4695
  if (config.suppressionList?.enabled) {
4433
- console.log(` ${pc10.green("\u2713")} Bounce/Complaint Suppression`);
4696
+ console.log(` ${pc11.green("\u2713")} Bounce/Complaint Suppression`);
4434
4697
  }
4435
4698
  if (config.eventTracking?.enabled) {
4436
- console.log(` ${pc10.green("\u2713")} Event Tracking (EventBridge)`);
4699
+ console.log(` ${pc11.green("\u2713")} Event Tracking (EventBridge)`);
4437
4700
  if (config.eventTracking.dynamoDBHistory) {
4438
4701
  console.log(
4439
- ` ${pc10.dim("\u2514\u2500")} Email History: ${pc10.cyan(config.eventTracking.archiveRetention || "90days")}`
4702
+ ` ${pc11.dim("\u2514\u2500")} Email History: ${pc11.cyan(config.eventTracking.archiveRetention || "90days")}`
4440
4703
  );
4441
4704
  }
4442
4705
  }
4443
4706
  if (config.dedicatedIp) {
4444
- console.log(` ${pc10.green("\u2713")} Dedicated IP Address`);
4707
+ console.log(` ${pc11.green("\u2713")} Dedicated IP Address`);
4445
4708
  }
4446
4709
  const currentCostData = calculateCosts(config, 5e4);
4447
4710
  console.log(
4448
4711
  `
4449
- Estimated Cost: ${pc10.cyan(`~${formatCost(currentCostData.total.monthly)}/mo`)}`
4712
+ Estimated Cost: ${pc11.cyan(`~${formatCost(currentCostData.total.monthly)}/mo`)}`
4450
4713
  );
4451
4714
  console.log("");
4452
- const upgradeAction = await clack10.select({
4715
+ const upgradeAction = await clack11.select({
4453
4716
  message: "What would you like to do?",
4454
4717
  options: [
4455
4718
  {
@@ -4484,8 +4747,8 @@ ${pc10.bold("Current Configuration:")}
4484
4747
  }
4485
4748
  ]
4486
4749
  });
4487
- if (clack10.isCancel(upgradeAction)) {
4488
- clack10.cancel("Upgrade cancelled.");
4750
+ if (clack11.isCancel(upgradeAction)) {
4751
+ clack11.cancel("Upgrade cancelled.");
4489
4752
  process.exit(0);
4490
4753
  }
4491
4754
  let updatedConfig = { ...config };
@@ -4503,15 +4766,15 @@ ${pc10.bold("Current Configuration:")}
4503
4766
  disabled: currentPresetIdx >= 0 && idx <= currentPresetIdx ? "Current or lower tier" : void 0
4504
4767
  })).filter((p) => !p.disabled);
4505
4768
  if (availablePresets.length === 0) {
4506
- clack10.log.warn("Already on highest preset (Enterprise)");
4769
+ clack11.log.warn("Already on highest preset (Enterprise)");
4507
4770
  process.exit(0);
4508
4771
  }
4509
- const selectedPreset = await clack10.select({
4772
+ const selectedPreset = await clack11.select({
4510
4773
  message: "Select new preset:",
4511
4774
  options: availablePresets
4512
4775
  });
4513
- if (clack10.isCancel(selectedPreset)) {
4514
- clack10.cancel("Upgrade cancelled.");
4776
+ if (clack11.isCancel(selectedPreset)) {
4777
+ clack11.cancel("Upgrade cancelled.");
4515
4778
  process.exit(0);
4516
4779
  }
4517
4780
  const presetConfig = getPreset(selectedPreset);
@@ -4525,11 +4788,11 @@ ${pc10.bold("Current Configuration:")}
4525
4788
  }
4526
4789
  case "tracking-domain": {
4527
4790
  if (!config.domain) {
4528
- clack10.log.error(
4791
+ clack11.log.error(
4529
4792
  "No sending domain configured. You must configure a sending domain before adding a custom tracking domain."
4530
4793
  );
4531
- clack10.log.info(
4532
- `Use ${pc10.cyan("wraps init")} to set up a sending domain first.`
4794
+ clack11.log.info(
4795
+ `Use ${pc11.cyan("wraps init")} to set up a sending domain first.`
4533
4796
  );
4534
4797
  process.exit(1);
4535
4798
  }
@@ -4540,21 +4803,21 @@ ${pc10.bold("Current Configuration:")}
4540
4803
  );
4541
4804
  const sendingDomain = domains.find((d) => d.domain === config.domain);
4542
4805
  if (!sendingDomain?.verified) {
4543
- clack10.log.error(
4544
- `Sending domain ${pc10.cyan(config.domain)} is not verified.`
4806
+ clack11.log.error(
4807
+ `Sending domain ${pc11.cyan(config.domain)} is not verified.`
4545
4808
  );
4546
- clack10.log.info(
4809
+ clack11.log.info(
4547
4810
  "You must verify your sending domain before adding a custom tracking domain."
4548
4811
  );
4549
- clack10.log.info(
4550
- `Use ${pc10.cyan("wraps verify")} to check DNS records and complete verification.`
4812
+ clack11.log.info(
4813
+ `Use ${pc11.cyan("wraps verify")} to check DNS records and complete verification.`
4551
4814
  );
4552
4815
  process.exit(1);
4553
4816
  }
4554
4817
  progress.info(
4555
- `Sending domain ${pc10.cyan(config.domain)} is verified ${pc10.green("\u2713")}`
4818
+ `Sending domain ${pc11.cyan(config.domain)} is verified ${pc11.green("\u2713")}`
4556
4819
  );
4557
- const trackingDomain = await clack10.text({
4820
+ const trackingDomain = await clack11.text({
4558
4821
  message: "Custom tracking redirect domain:",
4559
4822
  placeholder: "track.yourdomain.com",
4560
4823
  initialValue: config.tracking?.customRedirectDomain || "",
@@ -4564,8 +4827,8 @@ ${pc10.bold("Current Configuration:")}
4564
4827
  }
4565
4828
  }
4566
4829
  });
4567
- if (clack10.isCancel(trackingDomain)) {
4568
- clack10.cancel("Upgrade cancelled.");
4830
+ if (clack11.isCancel(trackingDomain)) {
4831
+ clack11.cancel("Upgrade cancelled.");
4569
4832
  process.exit(0);
4570
4833
  }
4571
4834
  updatedConfig = {
@@ -4580,7 +4843,7 @@ ${pc10.bold("Current Configuration:")}
4580
4843
  break;
4581
4844
  }
4582
4845
  case "retention": {
4583
- const retention = await clack10.select({
4846
+ const retention = await clack11.select({
4584
4847
  message: "Email history retention period:",
4585
4848
  options: [
4586
4849
  { value: "7days", label: "7 days", hint: "Minimal storage cost" },
@@ -4599,8 +4862,8 @@ ${pc10.bold("Current Configuration:")}
4599
4862
  ],
4600
4863
  initialValue: config.eventTracking?.archiveRetention || "90days"
4601
4864
  });
4602
- if (clack10.isCancel(retention)) {
4603
- clack10.cancel("Upgrade cancelled.");
4865
+ if (clack11.isCancel(retention)) {
4866
+ clack11.cancel("Upgrade cancelled.");
4604
4867
  process.exit(0);
4605
4868
  }
4606
4869
  updatedConfig = {
@@ -4616,7 +4879,7 @@ ${pc10.bold("Current Configuration:")}
4616
4879
  break;
4617
4880
  }
4618
4881
  case "events": {
4619
- const selectedEvents = await clack10.multiselect({
4882
+ const selectedEvents = await clack11.multiselect({
4620
4883
  message: "Select SES event types to track:",
4621
4884
  options: [
4622
4885
  { value: "SEND", label: "Send", hint: "Email sent to SES" },
@@ -4660,8 +4923,8 @@ ${pc10.bold("Current Configuration:")}
4660
4923
  ],
4661
4924
  required: true
4662
4925
  });
4663
- if (clack10.isCancel(selectedEvents)) {
4664
- clack10.cancel("Upgrade cancelled.");
4926
+ if (clack11.isCancel(selectedEvents)) {
4927
+ clack11.cancel("Upgrade cancelled.");
4665
4928
  process.exit(0);
4666
4929
  }
4667
4930
  updatedConfig = {
@@ -4676,16 +4939,16 @@ ${pc10.bold("Current Configuration:")}
4676
4939
  break;
4677
4940
  }
4678
4941
  case "dedicated-ip": {
4679
- const confirmed = await clack10.confirm({
4942
+ const confirmed = await clack11.confirm({
4680
4943
  message: "Enable dedicated IP? (Requires 100k+ emails/day, adds ~$50-100/mo)",
4681
4944
  initialValue: false
4682
4945
  });
4683
- if (clack10.isCancel(confirmed)) {
4684
- clack10.cancel("Upgrade cancelled.");
4946
+ if (clack11.isCancel(confirmed)) {
4947
+ clack11.cancel("Upgrade cancelled.");
4685
4948
  process.exit(0);
4686
4949
  }
4687
4950
  if (!confirmed) {
4688
- clack10.log.info("Dedicated IP not enabled.");
4951
+ clack11.log.info("Dedicated IP not enabled.");
4689
4952
  process.exit(0);
4690
4953
  }
4691
4954
  updatedConfig = {
@@ -4709,28 +4972,28 @@ ${pc10.bold("Current Configuration:")}
4709
4972
  const newCostData = calculateCosts(updatedConfig, 5e4);
4710
4973
  const costDiff = newCostData.total.monthly - currentCostData.total.monthly;
4711
4974
  console.log(`
4712
- ${pc10.bold("Cost Impact:")}`);
4975
+ ${pc11.bold("Cost Impact:")}`);
4713
4976
  console.log(
4714
- ` Current: ${pc10.cyan(`${formatCost(currentCostData.total.monthly)}/mo`)}`
4977
+ ` Current: ${pc11.cyan(`${formatCost(currentCostData.total.monthly)}/mo`)}`
4715
4978
  );
4716
4979
  console.log(
4717
- ` New: ${pc10.cyan(`${formatCost(newCostData.total.monthly)}/mo`)}`
4980
+ ` New: ${pc11.cyan(`${formatCost(newCostData.total.monthly)}/mo`)}`
4718
4981
  );
4719
4982
  if (costDiff > 0) {
4720
- console.log(` Change: ${pc10.yellow(`+${formatCost(costDiff)}/mo`)}`);
4983
+ console.log(` Change: ${pc11.yellow(`+${formatCost(costDiff)}/mo`)}`);
4721
4984
  } else if (costDiff < 0) {
4722
4985
  console.log(
4723
- ` Change: ${pc10.green(`${formatCost(Math.abs(costDiff))}/mo`)}`
4986
+ ` Change: ${pc11.green(`${formatCost(Math.abs(costDiff))}/mo`)}`
4724
4987
  );
4725
4988
  }
4726
4989
  console.log("");
4727
4990
  if (!options.yes) {
4728
- const confirmed = await clack10.confirm({
4991
+ const confirmed = await clack11.confirm({
4729
4992
  message: "Proceed with upgrade?",
4730
4993
  initialValue: true
4731
4994
  });
4732
- if (clack10.isCancel(confirmed) || !confirmed) {
4733
- clack10.cancel("Upgrade cancelled.");
4995
+ if (clack11.isCancel(confirmed) || !confirmed) {
4996
+ clack11.cancel("Upgrade cancelled.");
4734
4997
  process.exit(0);
4735
4998
  }
4736
4999
  }
@@ -4752,7 +5015,7 @@ ${pc10.bold("Cost Impact:")}`);
4752
5015
  "Updating Wraps infrastructure (this may take 2-3 minutes)",
4753
5016
  async () => {
4754
5017
  await ensurePulumiWorkDir();
4755
- const stack = await pulumi11.automation.LocalWorkspace.createOrSelectStack(
5018
+ const stack = await pulumi12.automation.LocalWorkspace.createOrSelectStack(
4756
5019
  {
4757
5020
  stackName: metadata.pulumiStackName || `wraps-${identity.accountId}-${region}`,
4758
5021
  projectName: "wraps-email",
@@ -4798,12 +5061,12 @@ ${pc10.bold("Cost Impact:")}`);
4798
5061
  }
4799
5062
  );
4800
5063
  } catch (error) {
4801
- clack10.log.error("Infrastructure upgrade failed");
5064
+ clack11.log.error("Infrastructure upgrade failed");
4802
5065
  if (error.message?.includes("stack is currently locked")) {
4803
- clack10.log.warn("\nThe Pulumi stack is locked from a previous run.");
4804
- clack10.log.info("To fix this, run:");
4805
- clack10.log.info(` ${pc10.cyan("rm -rf ~/.wraps/pulumi/.pulumi/locks")}`);
4806
- clack10.log.info("\nThen try running wraps upgrade again.");
5066
+ clack11.log.warn("\nThe Pulumi stack is locked from a previous run.");
5067
+ clack11.log.info("To fix this, run:");
5068
+ clack11.log.info(` ${pc11.cyan("rm -rf ~/.wraps/pulumi/.pulumi/locks")}`);
5069
+ clack11.log.info("\nThen try running wraps upgrade again.");
4807
5070
  }
4808
5071
  throw new Error(`Pulumi upgrade failed: ${error.message}`);
4809
5072
  }
@@ -4828,16 +5091,16 @@ ${pc10.bold("Cost Impact:")}`);
4828
5091
  customTrackingDomain: outputs.customTrackingDomain
4829
5092
  });
4830
5093
  console.log(`
4831
- ${pc10.green("\u2713")} ${pc10.bold("Upgrade complete!")}
5094
+ ${pc11.green("\u2713")} ${pc11.bold("Upgrade complete!")}
4832
5095
  `);
4833
5096
  if (upgradeAction === "preset" && newPreset) {
4834
5097
  console.log(
4835
- `Upgraded to ${pc10.cyan(newPreset)} preset (${pc10.green(formatCost(newCostData.total.monthly) + "/mo")})
5098
+ `Upgraded to ${pc11.cyan(newPreset)} preset (${pc11.green(formatCost(newCostData.total.monthly) + "/mo")})
4836
5099
  `
4837
5100
  );
4838
5101
  } else {
4839
5102
  console.log(
4840
- `Updated configuration (${pc10.green(formatCost(newCostData.total.monthly) + "/mo")})
5103
+ `Updated configuration (${pc11.green(formatCost(newCostData.total.monthly) + "/mo")})
4841
5104
  `
4842
5105
  );
4843
5106
  }
@@ -4848,15 +5111,16 @@ init_esm_shims();
4848
5111
  init_aws();
4849
5112
  import { Resolver } from "dns/promises";
4850
5113
  import { GetEmailIdentityCommand as GetEmailIdentityCommand3, SESv2Client as SESv2Client3 } from "@aws-sdk/client-sesv2";
4851
- import * as clack11 from "@clack/prompts";
4852
- import pc11 from "picocolors";
5114
+ import * as clack12 from "@clack/prompts";
5115
+ import pc12 from "picocolors";
4853
5116
  async function verify(options) {
4854
- clack11.intro(pc11.bold(`Verifying ${options.domain}`));
5117
+ clack12.intro(pc12.bold(`Verifying ${options.domain}`));
4855
5118
  const progress = new DeploymentProgress();
4856
5119
  const region = await getAWSRegion();
4857
5120
  const sesClient = new SESv2Client3({ region });
4858
5121
  let identity;
4859
5122
  let dkimTokens = [];
5123
+ let mailFromDomain;
4860
5124
  try {
4861
5125
  identity = await progress.execute(
4862
5126
  "Checking SES verification status",
@@ -4868,17 +5132,19 @@ async function verify(options) {
4868
5132
  }
4869
5133
  );
4870
5134
  dkimTokens = identity.DkimAttributes?.Tokens || [];
5135
+ mailFromDomain = identity.MailFromAttributes?.MailFromDomain;
4871
5136
  } catch (_error) {
4872
5137
  progress.stop();
4873
- clack11.log.error(`Domain ${options.domain} not found in SES`);
5138
+ clack12.log.error(`Domain ${options.domain} not found in SES`);
4874
5139
  console.log(
4875
5140
  `
4876
- Run ${pc11.cyan(`wraps init --domain ${options.domain}`)} to add this domain.
5141
+ Run ${pc12.cyan(`wraps init --domain ${options.domain}`)} to add this domain.
4877
5142
  `
4878
5143
  );
4879
5144
  process.exit(1);
4880
5145
  }
4881
5146
  const resolver = new Resolver();
5147
+ resolver.setServers(["8.8.8.8", "1.1.1.1"]);
4882
5148
  const dnsResults = [];
4883
5149
  for (const token of dkimTokens) {
4884
5150
  const dkimRecord = `${token}._domainkey.${options.domain}`;
@@ -4933,54 +5199,97 @@ Run ${pc11.cyan(`wraps init --domain ${options.domain}`)} to add this domain.
4933
5199
  status: "missing"
4934
5200
  });
4935
5201
  }
5202
+ if (mailFromDomain) {
5203
+ try {
5204
+ const mxRecords = await resolver.resolveMx(mailFromDomain);
5205
+ const expectedMx = `feedback-smtp.${region}.amazonses.com`;
5206
+ const hasMx = mxRecords.some(
5207
+ (r) => r.exchange === expectedMx || r.exchange === `${expectedMx}.`
5208
+ );
5209
+ dnsResults.push({
5210
+ name: mailFromDomain,
5211
+ type: "MX",
5212
+ status: hasMx ? "verified" : mxRecords.length > 0 ? "incorrect" : "missing",
5213
+ records: mxRecords.map((r) => `${r.priority} ${r.exchange}`)
5214
+ });
5215
+ } catch (_error) {
5216
+ dnsResults.push({
5217
+ name: mailFromDomain,
5218
+ type: "MX",
5219
+ status: "missing"
5220
+ });
5221
+ }
5222
+ try {
5223
+ const records = await resolver.resolveTxt(mailFromDomain);
5224
+ const spfRecord = records.flat().find((r) => r.startsWith("v=spf1"));
5225
+ const hasAmazonSES = spfRecord?.includes("include:amazonses.com");
5226
+ dnsResults.push({
5227
+ name: mailFromDomain,
5228
+ type: "TXT (SPF)",
5229
+ status: hasAmazonSES ? "verified" : spfRecord ? "incorrect" : "missing",
5230
+ records: spfRecord ? [spfRecord] : void 0
5231
+ });
5232
+ } catch (_error) {
5233
+ dnsResults.push({
5234
+ name: mailFromDomain,
5235
+ type: "TXT (SPF)",
5236
+ status: "missing"
5237
+ });
5238
+ }
5239
+ }
4936
5240
  progress.stop();
4937
5241
  const verificationStatus = identity.VerifiedForSendingStatus ? "verified" : "pending";
4938
5242
  const dkimStatus = identity.DkimAttributes?.Status || "PENDING";
4939
- clack11.note(
4940
- [
4941
- `${pc11.bold("Domain:")} ${options.domain}`,
4942
- `${pc11.bold("Verification Status:")} ${verificationStatus === "verified" ? pc11.green("\u2713 Verified") : pc11.yellow("\u23F1 Pending")}`,
4943
- `${pc11.bold("DKIM Status:")} ${dkimStatus === "SUCCESS" ? pc11.green("\u2713 Success") : pc11.yellow(`\u23F1 ${dkimStatus}`)}`
4944
- ].join("\n"),
4945
- "SES Status"
4946
- );
5243
+ const mailFromStatus = identity.MailFromAttributes?.MailFromDomainStatus || "NOT_CONFIGURED";
5244
+ const statusLines = [
5245
+ `${pc12.bold("Domain:")} ${options.domain}`,
5246
+ `${pc12.bold("Verification Status:")} ${verificationStatus === "verified" ? pc12.green("\u2713 Verified") : pc12.yellow("\u23F1 Pending")}`,
5247
+ `${pc12.bold("DKIM Status:")} ${dkimStatus === "SUCCESS" ? pc12.green("\u2713 Success") : pc12.yellow(`\u23F1 ${dkimStatus}`)}`
5248
+ ];
5249
+ if (mailFromDomain) {
5250
+ statusLines.push(
5251
+ `${pc12.bold("MAIL FROM Domain:")} ${mailFromDomain}`,
5252
+ `${pc12.bold("MAIL FROM Status:")} ${mailFromStatus === "SUCCESS" ? pc12.green("\u2713 Success") : mailFromStatus === "NOT_CONFIGURED" ? pc12.yellow("\u23F1 Not Configured") : pc12.yellow(`\u23F1 ${mailFromStatus}`)}`
5253
+ );
5254
+ }
5255
+ clack12.note(statusLines.join("\n"), "SES Status");
4947
5256
  const dnsLines = dnsResults.map((record) => {
4948
5257
  let statusIcon;
4949
5258
  let statusColor;
4950
5259
  if (record.status === "verified") {
4951
5260
  statusIcon = "\u2713";
4952
- statusColor = pc11.green;
5261
+ statusColor = pc12.green;
4953
5262
  } else if (record.status === "incorrect") {
4954
5263
  statusIcon = "\u2717";
4955
- statusColor = pc11.red;
5264
+ statusColor = pc12.red;
4956
5265
  } else {
4957
5266
  statusIcon = "\u2717";
4958
- statusColor = pc11.red;
5267
+ statusColor = pc12.red;
4959
5268
  }
4960
5269
  const recordInfo = record.records ? ` \u2192 ${record.records.join(", ")}` : "";
4961
5270
  return ` ${statusColor(statusIcon)} ${record.name} (${record.type}) ${statusColor(
4962
5271
  record.status
4963
5272
  )}${recordInfo}`;
4964
5273
  });
4965
- clack11.note(dnsLines.join("\n"), "DNS Records");
5274
+ clack12.note(dnsLines.join("\n"), "DNS Records");
4966
5275
  const allVerified = dnsResults.every((r) => r.status === "verified");
4967
5276
  const someIncorrect = dnsResults.some((r) => r.status === "incorrect");
4968
5277
  if (verificationStatus === "verified" && allVerified) {
4969
- clack11.outro(
4970
- pc11.green("\u2713 Domain is fully verified and ready to send emails!")
5278
+ clack12.outro(
5279
+ pc12.green("\u2713 Domain is fully verified and ready to send emails!")
4971
5280
  );
4972
5281
  } else if (someIncorrect) {
4973
- clack11.outro(
4974
- pc11.red("\u2717 Some DNS records are incorrect. Please update them.")
5282
+ clack12.outro(
5283
+ pc12.red("\u2717 Some DNS records are incorrect. Please update them.")
4975
5284
  );
4976
5285
  console.log(
4977
5286
  `
4978
- Run ${pc11.cyan("wraps status")} to see the correct DNS records.
5287
+ Run ${pc12.cyan("wraps status")} to see the correct DNS records.
4979
5288
  `
4980
5289
  );
4981
5290
  } else {
4982
- clack11.outro(
4983
- pc11.yellow("\u23F1 Waiting for DNS propagation and SES verification")
5291
+ clack12.outro(
5292
+ pc12.yellow("\u23F1 Waiting for DNS propagation and SES verification")
4984
5293
  );
4985
5294
  console.log("\nDNS records can take up to 48 hours to propagate.");
4986
5295
  console.log(
@@ -5015,44 +5324,61 @@ function printCompletionScript() {
5015
5324
 
5016
5325
  // src/cli.ts
5017
5326
  init_errors();
5327
+ var __filename2 = fileURLToPath4(import.meta.url);
5328
+ var __dirname3 = dirname2(__filename2);
5329
+ var packageJson = JSON.parse(
5330
+ readFileSync(join4(__dirname3, "../package.json"), "utf-8")
5331
+ );
5332
+ var VERSION = packageJson.version;
5018
5333
  setupTabCompletion();
5334
+ function showVersion() {
5335
+ console.log(`wraps v${VERSION}`);
5336
+ process.exit(0);
5337
+ }
5019
5338
  function showHelp() {
5020
- clack12.intro(pc12.bold("WRAPS CLI"));
5339
+ clack13.intro(pc13.bold(`WRAPS CLI v${VERSION}`));
5021
5340
  console.log("Deploy email infrastructure to your AWS account\n");
5022
5341
  console.log("Usage: wraps <command> [options]\n");
5023
5342
  console.log("Commands:");
5024
- console.log(` ${pc12.cyan("init")} Deploy new email infrastructure`);
5343
+ console.log(` ${pc13.cyan("init")} Deploy new email infrastructure`);
5025
5344
  console.log(
5026
- ` ${pc12.cyan("connect")} Connect to existing AWS SES infrastructure`
5345
+ ` ${pc13.cyan("connect")} Connect to existing AWS SES infrastructure`
5027
5346
  );
5028
5347
  console.log(
5029
- ` ${pc12.cyan("console")} Start local web dashboard for monitoring`
5348
+ ` ${pc13.cyan("console")} Start local web dashboard for monitoring`
5030
5349
  );
5031
5350
  console.log(
5032
- ` ${pc12.cyan("upgrade")} Add features to existing connection`
5351
+ ` ${pc13.cyan("update")} Update infrastructure with latest CLI changes`
5033
5352
  );
5034
- console.log(` ${pc12.cyan("status")} Show current infrastructure status`);
5035
5353
  console.log(
5036
- ` ${pc12.cyan("verify")} Verify domain DNS records and SES status`
5354
+ ` ${pc13.cyan("upgrade")} Add features to existing connection`
5037
5355
  );
5038
- console.log(` ${pc12.cyan("restore")} Restore original AWS configuration`);
5039
- console.log(` ${pc12.cyan("destroy")} Remove all deployed infrastructure`);
5040
- console.log(` ${pc12.cyan("completion")} Generate shell completion script
5356
+ console.log(` ${pc13.cyan("status")} Show current infrastructure status`);
5357
+ console.log(
5358
+ ` ${pc13.cyan("verify")} Verify domain DNS records and SES status`
5359
+ );
5360
+ console.log(` ${pc13.cyan("restore")} Restore original AWS configuration`);
5361
+ console.log(` ${pc13.cyan("destroy")} Remove all deployed infrastructure`);
5362
+ console.log(` ${pc13.cyan("completion")} Generate shell completion script
5041
5363
  `);
5042
5364
  console.log("Options:");
5043
5365
  console.log(
5044
- ` ${pc12.dim("--provider")} Hosting provider (vercel, aws, railway, other)`
5366
+ ` ${pc13.dim("--provider")} Hosting provider (vercel, aws, railway, other)`
5045
5367
  );
5046
- console.log(` ${pc12.dim("--region")} AWS region`);
5047
- console.log(` ${pc12.dim("--domain")} Domain to verify`);
5048
- console.log(` ${pc12.dim("--account")} AWS account ID or alias
5368
+ console.log(` ${pc13.dim("--region")} AWS region`);
5369
+ console.log(` ${pc13.dim("--domain")} Domain to verify`);
5370
+ console.log(` ${pc13.dim("--account")} AWS account ID or alias`);
5371
+ console.log(` ${pc13.dim("--version, -v")} Show version number
5049
5372
  `);
5050
5373
  console.log(
5051
- `Run ${pc12.cyan("wraps <command> --help")} for more information on a command.
5374
+ `Run ${pc13.cyan("wraps <command> --help")} for more information on a command.
5052
5375
  `
5053
5376
  );
5054
5377
  process.exit(0);
5055
5378
  }
5379
+ if (process.argv.includes("--version") || process.argv.includes("-v")) {
5380
+ showVersion();
5381
+ }
5056
5382
  if (process.argv.includes("--help") || process.argv.includes("-h")) {
5057
5383
  showHelp();
5058
5384
  }
@@ -5122,10 +5448,10 @@ async function run() {
5122
5448
  break;
5123
5449
  case "verify":
5124
5450
  if (!flags.domain) {
5125
- clack12.log.error("--domain flag is required");
5451
+ clack13.log.error("--domain flag is required");
5126
5452
  console.log(
5127
5453
  `
5128
- Usage: ${pc12.cyan("wraps verify --domain yourapp.com")}
5454
+ Usage: ${pc13.cyan("wraps verify --domain yourapp.com")}
5129
5455
  `
5130
5456
  );
5131
5457
  process.exit(1);
@@ -5147,6 +5473,12 @@ Usage: ${pc12.cyan("wraps verify --domain yourapp.com")}
5147
5473
  noOpen: flags.noOpen
5148
5474
  });
5149
5475
  break;
5476
+ case "update":
5477
+ await update({
5478
+ region: flags.region,
5479
+ yes: flags.yes
5480
+ });
5481
+ break;
5150
5482
  case "upgrade":
5151
5483
  await upgrade({
5152
5484
  region: flags.region,
@@ -5168,10 +5500,10 @@ Usage: ${pc12.cyan("wraps verify --domain yourapp.com")}
5168
5500
  printCompletionScript();
5169
5501
  break;
5170
5502
  default:
5171
- clack12.log.error(`Unknown command: ${command}`);
5503
+ clack13.log.error(`Unknown command: ${command}`);
5172
5504
  console.log(
5173
5505
  `
5174
- Run ${pc12.cyan("wraps --help")} for available commands.
5506
+ Run ${pc13.cyan("wraps --help")} for available commands.
5175
5507
  `
5176
5508
  );
5177
5509
  process.exit(1);