@wraps.dev/cli 1.1.1 → 1.1.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 CHANGED
@@ -598,7 +598,7 @@ To remove: wraps destroy --stack ${stackName}`,
598
598
  noStack: () => new WrapsError(
599
599
  "No Wraps infrastructure found in this AWS account",
600
600
  "NO_STACK",
601
- "Run: wraps init\nTo deploy new infrastructure",
601
+ "Run: wraps email init\nTo deploy new infrastructure",
602
602
  "https://wraps.dev/docs/cli/init"
603
603
  ),
604
604
  pulumiNotInstalled: () => new WrapsError(
@@ -762,11 +762,23 @@ function estimateStorageSize(emailsPerMonth, retention, numEventTypes = 8) {
762
762
  "7days": 0.25,
763
763
  "30days": 1,
764
764
  "90days": 3,
765
+ "3months": 3,
765
766
  "6months": 6,
767
+ "9months": 9,
766
768
  "1year": 12,
767
769
  "18months": 18,
768
- indefinite: 120
769
- // Assume 10 years for cost estimation
770
+ "2years": 24,
771
+ "30months": 30,
772
+ "3years": 36,
773
+ "4years": 48,
774
+ "5years": 60,
775
+ "6years": 72,
776
+ "7years": 84,
777
+ "8years": 96,
778
+ "9years": 108,
779
+ "10years": 120,
780
+ indefinite: 120,
781
+ permanent: 120
770
782
  }[retention];
771
783
  const totalKB = emailsPerMonth * numEventTypes * (retentionMonths ?? 12) * avgRecordSizeKB;
772
784
  return totalKB / 1024 / 1024;
@@ -777,11 +789,23 @@ function estimateArchiveStorageSize(emailsPerMonth, retention) {
777
789
  "7days": 0.25,
778
790
  "30days": 1,
779
791
  "90days": 3,
792
+ "3months": 3,
780
793
  "6months": 6,
794
+ "9months": 9,
781
795
  "1year": 12,
782
796
  "18months": 18,
783
- indefinite: 120
784
- // Assume 10 years for cost estimation
797
+ "2years": 24,
798
+ "30months": 30,
799
+ "3years": 36,
800
+ "4years": 48,
801
+ "5years": 60,
802
+ "6years": 72,
803
+ "7years": 84,
804
+ "8years": 96,
805
+ "9years": 108,
806
+ "10years": 120,
807
+ indefinite: 120,
808
+ permanent: 120
785
809
  }[retention];
786
810
  const totalKB = emailsPerMonth * (retentionMonths ?? 12) * avgEmailSizeKB;
787
811
  return totalKB / 1024 / 1024;
@@ -1857,6 +1881,39 @@ var init_prompts = __esm({
1857
1881
  }
1858
1882
  });
1859
1883
 
1884
+ // src/utils/shared/assume-role.ts
1885
+ var assume_role_exports = {};
1886
+ __export(assume_role_exports, {
1887
+ assumeRole: () => assumeRole
1888
+ });
1889
+ import { AssumeRoleCommand, STSClient as STSClient2 } from "@aws-sdk/client-sts";
1890
+ async function assumeRole(roleArn, region, sessionName = "wraps-console") {
1891
+ const sts = new STSClient2({ region });
1892
+ const response = await sts.send(
1893
+ new AssumeRoleCommand({
1894
+ RoleArn: roleArn,
1895
+ RoleSessionName: sessionName,
1896
+ DurationSeconds: 3600
1897
+ // 1 hour
1898
+ })
1899
+ );
1900
+ if (!response.Credentials) {
1901
+ throw new Error("Failed to assume role: No credentials returned");
1902
+ }
1903
+ return {
1904
+ accessKeyId: response.Credentials.AccessKeyId,
1905
+ secretAccessKey: response.Credentials.SecretAccessKey,
1906
+ sessionToken: response.Credentials.SessionToken,
1907
+ expiration: response.Credentials.Expiration
1908
+ };
1909
+ }
1910
+ var init_assume_role = __esm({
1911
+ "src/utils/shared/assume-role.ts"() {
1912
+ "use strict";
1913
+ init_esm_shims();
1914
+ }
1915
+ });
1916
+
1860
1917
  // src/utils/archive.ts
1861
1918
  import {
1862
1919
  GetArchiveMessageCommand,
@@ -2734,9 +2791,7 @@ async function eventDestinationExists(configSetName, eventDestName, region) {
2734
2791
  ConfigurationSetName: configSetName
2735
2792
  })
2736
2793
  );
2737
- return response.EventDestinations?.some(
2738
- (dest) => dest.Name === eventDestName
2739
- );
2794
+ return response.EventDestinations?.some((dest) => dest.Name === eventDestName) ?? false;
2740
2795
  } catch (error) {
2741
2796
  if (error.name === "NotFoundException") {
2742
2797
  return false;
@@ -2911,7 +2966,7 @@ async function createSQSResources() {
2911
2966
  // src/infrastructure/vercel-oidc.ts
2912
2967
  init_esm_shims();
2913
2968
  import * as aws7 from "@pulumi/aws";
2914
- async function getExistingOIDCProviderArn(url, accountId) {
2969
+ async function getExistingOIDCProviderArn(url) {
2915
2970
  try {
2916
2971
  const { IAMClient: IAMClient2, ListOpenIDConnectProvidersCommand } = await import("@aws-sdk/client-iam");
2917
2972
  const iam4 = new IAMClient2({});
@@ -2928,7 +2983,7 @@ async function getExistingOIDCProviderArn(url, accountId) {
2928
2983
  }
2929
2984
  async function createVercelOIDC(config2) {
2930
2985
  const url = `https://oidc.vercel.com/${config2.teamSlug}`;
