@wraps.dev/cli 2.18.3 → 2.18.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -8978,9 +8978,8 @@ Run ${pc27.cyan("wraps email init")} to deploy email infrastructure.
8978
8978
  process.exit(1);
8979
8979
  return;
8980
8980
  }
8981
- const emailConfig = metadata.services.email;
8982
- const domain = emailConfig.config.domain;
8983
- if (!domain) {
8981
+ const trackedDomains = getAllTrackedDomains(metadata);
8982
+ if (trackedDomains.length === 0) {
8984
8983
  progress.stop();
8985
8984
  clack25.log.error("No domain configured in email infrastructure");
8986
8985
  console.log(`
@@ -8989,6 +8988,24 @@ Run ${pc27.cyan("wraps email init")} to set up a domain.
8989
8988
  process.exit(1);
8990
8989
  return;
8991
8990
  }
8991
+ let domain;
8992
+ if (trackedDomains.length === 1) {
8993
+ domain = trackedDomains[0].domain;
8994
+ } else {
8995
+ const selected = await clack25.select({
8996
+ message: "Which domain do you want to send from?",
8997
+ options: trackedDomains.map((d) => ({
8998
+ value: d.domain,
8999
+ label: d.domain,
9000
+ hint: d.isPrimary ? "primary" : d.purpose
9001
+ }))
9002
+ });
9003
+ if (clack25.isCancel(selected)) {
9004
+ clack25.cancel("Operation cancelled.");
9005
+ process.exit(0);
9006
+ }
9007
+ domain = selected;
9008
+ }
8992
9009
  let toEmail = options.to;
8993
9010
  if (!toEmail) {
8994
9011
  if (isJsonMode() && !options.scenario) {
@@ -17714,12 +17731,7 @@ async function createSMTPCredentials(config2) {
17714
17731
  {
17715
17732
  Effect: "Allow",
17716
17733
  Action: "ses:SendRawEmail",
17717
- Resource: "*",
17718
- Condition: {
17719
- StringEquals: {
17720
- "ses:ConfigurationSetName": config2.configSetName
17721
- }
17722
- }
17734
+ Resource: "*"
17723
17735
  }
17724
17736
  ]
17725
17737
  })
@@ -19749,18 +19761,17 @@ Run ${pc23.cyan("wraps email init")} first to deploy email infrastructure.
19749
19761
  }
19750
19762
  clack21.log.step(`Adding ${pc23.cyan(domain)}`);
19751
19763
  const sesClient = new SESv2Client4({ region });
19764
+ let domainAlreadyExists = false;
19765
+ let dkimTokens = [];
19752
19766
  try {
19753
- await sesClient.send(
19767
+ const existing = await sesClient.send(
19754
19768
  new GetEmailIdentityCommand2({ EmailIdentity: domain })
19755
19769
  );
19756
- progress.stop();
19757
- clack21.log.warn(`Domain ${domain} already exists in SES`);
19758
- console.log(
19759
- `
19760
- Run ${pc23.cyan(`wraps email domains verify --domain ${domain}`)} to check verification status.
19761
- `
19770
+ domainAlreadyExists = true;
19771
+ dkimTokens = existing.DkimAttributes?.Tokens || [];
19772
+ clack21.log.info(
19773
+ `Domain ${pc23.cyan(domain)} already exists in SES \u2014 adopting into Wraps`
19762
19774
  );
19763
- return;
19764
19775
  } catch (error) {
19765
19776
  if (!isAWSNotFoundError(error)) {
19766
19777
  throw error;
@@ -19770,22 +19781,34 @@ Run ${pc23.cyan(`wraps email domains verify --domain ${domain}`)} to check verif
19770
19781
  if (!options.yes) {
19771
19782
  purpose = await promptDomainPurpose();
19772
19783
  }
19773
- const { CreateEmailIdentityCommand } = await import("@aws-sdk/client-sesv2");
19774
- await progress.execute("Creating SES identity", async () => {
19775
- await sesClient.send(
19776
- new CreateEmailIdentityCommand({
19777
- EmailIdentity: domain,
19778
- ConfigurationSetName: "wraps-email-tracking",
19779
- DkimSigningAttributes: {
19780
- NextSigningKeyLength: "RSA_2048_BIT"
19781
- }
19782
- })
19784
+ if (domainAlreadyExists) {
19785
+ const { PutEmailIdentityConfigurationSetAttributesCommand } = await import("@aws-sdk/client-sesv2");
19786
+ await progress.execute("Associating tracking configuration", async () => {
19787
+ await sesClient.send(
19788
+ new PutEmailIdentityConfigurationSetAttributesCommand({
19789
+ EmailIdentity: domain,
19790
+ ConfigurationSetName: "wraps-email-tracking"
19791
+ })
19792
+ );
19793
+ });
19794
+ } else {
19795
+ const { CreateEmailIdentityCommand } = await import("@aws-sdk/client-sesv2");
19796
+ await progress.execute("Creating SES identity", async () => {
19797
+ await sesClient.send(
19798
+ new CreateEmailIdentityCommand({
19799
+ EmailIdentity: domain,
19800
+ ConfigurationSetName: "wraps-email-tracking",
19801
+ DkimSigningAttributes: {
19802
+ NextSigningKeyLength: "RSA_2048_BIT"
19803
+ }
19804
+ })
19805
+ );
19806
+ });
19807
+ const sesIdentity = await sesClient.send(
19808
+ new GetEmailIdentityCommand2({ EmailIdentity: domain })
19783
19809
  );
19784
- });
19785
- const sesIdentity = await sesClient.send(
19786
- new GetEmailIdentityCommand2({ EmailIdentity: domain })
19787
- );
19788
- const dkimTokens = sesIdentity.DkimAttributes?.Tokens || [];
19810
+ dkimTokens = sesIdentity.DkimAttributes?.Tokens || [];
19811
+ }
19789
19812
  let mailFromDomain;
19790
19813
  if (options.yes) {
19791
19814
  mailFromDomain = `mail.${domain}`;
@@ -20477,12 +20500,30 @@ async function inboundInit(options) {
20477
20500
  if (!metadata?.services?.email) {
20478
20501
  throw errors.inboundRequiresOutbound();
20479
20502
  }
20480
- const emailService = metadata.services.email;
20481
- const emailConfig = emailService.config;
20482
- const domain = emailConfig.domain;
20483
- if (!domain) {
20503
+ const trackedDomains = getAllTrackedDomains(metadata);
20504
+ if (trackedDomains.length === 0) {
20484
20505
  throw errors.inboundRequiresOutbound();
20485
20506
  }
20507
+ let domain;
20508
+ if (trackedDomains.length === 1) {
20509
+ domain = trackedDomains[0].domain;
20510
+ } else {
20511
+ const selected = await clack22.select({
20512
+ message: "Which domain do you want to receive email on?",
20513
+ options: trackedDomains.map((d) => ({
20514
+ value: d.domain,
20515
+ label: d.domain,
20516
+ hint: d.isPrimary ? "primary" : d.purpose
20517
+ }))
20518
+ });
20519
+ if (clack22.isCancel(selected)) {
20520
+ clack22.cancel("Operation cancelled.");
20521
+ process.exit(0);
20522
+ }
20523
+ domain = selected;
20524
+ }
20525
+ const emailService = metadata.services.email;
20526
+ const emailConfig = emailService.config;
20486
20527
  const subdomain = options.root ? "" : options.subdomain ?? (options.yes ? "inbound" : await promptInboundSubdomain(domain));
20487
20528
  const receivingDomain = subdomain ? `${subdomain}.${domain}` : domain;
20488
20529
  clack22.log.info(`Receiving domain: ${pc24.cyan(receivingDomain)}`);
@@ -21658,6 +21699,21 @@ async function init2(options) {
21658
21699
  if (domain) {
21659
21700
  emailConfig.domain = domain;
21660
21701
  }
21702
+ if (domain && !options.quick && preset !== "custom") {
21703
+ const wantsMailFrom = await clack26.confirm({
21704
+ message: `Configure MAIL FROM for ${pc28.cyan(domain)}? ${pc28.dim("(improves DMARC alignment)")}`,
21705
+ initialValue: true
21706
+ });
21707
+ if (clack26.isCancel(wantsMailFrom)) {
21708
+ clack26.cancel("Operation cancelled.");
21709
+ process.exit(0);
21710
+ }
21711
+ if (wantsMailFrom) {
21712
+ const mailFromFull = await promptMailFromSubdomain(domain);
21713
+ const suffix = `.${domain}`;
21714
+ emailConfig.mailFromSubdomain = mailFromFull.endsWith(suffix) ? mailFromFull.slice(0, -suffix.length) || "mail" : "mail";
21715
+ }
21716
+ }
21661
21717
  let costSummary;
21662
21718
  if (!options.quick) {
21663
21719
  const estimatedVolume = await promptEstimatedVolume();