@wraps.dev/cli 2.1.0 → 2.2.1

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
@@ -147,7 +147,7 @@ var require_package = __commonJS({
147
147
  "package.json"(exports, module) {
148
148
  module.exports = {
149
149
  name: "@wraps.dev/cli",
150
- version: "2.1.0",
150
+ version: "2.2.1",
151
151
  description: "CLI for deploying Wraps email infrastructure to your AWS account",
152
152
  type: "module",
153
153
  main: "./dist/cli.js",
@@ -647,6 +647,19 @@ To remove: wraps destroy --stack ${stackName}`,
647
647
  "SMS_SIMULATOR_LIMIT",
648
648
  "Upgrade to a toll-free number for production use:\n wraps sms upgrade --phone-type toll-free",
649
649
  "https://wraps.dev/docs/cli-reference"
650
+ ),
651
+ // SMTP-specific errors
652
+ smtpRequiresSending: () => new WrapsError(
653
+ "SMTP credentials require email sending to be enabled",
654
+ "SMTP_REQUIRES_SENDING",
655
+ "Enable sending first:\n wraps email upgrade\nAnd select 'Custom configuration' to enable sending.",
656
+ "https://wraps.dev/docs/cli-reference"
657
+ ),
658
+ smtpCredentialsNotFound: () => new WrapsError(
659
+ "SMTP credentials not found",
660
+ "SMTP_CREDENTIALS_NOT_FOUND",
661
+ "Enable SMTP credentials:\n wraps email upgrade\nAnd select 'Enable SMTP credentials'",
662
+ "https://wraps.dev/docs/cli-reference"
650
663
  )
651
664
  };
652
665
  }
@@ -953,6 +966,15 @@ function calculateEmailArchivingCost(config2, emailsPerMonth) {
953
966
  description: `Email archiving (${retention}, ~${storageGB.toFixed(2)} GB at steady-state)`
954
967
  };
955
968
  }
969
+ function calculateSMTPCredentialsCost(config2) {
970
+ if (!config2.smtpCredentials?.enabled) {
971
+ return;
972
+ }
973
+ return {
974
+ monthly: 0,
975
+ description: "SMTP credentials (no additional cost)"
976
+ };
977
+ }
956
978
  function calculateCosts(config2, emailsPerMonth = 1e4) {
957
979
  const tracking = calculateTrackingCost(config2);
958
980
  const reputationMetrics = calculateReputationMetricsCost(config2);
@@ -961,8 +983,9 @@ function calculateCosts(config2, emailsPerMonth = 1e4) {
961
983
  const emailArchiving = calculateEmailArchivingCost(config2, emailsPerMonth);
962
984
  const dedicatedIp = calculateDedicatedIpCost(config2);
963
985
  const waf = calculateWafCost(config2, emailsPerMonth);
986
+ const smtpCredentials = calculateSMTPCredentialsCost(config2);
964
987
  const sesEmailCost = Math.max(0, emailsPerMonth - FREE_TIER.SES_EMAILS) * AWS_PRICING.SES_PER_EMAIL;
965
- const totalMonthlyCost = sesEmailCost + (tracking?.monthly || 0) + (reputationMetrics?.monthly || 0) + (eventTracking?.monthly || 0) + (dynamoDBHistory?.monthly || 0) + (emailArchiving?.monthly || 0) + (dedicatedIp?.monthly || 0) + (waf?.monthly || 0);
988
+ const totalMonthlyCost = sesEmailCost + (tracking?.monthly || 0) + (reputationMetrics?.monthly || 0) + (eventTracking?.monthly || 0) + (dynamoDBHistory?.monthly || 0) + (emailArchiving?.monthly || 0) + (dedicatedIp?.monthly || 0) + (waf?.monthly || 0) + (smtpCredentials?.monthly || 0);
966
989
  return {
967
990
  tracking,
968
991
  reputationMetrics,
@@ -971,6 +994,7 @@ function calculateCosts(config2, emailsPerMonth = 1e4) {
971
994
  emailArchiving,
972
995
  dedicatedIp,
973
996
  waf,
997
+ smtpCredentials,
974
998
  total: {
975
999
  monthly: totalMonthlyCost,
976
1000
  perEmail: AWS_PRICING.SES_PER_EMAIL,
@@ -1031,6 +1055,11 @@ function getCostSummary(config2, emailsPerMonth = 1e4) {
1031
1055
  ` - ${costs.waf.description}: ${formatCost(costs.waf.monthly)}`
1032
1056
  );
1033
1057
  }
1058
+ if (costs.smtpCredentials) {
1059
+ lines.push(
1060
+ ` - ${costs.smtpCredentials.description}: ${formatCost(costs.smtpCredentials.monthly)}`
1061
+ );
1062
+ }
1034
1063
  return lines.join("\n");
1035
1064
  }
1036
1065
  var AWS_PRICING, FREE_TIER;
@@ -2317,6 +2346,11 @@ function applyConfigUpdates(existingConfig, updates) {
2317
2346
  ...result.emailArchiving,
2318
2347
  ...value
2319
2348
  };
2349
+ } else if (key === "smtpCredentials" && typeof value === "object") {
2350
+ result.smtpCredentials = {
2351
+ ...result.smtpCredentials,
2352
+ ...value
2353
+ };
2320
2354
  } else {
2321
2355
  result[key] = value;
2322
2356
  }
@@ -2348,8 +2382,8 @@ function addServiceToConnection(accountId, region, provider, service, config2, p
2348
2382
  config: config2,
2349
2383
  deployedAt: timestamp
2350
2384
  };
2351
- } else if (service === "storage") {
2352
- existingMetadata.services.storage = {
2385
+ } else if (service === "cdn") {
2386
+ existingMetadata.services.cdn = {
2353
2387
  preset,
2354
2388
  config: config2,
2355
2389
  deployedAt: timestamp
@@ -2378,8 +2412,8 @@ function addServiceToConnection(accountId, region, provider, service, config2, p
2378
2412
  config: config2,
2379
2413
  deployedAt: timestamp
2380
2414
  };
2381
- } else if (service === "storage") {
2382
- metadata.services.storage = {
2415
+ } else if (service === "cdn") {
2416
+ metadata.services.cdn = {
2383
2417
  preset,
2384
2418
  config: config2,
2385
2419
  deployedAt: timestamp
@@ -2398,9 +2432,9 @@ function updateServiceConfig(metadata, service, config2) {
2398
2432
  ...metadata.services.sms.config,
2399
2433
  ...config2
2400
2434
  };
2401
- } else if (service === "storage" && metadata.services.storage) {
2402
- metadata.services.storage.config = {
2403
- ...metadata.services.storage.config,
2435
+ } else if (service === "cdn" && metadata.services.cdn) {
2436
+ metadata.services.cdn.config = {
2437
+ ...metadata.services.cdn.config,
2404
2438
  ...config2
2405
2439
  };
2406
2440
  } else {
@@ -2415,8 +2449,8 @@ function removeServiceFromConnection(metadata, service) {
2415
2449
  } else if (service === "sms") {
2416
2450
  const { sms, ...rest } = metadata.services;
2417
2451
  metadata.services = rest;
2418
- } else if (service === "storage") {
2419
- const { storage, ...rest } = metadata.services;
2452
+ } else if (service === "cdn") {
2453
+ const { cdn, ...rest } = metadata.services;
2420
2454
  metadata.services = rest;
2421
2455
  }
2422
2456
  metadata.timestamp = (/* @__PURE__ */ new Date()).toISOString();
@@ -2428,8 +2462,8 @@ function hasService(metadata, service) {
2428
2462
  if (service === "sms") {
2429
2463
  return metadata.services.sms !== void 0;
2430
2464
  }
2431
- if (service === "storage") {
2432
- return metadata.services.storage !== void 0;
2465
+ if (service === "cdn") {
2466
+ return metadata.services.cdn !== void 0;
2433
2467
  }
2434
2468
  return false;
2435
2469
  }
@@ -2441,8 +2475,8 @@ function getConfiguredServices(metadata) {
2441
2475
  if (metadata.services.sms) {
2442
2476
  services.push("sms");
2443
2477
  }
2444
- if (metadata.services.storage) {
2445
- services.push("storage");
2478
+ if (metadata.services.cdn) {
2479
+ services.push("cdn");
2446
2480
  }
2447
2481
  return services;
2448
2482
  }
@@ -3230,12 +3264,12 @@ var acm_exports = {};
3230
3264
  __export(acm_exports, {
3231
3265
  createACMCertificate: () => createACMCertificate
3232
3266
  });
3233
- import * as aws8 from "@pulumi/aws";
3267
+ import * as aws9 from "@pulumi/aws";
3234
3268
  async function createACMCertificate(config2) {
3235
- const usEast1Provider = new aws8.Provider("acm-us-east-1", {
3269
+ const usEast1Provider = new aws9.Provider("acm-us-east-1", {
3236
3270
  region: "us-east-1"
3237
3271
  });
3238
- const certificate = new aws8.acm.Certificate(
3272
+ const certificate = new aws9.acm.Certificate(
3239
3273
  "wraps-email-tracking-cert",
3240
3274
  {
3241
3275
  domainName: config2.domain,
@@ -3258,7 +3292,7 @@ async function createACMCertificate(config2) {
3258
3292
  );
3259
3293
  let certificateValidation;
3260
3294
  if (config2.hostedZoneId) {
3261
- const validationRecord = new aws8.route53.Record(
3295
+ const validationRecord = new aws9.route53.Record(
3262
3296
  "wraps-email-tracking-cert-validation",
3263
3297
  {
3264
3298
  zoneId: config2.hostedZoneId,
@@ -3268,7 +3302,7 @@ async function createACMCertificate(config2) {
3268
3302
  ttl: 60
3269
3303
  }
3270
3304
  );
3271
- certificateValidation = new aws8.acm.CertificateValidation(
3305
+ certificateValidation = new aws9.acm.CertificateValidation(
3272
3306
  "wraps-email-tracking-cert-validation-waiter",
3273
3307
  {
3274
3308
  certificateArn: certificate.arn,
@@ -3297,7 +3331,7 @@ var cloudfront_exports = {};
3297
3331
  __export(cloudfront_exports, {
3298
3332
  createCloudFrontTracking: () => createCloudFrontTracking
3299
3333
  });
3300
- import * as aws9 from "@pulumi/aws";
3334
+ import * as aws10 from "@pulumi/aws";
3301
3335
  async function findDistributionByAlias(alias) {
3302
3336
  try {
3303
3337
  const { CloudFrontClient, ListDistributionsCommand } = await import("@aws-sdk/client-cloudfront");
@@ -3313,10 +3347,10 @@ async function findDistributionByAlias(alias) {
3313
3347
  }
3314
3348
  }
3315
3349
  async function createWAFWebACL() {
3316
- const usEast1Provider = new aws9.Provider("waf-us-east-1", {
3350
+ const usEast1Provider = new aws10.Provider("waf-us-east-1", {
3317
3351
  region: "us-east-1"
3318
3352
  });
3319
- const webAcl = new aws9.wafv2.WebAcl(
3353
+ const webAcl = new aws10.wafv2.WebAcl(
3320
3354
  "wraps-email-tracking-waf",
3321
3355
  {
3322
3356
  scope: "CLOUDFRONT",
@@ -3431,14 +3465,14 @@ async function createCloudFrontTracking(config2) {
3431
3465
  Description: "Wraps email tracking CloudFront distribution"
3432
3466
  }
3433
3467
  };
3434
- const distribution = existingDistributionId ? new aws9.cloudfront.Distribution(
3468
+ const distribution = existingDistributionId ? new aws10.cloudfront.Distribution(
3435
3469
  "wraps-email-tracking-cdn",
3436
3470
  distributionConfig,
3437
3471
  {
3438
3472
  import: existingDistributionId
3439
3473
  // Import existing distribution
3440
3474
  }
3441
- ) : new aws9.cloudfront.Distribution(
3475
+ ) : new aws10.cloudfront.Distribution(
3442
3476
  "wraps-email-tracking-cdn",
3443
3477
  distributionConfig
3444
3478
  );
@@ -8143,7 +8177,7 @@ import pc8 from "picocolors";
8143
8177
 
8144
8178
  // src/infrastructure/email-stack.ts
8145
8179
  init_esm_shims();
8146
- import * as aws10 from "@pulumi/aws";
8180
+ import * as aws11 from "@pulumi/aws";
8147
8181
  import * as pulumi4 from "@pulumi/pulumi";
8148
8182
 
8149
8183
  // src/infrastructure/resources/dynamodb.ts
@@ -8366,10 +8400,10 @@ import * as pulumi2 from "@pulumi/pulumi";
8366
8400
  async function roleExists(roleName) {
8367
8401
  try {
8368
8402
  const { IAMClient: IAMClient3, GetRoleCommand: GetRoleCommand2 } = await import("@aws-sdk/client-iam");
8369
- const iam7 = new IAMClient3({
8403
+ const iam8 = new IAMClient3({
8370
8404
  region: process.env.AWS_REGION || "us-east-1"
8371
8405
  });
8372
- await iam7.send(new GetRoleCommand2({ RoleName: roleName }));
8406
+ await iam8.send(new GetRoleCommand2({ RoleName: roleName }));
8373
8407
  return true;
8374
8408
  } catch (error) {
8375
8409
  if (error.name === "NoSuchEntityException" || error.Code === "NoSuchEntity" || error.Error?.Code === "NoSuchEntity") {
@@ -8728,11 +8762,93 @@ async function createSESResources(config2) {
8728
8762
  };
8729
8763
  }
8730
8764
 
8731
- // src/infrastructure/resources/sqs.ts
8765
+ // src/infrastructure/resources/smtp-credentials.ts
8732
8766
  init_esm_shims();
8733
8767
  import * as aws6 from "@pulumi/aws";
8768
+ import { createHmac } from "crypto";
8769
+ function convertToSMTPPassword(secretAccessKey, region) {
8770
+ const DATE = "11111111";
8771
+ const SERVICE = "ses";
8772
+ const MESSAGE = "SendRawEmail";
8773
+ const TERMINAL = "aws4_request";
8774
+ const VERSION2 = 4;
8775
+ const kDate = createHmac("sha256", `AWS4${secretAccessKey}`).update(DATE).digest();
8776
+ const kRegion = createHmac("sha256", kDate).update(region).digest();
8777
+ const kService = createHmac("sha256", kRegion).update(SERVICE).digest();
8778
+ const kTerminal = createHmac("sha256", kService).update(TERMINAL).digest();
8779
+ const kMessage = createHmac("sha256", kTerminal).update(MESSAGE).digest();
8780
+ const signatureWithVersion = Buffer.concat([Buffer.from([VERSION2]), kMessage]);
8781
+ return signatureWithVersion.toString("base64");
8782
+ }
8783
+ async function userExists(userName) {
8784
+ try {
8785
+ const { IAMClient: IAMClient3, GetUserCommand } = await import("@aws-sdk/client-iam");
8786
+ const iam8 = new IAMClient3({ region: process.env.AWS_REGION || "us-east-1" });
8787
+ await iam8.send(new GetUserCommand({ UserName: userName }));
8788
+ return true;
8789
+ } catch (error) {
8790
+ if (error.name === "NoSuchEntityException" || error.Code === "NoSuchEntity") {
8791
+ return false;
8792
+ }
8793
+ return false;
8794
+ }
8795
+ }
8796
+ async function createSMTPCredentials(config2) {
8797
+ const userName = "wraps-email-smtp-user";
8798
+ const userAlreadyExists = await userExists(userName);
8799
+ const iamUser = userAlreadyExists ? new aws6.iam.User(
8800
+ userName,
8801
+ {
8802
+ name: userName,
8803
+ tags: {
8804
+ ManagedBy: "wraps-cli",
8805
+ Purpose: "SES SMTP Authentication"
8806
+ }
8807
+ },
8808
+ { import: userName }
8809
+ ) : new aws6.iam.User(userName, {
8810
+ name: userName,
8811
+ tags: {
8812
+ ManagedBy: "wraps-cli",
8813
+ Purpose: "SES SMTP Authentication"
8814
+ }
8815
+ });
8816
+ new aws6.iam.UserPolicy("wraps-email-smtp-policy", {
8817
+ user: iamUser.name,
8818
+ policy: JSON.stringify({
8819
+ Version: "2012-10-17",
8820
+ Statement: [
8821
+ {
8822
+ Effect: "Allow",
8823
+ Action: "ses:SendRawEmail",
8824
+ Resource: "*",
8825
+ Condition: {
8826
+ StringEquals: {
8827
+ "ses:ConfigurationSetName": config2.configSetName
8828
+ }
8829
+ }
8830
+ }
8831
+ ]
8832
+ })
8833
+ });
8834
+ const accessKey = new aws6.iam.AccessKey("wraps-email-smtp-key", {
8835
+ user: iamUser.name
8836
+ });
8837
+ const smtpPassword = accessKey.secret.apply(
8838
+ (secret) => convertToSMTPPassword(secret, config2.region)
8839
+ );
8840
+ return {
8841
+ iamUser,
8842
+ accessKey,
8843
+ smtpPassword
8844
+ };
8845
+ }
8846
+
8847
+ // src/infrastructure/resources/sqs.ts
8848
+ init_esm_shims();
8849
+ import * as aws7 from "@pulumi/aws";
8734
8850
  async function createSQSResources() {
8735
- const dlq = new aws6.sqs.Queue("wraps-email-events-dlq", {
8851
+ const dlq = new aws7.sqs.Queue("wraps-email-events-dlq", {
8736
8852
  name: "wraps-email-events-dlq",
8737
8853
  messageRetentionSeconds: 1209600,
8738
8854
  // 14 days
@@ -8741,7 +8857,7 @@ async function createSQSResources() {
8741
8857
  Description: "Dead letter queue for failed SES event processing"
8742
8858
  }
8743
8859
  });
8744
- const queue = new aws6.sqs.Queue("wraps-email-events", {
8860
+ const queue = new aws7.sqs.Queue("wraps-email-events", {
8745
8861
  name: "wraps-email-events",
8746
8862
  visibilityTimeoutSeconds: 300,
8747
8863
  // 5 minutes (Lambda timeout)
@@ -8769,14 +8885,14 @@ async function createSQSResources() {
8769
8885
 
8770
8886
  // src/infrastructure/vercel-oidc.ts
8771
8887
  init_esm_shims();
8772
- import * as aws7 from "@pulumi/aws";
8888
+ import * as aws8 from "@pulumi/aws";
8773
8889
  async function getExistingOIDCProviderArn(url) {
8774
8890
  try {
8775
8891
  const { IAMClient: IAMClient3, ListOpenIDConnectProvidersCommand } = await import("@aws-sdk/client-iam");
8776
- const iam7 = new IAMClient3({
8892
+ const iam8 = new IAMClient3({
8777
8893
  region: process.env.AWS_REGION || "us-east-1"
8778
8894
  });
8779
- const response = await iam7.send(new ListOpenIDConnectProvidersCommand({}));
8895
+ const response = await iam8.send(new ListOpenIDConnectProvidersCommand({}));
8780
8896
  const expectedArnSuffix = url.replace("https://", "");
8781
8897
  const provider = response.OpenIDConnectProviderList?.find(
8782
8898
  (p) => p.Arn?.endsWith(expectedArnSuffix)
@@ -8791,7 +8907,7 @@ async function createVercelOIDC(config2) {
8791
8907
  const url = `https://oidc.vercel.com/${config2.teamSlug}`;
8792
8908
  const existingArn = await getExistingOIDCProviderArn(url);
8793
8909
  if (existingArn) {
8794
- return new aws7.iam.OpenIdConnectProvider(
8910
+ return new aws8.iam.OpenIdConnectProvider(
8795
8911
  "wraps-vercel-oidc",
8796
8912
  {
8797
8913
  url,
@@ -8812,7 +8928,7 @@ async function createVercelOIDC(config2) {
8812
8928
  }
8813
8929
  );
8814
8930
  }
8815
- return new aws7.iam.OpenIdConnectProvider("wraps-vercel-oidc", {
8931
+ return new aws8.iam.OpenIdConnectProvider("wraps-vercel-oidc", {
8816
8932
  url,
8817
8933
  clientIdLists: [`https://vercel.com/${config2.teamSlug}`],
8818
8934
  thumbprintLists: [
@@ -8829,7 +8945,7 @@ async function createVercelOIDC(config2) {
8829
8945
 
8830
8946
  // src/infrastructure/email-stack.ts
8831
8947
  async function deployEmailStack(config2) {
8832
- const identity = await aws10.getCallerIdentity();
8948
+ const identity = await aws11.getCallerIdentity();
8833
8949
  const accountId = identity.accountId;
8834
8950
  let oidcProvider;
8835
8951
  if (config2.provider === "vercel" && config2.vercel) {
@@ -8934,6 +9050,13 @@ async function deployEmailStack(config2) {
8934
9050
  region: config2.region
8935
9051
  });
8936
9052
  }
9053
+ let smtpResources;
9054
+ if (emailConfig.smtpCredentials?.enabled && sesResources) {
9055
+ smtpResources = await createSMTPCredentials({
9056
+ configSetName: "wraps-email-tracking",
9057
+ region: config2.region
9058
+ });
9059
+ }
8937
9060
  return {
8938
9061
  roleArn: role.arn,
8939
9062
  configSetName: sesResources?.configSet.configurationSetName,
@@ -8953,7 +9076,12 @@ async function deployEmailStack(config2) {
8953
9076
  mailFromDomain: sesResources?.mailFromDomain,
8954
9077
  archiveArn: archiveResources?.archiveArn,
8955
9078
  archivingEnabled: emailConfig.emailArchiving?.enabled,
8956
- archiveRetention: emailConfig.emailArchiving?.enabled ? emailConfig.emailArchiving.retention : void 0
9079
+ archiveRetention: emailConfig.emailArchiving?.enabled ? emailConfig.emailArchiving.retention : void 0,
9080
+ // SMTP credentials (shown once, not stored)
9081
+ smtpUserArn: smtpResources?.iamUser.arn,
9082
+ smtpUsername: smtpResources?.accessKey.id,
9083
+ smtpPassword: smtpResources?.smtpPassword,
9084
+ smtpEndpoint: smtpResources ? `email-smtp.${config2.region}.amazonaws.com` : void 0
8957
9085
  };
8958
9086
  }
8959
9087
 
@@ -10000,13 +10128,13 @@ async function scanLambdaFunctions(region) {
10000
10128
  }
10001
10129
  }
10002
10130
  async function scanIAMRoles(region) {
10003
- const iam7 = new IAMClient({ region });
10131
+ const iam8 = new IAMClient({ region });
10004
10132
  const roles = [];
10005
10133
  try {
10006
10134
  let marker;
10007
10135
  let hasMore = true;
10008
10136
  while (hasMore) {
10009
- const listResponse = await iam7.send(
10137
+ const listResponse = await iam8.send(
10010
10138
  new ListRolesCommand({
10011
10139
  Marker: marker,
10012
10140
  MaxItems: 100
@@ -11901,6 +12029,11 @@ ${pc15.bold("Current Configuration:")}
11901
12029
  value: "wraps-dashboard",
11902
12030
  label: metadata.services.email?.webhookSecret ? "Manage Wraps Dashboard connection" : "Connect to Wraps Dashboard",
11903
12031
  hint: metadata.services.email?.webhookSecret ? "Regenerate secret or disconnect" : "Send events to dashboard for analytics"
12032
+ },
12033
+ {
12034
+ value: "smtp-credentials",
12035
+ label: metadata.services.email?.smtpCredentials?.enabled ? "Manage SMTP credentials" : "Enable SMTP credentials",
12036
+ hint: metadata.services.email?.smtpCredentials?.enabled ? "Rotate or disable credentials" : "Generate credentials for PHP, WordPress, etc."
11904
12037
  }
11905
12038
  ]
11906
12039
  });
@@ -12492,6 +12625,98 @@ ${pc15.bold("Webhook Configuration:")}`);
12492
12625
  newPreset = void 0;
12493
12626
  break;
12494
12627
  }
12628
+ case "smtp-credentials": {
12629
+ if (metadata.services.email?.smtpCredentials?.enabled) {
12630
+ clack14.log.info(
12631
+ `SMTP credentials are currently ${pc15.green("enabled")} (created ${metadata.services.email.smtpCredentials.createdAt})`
12632
+ );
12633
+ const smtpAction = await clack14.select({
12634
+ message: "What would you like to do?",
12635
+ options: [
12636
+ {
12637
+ value: "rotate",
12638
+ label: "Rotate credentials",
12639
+ hint: "Generate new credentials (invalidates old ones)"
12640
+ },
12641
+ {
12642
+ value: "disable",
12643
+ label: "Disable SMTP credentials",
12644
+ hint: "Delete IAM user and credentials"
12645
+ },
12646
+ {
12647
+ value: "cancel",
12648
+ label: "Cancel",
12649
+ hint: "Keep current credentials"
12650
+ }
12651
+ ]
12652
+ });
12653
+ if (clack14.isCancel(smtpAction) || smtpAction === "cancel") {
12654
+ clack14.log.info("No changes made.");
12655
+ process.exit(0);
12656
+ }
12657
+ if (smtpAction === "disable") {
12658
+ const confirmDisable = await clack14.confirm({
12659
+ message: "Are you sure? Any systems using these credentials will stop working immediately.",
12660
+ initialValue: false
12661
+ });
12662
+ if (clack14.isCancel(confirmDisable) || !confirmDisable) {
12663
+ clack14.log.info("SMTP credentials not disabled.");
12664
+ process.exit(0);
12665
+ }
12666
+ updatedConfig = {
12667
+ ...config2,
12668
+ smtpCredentials: { enabled: false }
12669
+ };
12670
+ if (metadata.services.email) {
12671
+ metadata.services.email.smtpCredentials = void 0;
12672
+ }
12673
+ newPreset = void 0;
12674
+ break;
12675
+ }
12676
+ clack14.log.info(
12677
+ "\nRotating credentials will invalidate your current SMTP password."
12678
+ );
12679
+ clack14.log.warn("You will need to update all systems using the old credentials.");
12680
+ const confirmRotate = await clack14.confirm({
12681
+ message: "Generate new SMTP credentials?",
12682
+ initialValue: false
12683
+ });
12684
+ if (clack14.isCancel(confirmRotate) || !confirmRotate) {
12685
+ clack14.log.info("Credential rotation cancelled.");
12686
+ process.exit(0);
12687
+ }
12688
+ }
12689
+ clack14.log.info(`
12690
+ ${pc15.bold("SMTP Credentials for Legacy Systems")}
12691
+ `);
12692
+ clack14.log.info(pc15.dim("Generate SMTP username/password that works with:"));
12693
+ clack14.log.info(pc15.dim(" - PHP mail() and PHPMailer"));
12694
+ clack14.log.info(pc15.dim(" - WordPress (WP Mail SMTP plugin)"));
12695
+ clack14.log.info(pc15.dim(" - Nodemailer and other SMTP libraries"));
12696
+ clack14.log.info(pc15.dim(" - Any SMTP-compatible email client"));
12697
+ console.log("");
12698
+ clack14.log.warn(
12699
+ "Credentials will be shown ONCE after deployment - save them immediately!"
12700
+ );
12701
+ console.log("");
12702
+ const confirmCreate = await clack14.confirm({
12703
+ message: "Create SMTP credentials?",
12704
+ initialValue: true
12705
+ });
12706
+ if (clack14.isCancel(confirmCreate) || !confirmCreate) {
12707
+ clack14.log.info("SMTP credentials not created.");
12708
+ process.exit(0);
12709
+ }
12710
+ updatedConfig = {
12711
+ ...config2,
12712
+ smtpCredentials: {
12713
+ enabled: true,
12714
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
12715
+ }
12716
+ };
12717
+ newPreset = void 0;
12718
+ break;
12719
+ }
12495
12720
  }
12496
12721
  const newCostData = calculateCosts(updatedConfig, 5e4);
12497
12722
  const costDiff = newCostData.total.monthly - currentCostData.total.monthly;
@@ -12643,7 +12868,12 @@ ${pc15.bold("Cost Impact:")}`);
12643
12868
  acmCertificateValidationRecords: result.acmCertificateValidationRecords,
12644
12869
  archiveArn: result.archiveArn,
12645
12870
  archivingEnabled: result.archivingEnabled,
12646
- archiveRetention: result.archiveRetention
12871
+ archiveRetention: result.archiveRetention,
12872
+ // SMTP credentials (shown once)
12873
+ smtpUserArn: result.smtpUserArn,
12874
+ smtpUsername: result.smtpUsername,
12875
+ smtpPassword: result.smtpPassword,
12876
+ smtpEndpoint: result.smtpEndpoint
12647
12877
  };
12648
12878
  }
12649
12879
  },
@@ -12679,7 +12909,12 @@ ${pc15.bold("Cost Impact:")}`);
12679
12909
  acmCertificateValidationRecords: pulumiOutputs.acmCertificateValidationRecords?.value,
12680
12910
  archiveArn: pulumiOutputs.archiveArn?.value,
12681
12911
  archivingEnabled: pulumiOutputs.archivingEnabled?.value,
12682
- archiveRetention: pulumiOutputs.archiveRetention?.value
12912
+ archiveRetention: pulumiOutputs.archiveRetention?.value,
12913
+ // SMTP credentials (shown once)
12914
+ smtpUserArn: pulumiOutputs.smtpUserArn?.value,
12915
+ smtpUsername: pulumiOutputs.smtpUsername?.value,
12916
+ smtpPassword: pulumiOutputs.smtpPassword?.value,
12917
+ smtpEndpoint: pulumiOutputs.smtpEndpoint?.value
12683
12918
  };
12684
12919
  }
12685
12920
  );
@@ -12823,6 +13058,31 @@ ${pc15.green("\u2713")} ${pc15.bold("Upgrade complete!")}
12823
13058
  )
12824
13059
  );
12825
13060
  }
13061
+ if (upgradeAction === "smtp-credentials" && outputs.smtpUsername && outputs.smtpPassword) {
13062
+ console.log(pc15.bold("\n\u{1F4E7} SMTP Connection Details\n"));
13063
+ console.log(` ${pc15.cyan("Server:")} ${outputs.smtpEndpoint}`);
13064
+ console.log(` ${pc15.cyan("Port:")} 587 (STARTTLS) or 465 (TLS)`);
13065
+ console.log(` ${pc15.cyan("Username:")} ${outputs.smtpUsername}`);
13066
+ console.log(` ${pc15.cyan("Password:")} ${outputs.smtpPassword}`);
13067
+ console.log(` ${pc15.cyan("Encryption:")} TLS/STARTTLS required
13068
+ `);
13069
+ console.log(pc15.yellow("\u26A0\uFE0F IMPORTANT: Save these credentials NOW!"));
13070
+ console.log(pc15.yellow(" They cannot be retrieved later.\n"));
13071
+ console.log(pc15.bold(" Environment Variables:\n"));
13072
+ console.log(pc15.dim(` SMTP_HOST=${outputs.smtpEndpoint}`));
13073
+ console.log(pc15.dim(` SMTP_PORT=587`));
13074
+ console.log(pc15.dim(` SMTP_USER=${outputs.smtpUsername}`));
13075
+ console.log(pc15.dim(` SMTP_PASS=${outputs.smtpPassword}
13076
+ `));
13077
+ if (metadata.services.email && outputs.smtpUserArn) {
13078
+ metadata.services.email.smtpCredentials = {
13079
+ enabled: true,
13080
+ iamUserArn: outputs.smtpUserArn,
13081
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
13082
+ };
13083
+ await saveConnectionMetadata(metadata);
13084
+ }
13085
+ }
12826
13086
  const enabledFeatures = [];
12827
13087
  if (updatedConfig.tracking?.enabled) {
12828
13088
  enabledFeatures.push("tracking");
@@ -12842,6 +13102,9 @@ ${pc15.green("\u2713")} ${pc15.bold("Upgrade complete!")}
12842
13102
  if (updatedConfig.emailArchiving?.enabled) {
12843
13103
  enabledFeatures.push("email_archiving");
12844
13104
  }
13105
+ if (updatedConfig.smtpCredentials?.enabled) {
13106
+ enabledFeatures.push("smtp_credentials");
13107
+ }
12845
13108
  trackServiceUpgrade("email", {
12846
13109
  from_preset: metadata.services.email?.preset,
12847
13110
  to_preset: newPreset,
@@ -12945,10 +13208,10 @@ Run ${pc18.cyan("wraps email init")} to deploy infrastructure first.
12945
13208
  process.exit(1);
12946
13209
  }
12947
13210
  const roleName = "wraps-console-access-role";
12948
- const iam7 = new IAMClient2({ region: "us-east-1" });
13211
+ const iam8 = new IAMClient2({ region: "us-east-1" });
12949
13212
  let roleExists4 = false;
12950
13213
  try {
12951
- await iam7.send(new GetRoleCommand({ RoleName: roleName }));
13214
+ await iam8.send(new GetRoleCommand({ RoleName: roleName }));
12952
13215
  roleExists4 = true;
12953
13216
  } catch (error) {
12954
13217
  if (error && typeof error === "object" && "name" in error && error.name !== "NoSuchEntity") {
@@ -12989,7 +13252,7 @@ Run ${pc18.cyan("wraps email init")} to deploy infrastructure first.
12989
13252
  const smsEventTracking = smsConfig?.eventTracking;
12990
13253
  await progress.execute("Updating IAM role permissions", async () => {
12991
13254
  const { PutRolePolicyCommand } = await import("@aws-sdk/client-iam");
12992
- await iam7.send(
13255
+ await iam8.send(
12993
13256
  new PutRolePolicyCommand({
12994
13257
  RoleName: roleName,
12995
13258
  PolicyName: "wraps-console-access-policy",
@@ -15113,11 +15376,11 @@ function createSMSRouter(config2) {
15113
15376
  return router;
15114
15377
  }
15115
15378
 
15116
- // src/console/routes/storage.ts
15379
+ // src/console/routes/cdn.ts
15117
15380
  init_esm_shims();
15118
15381
  init_metadata();
15119
15382
  import { Router as createRouter6 } from "express";
15120
- function createStorageRouter(config2) {
15383
+ function createCdnRouter(config2) {
15121
15384
  const router = createRouter6();
15122
15385
  router.get("/settings", async (_req, res) => {
15123
15386
  try {
@@ -15125,36 +15388,36 @@ function createStorageRouter(config2) {
15125
15388
  config2.accountId || "",
15126
15389
  config2.region
15127
15390
  );
15128
- if (!metadata?.services.storage) {
15391
+ if (!metadata?.services.cdn) {
15129
15392
  return res.status(404).json({
15130
- error: "No storage infrastructure found for this account and region"
15393
+ error: "No CDN infrastructure found for this account and region"
15131
15394
  });
15132
15395
  }
15133
- const storageService = metadata.services.storage;
15134
- const storageConfig = storageService.config;
15396
+ const cdnService = metadata.services.cdn;
15397
+ const cdnConfig = cdnService.config;
15135
15398
  const settings = {
15136
- bucketName: config2.storageBucketName || `wraps-storage-${config2.accountId}`,
15137
- bucketArn: `arn:aws:s3:::${config2.storageBucketName || `wraps-storage-${config2.accountId}`}`,
15399
+ bucketName: config2.cdnBucketName || `wraps-cdn-${config2.accountId}`,
15400
+ bucketArn: `arn:aws:s3:::${config2.cdnBucketName || `wraps-cdn-${config2.accountId}`}`,
15138
15401
  region: config2.region,
15139
- roleArn: config2.storageRoleArn || config2.roleArn,
15402
+ roleArn: config2.cdnRoleArn || config2.roleArn,
15140
15403
  cdn: {
15141
- enabled: storageConfig.cdn?.enabled ?? false,
15142
- distributionId: config2.storageDistributionId,
15143
- distributionDomain: config2.storageDistributionDomain,
15144
- customDomain: storageConfig.cdn?.customDomain,
15145
- status: config2.storageDistributionId ? "Deployed" : void 0
15404
+ enabled: cdnConfig.cdn?.enabled ?? false,
15405
+ distributionId: config2.cdnDistributionId,
15406
+ distributionDomain: config2.cdnDistributionDomain,
15407
+ customDomain: cdnConfig.cdn?.customDomain,
15408
+ status: config2.cdnDistributionId ? "Deployed" : void 0
15146
15409
  },
15147
- certificate: storageConfig.cdn?.customDomain ? {
15148
- arn: config2.storageCertificateArn,
15149
- status: config2.storageCertificateArn ? "ISSUED" : "PENDING_VALIDATION"
15410
+ certificate: cdnConfig.cdn?.customDomain ? {
15411
+ arn: config2.cdnCertificateArn,
15412
+ status: config2.cdnCertificateArn ? "ISSUED" : "PENDING_VALIDATION"
15150
15413
  } : void 0,
15151
- versioning: storageConfig.versioning ?? false,
15152
- retention: storageConfig.retention
15414
+ versioning: cdnConfig.versioning ?? false,
15415
+ retention: cdnConfig.retention
15153
15416
  };
15154
15417
  res.json(settings);
15155
15418
  } catch (error) {
15156
15419
  const errorMessage = error instanceof Error ? error.message : "Unknown error";
15157
- console.error("[Storage] Error fetching settings:", error);
15420
+ console.error("[CDN] Error fetching settings:", error);
15158
15421
  res.status(500).json({ error: errorMessage });
15159
15422
  }
15160
15423
  });
@@ -15164,16 +15427,16 @@ function createStorageRouter(config2) {
15164
15427
  config2.accountId || "",
15165
15428
  config2.region
15166
15429
  );
15167
- if (!metadata?.services.storage) {
15430
+ if (!metadata?.services.cdn) {
15168
15431
  return res.status(404).json({
15169
- error: "No storage infrastructure found for this account and region"
15432
+ error: "No CDN infrastructure found for this account and region"
15170
15433
  });
15171
15434
  }
15172
- const bucketName = config2.storageBucketName || `wraps-storage-${config2.accountId}`;
15435
+ const bucketName = config2.cdnBucketName || `wraps-cdn-${config2.accountId}`;
15173
15436
  const { S3Client, ListObjectsV2Command, GetObjectTaggingCommand } = await import("@aws-sdk/client-s3");
15174
15437
  const { assumeRole: assumeRole2 } = await Promise.resolve().then(() => (init_assume_role(), assume_role_exports));
15175
- const credentials = config2.storageRoleArn || config2.roleArn ? await assumeRole2(
15176
- config2.storageRoleArn || config2.roleArn,
15438
+ const credentials = config2.cdnRoleArn || config2.roleArn ? await assumeRole2(
15439
+ config2.cdnRoleArn || config2.roleArn,
15177
15440
  config2.region
15178
15441
  ) : void 0;
15179
15442
  const s3Client = new S3Client({ region: config2.region, credentials });
@@ -15183,7 +15446,7 @@ function createStorageRouter(config2) {
15183
15446
  MaxKeys: 100
15184
15447
  })
15185
15448
  );
15186
- const cdnUrl = config2.storageDistributionDomain ? `https://${metadata.services.storage.config.cdn?.customDomain || config2.storageDistributionDomain}` : null;
15449
+ const cdnUrl = config2.cdnDistributionDomain ? `https://${metadata.services.cdn.config.cdn?.customDomain || config2.cdnDistributionDomain}` : null;
15187
15450
  const getContentType = (key) => {
15188
15451
  const ext = key.split(".").pop()?.toLowerCase();
15189
15452
  const mimeTypes = {
@@ -15246,15 +15509,15 @@ function createStorageRouter(config2) {
15246
15509
  res.json({
15247
15510
  bucketName,
15248
15511
  region: config2.region,
15249
- cdnDomain: config2.storageDistributionDomain,
15250
- customDomain: metadata.services.storage.config.cdn?.customDomain,
15512
+ cdnDomain: config2.cdnDistributionDomain,
15513
+ customDomain: metadata.services.cdn.config.cdn?.customDomain,
15251
15514
  files,
15252
15515
  totalSize,
15253
15516
  fileCount: files.length
15254
15517
  });
15255
15518
  } catch (error) {
15256
15519
  const errorMessage = error instanceof Error ? error.message : "Unknown error";
15257
- console.error("[Storage] Error fetching files:", error);
15520
+ console.error("[CDN] Error fetching files:", error);
15258
15521
  res.status(500).json({ error: errorMessage });
15259
15522
  }
15260
15523
  });
@@ -15266,16 +15529,16 @@ function createStorageRouter(config2) {
15266
15529
  config2.accountId || "",
15267
15530
  config2.region
15268
15531
  );
15269
- if (!metadata?.services.storage) {
15532
+ if (!metadata?.services.cdn) {
15270
15533
  return res.status(404).json({
15271
- error: "No storage infrastructure found for this account and region"
15534
+ error: "No CDN infrastructure found for this account and region"
15272
15535
  });
15273
15536
  }
15274
- const bucketName = config2.storageBucketName || `wraps-storage-${config2.accountId}`;
15537
+ const bucketName = config2.cdnBucketName || `wraps-cdn-${config2.accountId}`;
15275
15538
  const { CloudWatchClient: CloudWatchClient3, GetMetricStatisticsCommand } = await import("@aws-sdk/client-cloudwatch");
15276
15539
  const { assumeRole: assumeRole2 } = await Promise.resolve().then(() => (init_assume_role(), assume_role_exports));
15277
- const credentials = config2.storageRoleArn || config2.roleArn ? await assumeRole2(
15278
- config2.storageRoleArn || config2.roleArn,
15540
+ const credentials = config2.cdnRoleArn || config2.roleArn ? await assumeRole2(
15541
+ config2.cdnRoleArn || config2.roleArn,
15279
15542
  config2.region
15280
15543
  ) : void 0;
15281
15544
  const cloudWatchClient = new CloudWatchClient3({
@@ -15317,7 +15580,7 @@ function createStorageRouter(config2) {
15317
15580
  );
15318
15581
  numberOfObjects = objectsResponse.Datapoints?.[0]?.Average || 0;
15319
15582
  } catch (err) {
15320
- console.log("[Storage] CloudWatch metrics not available yet");
15583
+ console.log("[CDN] CloudWatch metrics not available yet");
15321
15584
  }
15322
15585
  const usage = [];
15323
15586
  const bandwidth = [];
@@ -15353,7 +15616,7 @@ function createStorageRouter(config2) {
15353
15616
  });
15354
15617
  } catch (error) {
15355
15618
  const errorMessage = error instanceof Error ? error.message : "Unknown error";
15356
- console.error("[Storage] Error fetching metrics:", error);
15619
+ console.error("[CDN] Error fetching metrics:", error);
15357
15620
  res.status(500).json({ error: errorMessage });
15358
15621
  }
15359
15622
  });
@@ -15367,17 +15630,17 @@ function createStorageRouter(config2) {
15367
15630
  config2.accountId || "",
15368
15631
  config2.region
15369
15632
  );
15370
- if (!metadata?.services.storage) {
15633
+ if (!metadata?.services.cdn) {
15371
15634
  return res.status(404).json({
15372
- error: "No storage infrastructure found for this account and region"
15635
+ error: "No CDN infrastructure found for this account and region"
15373
15636
  });
15374
15637
  }
15375
- const bucketName = config2.storageBucketName || `wraps-storage-${config2.accountId}`;
15638
+ const bucketName = config2.cdnBucketName || `wraps-cdn-${config2.accountId}`;
15376
15639
  const { S3Client, PutObjectCommand } = await import("@aws-sdk/client-s3");
15377
15640
  const { getSignedUrl } = await import("@aws-sdk/s3-request-presigner");
15378
15641
  const { assumeRole: assumeRole2 } = await Promise.resolve().then(() => (init_assume_role(), assume_role_exports));
15379
- const credentials = config2.storageRoleArn || config2.roleArn ? await assumeRole2(
15380
- config2.storageRoleArn || config2.roleArn,
15642
+ const credentials = config2.cdnRoleArn || config2.roleArn ? await assumeRole2(
15643
+ config2.cdnRoleArn || config2.roleArn,
15381
15644
  config2.region
15382
15645
  ) : void 0;
15383
15646
  const s3Client = new S3Client({ region: config2.region, credentials });
@@ -15390,7 +15653,7 @@ function createStorageRouter(config2) {
15390
15653
  expiresIn: 3600
15391
15654
  // 1 hour
15392
15655
  });
15393
- const cdnUrl = metadata.services.storage.config.cdn?.customDomain ? `https://${metadata.services.storage.config.cdn.customDomain}/${filename}` : config2.storageDistributionDomain ? `https://${config2.storageDistributionDomain}/${filename}` : `s3://${bucketName}/${filename}`;
15656
+ const cdnUrl = metadata.services.cdn.config.cdn?.customDomain ? `https://${metadata.services.cdn.config.cdn.customDomain}/${filename}` : config2.cdnDistributionDomain ? `https://${config2.cdnDistributionDomain}/${filename}` : `s3://${bucketName}/${filename}`;
15394
15657
  res.json({
15395
15658
  uploadUrl,
15396
15659
  cdnUrl,
@@ -15399,7 +15662,7 @@ function createStorageRouter(config2) {
15399
15662
  });
15400
15663
  } catch (error) {
15401
15664
  const errorMessage = error instanceof Error ? error.message : "Unknown error";
15402
- console.error("[Storage] Error generating upload URL:", error);
15665
+ console.error("[CDN] Error generating upload URL:", error);
15403
15666
  res.status(500).json({ error: errorMessage });
15404
15667
  }
15405
15668
  });
@@ -15417,16 +15680,16 @@ function createStorageRouter(config2) {
15417
15680
  config2.accountId || "",
15418
15681
  config2.region
15419
15682
  );
15420
- if (!metadata?.services.storage) {
15683
+ if (!metadata?.services.cdn) {
15421
15684
  return res.status(404).json({
15422
- error: "No storage infrastructure found for this account and region"
15685
+ error: "No CDN infrastructure found for this account and region"
15423
15686
  });
15424
15687
  }
15425
- const bucketName = config2.storageBucketName || `wraps-storage-${config2.accountId}`;
15688
+ const bucketName = config2.cdnBucketName || `wraps-cdn-${config2.accountId}`;
15426
15689
  const { S3Client, GetObjectTaggingCommand, PutObjectTaggingCommand } = await import("@aws-sdk/client-s3");
15427
15690
  const { assumeRole: assumeRole2 } = await Promise.resolve().then(() => (init_assume_role(), assume_role_exports));
15428
- const credentials = config2.storageRoleArn || config2.roleArn ? await assumeRole2(
15429
- config2.storageRoleArn || config2.roleArn,
15691
+ const credentials = config2.cdnRoleArn || config2.roleArn ? await assumeRole2(
15692
+ config2.cdnRoleArn || config2.roleArn,
15430
15693
  config2.region
15431
15694
  ) : void 0;
15432
15695
  const s3Client = new S3Client({ region: config2.region, credentials });
@@ -15458,7 +15721,7 @@ function createStorageRouter(config2) {
15458
15721
  res.json({ success: true, key, starred });
15459
15722
  } catch (error) {
15460
15723
  const errorMessage = error instanceof Error ? error.message : "Unknown error";
15461
- console.error("[Storage] Error toggling star:", error);
15724
+ console.error("[CDN] Error toggling star:", error);
15462
15725
  res.status(500).json({ error: errorMessage });
15463
15726
  }
15464
15727
  });
@@ -15472,16 +15735,16 @@ function createStorageRouter(config2) {
15472
15735
  config2.accountId || "",
15473
15736
  config2.region
15474
15737
  );
15475
- if (!metadata?.services.storage) {
15738
+ if (!metadata?.services.cdn) {
15476
15739
  return res.status(404).json({
15477
- error: "No storage infrastructure found for this account and region"
15740
+ error: "No CDN infrastructure found for this account and region"
15478
15741
  });
15479
15742
  }
15480
- const bucketName = config2.storageBucketName || `wraps-storage-${config2.accountId}`;
15743
+ const bucketName = config2.cdnBucketName || `wraps-cdn-${config2.accountId}`;
15481
15744
  const { S3Client, DeleteObjectCommand } = await import("@aws-sdk/client-s3");
15482
15745
  const { assumeRole: assumeRole2 } = await Promise.resolve().then(() => (init_assume_role(), assume_role_exports));
15483
- const credentials = config2.storageRoleArn || config2.roleArn ? await assumeRole2(
15484
- config2.storageRoleArn || config2.roleArn,
15746
+ const credentials = config2.cdnRoleArn || config2.roleArn ? await assumeRole2(
15747
+ config2.cdnRoleArn || config2.roleArn,
15485
15748
  config2.region
15486
15749
  ) : void 0;
15487
15750
  const s3Client = new S3Client({ region: config2.region, credentials });
@@ -15494,7 +15757,7 @@ function createStorageRouter(config2) {
15494
15757
  res.json({ success: true, key });
15495
15758
  } catch (error) {
15496
15759
  const errorMessage = error instanceof Error ? error.message : "Unknown error";
15497
- console.error("[Storage] Error deleting file:", error);
15760
+ console.error("[CDN] Error deleting file:", error);
15498
15761
  res.status(500).json({ error: errorMessage });
15499
15762
  }
15500
15763
  });
@@ -15511,16 +15774,16 @@ function createStorageRouter(config2) {
15511
15774
  config2.accountId || "",
15512
15775
  config2.region
15513
15776
  );
15514
- if (!metadata?.services.storage) {
15777
+ if (!metadata?.services.cdn) {
15515
15778
  return res.status(404).json({
15516
- error: "No storage infrastructure found for this account and region"
15779
+ error: "No CDN infrastructure found for this account and region"
15517
15780
  });
15518
15781
  }
15519
- const bucketName = config2.storageBucketName || `wraps-storage-${config2.accountId}`;
15782
+ const bucketName = config2.cdnBucketName || `wraps-cdn-${config2.accountId}`;
15520
15783
  const { S3Client, CopyObjectCommand, DeleteObjectCommand } = await import("@aws-sdk/client-s3");
15521
15784
  const { assumeRole: assumeRole2 } = await Promise.resolve().then(() => (init_assume_role(), assume_role_exports));
15522
- const credentials = config2.storageRoleArn || config2.roleArn ? await assumeRole2(
15523
- config2.storageRoleArn || config2.roleArn,
15785
+ const credentials = config2.cdnRoleArn || config2.roleArn ? await assumeRole2(
15786
+ config2.cdnRoleArn || config2.roleArn,
15524
15787
  config2.region
15525
15788
  ) : void 0;
15526
15789
  const s3Client = new S3Client({ region: config2.region, credentials });
@@ -15537,11 +15800,11 @@ function createStorageRouter(config2) {
15537
15800
  Key: oldKey
15538
15801
  })
15539
15802
  );
15540
- const cdnUrl = metadata.services.storage.config.cdn?.customDomain ? `https://${metadata.services.storage.config.cdn.customDomain}/${newKey}` : config2.storageDistributionDomain ? `https://${config2.storageDistributionDomain}/${newKey}` : `s3://${bucketName}/${newKey}`;
15803
+ const cdnUrl = metadata.services.cdn.config.cdn?.customDomain ? `https://${metadata.services.cdn.config.cdn.customDomain}/${newKey}` : config2.cdnDistributionDomain ? `https://${config2.cdnDistributionDomain}/${newKey}` : `s3://${bucketName}/${newKey}`;
15541
15804
  res.json({ success: true, oldKey, newKey, cdnUrl });
15542
15805
  } catch (error) {
15543
15806
  const errorMessage = error instanceof Error ? error.message : "Unknown error";
15544
- console.error("[Storage] Error renaming file:", error);
15807
+ console.error("[CDN] Error renaming file:", error);
15545
15808
  res.status(500).json({ error: errorMessage });
15546
15809
  }
15547
15810
  });
@@ -15641,7 +15904,7 @@ async function startConsoleServer(config2) {
15641
15904
  app.use((_req, res, next) => {
15642
15905
  res.setHeader("X-Frame-Options", "DENY");
15643
15906
  res.setHeader("X-Content-Type-Options", "nosniff");
15644
- const customDomainSrc = config2.storageCustomDomain ? ` https://${config2.storageCustomDomain}` : "";
15907
+ const customDomainSrc = config2.cdnCustomDomain ? ` https://${config2.cdnCustomDomain}` : "";
15645
15908
  res.setHeader(
15646
15909
  "Content-Security-Policy",
15647
15910
  `default-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: blob: https://*.amazonaws.com https://*.cloudfront.net${customDomainSrc}; connect-src 'self' https://*.amazonaws.com https://*.cloudfront.net${customDomainSrc}`
@@ -15675,9 +15938,9 @@ async function startConsoleServer(config2) {
15675
15938
  app.use("/api/user", authenticateToken(authToken), createUserRouter(config2));
15676
15939
  app.use("/api/sms", authenticateToken(authToken), createSMSRouter(config2));
15677
15940
  app.use(
15678
- "/api/storage",
15941
+ "/api/cdn",
15679
15942
  authenticateToken(authToken),
15680
- createStorageRouter(config2)
15943
+ createCdnRouter(config2)
15681
15944
  );
15682
15945
  const staticDir = path2.join(__dirname2, "console");
15683
15946
  app.use(express.static(staticDir));
@@ -15763,15 +16026,15 @@ async function dashboard(options) {
15763
16026
  const smsPhoneNumberArn = smsStackOutputs.phoneNumberArn?.value;
15764
16027
  const smsPhoneNumberType = smsStackOutputs.phoneNumberType?.value;
15765
16028
  const smsConfigSetName = smsStackOutputs.configSetName?.value;
15766
- const storageBucketName = storageStackOutputs.bucketName?.value;
15767
- const storageDistributionId = storageStackOutputs.distributionId?.value;
15768
- const storageDistributionDomain = storageStackOutputs.distributionDomain?.value;
15769
- const storageCertificateArn = storageStackOutputs.acmCertificateArn?.value;
16029
+ const cdnBucketName = storageStackOutputs.bucketName?.value;
16030
+ const cdnDistributionId = storageStackOutputs.distributionId?.value;
16031
+ const cdnDistributionDomain = storageStackOutputs.distributionDomain?.value;
16032
+ const cdnCertificateArn = storageStackOutputs.acmCertificateArn?.value;
15770
16033
  let smsProtectEnabled = false;
15771
16034
  let smsAllowedCountries;
15772
16035
  let smsAitFiltering;
15773
16036
  let smsArchiveRetention;
15774
- let storageCustomDomain;
16037
+ let cdnCustomDomain;
15775
16038
  try {
15776
16039
  const metadata = await loadConnectionMetadata(identity.accountId, region);
15777
16040
  if (metadata?.services?.sms?.config) {
@@ -15785,8 +16048,8 @@ async function dashboard(options) {
15785
16048
  smsArchiveRetention = smsConfig.eventTracking.archiveRetention;
15786
16049
  }
15787
16050
  }
15788
- if (metadata?.services?.storage?.config?.cdn?.customDomain) {
15789
- storageCustomDomain = metadata.services.storage.config.cdn.customDomain;
16051
+ if (metadata?.services?.cdn?.config?.cdn?.customDomain) {
16052
+ cdnCustomDomain = metadata.services.cdn.config.cdn.customDomain;
15790
16053
  }
15791
16054
  } catch {
15792
16055
  }
@@ -15817,13 +16080,13 @@ async function dashboard(options) {
15817
16080
  smsAitFiltering,
15818
16081
  smsArchiveRetention,
15819
16082
  // Storage config (don't pass roleArn - use current credentials like email)
15820
- storageBucketName,
15821
- storageRoleArn: void 0,
16083
+ cdnBucketName,
16084
+ cdnRoleArn: void 0,
15822
16085
  // Use current credentials instead of assuming role
15823
- storageDistributionId,
15824
- storageDistributionDomain,
15825
- storageCustomDomain,
15826
- storageCertificateArn
16086
+ cdnDistributionId,
16087
+ cdnDistributionDomain,
16088
+ cdnCustomDomain,
16089
+ cdnCertificateArn
15827
16090
  });
15828
16091
  console.log(`\\n${pc19.bold("Dashboard:")} ${pc19.cyan(url)}`);
15829
16092
  console.log(`${pc19.dim("Press Ctrl+C to stop")}\\n`);
@@ -16014,15 +16277,15 @@ import pc22 from "picocolors";
16014
16277
 
16015
16278
  // src/infrastructure/sms-stack.ts
16016
16279
  init_esm_shims();
16017
- import * as aws11 from "@pulumi/aws";
16280
+ import * as aws12 from "@pulumi/aws";
16018
16281
  import * as pulumi14 from "@pulumi/pulumi";
16019
16282
  async function roleExists2(roleName) {
16020
16283
  try {
16021
16284
  const { IAMClient: IAMClient3, GetRoleCommand: GetRoleCommand2 } = await import("@aws-sdk/client-iam");
16022
- const iam7 = new IAMClient3({
16285
+ const iam8 = new IAMClient3({
16023
16286
  region: process.env.AWS_REGION || "us-east-1"
16024
16287
  });
16025
- await iam7.send(new GetRoleCommand2({ RoleName: roleName }));
16288
+ await iam8.send(new GetRoleCommand2({ RoleName: roleName }));
16026
16289
  return true;
16027
16290
  } catch (error) {
16028
16291
  if (error.name === "NoSuchEntityException" || error.Code === "NoSuchEntity" || error.Error?.Code === "NoSuchEntity") {
@@ -16092,7 +16355,7 @@ async function createSMSIAMRole(config2) {
16092
16355
  }
16093
16356
  const roleName = "wraps-sms-role";
16094
16357
  const exists = await roleExists2(roleName);
16095
- const role = exists ? new aws11.iam.Role(
16358
+ const role = exists ? new aws12.iam.Role(
16096
16359
  roleName,
16097
16360
  {
16098
16361
  name: roleName,
@@ -16107,7 +16370,7 @@ async function createSMSIAMRole(config2) {
16107
16370
  import: roleName,
16108
16371
  customTimeouts: { create: "2m", update: "2m", delete: "2m" }
16109
16372
  }
16110
- ) : new aws11.iam.Role(
16373
+ ) : new aws12.iam.Role(
16111
16374
  roleName,
16112
16375
  {
16113
16376
  name: roleName,
@@ -16202,7 +16465,7 @@ async function createSMSIAMRole(config2) {
16202
16465
  Resource: "arn:aws:logs:*:*:log-group:/aws/lambda/wraps-sms-*"
16203
16466
  });
16204
16467
  }
16205
- new aws11.iam.RolePolicy("wraps-sms-policy", {
16468
+ new aws12.iam.RolePolicy("wraps-sms-policy", {
16206
16469
  role: role.name,
16207
16470
  policy: JSON.stringify({
16208
16471
  Version: "2012-10-17",
@@ -16212,7 +16475,7 @@ async function createSMSIAMRole(config2) {
16212
16475
  return role;
16213
16476
  }
16214
16477
  function createSMSConfigurationSet() {
16215
- return new aws11.pinpoint.Smsvoicev2ConfigurationSet("wraps-sms-config", {
16478
+ return new aws12.pinpoint.Smsvoicev2ConfigurationSet("wraps-sms-config", {
16216
16479
  name: "wraps-sms-config",
16217
16480
  defaultMessageType: "TRANSACTIONAL",
16218
16481
  tags: {
@@ -16222,7 +16485,7 @@ function createSMSConfigurationSet() {
16222
16485
  });
16223
16486
  }
16224
16487
  function createSMSOptOutList() {
16225
- return new aws11.pinpoint.Smsvoicev2OptOutList("wraps-sms-optouts", {
16488
+ return new aws12.pinpoint.Smsvoicev2OptOutList("wraps-sms-optouts", {
16226
16489
  name: "wraps-sms-optouts",
16227
16490
  tags: {
16228
16491
  ManagedBy: "wraps-cli",
@@ -16286,7 +16549,7 @@ async function createSMSPhoneNumber(phoneNumberType, optOutList) {
16286
16549
  }
16287
16550
  };
16288
16551
  if (existingArn) {
16289
- return new aws11.pinpoint.Smsvoicev2PhoneNumber(
16552
+ return new aws12.pinpoint.Smsvoicev2PhoneNumber(
16290
16553
  "wraps-sms-number",
16291
16554
  phoneConfig,
16292
16555
  {
@@ -16295,7 +16558,7 @@ async function createSMSPhoneNumber(phoneNumberType, optOutList) {
16295
16558
  }
16296
16559
  );
16297
16560
  }
16298
- return new aws11.pinpoint.Smsvoicev2PhoneNumber(
16561
+ return new aws12.pinpoint.Smsvoicev2PhoneNumber(
16299
16562
  "wraps-sms-number",
16300
16563
  phoneConfig,
16301
16564
  {
@@ -16338,10 +16601,10 @@ async function createSMSSQSResources() {
16338
16601
  Description: "Dead letter queue for failed SMS event processing"
16339
16602
  }
16340
16603
  };
16341
- const dlq = dlqUrl ? new aws11.sqs.Queue(dlqName, dlqConfig, {
16604
+ const dlq = dlqUrl ? new aws12.sqs.Queue(dlqName, dlqConfig, {
16342
16605
  import: dlqUrl,
16343
16606
  customTimeouts: { create: "2m", update: "2m", delete: "2m" }
16344
- }) : new aws11.sqs.Queue(dlqName, dlqConfig, {
16607
+ }) : new aws12.sqs.Queue(dlqName, dlqConfig, {
16345
16608
  customTimeouts: { create: "2m", update: "2m", delete: "2m" }
16346
16609
  });
16347
16610
  const queueConfig = {
@@ -16364,10 +16627,10 @@ async function createSMSSQSResources() {
16364
16627
  Description: "Queue for SMS events from SNS"
16365
16628
  }
16366
16629
  };
16367
- const queue = queueUrl ? new aws11.sqs.Queue(queueName, queueConfig, {
16630
+ const queue = queueUrl ? new aws12.sqs.Queue(queueName, queueConfig, {
16368
16631
  import: queueUrl,
16369
16632
  customTimeouts: { create: "2m", update: "2m", delete: "2m" }
16370
- }) : new aws11.sqs.Queue(queueName, queueConfig, {
16633
+ }) : new aws12.sqs.Queue(queueName, queueConfig, {
16371
16634
  customTimeouts: { create: "2m", update: "2m", delete: "2m" }
16372
16635
  });
16373
16636
  return { queue, dlq };
@@ -16407,13 +16670,13 @@ async function createSMSSNSResources(config2) {
16407
16670
  Description: "SNS topic for SMS delivery events"
16408
16671
  }
16409
16672
  };
16410
- const topic = topicArn ? new aws11.sns.Topic("wraps-sms-events-topic", topicConfig, {
16673
+ const topic = topicArn ? new aws12.sns.Topic("wraps-sms-events-topic", topicConfig, {
16411
16674
  import: topicArn,
16412
16675
  customTimeouts: { create: "2m", update: "2m", delete: "2m" }
16413
- }) : new aws11.sns.Topic("wraps-sms-events-topic", topicConfig, {
16676
+ }) : new aws12.sns.Topic("wraps-sms-events-topic", topicConfig, {
16414
16677
  customTimeouts: { create: "2m", update: "2m", delete: "2m" }
16415
16678
  });
16416
- new aws11.sns.TopicPolicy("wraps-sms-events-topic-policy", {
16679
+ new aws12.sns.TopicPolicy("wraps-sms-events-topic-policy", {
16417
16680
  arn: topic.arn,
16418
16681
  policy: topic.arn.apply(
16419
16682
  (topicArn2) => JSON.stringify({
@@ -16430,7 +16693,7 @@ async function createSMSSNSResources(config2) {
16430
16693
  })
16431
16694
  )
16432
16695
  });
16433
- new aws11.sqs.QueuePolicy("wraps-sms-events-queue-policy", {
16696
+ new aws12.sqs.QueuePolicy("wraps-sms-events-queue-policy", {
16434
16697
  queueUrl: config2.queueUrl,
16435
16698
  policy: pulumi14.all([config2.queueArn, topic.arn]).apply(
16436
16699
  ([queueArn, topicArn2]) => JSON.stringify({
@@ -16449,7 +16712,7 @@ async function createSMSSNSResources(config2) {
16449
16712
  })
16450
16713
  )
16451
16714
  });
16452
- const subscription = new aws11.sns.TopicSubscription(
16715
+ const subscription = new aws12.sns.TopicSubscription(
16453
16716
  "wraps-sms-events-subscription",
16454
16717
  {
16455
16718
  topic: topic.arn,
@@ -16498,17 +16761,17 @@ async function createSMSDynamoDBTable() {
16498
16761
  Service: "sms"
16499
16762
  }
16500
16763
  };
16501
- return exists ? new aws11.dynamodb.Table(tableName, tableConfig, {
16764
+ return exists ? new aws12.dynamodb.Table(tableName, tableConfig, {
16502
16765
  import: tableName,
16503
16766
  customTimeouts: { create: "5m", update: "5m", delete: "5m" }
16504
- }) : new aws11.dynamodb.Table(tableName, tableConfig, {
16767
+ }) : new aws12.dynamodb.Table(tableName, tableConfig, {
16505
16768
  customTimeouts: { create: "5m", update: "5m", delete: "5m" }
16506
16769
  });
16507
16770
  }
16508
16771
  async function deploySMSLambdaFunction(config2) {
16509
16772
  const { getLambdaCode: getLambdaCode2 } = await Promise.resolve().then(() => (init_lambda(), lambda_exports));
16510
16773
  const codeDir = await getLambdaCode2("sms-event-processor");
16511
- const lambdaRole = new aws11.iam.Role("wraps-sms-lambda-role", {
16774
+ const lambdaRole = new aws12.iam.Role("wraps-sms-lambda-role", {
16512
16775
  name: "wraps-sms-lambda-role",
16513
16776
  assumeRolePolicy: JSON.stringify({
16514
16777
  Version: "2012-10-17",
@@ -16525,11 +16788,11 @@ async function deploySMSLambdaFunction(config2) {
16525
16788
  Service: "sms"
16526
16789
  }
16527
16790
  });
16528
- new aws11.iam.RolePolicyAttachment("wraps-sms-lambda-basic-execution", {
16791
+ new aws12.iam.RolePolicyAttachment("wraps-sms-lambda-basic-execution", {
16529
16792
  role: lambdaRole.name,
16530
16793
  policyArn: "arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"
16531
16794
  });
16532
- new aws11.iam.RolePolicy("wraps-sms-lambda-policy", {
16795
+ new aws12.iam.RolePolicy("wraps-sms-lambda-policy", {
16533
16796
  role: lambdaRole.name,
16534
16797
  policy: pulumi14.all([config2.tableName, config2.queueArn]).apply(
16535
16798
  ([tableName, queueArn]) => JSON.stringify({
@@ -16561,7 +16824,7 @@ async function deploySMSLambdaFunction(config2) {
16561
16824
  })
16562
16825
  )
16563
16826
  });
16564
- const eventProcessor = new aws11.lambda.Function(
16827
+ const eventProcessor = new aws12.lambda.Function(
16565
16828
  "wraps-sms-event-processor",
16566
16829
  {
16567
16830
  name: "wraps-sms-event-processor",
@@ -16588,7 +16851,7 @@ async function deploySMSLambdaFunction(config2) {
16588
16851
  customTimeouts: { create: "5m", update: "5m", delete: "2m" }
16589
16852
  }
16590
16853
  );
16591
- new aws11.lambda.EventSourceMapping(
16854
+ new aws12.lambda.EventSourceMapping(
16592
16855
  "wraps-sms-event-source-mapping",
16593
16856
  {
16594
16857
  eventSourceArn: config2.queueArn,
@@ -16604,7 +16867,7 @@ async function deploySMSLambdaFunction(config2) {
16604
16867
  return eventProcessor;
16605
16868
  }
16606
16869
  async function deploySMSStack(config2) {
16607
- const identity = await aws11.getCallerIdentity();
16870
+ const identity = await aws12.getCallerIdentity();
16608
16871
  const accountId = identity.accountId;
16609
16872
  let oidcProvider;
16610
16873
  if (config2.provider === "vercel" && config2.vercel) {
@@ -19843,7 +20106,7 @@ Run ${pc29.cyan(`wraps sms verify-number --phone-number ${phoneNumber} --resend`
19843
20106
  }
19844
20107
  }
19845
20108
 
19846
- // src/commands/storage/destroy.ts
20109
+ // src/commands/cdn/destroy.ts
19847
20110
  init_esm_shims();
19848
20111
  init_events();
19849
20112
  init_route53();
@@ -19854,11 +20117,11 @@ init_metadata();
19854
20117
  import * as clack28 from "@clack/prompts";
19855
20118
  import * as pulumi20 from "@pulumi/pulumi";
19856
20119
  import pc30 from "picocolors";
19857
- async function storageDestroy(options) {
20120
+ async function cdnDestroy(options) {
19858
20121
  const startTime = Date.now();
19859
20122
  clack28.intro(
19860
20123
  pc30.bold(
19861
- options.preview ? "Storage Infrastructure Destruction Preview" : "Storage Infrastructure Teardown"
20124
+ options.preview ? "CDN Infrastructure Destruction Preview" : "CDN Infrastructure Teardown"
19862
20125
  )
19863
20126
  );
19864
20127
  const progress = new DeploymentProgress();
@@ -19868,16 +20131,16 @@ async function storageDestroy(options) {
19868
20131
  );
19869
20132
  let region = options.region || await getAWSRegion();
19870
20133
  if (!(options.region || process.env.AWS_REGION || process.env.AWS_DEFAULT_REGION)) {
19871
- const storageConnections = await findConnectionsWithService(
20134
+ const cdnConnections = await findConnectionsWithService(
19872
20135
  identity.accountId,
19873
- "storage"
20136
+ "cdn"
19874
20137
  );
19875
- if (storageConnections.length === 1) {
19876
- region = storageConnections[0].region;
19877
- } else if (storageConnections.length > 1) {
20138
+ if (cdnConnections.length === 1) {
20139
+ region = cdnConnections[0].region;
20140
+ } else if (cdnConnections.length > 1) {
19878
20141
  const selectedRegion = await clack28.select({
19879
- message: "Multiple storage deployments found. Which region to destroy?",
19880
- options: storageConnections.map((conn) => ({
20142
+ message: "Multiple CDN deployments found. Which region to destroy?",
20143
+ options: cdnConnections.map((conn) => ({
19881
20144
  value: conn.region,
19882
20145
  label: conn.region
19883
20146
  }))
@@ -19890,17 +20153,17 @@ async function storageDestroy(options) {
19890
20153
  }
19891
20154
  }
19892
20155
  const metadata = await loadConnectionMetadata(identity.accountId, region);
19893
- const storageService = metadata?.services?.storage;
19894
- const storageConfig = storageService?.config;
19895
- const customDomain = storageConfig?.cdn?.customDomain;
19896
- const storedStackName = storageService?.pulumiStackName;
20156
+ const cdnService = metadata?.services?.cdn;
20157
+ const cdnConfig = cdnService?.config;
20158
+ const customDomain = cdnConfig?.cdn?.customDomain;
20159
+ const storedStackName = cdnService?.pulumiStackName;
19897
20160
  if (!(options.force || options.preview)) {
19898
20161
  clack28.log.warn(
19899
20162
  pc30.yellow("This will delete your S3 bucket and all files in it!")
19900
20163
  );
19901
20164
  const confirmed = await clack28.confirm({
19902
20165
  message: pc30.red(
19903
- "Are you sure you want to destroy all storage infrastructure?"
20166
+ "Are you sure you want to destroy all CDN infrastructure?"
19904
20167
  ),
19905
20168
  initialValue: false
19906
20169
  });
@@ -19935,7 +20198,7 @@ async function storageDestroy(options) {
19935
20198
  "Generating destruction preview",
19936
20199
  async () => {
19937
20200
  await ensurePulumiWorkDir();
19938
- const stackName = storedStackName || `wraps-storage-${identity.accountId}-${region}`;
20201
+ const stackName = storedStackName || `wraps-cdn-${identity.accountId}-${region}`;
19939
20202
  let stack;
19940
20203
  try {
19941
20204
  stack = await pulumi20.automation.LocalWorkspace.selectStack({
@@ -19943,7 +20206,7 @@ async function storageDestroy(options) {
19943
20206
  workDir: getPulumiWorkDir()
19944
20207
  });
19945
20208
  } catch (_error) {
19946
- throw new Error("No storage infrastructure found to preview");
20209
+ throw new Error("No CDN infrastructure found to preview");
19947
20210
  }
19948
20211
  const result = await previewWithResourceChanges(stack, {
19949
20212
  diff: true
@@ -19955,7 +20218,7 @@ async function storageDestroy(options) {
19955
20218
  changeSummary: previewResult.changeSummary,
19956
20219
  resourceChanges: previewResult.resourceChanges,
19957
20220
  costEstimate: "Monthly cost after destruction: $0.00",
19958
- commandName: "wraps storage destroy"
20221
+ commandName: "wraps cdn destroy"
19959
20222
  });
19960
20223
  if (customDomain) {
19961
20224
  const previewHostedZone = await findHostedZone(customDomain, region);
@@ -19968,7 +20231,7 @@ async function storageDestroy(options) {
19968
20231
  clack28.outro(
19969
20232
  pc30.green("Preview complete. Run without --preview to destroy.")
19970
20233
  );
19971
- trackServiceRemoved("storage", {
20234
+ trackServiceRemoved("cdn", {
19972
20235
  preview: true,
19973
20236
  region,
19974
20237
  duration_ms: Date.now() - startTime
@@ -19976,8 +20239,8 @@ async function storageDestroy(options) {
19976
20239
  return;
19977
20240
  } catch (error) {
19978
20241
  progress.stop();
19979
- if (error.message.includes("No storage infrastructure found")) {
19980
- clack28.log.warn("No storage infrastructure found to preview");
20242
+ if (error.message.includes("No CDN infrastructure found")) {
20243
+ clack28.log.warn("No CDN infrastructure found to preview");
19981
20244
  process.exit(0);
19982
20245
  }
19983
20246
  trackError("PREVIEW_FAILED", "storage destroy", { step: "preview" });
@@ -19989,7 +20252,7 @@ async function storageDestroy(options) {
19989
20252
  await progress.execute(
19990
20253
  `Deleting DNS records for ${customDomain}`,
19991
20254
  async () => {
19992
- await deleteStorageDNSRecords(hostedZone.id, customDomain);
20255
+ await deleteCdnDNSRecords(hostedZone.id, customDomain);
19993
20256
  }
19994
20257
  );
19995
20258
  } catch (error) {
@@ -19997,7 +20260,7 @@ async function storageDestroy(options) {
19997
20260
  clack28.log.info("You may need to delete them manually from Route53");
19998
20261
  }
19999
20262
  }
20000
- const bucketName = `wraps-storage-${identity.accountId}`;
20263
+ const bucketName = `wraps-cdn-${identity.accountId}`;
20001
20264
  try {
20002
20265
  await progress.execute(
20003
20266
  "Emptying S3 bucket (this may take a while for large buckets)",
@@ -20010,10 +20273,10 @@ async function storageDestroy(options) {
20010
20273
  }
20011
20274
  try {
20012
20275
  await progress.execute(
20013
- "Destroying storage infrastructure (this may take 2-3 minutes)",
20276
+ "Destroying CDN infrastructure (this may take 2-3 minutes)",
20014
20277
  async () => {
20015
20278
  await ensurePulumiWorkDir();
20016
- const stackName = storedStackName || `wraps-storage-${identity.accountId}-${region}`;
20279
+ const stackName = storedStackName || `wraps-cdn-${identity.accountId}-${region}`;
20017
20280
  let stack;
20018
20281
  try {
20019
20282
  stack = await pulumi20.automation.LocalWorkspace.selectStack({
@@ -20021,7 +20284,7 @@ async function storageDestroy(options) {
20021
20284
  workDir: getPulumiWorkDir()
20022
20285
  });
20023
20286
  } catch (_error) {
20024
- throw new Error("No storage infrastructure found to destroy");
20287
+ throw new Error("No CDN infrastructure found to destroy");
20025
20288
  }
20026
20289
  await stack.destroy({ onOutput: () => {
20027
20290
  } });
@@ -20030,10 +20293,10 @@ async function storageDestroy(options) {
20030
20293
  );
20031
20294
  } catch (error) {
20032
20295
  progress.stop();
20033
- if (error.message.includes("No storage infrastructure found")) {
20034
- clack28.log.warn("No storage infrastructure found");
20296
+ if (error.message.includes("No CDN infrastructure found")) {
20297
+ clack28.log.warn("No CDN infrastructure found");
20035
20298
  if (metadata) {
20036
- removeServiceFromConnection(metadata, "storage");
20299
+ removeServiceFromConnection(metadata, "cdn");
20037
20300
  await saveConnectionMetadata(metadata);
20038
20301
  }
20039
20302
  process.exit(0);
@@ -20043,11 +20306,11 @@ async function storageDestroy(options) {
20043
20306
  throw errors.stackLocked();
20044
20307
  }
20045
20308
  trackError("DESTROY_FAILED", "storage destroy", { step: "destroy" });
20046
- clack28.log.error("Storage infrastructure destruction failed");
20309
+ clack28.log.error("CDN infrastructure destruction failed");
20047
20310
  throw error;
20048
20311
  }
20049
20312
  if (metadata) {
20050
- removeServiceFromConnection(metadata, "storage");
20313
+ removeServiceFromConnection(metadata, "cdn");
20051
20314
  const hasOtherServices = Object.keys(metadata.services).length > 0;
20052
20315
  if (hasOtherServices) {
20053
20316
  await saveConnectionMetadata(metadata);
@@ -20061,7 +20324,7 @@ async function storageDestroy(options) {
20061
20324
  if (shouldCleanDNS && hostedZone) {
20062
20325
  deletedItems.push("Route53 DNS records");
20063
20326
  }
20064
- clack28.outro(pc30.green("Storage infrastructure has been removed"));
20327
+ clack28.outro(pc30.green("CDN infrastructure has been removed"));
20065
20328
  console.log(`
20066
20329
  ${pc30.bold("Cleaned up:")}`);
20067
20330
  for (const item of deletedItems) {
@@ -20069,10 +20332,10 @@ ${pc30.bold("Cleaned up:")}`);
20069
20332
  }
20070
20333
  console.log(
20071
20334
  `
20072
- Run ${pc30.cyan("wraps storage init")} to deploy storage infrastructure again.
20335
+ Run ${pc30.cyan("wraps cdn init")} to deploy CDN infrastructure again.
20073
20336
  `
20074
20337
  );
20075
- trackServiceRemoved("storage", {
20338
+ trackServiceRemoved("cdn", {
20076
20339
  reason: "user_initiated",
20077
20340
  region,
20078
20341
  duration_ms: Date.now() - startTime,
@@ -20111,7 +20374,7 @@ async function emptyS3Bucket(bucketName, region) {
20111
20374
  versionIdMarker = listResponse.NextVersionIdMarker;
20112
20375
  } while (keyMarker);
20113
20376
  }
20114
- async function deleteStorageDNSRecords(hostedZoneId, customDomain) {
20377
+ async function deleteCdnDNSRecords(hostedZoneId, customDomain) {
20115
20378
  const {
20116
20379
  Route53Client: Route53Client2,
20117
20380
  ListResourceRecordSetsCommand: ListResourceRecordSetsCommand2,
@@ -20144,20 +20407,20 @@ async function deleteStorageDNSRecords(hostedZoneId, customDomain) {
20144
20407
  );
20145
20408
  }
20146
20409
 
20147
- // src/commands/storage/init.ts
20410
+ // src/commands/cdn/init.ts
20148
20411
  init_esm_shims();
20149
20412
  import * as clack29 from "@clack/prompts";
20150
20413
  import * as pulumi23 from "@pulumi/pulumi";
20151
20414
  import pc31 from "picocolors";
20152
20415
 
20153
- // src/infrastructure/storage-stack.ts
20416
+ // src/infrastructure/cdn-stack.ts
20154
20417
  init_esm_shims();
20155
- import * as aws13 from "@pulumi/aws";
20418
+ import * as aws14 from "@pulumi/aws";
20156
20419
  import * as pulumi22 from "@pulumi/pulumi";
20157
20420
 
20158
- // src/infrastructure/resources/s3-storage.ts
20421
+ // src/infrastructure/resources/s3-cdn.ts
20159
20422
  init_esm_shims();
20160
- import * as aws12 from "@pulumi/aws";
20423
+ import * as aws13 from "@pulumi/aws";
20161
20424
  import * as pulumi21 from "@pulumi/pulumi";
20162
20425
  function retentionToDays(retention) {
20163
20426
  switch (retention) {
@@ -20177,25 +20440,25 @@ function retentionToDays(retention) {
20177
20440
  return null;
20178
20441
  }
20179
20442
  }
20180
- async function createStorageBucket(config2) {
20181
- const bucketName = config2.storageConfig.bucketName || `wraps-storage-${config2.accountId}`;
20182
- const bucket = new aws12.s3.BucketV2("wraps-storage-bucket", {
20443
+ async function createCdnBucket(config2) {
20444
+ const bucketName = config2.cdnConfig.bucketName || `wraps-cdn-${config2.accountId}`;
20445
+ const bucket = new aws13.s3.BucketV2("wraps-cdn-bucket", {
20183
20446
  bucket: bucketName,
20184
20447
  tags: {
20185
20448
  ManagedBy: "wraps-cli",
20186
- Service: "storage"
20449
+ Service: "cdn"
20187
20450
  }
20188
20451
  });
20189
- if (config2.storageConfig.versioning) {
20190
- new aws12.s3.BucketVersioningV2("wraps-storage-versioning", {
20452
+ if (config2.cdnConfig.versioning) {
20453
+ new aws13.s3.BucketVersioningV2("wraps-cdn-versioning", {
20191
20454
  bucket: bucket.id,
20192
20455
  versioningConfiguration: {
20193
20456
  status: "Enabled"
20194
20457
  }
20195
20458
  });
20196
20459
  }
20197
- new aws12.s3.BucketServerSideEncryptionConfigurationV2(
20198
- "wraps-storage-encryption",
20460
+ new aws13.s3.BucketServerSideEncryptionConfigurationV2(
20461
+ "wraps-cdn-encryption",
20199
20462
  {
20200
20463
  bucket: bucket.id,
20201
20464
  rules: [
@@ -20218,9 +20481,9 @@ async function createStorageBucket(config2) {
20218
20481
  "http://localhost:5555",
20219
20482
  "http://localhost:5556",
20220
20483
  "http://localhost:8080",
20221
- ...config2.storageConfig.additionalOrigins || []
20484
+ ...config2.cdnConfig.additionalOrigins || []
20222
20485
  ];
20223
- new aws12.s3.BucketCorsConfigurationV2("wraps-storage-cors", {
20486
+ new aws13.s3.BucketCorsConfigurationV2("wraps-cdn-cors", {
20224
20487
  bucket: bucket.id,
20225
20488
  corsRules: [
20226
20489
  {
@@ -20232,9 +20495,9 @@ async function createStorageBucket(config2) {
20232
20495
  }
20233
20496
  ]
20234
20497
  });
20235
- const retentionDays = config2.storageConfig.retention ? retentionToDays(config2.storageConfig.retention) : null;
20498
+ const retentionDays = config2.cdnConfig.retention ? retentionToDays(config2.cdnConfig.retention) : null;
20236
20499
  if (retentionDays) {
20237
- new aws12.s3.BucketLifecycleConfigurationV2("wraps-storage-lifecycle", {
20500
+ new aws13.s3.BucketLifecycleConfigurationV2("wraps-cdn-lifecycle", {
20238
20501
  bucket: bucket.id,
20239
20502
  rules: [
20240
20503
  {
@@ -20247,7 +20510,7 @@ async function createStorageBucket(config2) {
20247
20510
  ]
20248
20511
  });
20249
20512
  }
20250
- new aws12.s3.BucketPublicAccessBlock("wraps-storage-public-access", {
20513
+ new aws13.s3.BucketPublicAccessBlock("wraps-cdn-public-access", {
20251
20514
  bucket: bucket.id,
20252
20515
  blockPublicAcls: true,
20253
20516
  blockPublicPolicy: true,
@@ -20260,16 +20523,16 @@ async function createStorageBucket(config2) {
20260
20523
  bucketArn: bucket.arn
20261
20524
  };
20262
20525
  }
20263
- async function createStorageWAF() {
20264
- const usEast1Provider = new aws12.Provider("storage-waf-us-east-1", {
20526
+ async function createCdnWAF() {
20527
+ const usEast1Provider = new aws13.Provider("storage-waf-us-east-1", {
20265
20528
  region: "us-east-1"
20266
20529
  });
20267
- const webAcl = new aws12.wafv2.WebAcl(
20268
- "wraps-storage-waf",
20530
+ const webAcl = new aws13.wafv2.WebAcl(
20531
+ "wraps-cdn-waf",
20269
20532
  {
20270
20533
  scope: "CLOUDFRONT",
20271
20534
  // WAF for CloudFront must use CLOUDFRONT scope
20272
- description: "Rate limiting protection for Wraps storage CDN",
20535
+ description: "Rate limiting protection for Wraps CDN",
20273
20536
  defaultAction: {
20274
20537
  allow: {}
20275
20538
  // Allow by default
@@ -20292,20 +20555,20 @@ async function createStorageWAF() {
20292
20555
  visibilityConfig: {
20293
20556
  sampledRequestsEnabled: true,
20294
20557
  cloudwatchMetricsEnabled: true,
20295
- metricName: "StorageRateLimitRule"
20558
+ metricName: "CdnRateLimitRule"
20296
20559
  }
20297
20560
  }
20298
20561
  ],
20299
20562
  visibilityConfig: {
20300
20563
  sampledRequestsEnabled: true,
20301
20564
  cloudwatchMetricsEnabled: true,
20302
- metricName: "wraps-storage-waf"
20565
+ metricName: "wraps-cdn-waf"
20303
20566
  },
20304
20567
  tags: {
20305
- Name: "wraps-storage-waf",
20568
+ Name: "wraps-cdn-waf",
20306
20569
  ManagedBy: "wraps-cli",
20307
- Service: "storage",
20308
- Description: "WAF for Wraps storage CDN with rate limiting"
20570
+ Service: "cdn",
20571
+ Description: "WAF for Wraps CDN with rate limiting"
20309
20572
  }
20310
20573
  },
20311
20574
  {
@@ -20314,11 +20577,11 @@ async function createStorageWAF() {
20314
20577
  );
20315
20578
  return webAcl;
20316
20579
  }
20317
- async function createStorageCDN(config2) {
20318
- const webAcl = config2.wafEnabled ? await createStorageWAF() : void 0;
20319
- const oac = new aws12.cloudfront.OriginAccessControl("wraps-storage-oac", {
20320
- name: "wraps-storage-oac",
20321
- description: "OAC for Wraps storage S3 bucket",
20580
+ async function createCdnDistribution(config2) {
20581
+ const webAcl = config2.wafEnabled ? await createCdnWAF() : void 0;
20582
+ const oac = new aws13.cloudfront.OriginAccessControl("wraps-cdn-oac", {
20583
+ name: "wraps-cdn-oac",
20584
+ description: "OAC for Wraps CDN S3 bucket",
20322
20585
  originAccessControlOriginType: "s3",
20323
20586
  signingBehavior: "always",
20324
20587
  signingProtocol: "sigv4"
@@ -20333,7 +20596,7 @@ async function createStorageCDN(config2) {
20333
20596
  };
20334
20597
  const originConfig = {
20335
20598
  domainName: config2.bucket.bucketRegionalDomainName,
20336
- originId: "s3-storage",
20599
+ originId: "s3-cdn",
20337
20600
  originAccessControlId: oac.id
20338
20601
  };
20339
20602
  if (config2.originShield) {
@@ -20351,9 +20614,9 @@ async function createStorageCDN(config2) {
20351
20614
  } : {
20352
20615
  restrictionType: "none"
20353
20616
  };
20354
- const distribution = new aws12.cloudfront.Distribution("wraps-storage-cdn", {
20617
+ const distribution = new aws13.cloudfront.Distribution("wraps-cdn-cdn", {
20355
20618
  enabled: true,
20356
- comment: "Wraps storage CDN",
20619
+ comment: "Wraps CDN",
20357
20620
  aliases,
20358
20621
  // Attach WAF Web ACL for rate limiting protection (only if enabled)
20359
20622
  webAclId: webAcl?.arn,
@@ -20361,7 +20624,7 @@ async function createStorageCDN(config2) {
20361
20624
  origins: [originConfig],
20362
20625
  // Default cache behavior for static assets
20363
20626
  defaultCacheBehavior: {
20364
- targetOriginId: "s3-storage",
20627
+ targetOriginId: "s3-cdn",
20365
20628
  viewerProtocolPolicy: "redirect-to-https",
20366
20629
  // Allow GET, HEAD, OPTIONS only (uploads go direct to S3 via presigned URLs)
20367
20630
  allowedMethods: ["GET", "HEAD", "OPTIONS"],
@@ -20389,12 +20652,12 @@ async function createStorageCDN(config2) {
20389
20652
  // SSL certificate
20390
20653
  viewerCertificate,
20391
20654
  tags: {
20392
- Name: "wraps-storage",
20655
+ Name: "wraps-cdn",
20393
20656
  ManagedBy: "wraps-cli",
20394
- Service: "storage"
20657
+ Service: "cdn"
20395
20658
  }
20396
20659
  });
20397
- new aws12.s3.BucketPolicy("wraps-storage-bucket-policy", {
20660
+ new aws13.s3.BucketPolicy("wraps-cdn-bucket-policy", {
20398
20661
  bucket: config2.bucket.id,
20399
20662
  policy: pulumi21.interpolate`{
20400
20663
  "Version": "2012-10-17",
@@ -20422,19 +20685,19 @@ async function createStorageCDN(config2) {
20422
20685
  webAcl
20423
20686
  };
20424
20687
  }
20425
- async function createStorageACMCertificate(config2) {
20426
- const usEast1Provider = new aws12.Provider("storage-acm-us-east-1", {
20688
+ async function createCdnACMCertificate(config2) {
20689
+ const usEast1Provider = new aws13.Provider("storage-acm-us-east-1", {
20427
20690
  region: "us-east-1"
20428
20691
  });
20429
- const certificate = new aws12.acm.Certificate(
20430
- "wraps-storage-cert",
20692
+ const certificate = new aws13.acm.Certificate(
20693
+ "wraps-cdn-cert",
20431
20694
  {
20432
20695
  domainName: config2.domain,
20433
20696
  validationMethod: "DNS",
20434
20697
  tags: {
20435
20698
  ManagedBy: "wraps-cli",
20436
- Service: "storage",
20437
- Description: "SSL certificate for Wraps storage CDN domain"
20699
+ Service: "cdn",
20700
+ Description: "SSL certificate for Wraps CDN domain"
20438
20701
  }
20439
20702
  },
20440
20703
  {
@@ -20450,8 +20713,8 @@ async function createStorageACMCertificate(config2) {
20450
20713
  );
20451
20714
  let certificateValidation;
20452
20715
  if (config2.hostedZoneId) {
20453
- const validationRecord = new aws12.route53.Record(
20454
- "wraps-storage-cert-validation",
20716
+ const validationRecord = new aws13.route53.Record(
20717
+ "wraps-cdn-cert-validation",
20455
20718
  {
20456
20719
  zoneId: config2.hostedZoneId,
20457
20720
  name: certificate.domainValidationOptions[0].resourceRecordName,
@@ -20460,8 +20723,8 @@ async function createStorageACMCertificate(config2) {
20460
20723
  ttl: 60
20461
20724
  }
20462
20725
  );
20463
- certificateValidation = new aws12.acm.CertificateValidation(
20464
- "wraps-storage-cert-validation-waiter",
20726
+ certificateValidation = new aws13.acm.CertificateValidation(
20727
+ "wraps-cdn-cert-validation-waiter",
20465
20728
  {
20466
20729
  certificateArn: certificate.arn,
20467
20730
  validationRecordFqdns: [validationRecord.fqdn]
@@ -20478,14 +20741,14 @@ async function createStorageACMCertificate(config2) {
20478
20741
  };
20479
20742
  }
20480
20743
 
20481
- // src/infrastructure/storage-stack.ts
20744
+ // src/infrastructure/cdn-stack.ts
20482
20745
  async function roleExists3(roleName) {
20483
20746
  try {
20484
20747
  const { IAMClient: IAMClient3, GetRoleCommand: GetRoleCommand2 } = await import("@aws-sdk/client-iam");
20485
- const iam7 = new IAMClient3({
20748
+ const iam8 = new IAMClient3({
20486
20749
  region: process.env.AWS_REGION || "us-east-1"
20487
20750
  });
20488
- await iam7.send(new GetRoleCommand2({ RoleName: roleName }));
20751
+ await iam8.send(new GetRoleCommand2({ RoleName: roleName }));
20489
20752
  return true;
20490
20753
  } catch (error) {
20491
20754
  if (error.name === "NoSuchEntityException" || error.Code === "NoSuchEntity" || error.Error?.Code === "NoSuchEntity") {
@@ -20495,7 +20758,7 @@ async function roleExists3(roleName) {
20495
20758
  return false;
20496
20759
  }
20497
20760
  }
20498
- async function createStorageIAMRole(config2) {
20761
+ async function createCdnIAMRole(config2) {
20499
20762
  let assumeRolePolicy;
20500
20763
  if (config2.provider === "vercel" && config2.oidcProvider) {
20501
20764
  assumeRolePolicy = pulumi22.interpolate`{
@@ -20530,28 +20793,28 @@ async function createStorageIAMRole(config2) {
20530
20793
  } else {
20531
20794
  throw new Error("Other providers not yet implemented");
20532
20795
  }
20533
- const roleName = "wraps-storage-role";
20796
+ const roleName = "wraps-cdn-role";
20534
20797
  const exists = await roleExists3(roleName);
20535
- const role = exists ? new aws13.iam.Role(
20798
+ const role = exists ? new aws14.iam.Role(
20536
20799
  roleName,
20537
20800
  {
20538
20801
  name: roleName,
20539
20802
  assumeRolePolicy,
20540
20803
  tags: {
20541
20804
  ManagedBy: "wraps-cli",
20542
- Service: "storage",
20805
+ Service: "cdn",
20543
20806
  Provider: config2.provider
20544
20807
  }
20545
20808
  },
20546
20809
  {
20547
20810
  import: roleName
20548
20811
  }
20549
- ) : new aws13.iam.Role(roleName, {
20812
+ ) : new aws14.iam.Role(roleName, {
20550
20813
  name: roleName,
20551
20814
  assumeRolePolicy,
20552
20815
  tags: {
20553
20816
  ManagedBy: "wraps-cli",
20554
- Service: "storage",
20817
+ Service: "cdn",
20555
20818
  Provider: config2.provider
20556
20819
  }
20557
20820
  });
@@ -20584,7 +20847,7 @@ async function createStorageIAMRole(config2) {
20584
20847
  Resource: config2.distributionArn
20585
20848
  });
20586
20849
  }
20587
- new aws13.iam.RolePolicy("wraps-storage-policy", {
20850
+ new aws14.iam.RolePolicy("wraps-cdn-policy", {
20588
20851
  role: role.name,
20589
20852
  policy: pulumi22.all([statements]).apply(
20590
20853
  ([stmts]) => JSON.stringify({
@@ -20595,7 +20858,7 @@ async function createStorageIAMRole(config2) {
20595
20858
  });
20596
20859
  return role;
20597
20860
  }
20598
- async function deployStorageStack(config2) {
20861
+ async function deployCdnStack(config2) {
20599
20862
  const accountId = config2.accountId;
20600
20863
  let oidcProvider;
20601
20864
  if (config2.provider === "vercel" && config2.vercel) {
@@ -20604,42 +20867,42 @@ async function deployStorageStack(config2) {
20604
20867
  accountId
20605
20868
  });
20606
20869
  }
20607
- const bucketResources = await createStorageBucket({
20870
+ const bucketResources = await createCdnBucket({
20608
20871
  accountId,
20609
20872
  region: config2.region,
20610
- storageConfig: config2.storageConfig
20873
+ cdnConfig: config2.cdnConfig
20611
20874
  });
20612
20875
  let acmResources;
20613
20876
  let hostedZone = null;
20614
- if (config2.storageConfig.cdn.customDomain) {
20615
- const domainParts = config2.storageConfig.cdn.customDomain.split(".");
20616
- const rootDomain = domainParts.length > 2 ? domainParts.slice(-2).join(".") : config2.storageConfig.cdn.customDomain;
20877
+ if (config2.cdnConfig.cdn.customDomain) {
20878
+ const domainParts = config2.cdnConfig.cdn.customDomain.split(".");
20879
+ const rootDomain = domainParts.length > 2 ? domainParts.slice(-2).join(".") : config2.cdnConfig.cdn.customDomain;
20617
20880
  const { findHostedZone: findHostedZone2 } = await Promise.resolve().then(() => (init_route53(), route53_exports));
20618
20881
  hostedZone = await findHostedZone2(rootDomain, config2.region);
20619
- acmResources = await createStorageACMCertificate({
20620
- domain: config2.storageConfig.cdn.customDomain,
20882
+ acmResources = await createCdnACMCertificate({
20883
+ domain: config2.cdnConfig.cdn.customDomain,
20621
20884
  hostedZoneId: hostedZone?.id
20622
20885
  });
20623
20886
  }
20624
20887
  let cdnResources;
20625
- if (config2.storageConfig.cdn.enabled) {
20888
+ if (config2.cdnConfig.cdn.enabled) {
20626
20889
  const hasAutoValidation2 = acmResources?.certificateValidation;
20627
20890
  const useCertFromUpgrade = config2.certValidated && config2.existingCertArn;
20628
20891
  const certificateArn = useCertFromUpgrade ? pulumi22.output(config2.existingCertArn) : hasAutoValidation2 ? acmResources?.certificateValidation?.certificateArn : void 0;
20629
- const customDomainForCdn = useCertFromUpgrade || hasAutoValidation2 ? config2.storageConfig.cdn.customDomain : void 0;
20630
- cdnResources = await createStorageCDN({
20892
+ const customDomainForCdn = useCertFromUpgrade || hasAutoValidation2 ? config2.cdnConfig.cdn.customDomain : void 0;
20893
+ cdnResources = await createCdnDistribution({
20631
20894
  bucket: bucketResources.bucket,
20632
20895
  bucketRegion: config2.region,
20633
20896
  // For Origin Shield
20634
20897
  customDomain: customDomainForCdn,
20635
20898
  certificateArn,
20636
- priceClass: config2.storageConfig.cdn.priceClass,
20637
- originShield: config2.storageConfig.cdn.originShield,
20638
- geoRestriction: config2.storageConfig.cdn.geoRestriction,
20639
- wafEnabled: config2.storageConfig.cdn.wafEnabled
20899
+ priceClass: config2.cdnConfig.cdn.priceClass,
20900
+ originShield: config2.cdnConfig.cdn.originShield,
20901
+ geoRestriction: config2.cdnConfig.cdn.geoRestriction,
20902
+ wafEnabled: config2.cdnConfig.cdn.wafEnabled
20640
20903
  });
20641
20904
  }
20642
- const role = await createStorageIAMRole({
20905
+ const role = await createCdnIAMRole({
20643
20906
  provider: config2.provider,
20644
20907
  oidcProvider,
20645
20908
  vercelTeamSlug: config2.vercel?.teamSlug,
@@ -20649,8 +20912,8 @@ async function deployStorageStack(config2) {
20649
20912
  });
20650
20913
  const hasAutoValidation = acmResources?.certificateValidation;
20651
20914
  const hasCertFromUpgrade = config2.certValidated && config2.existingCertArn;
20652
- const customDomainActive = config2.storageConfig.cdn.customDomain && (hasAutoValidation || hasCertFromUpgrade);
20653
- const customDomainPending = config2.storageConfig.cdn.customDomain && !hasAutoValidation && !hasCertFromUpgrade;
20915
+ const customDomainActive = config2.cdnConfig.cdn.customDomain && (hasAutoValidation || hasCertFromUpgrade);
20916
+ const customDomainPending = config2.cdnConfig.cdn.customDomain && !hasAutoValidation && !hasCertFromUpgrade;
20654
20917
  return {
20655
20918
  roleArn: role.arn,
20656
20919
  bucketName: bucketResources.bucketName,
@@ -20659,17 +20922,17 @@ async function deployStorageStack(config2) {
20659
20922
  distributionId: cdnResources?.distributionId,
20660
20923
  distributionDomain: cdnResources?.domainName,
20661
20924
  // Report custom domain if it's actually configured on CloudFront (auto or manual validation)
20662
- customDomain: customDomainActive ? config2.storageConfig.cdn.customDomain : void 0,
20925
+ customDomain: customDomainActive ? config2.cdnConfig.cdn.customDomain : void 0,
20663
20926
  // Report pending custom domain that needs manual cert validation
20664
- customDomainPending: customDomainPending ? config2.storageConfig.cdn.customDomain : void 0,
20927
+ customDomainPending: customDomainPending ? config2.cdnConfig.cdn.customDomain : void 0,
20665
20928
  acmCertificateArn: acmResources?.certificate.arn,
20666
20929
  acmCertificateValidationRecords: acmResources?.validationRecords,
20667
- versioning: config2.storageConfig.versioning ?? false,
20668
- retention: config2.storageConfig.retention
20930
+ versioning: config2.cdnConfig.versioning ?? false,
20931
+ retention: config2.cdnConfig.retention
20669
20932
  };
20670
20933
  }
20671
20934
 
20672
- // src/commands/storage/init.ts
20935
+ // src/commands/cdn/init.ts
20673
20936
  init_events();
20674
20937
  init_aws();
20675
20938
  init_errors();
@@ -20677,7 +20940,7 @@ init_fs();
20677
20940
  init_metadata();
20678
20941
  init_prompts();
20679
20942
 
20680
- // src/utils/storage/costs.ts
20943
+ // src/utils/cdn/costs.ts
20681
20944
  init_esm_shims();
20682
20945
  var PRICING = {
20683
20946
  // S3 Storage (per GB/month)
@@ -20763,7 +21026,7 @@ function getCostSummary2(config2, estimatedStorageGB = 10, estimatedBandwidthGB
20763
21026
  return lines.join("\n");
20764
21027
  }
20765
21028
 
20766
- // src/utils/storage/presets.ts
21029
+ // src/utils/cdn/presets.ts
20767
21030
  init_esm_shims();
20768
21031
  var STARTER_PRESET2 = {
20769
21032
  cdn: {
@@ -20878,13 +21141,13 @@ function validateConfig2(config2) {
20878
21141
  return warnings;
20879
21142
  }
20880
21143
 
20881
- // src/commands/storage/init.ts
20882
- async function promptStoragePreset() {
21144
+ // src/commands/cdn/init.ts
21145
+ async function promptCdnPreset() {
20883
21146
  const starterInfo = getPresetInfo2("starter");
20884
21147
  const productionInfo = getPresetInfo2("production");
20885
21148
  const customInfo = getPresetInfo2("custom");
20886
21149
  const result = await clack29.select({
20887
- message: "Select a storage configuration preset:",
21150
+ message: "Select a CDN configuration preset:",
20888
21151
  options: [
20889
21152
  {
20890
21153
  value: "production",
@@ -20909,7 +21172,7 @@ async function promptStoragePreset() {
20909
21172
  }
20910
21173
  return result;
20911
21174
  }
20912
- async function promptCustomStorageConfig() {
21175
+ async function promptCustomCdnConfig() {
20913
21176
  const cdnEnabled = await clack29.confirm({
20914
21177
  message: "Enable CloudFront CDN for fast global delivery?",
20915
21178
  initialValue: true
@@ -21104,7 +21367,7 @@ async function init3(options) {
21104
21367
  const startTime = Date.now();
21105
21368
  clack29.intro(
21106
21369
  pc31.bold(
21107
- options.preview ? "Wraps Storage Infrastructure Preview" : "Wraps Storage Infrastructure Setup"
21370
+ options.preview ? "Wraps CDN Infrastructure Preview" : "Wraps CDN Infrastructure Setup"
21108
21371
  )
21109
21372
  );
21110
21373
  const progress = new DeploymentProgress();
@@ -21137,45 +21400,45 @@ async function init3(options) {
21137
21400
  identity.accountId,
21138
21401
  region
21139
21402
  );
21140
- if (existingConnection && hasService(existingConnection, "storage")) {
21403
+ if (existingConnection && hasService(existingConnection, "cdn")) {
21141
21404
  clack29.log.warn(
21142
- `Storage service already exists for account ${pc31.cyan(identity.accountId)} in region ${pc31.cyan(region)}`
21405
+ `CDN service already exists for account ${pc31.cyan(identity.accountId)} in region ${pc31.cyan(region)}`
21143
21406
  );
21144
21407
  clack29.log.info(
21145
- `Created: ${existingConnection.services.storage?.deployedAt}`
21408
+ `Created: ${existingConnection.services.cdn?.deployedAt}`
21146
21409
  );
21147
21410
  clack29.log.info(
21148
- `Use ${pc31.cyan("wraps storage status")} to view current setup`
21411
+ `Use ${pc31.cyan("wraps cdn status")} to view current setup`
21149
21412
  );
21150
21413
  process.exit(0);
21151
21414
  }
21152
21415
  let preset = options.preset;
21153
21416
  if (!preset) {
21154
- preset = await promptStoragePreset();
21417
+ preset = await promptCdnPreset();
21155
21418
  }
21156
- let storageConfig;
21419
+ let cdnConfig;
21157
21420
  if (preset === "custom") {
21158
- storageConfig = await promptCustomStorageConfig();
21421
+ cdnConfig = await promptCustomCdnConfig();
21159
21422
  } else {
21160
- storageConfig = getPreset2(preset);
21423
+ cdnConfig = getPreset2(preset);
21161
21424
  }
21162
21425
  let customDomain = options.domain;
21163
- if (!customDomain && storageConfig.cdn.enabled) {
21426
+ if (!customDomain && cdnConfig.cdn.enabled) {
21164
21427
  customDomain = await promptCustomDomain();
21165
21428
  }
21166
21429
  if (customDomain) {
21167
- storageConfig.cdn.customDomain = customDomain;
21430
+ cdnConfig.cdn.customDomain = customDomain;
21168
21431
  }
21169
21432
  const estimatedUsage = await promptEstimatedUsage();
21170
21433
  progress.info(`
21171
21434
  ${pc31.bold("Cost Estimate:")}`);
21172
21435
  const costSummary = getCostSummary2(
21173
- storageConfig,
21436
+ cdnConfig,
21174
21437
  estimatedUsage.storageGB,
21175
21438
  estimatedUsage.bandwidthGB
21176
21439
  );
21177
21440
  clack29.log.info(costSummary);
21178
- const warnings = validateConfig2(storageConfig);
21441
+ const warnings = validateConfig2(cdnConfig);
21179
21442
  if (warnings.length > 0) {
21180
21443
  progress.info(`
21181
21444
  ${pc31.yellow(pc31.bold("Configuration Notes:"))}`);
@@ -21187,8 +21450,8 @@ ${pc31.yellow(pc31.bold("Configuration Notes:"))}`);
21187
21450
  identity.accountId,
21188
21451
  region,
21189
21452
  provider,
21190
- "storage",
21191
- storageConfig,
21453
+ "cdn",
21454
+ cdnConfig,
21192
21455
  preset === "custom" ? void 0 : preset,
21193
21456
  existingConnection || void 0
21194
21457
  );
@@ -21207,7 +21470,7 @@ ${pc31.yellow(pc31.bold("Configuration Notes:"))}`);
21207
21470
  region,
21208
21471
  accountId: identity.accountId,
21209
21472
  vercel: vercelConfig,
21210
- storageConfig
21473
+ cdnConfig
21211
21474
  };
21212
21475
  if (options.preview) {
21213
21476
  try {
@@ -21217,10 +21480,10 @@ ${pc31.yellow(pc31.bold("Configuration Notes:"))}`);
21217
21480
  await ensurePulumiWorkDir();
21218
21481
  const stack = await pulumi23.automation.LocalWorkspace.createOrSelectStack(
21219
21482
  {
21220
- stackName: `wraps-storage-${identity.accountId}-${region}`,
21221
- projectName: "wraps-storage",
21483
+ stackName: `wraps-cdn-${identity.accountId}-${region}`,
21484
+ projectName: "wraps-cdn",
21222
21485
  program: async () => {
21223
- const result2 = await deployStorageStack(stackConfig);
21486
+ const result2 = await deployCdnStack(stackConfig);
21224
21487
  return {
21225
21488
  roleArn: result2.roleArn,
21226
21489
  bucketName: result2.bucketName,
@@ -21252,12 +21515,12 @@ ${pc31.yellow(pc31.bold("Configuration Notes:"))}`);
21252
21515
  changeSummary: previewResult.changeSummary,
21253
21516
  resourceChanges: previewResult.resourceChanges,
21254
21517
  costEstimate: costSummary,
21255
- commandName: "wraps storage init"
21518
+ commandName: "wraps cdn init"
21256
21519
  });
21257
21520
  clack29.outro(
21258
21521
  pc31.green("Preview complete. Run without --preview to deploy.")
21259
21522
  );
21260
- trackServiceInit("storage", true, {
21523
+ trackServiceInit("cdn", true, {
21261
21524
  preset,
21262
21525
  provider,
21263
21526
  region,
@@ -21276,15 +21539,15 @@ ${pc31.yellow(pc31.bold("Configuration Notes:"))}`);
21276
21539
  let outputs;
21277
21540
  try {
21278
21541
  outputs = await progress.execute(
21279
- "Deploying storage infrastructure (this may take 2-3 minutes)",
21542
+ "Deploying CDN infrastructure (this may take 2-3 minutes)",
21280
21543
  async () => {
21281
21544
  await ensurePulumiWorkDir();
21282
21545
  const stack = await pulumi23.automation.LocalWorkspace.createOrSelectStack(
21283
21546
  {
21284
- stackName: `wraps-storage-${identity.accountId}-${region}`,
21285
- projectName: "wraps-storage",
21547
+ stackName: `wraps-cdn-${identity.accountId}-${region}`,
21548
+ projectName: "wraps-cdn",
21286
21549
  program: async () => {
21287
- const result = await deployStorageStack(stackConfig);
21550
+ const result = await deployCdnStack(stackConfig);
21288
21551
  return {
21289
21552
  roleArn: result.roleArn,
21290
21553
  bucketName: result.bucketName,
@@ -21311,7 +21574,7 @@ ${pc31.yellow(pc31.bold("Configuration Notes:"))}`);
21311
21574
  }
21312
21575
  );
21313
21576
  await stack.workspace.selectStack(
21314
- `wraps-storage-${identity.accountId}-${region}`
21577
+ `wraps-cdn-${identity.accountId}-${region}`
21315
21578
  );
21316
21579
  await stack.setConfig("aws:region", { value: region });
21317
21580
  const upResult = await stack.up({ onOutput: () => {
@@ -21334,7 +21597,7 @@ ${pc31.yellow(pc31.bold("Configuration Notes:"))}`);
21334
21597
  }
21335
21598
  );
21336
21599
  } catch (error) {
21337
- trackServiceInit("storage", false, {
21600
+ trackServiceInit("cdn", false, {
21338
21601
  preset,
21339
21602
  provider,
21340
21603
  region,
@@ -21347,8 +21610,8 @@ ${pc31.yellow(pc31.bold("Configuration Notes:"))}`);
21347
21610
  trackError("DEPLOYMENT_FAILED", "storage:init", { step: "deploy" });
21348
21611
  throw new Error(`Pulumi deployment failed: ${error.message}`);
21349
21612
  }
21350
- if (metadata.services.storage) {
21351
- metadata.services.storage.pulumiStackName = `wraps-storage-${identity.accountId}-${region}`;
21613
+ if (metadata.services.cdn) {
21614
+ metadata.services.cdn.pulumiStackName = `wraps-cdn-${identity.accountId}-${region}`;
21352
21615
  }
21353
21616
  await saveConnectionMetadata(metadata);
21354
21617
  progress.info("Connection metadata saved");
@@ -21386,12 +21649,12 @@ ${pc31.yellow(pc31.bold("Configuration Notes:"))}`);
21386
21649
  );
21387
21650
  const dnsRecords = [];
21388
21651
  const existingValue = existingCname?.ResourceRecords?.[0]?.Value || null;
21389
- let cdnStatus = "new";
21652
+ let cdnStatus2 = "new";
21390
21653
  let cdnConflictReason;
21391
21654
  if (existingValue === outputs.distributionDomain) {
21392
- cdnStatus = "no_change";
21655
+ cdnStatus2 = "no_change";
21393
21656
  } else if (existingValue) {
21394
- cdnStatus = "conflict";
21657
+ cdnStatus2 = "conflict";
21395
21658
  cdnConflictReason = `Currently points to ${existingValue}`;
21396
21659
  }
21397
21660
  dnsRecords.push({
@@ -21399,7 +21662,7 @@ ${pc31.yellow(pc31.bold("Configuration Notes:"))}`);
21399
21662
  type: "CNAME",
21400
21663
  proposedValue: outputs.distributionDomain,
21401
21664
  existingValue,
21402
- status: cdnStatus,
21665
+ status: cdnStatus2,
21403
21666
  conflictReason: cdnConflictReason
21404
21667
  });
21405
21668
  console.log();
@@ -21479,7 +21742,7 @@ ${pc31.yellow(pc31.bold("Configuration Notes:"))}`);
21479
21742
  }
21480
21743
  }
21481
21744
  }
21482
- displayStorageSuccess({
21745
+ displayCdnSuccess({
21483
21746
  bucketName: outputs.bucketName,
21484
21747
  region: outputs.region,
21485
21748
  distributionDomain: outputs.distributionDomain,
@@ -21494,31 +21757,31 @@ ${pc31.yellow(pc31.bold("Configuration Notes:"))}`);
21494
21757
  });
21495
21758
  const duration = Date.now() - startTime;
21496
21759
  const enabledFeatures = [];
21497
- if (storageConfig.cdn.enabled) {
21760
+ if (cdnConfig.cdn.enabled) {
21498
21761
  enabledFeatures.push("cdn");
21499
21762
  }
21500
- if (storageConfig.cdn.customDomain) {
21763
+ if (cdnConfig.cdn.customDomain) {
21501
21764
  enabledFeatures.push("custom_domain");
21502
21765
  }
21503
- if (storageConfig.versioning) {
21766
+ if (cdnConfig.versioning) {
21504
21767
  enabledFeatures.push("versioning");
21505
21768
  }
21506
- trackServiceInit("storage", true, {
21769
+ trackServiceInit("cdn", true, {
21507
21770
  preset,
21508
21771
  provider,
21509
21772
  region,
21510
21773
  features: enabledFeatures,
21511
21774
  duration_ms: duration
21512
21775
  });
21513
- trackServiceDeployed("storage", {
21776
+ trackServiceDeployed("cdn", {
21514
21777
  duration_ms: duration,
21515
21778
  region,
21516
21779
  features: enabledFeatures,
21517
21780
  preset
21518
21781
  });
21519
21782
  }
21520
- function displayStorageSuccess(options) {
21521
- clack29.log.success(pc31.green(pc31.bold("Storage infrastructure deployed!")));
21783
+ function displayCdnSuccess(options) {
21784
+ clack29.log.success(pc31.green(pc31.bold("CDN infrastructure deployed!")));
21522
21785
  clack29.log.info(`
21523
21786
  ${pc31.bold("Infrastructure:")}`);
21524
21787
  clack29.log.info(` S3 Bucket: ${pc31.cyan(options.bucketName)}`);
@@ -21558,7 +21821,7 @@ ${pc31.yellow(pc31.bold("DNS Records Required:"))}`);
21558
21821
  `);
21559
21822
  }
21560
21823
  clack29.log.warn(
21561
- "Custom domain requires manual setup:\n 1. Add the SSL certificate validation DNS record above\n 2. Wait for certificate to be validated (check AWS ACM console)\n 3. Run 'wraps storage upgrade' to add custom domain to CloudFront\n 4. Add the CDN CNAME record to point your domain to CloudFront"
21824
+ "Custom domain requires manual setup:\n 1. Add the SSL certificate validation DNS record above\n 2. Wait for certificate to be validated (check AWS ACM console)\n 3. Run 'wraps cdn upgrade' to add custom domain to CloudFront\n 4. Add the CDN CNAME record to point your domain to CloudFront"
21562
21825
  );
21563
21826
  } else if (options.customDomain && !options.dnsAutoCreated && options.distributionDomain) {
21564
21827
  clack29.log.info(`
@@ -21572,19 +21835,19 @@ ${pc31.yellow(pc31.bold("DNS Record Required:"))}`);
21572
21835
  clack29.log.info(`
21573
21836
  ${pc31.bold("Next Steps:")}`);
21574
21837
  clack29.log.info(
21575
- ` 1. ${pc31.cyan("wraps storage status")} - View your storage setup`
21838
+ ` 1. ${pc31.cyan("wraps cdn status")} - View your CDN setup`
21576
21839
  );
21577
21840
  if (options.customDomainPending) {
21578
21841
  clack29.log.info(" 2. Add DNS records above and validate SSL certificate");
21579
21842
  clack29.log.info(
21580
- ` 3. ${pc31.cyan("wraps storage upgrade")} - Add custom domain to CloudFront`
21843
+ ` 3. ${pc31.cyan("wraps cdn upgrade")} - Add custom domain to CloudFront`
21581
21844
  );
21582
21845
  } else if (options.customDomain && !options.dnsAutoCreated) {
21583
21846
  clack29.log.info(
21584
21847
  " 2. Add DNS record above to point your domain to CloudFront"
21585
21848
  );
21586
21849
  clack29.log.info(
21587
- ` 3. ${pc31.cyan("wraps storage verify")} - Check DNS propagation`
21850
+ ` 3. ${pc31.cyan("wraps cdn verify")} - Check DNS propagation`
21588
21851
  );
21589
21852
  }
21590
21853
  const cdnUrl = options.customDomain ? `https://${options.customDomain}` : options.distributionDomain ? `https://${options.distributionDomain}` : null;
@@ -21596,15 +21859,15 @@ ${pc31.bold("CDN URL:")}`);
21596
21859
  if (options.customDomainPending) {
21597
21860
  clack29.outro(
21598
21861
  pc31.yellow(
21599
- "Storage deployed! Custom domain pending certificate validation."
21862
+ "CDN deployed! Custom domain pending certificate validation."
21600
21863
  )
21601
21864
  );
21602
21865
  } else {
21603
- clack29.outro(pc31.green("Storage is ready!"));
21866
+ clack29.outro(pc31.green("CDN is ready!"));
21604
21867
  }
21605
21868
  }
21606
21869
 
21607
- // src/commands/storage/status.ts
21870
+ // src/commands/cdn/status.ts
21608
21871
  init_esm_shims();
21609
21872
  init_client();
21610
21873
  init_events();
@@ -21614,26 +21877,26 @@ init_metadata();
21614
21877
  import * as clack30 from "@clack/prompts";
21615
21878
  import * as pulumi24 from "@pulumi/pulumi";
21616
21879
  import pc32 from "picocolors";
21617
- async function storageStatus(options) {
21880
+ async function cdnStatus(options) {
21618
21881
  const startTime = Date.now();
21619
21882
  const progress = new DeploymentProgress();
21620
- clack30.intro(pc32.bold("Wraps Storage Status"));
21883
+ clack30.intro(pc32.bold("Wraps CDN Status"));
21621
21884
  const identity = await progress.execute(
21622
- "Loading storage infrastructure status",
21885
+ "Loading CDN infrastructure status",
21623
21886
  async () => validateAWSCredentials()
21624
21887
  );
21625
21888
  let region = options.region || await getAWSRegion();
21626
21889
  if (!(options.region || process.env.AWS_REGION || process.env.AWS_DEFAULT_REGION)) {
21627
- const storageConnections = await findConnectionsWithService(
21890
+ const cdnConnections = await findConnectionsWithService(
21628
21891
  identity.accountId,
21629
- "storage"
21892
+ "cdn"
21630
21893
  );
21631
- if (storageConnections.length === 1) {
21632
- region = storageConnections[0].region;
21633
- } else if (storageConnections.length > 1) {
21894
+ if (cdnConnections.length === 1) {
21895
+ region = cdnConnections[0].region;
21896
+ } else if (cdnConnections.length > 1) {
21634
21897
  const selectedRegion = await clack30.select({
21635
- message: "Multiple storage deployments found. Which region?",
21636
- options: storageConnections.map((conn) => ({
21898
+ message: "Multiple CDN deployments found. Which region?",
21899
+ options: cdnConnections.map((conn) => ({
21637
21900
  value: conn.region,
21638
21901
  label: conn.region
21639
21902
  }))
@@ -21649,22 +21912,22 @@ async function storageStatus(options) {
21649
21912
  try {
21650
21913
  await ensurePulumiWorkDir();
21651
21914
  const stack = await pulumi24.automation.LocalWorkspace.selectStack({
21652
- stackName: `wraps-storage-${identity.accountId}-${region}`,
21915
+ stackName: `wraps-cdn-${identity.accountId}-${region}`,
21653
21916
  workDir: getPulumiWorkDir()
21654
21917
  });
21655
21918
  stackOutputs = await stack.outputs();
21656
21919
  } catch (_error) {
21657
21920
  progress.stop();
21658
- clack30.log.error("No storage infrastructure found");
21921
+ clack30.log.error("No CDN infrastructure found");
21659
21922
  console.log(
21660
21923
  `
21661
- Run ${pc32.cyan("wraps storage init")} to deploy storage infrastructure.
21924
+ Run ${pc32.cyan("wraps cdn init")} to deploy CDN infrastructure.
21662
21925
  `
21663
21926
  );
21664
21927
  process.exit(1);
21665
21928
  }
21666
21929
  progress.stop();
21667
- displayStorageStatus({
21930
+ displayCdnStatus({
21668
21931
  bucketName: stackOutputs.bucketName?.value,
21669
21932
  region: stackOutputs.region?.value || region,
21670
21933
  distributionId: stackOutputs.distributionId?.value,
@@ -21686,9 +21949,9 @@ Run ${pc32.cyan("wraps storage init")} to deploy storage infrastructure.
21686
21949
  });
21687
21950
  getTelemetryClient().showFooterOnce();
21688
21951
  }
21689
- function displayStorageStatus(options) {
21952
+ function displayCdnStatus(options) {
21690
21953
  clack30.log.info(`
21691
- ${pc32.bold("Storage Infrastructure:")}`);
21954
+ ${pc32.bold("CDN Infrastructure:")}`);
21692
21955
  clack30.log.info(` S3 Bucket: ${pc32.cyan(options.bucketName)}`);
21693
21956
  clack30.log.info(` Region: ${pc32.cyan(options.region)}`);
21694
21957
  clack30.log.info(
@@ -21755,19 +22018,19 @@ ${pc32.bold("File URLs:")}`);
21755
22018
  ${pc32.bold("Commands:")}`);
21756
22019
  if (hasPendingCert) {
21757
22020
  clack30.log.info(
21758
- ` ${pc32.cyan("wraps storage upgrade")} - Add custom domain after cert validation`
22021
+ ` ${pc32.cyan("wraps cdn upgrade")} - Add custom domain after cert validation`
21759
22022
  );
21760
22023
  }
21761
22024
  clack30.log.info(
21762
- ` ${pc32.cyan("wraps storage verify")} - Check DNS and certificate status`
22025
+ ` ${pc32.cyan("wraps cdn verify")} - Check DNS and certificate status`
21763
22026
  );
21764
22027
  clack30.log.info(
21765
- ` ${pc32.cyan("wraps storage destroy")} - Remove storage infrastructure`
22028
+ ` ${pc32.cyan("wraps cdn destroy")} - Remove CDN infrastructure`
21766
22029
  );
21767
22030
  clack30.outro("");
21768
22031
  }
21769
22032
 
21770
- // src/commands/storage/sync.ts
22033
+ // src/commands/cdn/sync.ts
21771
22034
  init_esm_shims();
21772
22035
  import * as clack31 from "@clack/prompts";
21773
22036
  import * as pulumi25 from "@pulumi/pulumi";
@@ -21777,26 +22040,26 @@ init_events();
21777
22040
  init_aws();
21778
22041
  init_fs();
21779
22042
  init_metadata();
21780
- async function storageSync(options) {
22043
+ async function cdnSync(options) {
21781
22044
  const startTime = Date.now();
21782
22045
  const progress = new DeploymentProgress();
21783
- clack31.intro(pc33.bold("Wraps Storage Sync"));
22046
+ clack31.intro(pc33.bold("Wraps CDN Sync"));
21784
22047
  const identity = await progress.execute(
21785
22048
  "Validating AWS credentials",
21786
22049
  async () => validateAWSCredentials()
21787
22050
  );
21788
22051
  let region = options.region || await getAWSRegion();
21789
22052
  if (!(options.region || process.env.AWS_REGION || process.env.AWS_DEFAULT_REGION)) {
21790
- const storageConnections = await findConnectionsWithService(
22053
+ const cdnConnections = await findConnectionsWithService(
21791
22054
  identity.accountId,
21792
- "storage"
22055
+ "cdn"
21793
22056
  );
21794
- if (storageConnections.length === 1) {
21795
- region = storageConnections[0].region;
21796
- } else if (storageConnections.length > 1) {
22057
+ if (cdnConnections.length === 1) {
22058
+ region = cdnConnections[0].region;
22059
+ } else if (cdnConnections.length > 1) {
21797
22060
  const selectedRegion = await clack31.select({
21798
- message: "Multiple storage deployments found. Which region?",
21799
- options: storageConnections.map((conn) => ({
22061
+ message: "Multiple CDN deployments found. Which region?",
22062
+ options: cdnConnections.map((conn) => ({
21800
22063
  value: conn.region,
21801
22064
  label: conn.region
21802
22065
  }))
@@ -21809,25 +22072,25 @@ async function storageSync(options) {
21809
22072
  }
21810
22073
  }
21811
22074
  const metadata = await loadConnectionMetadata(identity.accountId, region);
21812
- if (!metadata?.services.storage) {
22075
+ if (!metadata?.services.cdn) {
21813
22076
  clack31.log.error(
21814
- `No storage infrastructure found for account ${pc33.cyan(identity.accountId)} in region ${pc33.cyan(region)}`
22077
+ `No CDN infrastructure found for account ${pc33.cyan(identity.accountId)} in region ${pc33.cyan(region)}`
21815
22078
  );
21816
22079
  clack31.log.info(
21817
- `Use ${pc33.cyan("wraps storage init")} to deploy storage infrastructure.`
22080
+ `Use ${pc33.cyan("wraps cdn init")} to deploy CDN infrastructure.`
21818
22081
  );
21819
22082
  process.exit(1);
21820
22083
  }
21821
- const storageService = metadata.services.storage;
21822
- const storageConfig = storageService.config;
22084
+ const cdnService = metadata.services.cdn;
22085
+ const cdnConfig = cdnService.config;
21823
22086
  progress.info(`Found storage deployment in ${pc33.cyan(region)}`);
21824
22087
  let certValidated = false;
21825
22088
  let existingCertArn;
21826
- if (storageConfig.cdn.customDomain) {
22089
+ if (cdnConfig.cdn.customDomain) {
21827
22090
  try {
21828
22091
  await ensurePulumiWorkDir();
21829
22092
  const checkStack = await pulumi25.automation.LocalWorkspace.selectStack({
21830
- stackName: `wraps-storage-${identity.accountId}-${region}`,
22093
+ stackName: `wraps-cdn-${identity.accountId}-${region}`,
21831
22094
  workDir: getPulumiWorkDir()
21832
22095
  });
21833
22096
  const stackOutputs = await checkStack.outputs();
@@ -21843,7 +22106,7 @@ async function storageSync(options) {
21843
22106
  certValidated = true;
21844
22107
  existingCertArn = stackOutputs.acmCertificateArn.value;
21845
22108
  progress.info(
21846
- `Certificate validated for ${pc33.cyan(storageConfig.cdn.customDomain)}`
22109
+ `Certificate validated for ${pc33.cyan(cdnConfig.cdn.customDomain)}`
21847
22110
  );
21848
22111
  }
21849
22112
  }
@@ -21855,20 +22118,20 @@ async function storageSync(options) {
21855
22118
  region,
21856
22119
  accountId: identity.accountId,
21857
22120
  vercel: metadata.vercel,
21858
- storageConfig,
22121
+ cdnConfig,
21859
22122
  // Pass cert validation flags if cert is validated externally
21860
22123
  certValidated,
21861
22124
  existingCertArn
21862
22125
  };
21863
22126
  try {
21864
- await progress.execute("Syncing storage infrastructure", async () => {
22127
+ await progress.execute("Syncing CDN infrastructure", async () => {
21865
22128
  await ensurePulumiWorkDir();
21866
22129
  const stack = await pulumi25.automation.LocalWorkspace.createOrSelectStack(
21867
22130
  {
21868
- stackName: `wraps-storage-${identity.accountId}-${region}`,
21869
- projectName: "wraps-storage",
22131
+ stackName: `wraps-cdn-${identity.accountId}-${region}`,
22132
+ projectName: "wraps-cdn",
21870
22133
  program: async () => {
21871
- const result2 = await deployStorageStack(stackConfig);
22134
+ const result2 = await deployCdnStack(stackConfig);
21872
22135
  return {
21873
22136
  roleArn: result2.roleArn,
21874
22137
  bucketName: result2.bucketName,
@@ -21911,7 +22174,7 @@ async function storageSync(options) {
21911
22174
  clack31.log.error(`Sync failed: ${error.message}`);
21912
22175
  process.exit(1);
21913
22176
  }
21914
- clack31.log.success(pc33.green("Storage infrastructure synced!"));
22177
+ clack31.log.success(pc33.green("CDN infrastructure synced!"));
21915
22178
  trackCommand("storage:sync", {
21916
22179
  success: true,
21917
22180
  region,
@@ -21921,7 +22184,7 @@ async function storageSync(options) {
21921
22184
  clack31.outro("");
21922
22185
  }
21923
22186
 
21924
- // src/commands/storage/upgrade.ts
22187
+ // src/commands/cdn/upgrade.ts
21925
22188
  init_esm_shims();
21926
22189
  import * as clack32 from "@clack/prompts";
21927
22190
  import * as pulumi26 from "@pulumi/pulumi";
@@ -21931,12 +22194,12 @@ init_events();
21931
22194
  init_aws();
21932
22195
  init_fs();
21933
22196
  init_metadata();
21934
- async function storageUpgrade(options) {
22197
+ async function cdnUpgrade(options) {
21935
22198
  const startTime = Date.now();
21936
22199
  const progress = new DeploymentProgress();
21937
22200
  clack32.intro(
21938
22201
  pc34.bold(
21939
- options.preview ? "Wraps Storage Upgrade Preview" : "Wraps Storage Upgrade"
22202
+ options.preview ? "Wraps CDN Upgrade Preview" : "Wraps CDN Upgrade"
21940
22203
  )
21941
22204
  );
21942
22205
  const identity = await progress.execute(
@@ -21946,16 +22209,16 @@ async function storageUpgrade(options) {
21946
22209
  progress.info(`Connected to AWS account: ${pc34.cyan(identity.accountId)}`);
21947
22210
  let region = options.region || await getAWSRegion();
21948
22211
  if (!(options.region || process.env.AWS_REGION || process.env.AWS_DEFAULT_REGION)) {
21949
- const storageConnections = await findConnectionsWithService(
22212
+ const cdnConnections = await findConnectionsWithService(
21950
22213
  identity.accountId,
21951
- "storage"
22214
+ "cdn"
21952
22215
  );
21953
- if (storageConnections.length === 1) {
21954
- region = storageConnections[0].region;
21955
- } else if (storageConnections.length > 1) {
22216
+ if (cdnConnections.length === 1) {
22217
+ region = cdnConnections[0].region;
22218
+ } else if (cdnConnections.length > 1) {
21956
22219
  const selectedRegion = await clack32.select({
21957
- message: "Multiple storage deployments found. Which region?",
21958
- options: storageConnections.map((conn) => ({
22220
+ message: "Multiple CDN deployments found. Which region?",
22221
+ options: cdnConnections.map((conn) => ({
21959
22222
  value: conn.region,
21960
22223
  label: conn.region
21961
22224
  }))
@@ -21968,22 +22231,22 @@ async function storageUpgrade(options) {
21968
22231
  }
21969
22232
  }
21970
22233
  const metadata = await loadConnectionMetadata(identity.accountId, region);
21971
- if (!metadata?.services.storage) {
22234
+ if (!metadata?.services.cdn) {
21972
22235
  clack32.log.error(
21973
- `No storage infrastructure found for account ${pc34.cyan(identity.accountId)} in region ${pc34.cyan(region)}`
22236
+ `No CDN infrastructure found for account ${pc34.cyan(identity.accountId)} in region ${pc34.cyan(region)}`
21974
22237
  );
21975
22238
  clack32.log.info(
21976
- `Use ${pc34.cyan("wraps storage init")} to deploy storage infrastructure.`
22239
+ `Use ${pc34.cyan("wraps cdn init")} to deploy CDN infrastructure.`
21977
22240
  );
21978
22241
  process.exit(1);
21979
22242
  }
21980
- const storageService = metadata.services.storage;
21981
- const storageConfig = storageService.config;
22243
+ const cdnService = metadata.services.cdn;
22244
+ const cdnConfig = cdnService.config;
21982
22245
  let stackOutputs = {};
21983
22246
  try {
21984
22247
  await ensurePulumiWorkDir();
21985
22248
  const stack = await pulumi26.automation.LocalWorkspace.selectStack({
21986
- stackName: `wraps-storage-${identity.accountId}-${region}`,
22249
+ stackName: `wraps-cdn-${identity.accountId}-${region}`,
21987
22250
  workDir: getPulumiWorkDir()
21988
22251
  });
21989
22252
  stackOutputs = await stack.outputs();
@@ -21992,7 +22255,7 @@ async function storageUpgrade(options) {
21992
22255
  process.exit(1);
21993
22256
  }
21994
22257
  let cloudFrontHasCustomDomain = false;
21995
- if (stackOutputs.distributionId?.value && storageConfig.cdn?.customDomain) {
22258
+ if (stackOutputs.distributionId?.value && cdnConfig.cdn?.customDomain) {
21996
22259
  try {
21997
22260
  const { CloudFrontClient, GetDistributionCommand } = await import("@aws-sdk/client-cloudfront");
21998
22261
  const cfClient = new CloudFrontClient({ region: "us-east-1" });
@@ -22003,16 +22266,16 @@ async function storageUpgrade(options) {
22003
22266
  );
22004
22267
  const aliases = cfResponse.Distribution?.DistributionConfig?.Aliases?.Items || [];
22005
22268
  cloudFrontHasCustomDomain = aliases.includes(
22006
- storageConfig.cdn.customDomain
22269
+ cdnConfig.cdn.customDomain
22007
22270
  );
22008
22271
  } catch {
22009
22272
  }
22010
22273
  }
22011
22274
  const hasPendingCert = stackOutputs.acmCertificateArn?.value && !stackOutputs.customDomain?.value && !cloudFrontHasCustomDomain;
22012
22275
  const pendingDomain = stackOutputs.customDomainPending?.value || (hasPendingCert && stackOutputs.acmCertificateValidationRecords?.value?.[0]?.name ? stackOutputs.acmCertificateValidationRecords.value[0].name.replace(/^_[^.]+\./, "").replace(/\.$/, "") : void 0);
22013
- const activeCustomDomain = cloudFrontHasCustomDomain ? storageConfig.cdn?.customDomain : stackOutputs.customDomain?.value;
22276
+ const activeCustomDomain = cloudFrontHasCustomDomain ? cdnConfig.cdn?.customDomain : stackOutputs.customDomain?.value;
22014
22277
  if (!hasPendingCert) {
22015
- clack32.log.info("No pending upgrades found for storage infrastructure.");
22278
+ clack32.log.info("No pending upgrades found for CDN infrastructure.");
22016
22279
  clack32.log.info(
22017
22280
  `
22018
22281
  Current configuration:
@@ -22059,7 +22322,7 @@ Pending domain: ${pc34.cyan(pendingDomain)}`);
22059
22322
  clack32.log.info(
22060
22323
  "After adding the DNS record, wait for validation (typically 5-30 minutes)."
22061
22324
  );
22062
- clack32.log.info(`Then run ${pc34.cyan("wraps storage upgrade")} again.
22325
+ clack32.log.info(`Then run ${pc34.cyan("wraps cdn upgrade")} again.
22063
22326
  `);
22064
22327
  process.exit(0);
22065
22328
  }
@@ -22081,9 +22344,9 @@ Ready to add custom domain: ${pc34.cyan(pendingDomain)}`);
22081
22344
  }
22082
22345
  }
22083
22346
  const updatedConfig = {
22084
- ...storageConfig,
22347
+ ...cdnConfig,
22085
22348
  cdn: {
22086
- ...storageConfig.cdn,
22349
+ ...cdnConfig.cdn,
22087
22350
  customDomain: pendingDomain
22088
22351
  }
22089
22352
  };
@@ -22092,8 +22355,8 @@ Ready to add custom domain: ${pc34.cyan(pendingDomain)}`);
22092
22355
  region,
22093
22356
  accountId: identity.accountId,
22094
22357
  vercel: metadata.vercel,
22095
- storageConfig: updatedConfig,
22096
- // Tell deployStorageStack that the cert is validated externally (manual DNS)
22358
+ cdnConfig: updatedConfig,
22359
+ // Tell deployCdnStack that the cert is validated externally (manual DNS)
22097
22360
  certValidated: true,
22098
22361
  existingCertArn: stackOutputs.acmCertificateArn.value
22099
22362
  };
@@ -22123,10 +22386,10 @@ Ready to add custom domain: ${pc34.cyan(pendingDomain)}`);
22123
22386
  await ensurePulumiWorkDir();
22124
22387
  const stack = await pulumi26.automation.LocalWorkspace.createOrSelectStack(
22125
22388
  {
22126
- stackName: `wraps-storage-${identity.accountId}-${region}`,
22127
- projectName: "wraps-storage",
22389
+ stackName: `wraps-cdn-${identity.accountId}-${region}`,
22390
+ projectName: "wraps-cdn",
22128
22391
  program: async () => {
22129
- const result = await deployStorageStack(stackConfig);
22392
+ const result = await deployCdnStack(stackConfig);
22130
22393
  return {
22131
22394
  roleArn: result.roleArn,
22132
22395
  bucketName: result.bucketName,
@@ -22169,8 +22432,8 @@ Ready to add custom domain: ${pc34.cyan(pendingDomain)}`);
22169
22432
  clack32.log.error(`Upgrade failed: ${error.message}`);
22170
22433
  process.exit(1);
22171
22434
  }
22172
- if (metadata.services.storage) {
22173
- metadata.services.storage.config = updatedConfig;
22435
+ if (metadata.services.cdn) {
22436
+ metadata.services.cdn.config = updatedConfig;
22174
22437
  await saveConnectionMetadata(metadata);
22175
22438
  }
22176
22439
  clack32.log.success(`
@@ -22201,7 +22464,7 @@ ${pc34.green("")} ${pc34.bold("Upgrade complete!")}
22201
22464
  clack32.outro("");
22202
22465
  }
22203
22466
 
22204
- // src/commands/storage/verify.ts
22467
+ // src/commands/cdn/verify.ts
22205
22468
  init_esm_shims();
22206
22469
  init_events();
22207
22470
  init_aws();
@@ -22257,26 +22520,26 @@ async function checkDistributionStatus(distributionId, region) {
22257
22520
  return { status: "ERROR", enabled: false, aliases: [] };
22258
22521
  }
22259
22522
  }
22260
- async function storageVerify(options) {
22523
+ async function cdnVerify(options) {
22261
22524
  const startTime = Date.now();
22262
22525
  const progress = new DeploymentProgress();
22263
- clack33.intro(pc35.bold("Wraps Storage Verification"));
22526
+ clack33.intro(pc35.bold("Wraps CDN Verification"));
22264
22527
  const identity = await progress.execute(
22265
- "Loading storage infrastructure",
22528
+ "Loading CDN infrastructure",
22266
22529
  async () => validateAWSCredentials()
22267
22530
  );
22268
22531
  let region = options.region || await getAWSRegion();
22269
22532
  if (!(options.region || process.env.AWS_REGION || process.env.AWS_DEFAULT_REGION)) {
22270
- const storageConnections = await findConnectionsWithService(
22533
+ const cdnConnections = await findConnectionsWithService(
22271
22534
  identity.accountId,
22272
- "storage"
22535
+ "cdn"
22273
22536
  );
22274
- if (storageConnections.length === 1) {
22275
- region = storageConnections[0].region;
22276
- } else if (storageConnections.length > 1) {
22537
+ if (cdnConnections.length === 1) {
22538
+ region = cdnConnections[0].region;
22539
+ } else if (cdnConnections.length > 1) {
22277
22540
  const selectedRegion = await clack33.select({
22278
- message: "Multiple storage deployments found. Which region?",
22279
- options: storageConnections.map((conn) => ({
22541
+ message: "Multiple CDN deployments found. Which region?",
22542
+ options: cdnConnections.map((conn) => ({
22280
22543
  value: conn.region,
22281
22544
  label: conn.region
22282
22545
  }))
@@ -22292,16 +22555,16 @@ async function storageVerify(options) {
22292
22555
  try {
22293
22556
  await ensurePulumiWorkDir();
22294
22557
  const stack = await pulumi27.automation.LocalWorkspace.selectStack({
22295
- stackName: `wraps-storage-${identity.accountId}-${region}`,
22558
+ stackName: `wraps-cdn-${identity.accountId}-${region}`,
22296
22559
  workDir: getPulumiWorkDir()
22297
22560
  });
22298
22561
  stackOutputs = await stack.outputs();
22299
22562
  } catch (_error) {
22300
22563
  progress.stop();
22301
- clack33.log.error("No storage infrastructure found");
22564
+ clack33.log.error("No CDN infrastructure found");
22302
22565
  console.log(
22303
22566
  `
22304
- Run ${pc35.cyan("wraps storage init")} to deploy storage infrastructure.
22567
+ Run ${pc35.cyan("wraps cdn init")} to deploy CDN infrastructure.
22305
22568
  `
22306
22569
  );
22307
22570
  process.exit(1);
@@ -22384,7 +22647,7 @@ ${pc35.bold("Custom Domain DNS:")}`);
22384
22647
  ` ${pc35.yellow("!")} CloudFront alias not configured for ${targetDomain}`
22385
22648
  );
22386
22649
  clack33.log.info(
22387
- ` Run ${pc35.cyan("wraps storage upgrade")} to add the custom domain to CloudFront.`
22650
+ ` Run ${pc35.cyan("wraps cdn upgrade")} to add the custom domain to CloudFront.`
22388
22651
  );
22389
22652
  allPassed = false;
22390
22653
  }
@@ -22425,12 +22688,12 @@ ${pc35.bold("Certificate Validation DNS:")}`);
22425
22688
  const cdnUrl = targetDomain ? `https://${targetDomain}` : distributionDomain ? `https://${distributionDomain}` : null;
22426
22689
  if (cdnUrl) {
22427
22690
  clack33.log.info(`
22428
- Your storage is accessible at: ${pc35.cyan(cdnUrl)}`);
22691
+ Your CDN is accessible at: ${pc35.cyan(cdnUrl)}`);
22429
22692
  }
22430
22693
  if (stackOutputsStale) {
22431
22694
  clack33.log.info(
22432
22695
  `
22433
- ${pc35.dim("Tip:")} Run ${pc35.cyan("wraps storage sync")} to update infrastructure state.`
22696
+ ${pc35.dim("Tip:")} Run ${pc35.cyan("wraps cdn sync")} to update infrastructure state.`
22434
22697
  );
22435
22698
  }
22436
22699
  } else {
@@ -22438,7 +22701,7 @@ ${pc35.dim("Tip:")} Run ${pc35.cyan("wraps storage sync")} to update infrastruct
22438
22701
  if (validationRecords && validationRecords.length > 0) {
22439
22702
  clack33.log.info("\nDNS records may take up to 48 hours to propagate.");
22440
22703
  clack33.log.info(
22441
- `Run ${pc35.cyan("wraps storage verify")} again to check status.`
22704
+ `Run ${pc35.cyan("wraps cdn verify")} again to check status.`
22442
22705
  );
22443
22706
  }
22444
22707
  }
@@ -22600,7 +22863,7 @@ function showHelp() {
22600
22863
  ` ${pc38.cyan("sms")} SMS infrastructure (AWS End User Messaging)`
22601
22864
  );
22602
22865
  console.log(
22603
- ` ${pc38.cyan("storage")} File storage infrastructure (AWS S3 + CloudFront)
22866
+ ` ${pc38.cyan("cdn")} CDN infrastructure (AWS S3 + CloudFront)
22604
22867
  `
22605
22868
  );
22606
22869
  console.log("Email Commands:");
@@ -22649,24 +22912,24 @@ function showHelp() {
22649
22912
  ` ${pc38.cyan("sms destroy")} Remove SMS infrastructure
22650
22913
  `
22651
22914
  );
22652
- console.log("Storage Commands:");
22915
+ console.log("CDN Commands:");
22653
22916
  console.log(
22654
- ` ${pc38.cyan("storage init")} Deploy storage infrastructure (S3 + CDN)`
22917
+ ` ${pc38.cyan("cdn init")} Deploy CDN infrastructure (S3 + CloudFront)`
22655
22918
  );
22656
22919
  console.log(
22657
- ` ${pc38.cyan("storage status")} Show storage infrastructure details`
22920
+ ` ${pc38.cyan("cdn status")} Show CDN infrastructure details`
22658
22921
  );
22659
22922
  console.log(
22660
- ` ${pc38.cyan("storage verify")} Check DNS and certificate status`
22923
+ ` ${pc38.cyan("cdn verify")} Check DNS and certificate status`
22661
22924
  );
22662
22925
  console.log(
22663
- ` ${pc38.cyan("storage upgrade")} Add custom domain after cert validation`
22926
+ ` ${pc38.cyan("cdn upgrade")} Add custom domain after cert validation`
22664
22927
  );
22665
22928
  console.log(
22666
- ` ${pc38.cyan("storage sync")} Sync infrastructure with current config`
22929
+ ` ${pc38.cyan("cdn sync")} Sync infrastructure with current config`
22667
22930
  );
22668
22931
  console.log(
22669
- ` ${pc38.cyan("storage destroy")} Remove storage infrastructure
22932
+ ` ${pc38.cyan("cdn destroy")} Remove CDN infrastructure
22670
22933
  `
22671
22934
  );
22672
22935
  console.log("Local Development:");
@@ -22874,8 +23137,8 @@ if (!primaryCommand) {
22874
23137
  hint: "AWS End User Messaging"
22875
23138
  },
22876
23139
  {
22877
- value: "storage-init",
22878
- label: "Deploy Storage",
23140
+ value: "cdn-init",
23141
+ label: "Deploy CDN",
22879
23142
  hint: "AWS S3 + CloudFront CDN"
22880
23143
  },
22881
23144
  {
@@ -22933,7 +23196,7 @@ if (!primaryCommand) {
22933
23196
  yes: flags.yes
22934
23197
  });
22935
23198
  break;
22936
- case "storage-init":
23199
+ case "cdn-init":
22937
23200
  await init3({
22938
23201
  provider: flags.provider,
22939
23202
  region: flags.region,
@@ -23233,7 +23496,7 @@ Run ${pc38.cyan("wraps --help")} for available commands.
23233
23496
  });
23234
23497
  return;
23235
23498
  }
23236
- if (primaryCommand === "storage" && subCommand) {
23499
+ if (primaryCommand === "cdn" && subCommand) {
23237
23500
  switch (subCommand) {
23238
23501
  case "init":
23239
23502
  await init3({
@@ -23246,36 +23509,36 @@ Run ${pc38.cyan("wraps --help")} for available commands.
23246
23509
  });
23247
23510
  break;
23248
23511
  case "status":
23249
- await storageStatus({
23512
+ await cdnStatus({
23250
23513
  region: flags.region
23251
23514
  });
23252
23515
  break;
23253
23516
  case "verify":
23254
- await storageVerify({
23517
+ await cdnVerify({
23255
23518
  region: flags.region
23256
23519
  });
23257
23520
  break;
23258
23521
  case "upgrade":
23259
- await storageUpgrade({
23522
+ await cdnUpgrade({
23260
23523
  region: flags.region,
23261
23524
  yes: flags.yes,
23262
23525
  preview: flags.preview
23263
23526
  });
23264
23527
  break;
23265
23528
  case "sync":
23266
- await storageSync({
23529
+ await cdnSync({
23267
23530
  region: flags.region
23268
23531
  });
23269
23532
  break;
23270
23533
  case "destroy":
23271
- await storageDestroy({
23534
+ await cdnDestroy({
23272
23535
  force: flags.force,
23273
23536
  region: flags.region,
23274
23537
  preview: flags.preview
23275
23538
  });
23276
23539
  break;
23277
23540
  default:
23278
- clack36.log.error(`Unknown storage command: ${subCommand}`);
23541
+ clack36.log.error(`Unknown cdn command: ${subCommand}`);
23279
23542
  console.log(
23280
23543
  `
23281
23544
  Run ${pc38.cyan("wraps --help")} for available commands.
@@ -23283,12 +23546,12 @@ Run ${pc38.cyan("wraps --help")} for available commands.
23283
23546
  );
23284
23547
  process.exit(1);
23285
23548
  }
23286
- const storageDuration = Date.now() - startTime;
23287
- const storageCommandName = `storage:${subCommand}`;
23288
- trackCommand(storageCommandName, {
23549
+ const cdnDuration = Date.now() - startTime;
23550
+ const cdnCommandName = `cdn:${subCommand}`;
23551
+ trackCommand(cdnCommandName, {
23289
23552
  success: true,
23290
- duration_ms: storageDuration,
23291
- service: "storage"
23553
+ duration_ms: cdnDuration,
23554
+ service: "cdn"
23292
23555
  });
23293
23556
  return;
23294
23557
  }
@@ -23411,7 +23674,7 @@ Available commands: ${pc38.cyan("enable")}, ${pc38.cyan("disable")}, ${pc38.cyan
23411
23674
  // Show help for service without subcommand
23412
23675
  case "email":
23413
23676
  case "sms":
23414
- case "storage":
23677
+ case "cdn":
23415
23678
  case "aws":
23416
23679
  console.log(
23417
23680
  `