2931
- const existingArn = await getExistingOIDCProviderArn(url, config2.accountId);
2986
+ const existingArn = await getExistingOIDCProviderArn(url);
2932
2987
  if (existingArn) {
2933
2988
  return new aws7.iam.OpenIdConnectProvider(
2934
2989
  "wraps-vercel-oidc",
@@ -3624,7 +3679,7 @@ async function config(options) {
3624
3679
  `No Wraps connection found for account ${pc3.cyan(identity.accountId)} in region ${pc3.cyan(region)}`
3625
3680
  );
3626
3681
  clack3.log.info(
3627
- `Use ${pc3.cyan("wraps init")} to create new infrastructure or ${pc3.cyan("wraps connect")} to connect existing.`
3682
+ `Use ${pc3.cyan("wraps email init")} to create new infrastructure or ${pc3.cyan("wraps email connect")} to connect existing.`
3628
3683
  );
3629
3684
  process.exit(1);
3630
3685
  }
@@ -4059,7 +4114,7 @@ async function connect(options) {
4059
4114
  if (scan.identities.length === 0) {
4060
4115
  clack5.log.warn("No SES identities found in this region.");
4061
4116
  clack5.log.info(
4062
- `Use ${pc5.cyan("wraps init")} to create new email infrastructure instead.`
4117
+ `Use ${pc5.cyan("wraps email init")} to create new email infrastructure instead.`
4063
4118
  );
4064
4119
  process.exit(0);
4065
4120
  }
@@ -4161,7 +4216,7 @@ async function connect(options) {
4161
4216
  clack5.log.warn("\nThe Pulumi stack is locked from a previous run.");
4162
4217
  clack5.log.info("To fix this, run:");
4163
4218
  clack5.log.info(` ${pc5.cyan("rm -rf ~/.wraps/pulumi/.pulumi/locks")}`);
4164
- clack5.log.info("\nThen try running wraps connect again.");
4219
+ clack5.log.info("\nThen try running wraps email connect again.");
4165
4220
  }
4166
4221
  throw new Error(`Pulumi deployment failed: ${error.message}`);
4167
4222
  }
@@ -4798,7 +4853,7 @@ ${pc7.yellow(pc7.bold("Configuration Warnings:"))}`);
4798
4853
  clack7.log.warn("\nThe Pulumi stack is locked from a previous run.");
4799
4854
  clack7.log.info("To fix this, run:");
4800
4855
  clack7.log.info(` ${pc7.cyan("rm -rf ~/.wraps/pulumi/.pulumi/locks")}`);
4801
- clack7.log.info("\nThen try running wraps init again.");
4856
+ clack7.log.info("\nThen try running wraps email init again.");
4802
4857
  }
4803
4858
  throw new Error(`Pulumi deployment failed: ${error.message}`);
4804
4859
  }
@@ -4883,7 +4938,7 @@ async function restore(options) {
4883
4938
  `No Wraps connection found for account ${pc8.cyan(identity.accountId)} in region ${pc8.cyan(region)}`
4884
4939
  );
4885
4940
  clack8.log.info(
4886
- `Use ${pc8.cyan("wraps init")} or ${pc8.cyan("wraps connect")} to create a connection first.`
4941
+ `Use ${pc8.cyan("wraps email init")} or ${pc8.cyan("wraps email connect")} to create a connection first.`
4887
4942
  );
4888
4943
  process.exit(1);
4889
4944
  }
@@ -4997,7 +5052,7 @@ async function upgrade(options) {
4997
5052
  `No Wraps connection found for account ${pc9.cyan(identity.accountId)} in region ${pc9.cyan(region)}`
4998
5053
  );
4999
5054
  clack9.log.info(
5000
- `Use ${pc9.cyan("wraps init")} to create new infrastructure or ${pc9.cyan("wraps connect")} to connect existing.`
5055
+ `Use ${pc9.cyan("wraps email init")} to create new infrastructure or ${pc9.cyan("wraps email connect")} to connect existing.`
5001
5056
  );
5002
5057
  process.exit(1);
5003
5058
  }
@@ -5041,10 +5096,23 @@ ${pc9.bold("Current Configuration:")}
5041
5096
  "7days": "7 days",
5042
5097
  "30days": "30 days",
5043
5098
  "90days": "90 days",
5099
+ "3months": "3 months",
5044
5100
  "6months": "6 months",
5101
+ "9months": "9 months",
5045
5102
  "1year": "1 year",
5046
5103
  "18months": "18 months",
5047
- indefinite: "indefinite"
5104
+ "2years": "2 years",
5105
+ "30months": "30 months",
5106
+ "3years": "3 years",
5107
+ "4years": "4 years",
5108
+ "5years": "5 years",
5109
+ "6years": "6 years",
5110
+ "7years": "7 years",
5111
+ "8years": "8 years",
5112
+ "9years": "9 years",
5113
+ "10years": "10 years",
5114
+ indefinite: "indefinite",
5115
+ permanent: "permanent"
5048
5116
  }[config2.emailArchiving.retention] || "90 days";
5049
5117
  console.log(` ${pc9.green("\u2713")} Email Archiving (${retentionLabel})`);
5050
5118
  }
@@ -5299,7 +5367,7 @@ ${pc9.bold("Current Configuration:")}
5299
5367
  "No sending domain configured. You must configure a sending domain before adding a custom tracking domain."
5300
5368
  );
5301
5369
  clack9.log.info(
5302
- `Use ${pc9.cyan("wraps init")} to set up a sending domain first.`
5370
+ `Use ${pc9.cyan("wraps email init")} to set up a sending domain first.`
5303
5371
  );
5304
5372
  process.exit(1);
5305
5373
  }
@@ -5714,17 +5782,7 @@ ${pc9.bold("Cost Impact:")}`);
5714
5782
  if (outputs.httpsTrackingEnabled && outputs.acmCertificateValidationRecords) {
5715
5783
  acmValidationRecords.push(...outputs.acmCertificateValidationRecords);
5716
5784
  }
5717
- let needsCertificateValidation = false;
5718
- let certificateStatus;
5719
- if (outputs.httpsTrackingEnabled && acmValidationRecords.length > 0 && !outputs.cloudFrontDomain) {
5720
- try {
5721
- const { ACMClient: ACMClient2, DescribeCertificateCommand: DescribeCertificateCommand2 } = await import("@aws-sdk/client-acm");
5722
- const acmClient = new ACMClient2({ region: "us-east-1" });
5723
- needsCertificateValidation = true;
5724
- } catch (error) {
5725
- needsCertificateValidation = true;
5726
- }
5727
- }
5785
+ const needsCertificateValidation = outputs.httpsTrackingEnabled && acmValidationRecords.length > 0 && !outputs.cloudFrontDomain;
5728
5786
  displaySuccess({
5729
5787
  roleArn: outputs.roleArn,
5730
5788
  configSetName: outputs.configSetName,
@@ -5817,34 +5875,9 @@ import { Router as createRouter } from "express";
5817
5875
 
5818
5876
  // src/console/services/ses-service.ts
5819
5877
  init_esm_shims();
5878
+ init_assume_role();
5820
5879
  import { GetSendQuotaCommand, SESClient as SESClient3 } from "@aws-sdk/client-ses";
5821
5880
  import { GetEmailIdentityCommand as GetEmailIdentityCommand2, SESv2Client as SESv2Client3 } from "@aws-sdk/client-sesv2";
5822
-
5823
- // src/utils/shared/assume-role.ts
5824
- init_esm_shims();
5825
- import { AssumeRoleCommand, STSClient as STSClient2 } from "@aws-sdk/client-sts";
5826
- async function assumeRole(roleArn, region, sessionName = "wraps-console") {
5827
- const sts = new STSClient2({ region });
5828
- const response = await sts.send(
5829
- new AssumeRoleCommand({
5830
- RoleArn: roleArn,
5831
- RoleSessionName: sessionName,
5832
- DurationSeconds: 3600
5833
- // 1 hour
5834
- })
5835
- );
5836
- if (!response.Credentials) {
5837
- throw new Error("Failed to assume role: No credentials returned");
5838
- }
5839
- return {
5840
- accessKeyId: response.Credentials.AccessKeyId,
5841
- secretAccessKey: response.Credentials.SecretAccessKey,
5842
- sessionToken: response.Credentials.SessionToken,
5843
- expiration: response.Credentials.Expiration
5844
- };
5845
- }
5846
-
5847
- // src/console/services/ses-service.ts
5848
5881
  async function fetchSendQuota(roleArn, region) {
5849
5882
  const credentials = roleArn ? await assumeRole(roleArn, region) : void 0;
5850
5883
  const ses = new SESClient3({ region, credentials });
@@ -6331,6 +6364,7 @@ import { Router as createRouter3 } from "express";
6331
6364
 
6332
6365
  // src/console/services/aws-metrics.ts
6333
6366
  init_esm_shims();
6367
+ init_assume_role();
6334
6368
  import {
6335
6369
  CloudWatchClient,
6336
6370
  GetMetricDataCommand
@@ -6544,6 +6578,7 @@ import { Router as createRouter4 } from "express";
6544
6578
 
6545
6579
  // src/console/services/settings-service.ts
6546
6580
  init_esm_shims();
6581
+ init_assume_role();
6547
6582
  import {
6548
6583
  GetConfigurationSetCommand,
6549
6584
  GetEmailIdentityCommand as GetEmailIdentityCommand3,
@@ -6774,7 +6809,7 @@ function createSettingsRouter(config2) {
6774
6809
  `[Settings] Updating sending options for ${configSetName}: ${enabled}`
6775
6810
  );
6776
6811
  const { SESv2Client: SESv2Client5, PutConfigurationSetSendingOptionsCommand } = await import("@aws-sdk/client-sesv2");
6777
- const { assumeRole: assumeRole2 } = await import("../../utils/assume-role.js");
6812
+ const { assumeRole: assumeRole2 } = await Promise.resolve().then(() => (init_assume_role(), assume_role_exports));
6778
6813
  const credentials = config2.roleArn ? await assumeRole2(config2.roleArn, config2.region) : void 0;
6779
6814
  const sesClient = new SESv2Client5({ region: config2.region, credentials });
6780
6815
  await sesClient.send(
@@ -6811,7 +6846,7 @@ function createSettingsRouter(config2) {
6811
6846
  `[Settings] Updating reputation options for ${configSetName}: ${enabled}`
6812
6847
  );
6813
6848
  const { SESv2Client: SESv2Client5, PutConfigurationSetReputationOptionsCommand } = await import("@aws-sdk/client-sesv2");
6814
- const { assumeRole: assumeRole2 } = await import("../../utils/assume-role.js");
6849
+ const { assumeRole: assumeRole2 } = await Promise.resolve().then(() => (init_assume_role(), assume_role_exports));
6815
6850
  const credentials = config2.roleArn ? await assumeRole2(config2.roleArn, config2.region) : void 0;
6816
6851
  const sesClient = new SESv2Client5({ region: config2.region, credentials });
6817
6852
  await sesClient.send(
@@ -6854,7 +6889,7 @@ function createSettingsRouter(config2) {
6854
6889
  `[Settings] Updating tracking domain for ${configSetName}: ${domain}`
6855
6890
  );
6856
6891
  const { SESv2Client: SESv2Client5, PutConfigurationSetTrackingOptionsCommand } = await import("@aws-sdk/client-sesv2");
6857
- const { assumeRole: assumeRole2 } = await import("../../utils/assume-role.js");
6892
+ const { assumeRole: assumeRole2 } = await Promise.resolve().then(() => (init_assume_role(), assume_role_exports));
6858
6893
  const credentials = config2.roleArn ? await assumeRole2(config2.roleArn, config2.region) : void 0;
6859
6894
  const sesClient = new SESv2Client5({
6860
6895
  region: config2.region,
@@ -6902,7 +6937,7 @@ function createUserRouter(config2) {
6902
6937
  try {
6903
6938
  if (config2.roleArn) {
6904
6939
  console.log("[User API] Attempting to fetch account alias via IAM");
6905
- const { assumeRole: assumeRole2 } = await import("../../utils/assume-role.js");
6940
+ const { assumeRole: assumeRole2 } = await Promise.resolve().then(() => (init_assume_role(), assume_role_exports));
6906
6941
  const { IAMClient: IAMClient2, ListAccountAliasesCommand } = await import("@aws-sdk/client-iam");
6907
6942
  const credentials = await assumeRole2(config2.roleArn, region);
6908
6943
  const iamClient = new IAMClient2({ region, credentials });
@@ -7045,7 +7080,7 @@ async function dashboard(options) {
7045
7080
  progress.stop();
7046
7081
  clack10.log.error("No Wraps infrastructure found");
7047
7082
  console.log(
7048
- `\\nRun ${pc10.cyan("wraps init")} to deploy infrastructure first.\\n`
7083
+ `\\nRun ${pc10.cyan("wraps email init")} to deploy infrastructure first.\\n`
7049
7084
  );
7050
7085
  process.exit(1);
7051
7086
  }
@@ -7139,7 +7174,7 @@ async function destroy(options) {
7139
7174
  clack11.outro(pc11.green("All Wraps infrastructure has been removed"));
7140
7175
  console.log(
7141
7176
  `
7142
- Run ${pc11.cyan("wraps init")} to deploy infrastructure again.
7177
+ Run ${pc11.cyan("wraps email init")} to deploy infrastructure again.
7143
7178
  `
7144
7179
  );
7145
7180
  }
@@ -7168,9 +7203,11 @@ async function status(_options) {
7168
7203
  } catch (_error) {
7169
7204
  progress.stop();
7170
7205
  clack12.log.error("No Wraps infrastructure found");
7171
- console.log(`
7172
- Run ${pc12.cyan("wraps init")} to deploy infrastructure.
7173
- `);
7206
+ console.log(
7207
+ `
7208
+ Run ${pc12.cyan("wraps email init")} to deploy infrastructure.
7209
+ `
7210
+ );
7174
7211
  process.exit(1);
7175
7212
  }
7176
7213
  const domains = await listSESDomains(region);
@@ -7235,7 +7272,7 @@ function printCompletionScript() {
7235
7272
  console.log("# For now, here are the available commands:\n");
7236
7273
  console.log("# Commands:");
7237
7274
  console.log(
7238
- "# wraps init [--provider vercel|aws|railway|other] [--region <region>] [--domain <domain>]"
7275
+ "# wraps email init [--provider vercel|aws|railway|other] [--region <region>] [--domain <domain>]"
7239
7276
  );
7240
7277
  console.log("# wraps status [--account <account-id>]");
7241
7278
  console.log("# wraps completion\n");
@@ -7367,7 +7404,74 @@ args.options([
7367
7404
  var flags = args.parse(process.argv);
7368
7405
  var [primaryCommand, subCommand] = args.sub;
7369
7406
  if (!primaryCommand) {
7370
- showHelp();
7407
+ async function selectService() {
7408
+ clack13.intro(pc13.bold(`WRAPS CLI v${VERSION}`));
7409
+ console.log("Welcome! Let's get started deploying your infrastructure.\n");
7410
+ const service = await clack13.select({
7411
+ message: "Which service would you like to set up?",
7412
+ options: [
7413
+ {
7414
+ value: "email",
7415
+ label: "Email",
7416
+ hint: "AWS SES email infrastructure"
7417
+ },
7418
+ {
7419
+ value: "sms",
7420
+ label: "SMS",
7421
+ hint: "Coming soon - AWS End User Messaging"
7422
+ }
7423
+ ]
7424
+ });
7425
+ if (clack13.isCancel(service)) {
7426
+ clack13.cancel("Operation cancelled.");
7427
+ process.exit(0);
7428
+ }
7429
+ if (service === "sms") {
7430
+ clack13.log.warn("SMS infrastructure is coming soon!");
7431
+ console.log(
7432
+ `
7433
+ Check back soon or follow our progress at ${pc13.cyan("https://github.com/wraps-team/wraps")}
7434
+ `
7435
+ );
7436
+ process.exit(0);
7437
+ }
7438
+ const action = await clack13.select({
7439
+ message: "What would you like to do?",
7440
+ options: [
7441
+ {
7442
+ value: "init",
7443
+ label: "Deploy new infrastructure",
7444
+ hint: "Create new AWS SES infrastructure"
7445
+ },
7446
+ {
7447
+ value: "connect",
7448
+ label: "Connect existing infrastructure",
7449
+ hint: "Connect to existing AWS SES setup"
7450
+ }
7451
+ ]
7452
+ });
7453
+ if (clack13.isCancel(action)) {
7454
+ clack13.cancel("Operation cancelled.");
7455
+ process.exit(0);
7456
+ }
7457
+ if (action === "init") {
7458
+ await init({
7459
+ provider: flags.provider,
7460
+ region: flags.region,
7461
+ domain: flags.domain,
7462
+ preset: flags.preset,
7463
+ yes: flags.yes
7464
+ });
7465
+ } else {
7466
+ await connect({
7467
+ provider: flags.provider,
7468
+ region: flags.region,
7469
+ yes: flags.yes
7470
+ });
7471
+ }
7472
+ }
7473
+ selectService().catch(handleCLIError);
7474
+ process.exit(0);
7371
7475
  }
7372
7476
  async function run() {
7373
7477
  try {