@wraps.dev/cli 2.3.4 → 2.3.5

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
@@ -1023,7 +1023,7 @@ To remove: wraps destroy --stack ${stackName}`,
1023
1023
  "The Pulumi stack is locked from a previous run",
1024
1024
  "STACK_LOCKED",
1025
1025
  "This happens when a previous deployment was interrupted.\n\nTo unlock, run:\n rm -rf ~/.wraps/pulumi/.pulumi/locks\n\nThen try your command again.",
1026
- "https://wraps.dev/docs/guides/aws-setup/troubleshooting"
1026
+ "https://wraps.dev/docs/guides/aws-setup/permissions/troubleshooting"
1027
1027
  ),
1028
1028
  // SMS-specific errors
1029
1029
  smsNotConfigured: () => new WrapsError(
@@ -1080,7 +1080,7 @@ To remove: wraps destroy --stack ${stackName}`,
1080
1080
  `AWS SSO session has expired${profile ? ` for profile "${profile}"` : ""}`,
1081
1081
  "SSO_SESSION_EXPIRED",
1082
1082
  profile ? `Run: aws sso login --profile ${profile}` : "Run: aws sso login",
1083
- "https://wraps.dev/docs/guides/aws-setup"
1083
+ "https://wraps.dev/docs/guides/aws-setup/permissions"
1084
1084
  ),
1085
1085
  profileNotFound: (profile, availableProfiles) => new WrapsError(
1086
1086
  `AWS profile "${profile}" not found`,
@@ -1092,25 +1092,25 @@ Set a valid profile:
1092
1092
 
1093
1093
  Or configure a new profile:
1094
1094
  aws configure --profile ${profile}` : "No AWS profiles configured.\n\nConfigure AWS credentials:\n aws configure\n\nOr set up SSO:\n aws configure sso",
1095
- "https://wraps.dev/docs/guides/aws-setup"
1095
+ "https://wraps.dev/docs/guides/aws-setup/permissions"
1096
1096
  ),
1097
1097
  credentialsFileMissing: () => new WrapsError(
1098
1098
  "AWS credentials file not found",
1099
1099
  "CREDENTIALS_FILE_MISSING",
1100
1100
  "Configure AWS credentials:\n aws configure\n\nOr set environment variables:\n export AWS_ACCESS_KEY_ID=<your-key>\n export AWS_SECRET_ACCESS_KEY=<your-secret>",
1101
- "https://wraps.dev/docs/guides/aws-setup"
1101
+ "https://wraps.dev/docs/guides/aws-setup/permissions"
1102
1102
  ),
1103
1103
  accessKeyInvalid: () => new WrapsError(
1104
1104
  "AWS access key is invalid or has been deactivated",
1105
1105
  "ACCESS_KEY_INVALID",
1106
1106
  "Check your AWS access keys in the IAM console.\n\nReconfigure credentials:\n aws configure\n\nOr generate new access keys in AWS IAM.",
1107
- "https://wraps.dev/docs/guides/aws-setup"
1107
+ "https://wraps.dev/docs/guides/aws-setup/permissions"
1108
1108
  ),
1109
1109
  sessionTokenExpired: () => new WrapsError(
1110
1110
  "AWS session token has expired",
1111
1111
  "SESSION_TOKEN_EXPIRED",
1112
1112
  "Your temporary credentials have expired.\n\nFor SSO users:\n aws sso login\n\nFor assumed roles:\n Re-run your assume-role command",
1113
- "https://wraps.dev/docs/guides/aws-setup"
1113
+ "https://wraps.dev/docs/guides/aws-setup/permissions"
1114
1114
  ),
1115
1115
  // IAM permission errors
1116
1116
  iamPermissionDenied: (action, resource, suggestion) => new WrapsError(
@@ -9210,7 +9210,7 @@ function createNodeDnsProvider(options = {}) {
9210
9210
  if (options.servers && options.servers.length > 0) {
9211
9211
  resolver.setServers(options.servers);
9212
9212
  }
9213
- async function withTimeout(promise, operation) {
9213
+ async function withTimeout2(promise, operation) {
9214
9214
  const timeoutPromise = new Promise((_, reject) => {
9215
9215
  setTimeout(() => {
9216
9216
  reject(new Error(`DNS ${operation} timed out after ${timeout}ms`));
@@ -9220,7 +9220,7 @@ function createNodeDnsProvider(options = {}) {
9220
9220
  }
9221
9221
  async function resolveTxt(domain) {
9222
9222
  try {
9223
- const records = await withTimeout(
9223
+ const records = await withTimeout2(
9224
9224
  dns.resolveTxt(domain),
9225
9225
  `TXT lookup for ${domain}`
9226
9226
  );
@@ -9234,7 +9234,7 @@ function createNodeDnsProvider(options = {}) {
9234
9234
  }
9235
9235
  async function resolveMx(domain) {
9236
9236
  try {
9237
- const records = await withTimeout(
9237
+ const records = await withTimeout2(
9238
9238
  dns.resolveMx(domain),
9239
9239
  `MX lookup for ${domain}`
9240
9240
  );
@@ -9248,7 +9248,7 @@ function createNodeDnsProvider(options = {}) {
9248
9248
  }
9249
9249
  async function resolveA(domain) {
9250
9250
  try {
9251
- const records = await withTimeout(
9251
+ const records = await withTimeout2(
9252
9252
  dns.resolve4(domain),
9253
9253
  `A lookup for ${domain}`
9254
9254
  );
@@ -9262,7 +9262,7 @@ function createNodeDnsProvider(options = {}) {
9262
9262
  }
9263
9263
  async function resolveAaaa(domain) {
9264
9264
  try {
9265
- const records = await withTimeout(
9265
+ const records = await withTimeout2(
9266
9266
  dns.resolve6(domain),
9267
9267
  `AAAA lookup for ${domain}`
9268
9268
  );
@@ -9276,7 +9276,7 @@ function createNodeDnsProvider(options = {}) {
9276
9276
  }
9277
9277
  async function resolvePtr(ip) {
9278
9278
  try {
9279
- const records = await withTimeout(
9279
+ const records = await withTimeout2(
9280
9280
  dns.reverse(ip),
9281
9281
  `PTR lookup for ${ip}`
9282
9282
  );
@@ -9290,7 +9290,7 @@ function createNodeDnsProvider(options = {}) {
9290
9290
  }
9291
9291
  async function resolveCaa(domain) {
9292
9292
  try {
9293
- const records = await withTimeout(
9293
+ const records = await withTimeout2(
9294
9294
  dns.resolveCaa(domain),
9295
9295
  `CAA lookup for ${domain}`
9296
9296
  );
@@ -9322,7 +9322,7 @@ function createNodeDnsProvider(options = {}) {
9322
9322
  }
9323
9323
  async function resolveCname(domain) {
9324
9324
  try {
9325
- const records = await withTimeout(
9325
+ const records = await withTimeout2(
9326
9326
  dns.resolveCname(domain),
9327
9327
  `CNAME lookup for ${domain}`
9328
9328
  );
@@ -11885,14 +11885,13 @@ async function tryGetSesDkimTokens(domain) {
11885
11885
  // src/commands/email/config.ts
11886
11886
  init_esm_shims();
11887
11887
  import * as clack13 from "@clack/prompts";
11888
- import * as pulumi13 from "@pulumi/pulumi";
11888
+ import * as pulumi12 from "@pulumi/pulumi";
11889
11889
  import pc14 from "picocolors";
11890
11890
 
11891
11891
  // src/infrastructure/email-stack.ts
11892
11892
  init_esm_shims();
11893
11893
  init_dist();
11894
11894
  import * as aws14 from "@pulumi/aws";
11895
- import * as pulumi12 from "@pulumi/pulumi";
11896
11895
 
11897
11896
  // src/infrastructure/resources/alerting.ts
11898
11897
  init_esm_shims();
@@ -13066,7 +13065,7 @@ ${pc14.bold("Current Configuration:")}
13066
13065
  "Generating update preview",
13067
13066
  async () => {
13068
13067
  await ensurePulumiWorkDir();
13069
- const stack = await pulumi13.automation.LocalWorkspace.createOrSelectStack(
13068
+ const stack = await pulumi12.automation.LocalWorkspace.createOrSelectStack(
13070
13069
  {
13071
13070
  stackName: metadata.services.email?.pulumiStackName || `wraps-${identity.accountId}-${region}`,
13072
13071
  projectName: "wraps-email",
@@ -13130,7 +13129,7 @@ ${pc14.bold("Current Configuration:")}
13130
13129
  "Updating Wraps infrastructure (this may take 2-3 minutes)",
13131
13130
  async () => {
13132
13131
  await ensurePulumiWorkDir();
13133
- const stack = await pulumi13.automation.LocalWorkspace.createOrSelectStack(
13132
+ const stack = await pulumi12.automation.LocalWorkspace.createOrSelectStack(
13134
13133
  {
13135
13134
  stackName: metadata.services.email?.pulumiStackName || `wraps-${identity.accountId}-${region}`,
13136
13135
  projectName: "wraps-email",
@@ -13227,7 +13226,7 @@ ${pc14.green("\u2713")} ${pc14.bold("Update complete!")}
13227
13226
  // src/commands/email/connect.ts
13228
13227
  init_esm_shims();
13229
13228
  import * as clack14 from "@clack/prompts";
13230
- import * as pulumi14 from "@pulumi/pulumi";
13229
+ import * as pulumi13 from "@pulumi/pulumi";
13231
13230
  import pc15 from "picocolors";
13232
13231
  init_events();
13233
13232
  init_presets();
@@ -13567,7 +13566,7 @@ async function connect2(options) {
13567
13566
  "Generating infrastructure preview",
13568
13567
  async () => {
13569
13568
  await ensurePulumiWorkDir();
13570
- const stack = await pulumi14.automation.LocalWorkspace.createOrSelectStack(
13569
+ const stack = await pulumi13.automation.LocalWorkspace.createOrSelectStack(
13571
13570
  {
13572
13571
  stackName: `wraps-${identity.accountId}-${region}`,
13573
13572
  projectName: "wraps-email",
@@ -13631,7 +13630,7 @@ async function connect2(options) {
13631
13630
  "Deploying Wraps infrastructure (this may take 2-3 minutes)",
13632
13631
  async () => {
13633
13632
  await ensurePulumiWorkDir();
13634
- const stack = await pulumi14.automation.LocalWorkspace.createOrSelectStack(
13633
+ const stack = await pulumi13.automation.LocalWorkspace.createOrSelectStack(
13635
13634
  {
13636
13635
  stackName: `wraps-${identity.accountId}-${region}`,
13637
13636
  projectName: "wraps-email",
@@ -13791,8 +13790,42 @@ init_errors();
13791
13790
  init_fs();
13792
13791
  init_metadata();
13793
13792
  import * as clack15 from "@clack/prompts";
13794
- import * as pulumi15 from "@pulumi/pulumi";
13793
+ import * as pulumi14 from "@pulumi/pulumi";
13795
13794
  import pc16 from "picocolors";
13795
+
13796
+ // src/utils/shared/timeout.ts
13797
+ init_esm_shims();
13798
+ init_errors();
13799
+ var DEFAULT_PULUMI_TIMEOUT_MS = 10 * 60 * 1e3;
13800
+ var TimeoutError = class extends WrapsError {
13801
+ constructor(operation, timeoutMs) {
13802
+ const timeoutMinutes = Math.round(timeoutMs / 6e4);
13803
+ super(
13804
+ `Operation "${operation}" timed out after ${timeoutMinutes} minute${timeoutMinutes === 1 ? "" : "s"}`,
13805
+ "OPERATION_TIMEOUT",
13806
+ "The operation took longer than expected. This can happen due to:\n - Slow network connection\n - AWS API throttling\n - Large number of resources\n\nYou can try:\n 1. Check AWS CloudFormation/Pulumi state for partial deployments\n 2. Run the command again (it will resume where it left off)\n 3. Check your AWS console for any stuck resources",
13807
+ "https://wraps.dev/docs/guides/aws-setup/troubleshooting"
13808
+ );
13809
+ }
13810
+ };
13811
+ async function withTimeout(promise, timeoutMs, operation) {
13812
+ let timeoutId;
13813
+ const timeoutPromise = new Promise((_, reject) => {
13814
+ timeoutId = setTimeout(() => {
13815
+ reject(new TimeoutError(operation, timeoutMs));
13816
+ }, timeoutMs);
13817
+ });
13818
+ try {
13819
+ const result = await Promise.race([promise, timeoutPromise]);
13820
+ return result;
13821
+ } finally {
13822
+ if (timeoutId) {
13823
+ clearTimeout(timeoutId);
13824
+ }
13825
+ }
13826
+ }
13827
+
13828
+ // src/commands/email/destroy.ts
13796
13829
  async function getEmailIdentityInfo(domain, region) {
13797
13830
  try {
13798
13831
  const { SESv2Client: SESv2Client6, GetEmailIdentityCommand: GetEmailIdentityCommand5 } = await import("@aws-sdk/client-sesv2");
@@ -13893,10 +13926,10 @@ async function emailDestroy(options) {
13893
13926
  "Generating destruction preview",
13894
13927
  async () => {
13895
13928
  await ensurePulumiWorkDir();
13896
- const stackName = storedStackName || `wraps-email-${identity.accountId}-${region}`;
13929
+ const stackName = storedStackName || `wraps-${identity.accountId}-${region}`;
13897
13930
  let stack;
13898
13931
  try {
13899
- stack = await pulumi15.automation.LocalWorkspace.selectStack({
13932
+ stack = await pulumi14.automation.LocalWorkspace.selectStack({
13900
13933
  stackName,
13901
13934
  workDir: getPulumiWorkDir()
13902
13935
  });
@@ -13964,18 +13997,23 @@ async function emailDestroy(options) {
13964
13997
  "Destroying email infrastructure (this may take 2-3 minutes)",
13965
13998
  async () => {
13966
13999
  await ensurePulumiWorkDir();
13967
- const stackName = storedStackName || `wraps-email-${identity.accountId}-${region}`;
14000
+ const stackName = storedStackName || `wraps-${identity.accountId}-${region}`;
13968
14001
  let stack;
13969
14002
  try {
13970
- stack = await pulumi15.automation.LocalWorkspace.selectStack({
14003
+ stack = await pulumi14.automation.LocalWorkspace.selectStack({
13971
14004
  stackName,
13972
14005
  workDir: getPulumiWorkDir()
13973
14006
  });
13974
14007
  } catch (_error) {
13975
14008
  throw new Error("No email infrastructure found to destroy");
13976
14009
  }
13977
- await stack.destroy({ onOutput: () => {
13978
- } });
14010
+ await withTimeout(
14011
+ stack.destroy({ onOutput: () => {
14012
+ } }),
14013
+ // Suppress Pulumi output
14014
+ DEFAULT_PULUMI_TIMEOUT_MS,
14015
+ "Pulumi destroy"
14016
+ );
13979
14017
  await stack.workspace.removeStack(stackName);
13980
14018
  }
13981
14019
  );
@@ -14479,7 +14517,7 @@ async function removeDomain(options) {
14479
14517
  // src/commands/email/init.ts
14480
14518
  init_esm_shims();
14481
14519
  import * as clack17 from "@clack/prompts";
14482
- import * as pulumi16 from "@pulumi/pulumi";
14520
+ import * as pulumi15 from "@pulumi/pulumi";
14483
14521
  import pc18 from "picocolors";
14484
14522
  init_events();
14485
14523
  init_costs();
@@ -14487,6 +14525,141 @@ init_presets();
14487
14525
  init_aws();
14488
14526
  init_errors();
14489
14527
  init_fs();
14528
+
14529
+ // src/utils/shared/iam-check.ts
14530
+ init_esm_shims();
14531
+ var CORE_IAM_ACTIONS = [
14532
+ "iam:CreateRole",
14533
+ "iam:GetRole",
14534
+ "iam:PutRolePolicy",
14535
+ "iam:DeleteRole",
14536
+ "iam:DeleteRolePolicy",
14537
+ "iam:TagRole",
14538
+ "ses:CreateConfigurationSet",
14539
+ "ses:DeleteConfigurationSet",
14540
+ "ses:CreateEmailIdentity",
14541
+ "ses:DeleteEmailIdentity",
14542
+ "ses:GetEmailIdentity",
14543
+ "ses:PutEmailIdentityDkimAttributes"
14544
+ ];
14545
+ var EVENT_TRACKING_ACTIONS = [
14546
+ "events:CreateEventBus",
14547
+ "events:DeleteEventBus",
14548
+ "events:PutRule",
14549
+ "events:DeleteRule",
14550
+ "events:PutTargets",
14551
+ "events:RemoveTargets",
14552
+ "sqs:CreateQueue",
14553
+ "sqs:DeleteQueue",
14554
+ "sqs:SetQueueAttributes",
14555
+ "sqs:GetQueueAttributes"
14556
+ ];
14557
+ var DYNAMODB_ACTIONS = [
14558
+ "dynamodb:CreateTable",
14559
+ "dynamodb:DeleteTable",
14560
+ "dynamodb:DescribeTable",
14561
+ "dynamodb:UpdateTable",
14562
+ "dynamodb:TagResource"
14563
+ ];
14564
+ var LAMBDA_ACTIONS = [
14565
+ "lambda:CreateFunction",
14566
+ "lambda:DeleteFunction",
14567
+ "lambda:UpdateFunctionCode",
14568
+ "lambda:UpdateFunctionConfiguration",
14569
+ "lambda:GetFunction",
14570
+ "lambda:AddPermission",
14571
+ "lambda:RemovePermission",
14572
+ "lambda:CreateEventSourceMapping",
14573
+ "lambda:DeleteEventSourceMapping"
14574
+ ];
14575
+ function getRequiredActions(config2) {
14576
+ const actions = [...CORE_IAM_ACTIONS];
14577
+ if (config2.eventTracking?.enabled) {
14578
+ actions.push(...EVENT_TRACKING_ACTIONS);
14579
+ }
14580
+ if (config2.eventTracking?.dynamoDBHistory) {
14581
+ actions.push(...DYNAMODB_ACTIONS);
14582
+ actions.push(...LAMBDA_ACTIONS);
14583
+ }
14584
+ return [...new Set(actions)];
14585
+ }
14586
+ async function checkIAMPermissions(userArn, actions, region) {
14587
+ try {
14588
+ const { IAMClient: IAMClient3, SimulatePrincipalPolicyCommand } = await import("@aws-sdk/client-iam");
14589
+ const client = new IAMClient3({ region });
14590
+ const batchSize = 100;
14591
+ const batches = [];
14592
+ for (let i = 0; i < actions.length; i += batchSize) {
14593
+ batches.push(actions.slice(i, i + batchSize));
14594
+ }
14595
+ const allowedActions = [];
14596
+ const deniedActions = [];
14597
+ for (const batch of batches) {
14598
+ const command = new SimulatePrincipalPolicyCommand({
14599
+ PolicySourceArn: userArn,
14600
+ ActionNames: batch,
14601
+ // Use a wildcard resource since we're checking general permissions
14602
+ // More specific resource-level checks could be added later
14603
+ ResourceArns: ["*"]
14604
+ });
14605
+ const response = await client.send(command);
14606
+ for (const result of response.EvaluationResults || []) {
14607
+ const actionName = result.EvalActionName;
14608
+ const decision = result.EvalDecision;
14609
+ if (decision === "allowed") {
14610
+ if (actionName) allowedActions.push(actionName);
14611
+ } else if (actionName) deniedActions.push(actionName);
14612
+ }
14613
+ }
14614
+ return {
14615
+ success: deniedActions.length === 0,
14616
+ deniedActions,
14617
+ allowedActions,
14618
+ skipped: false
14619
+ };
14620
+ } catch (error) {
14621
+ if (error instanceof Error && (error.name === "AccessDenied" || error.name === "AccessDeniedException" || error.message?.includes("AccessDenied") || error.message?.includes("iam:SimulatePrincipalPolicy"))) {
14622
+ return {
14623
+ success: true,
14624
+ // Don't block on this
14625
+ deniedActions: [],
14626
+ allowedActions: [],
14627
+ skipped: true,
14628
+ skipReason: "Unable to verify permissions (iam:SimulatePrincipalPolicy not allowed). Deployment will proceed, but may fail if permissions are missing."
14629
+ };
14630
+ }
14631
+ return {
14632
+ success: true,
14633
+ deniedActions: [],
14634
+ allowedActions: [],
14635
+ skipped: true,
14636
+ skipReason: `Permission check failed: ${error instanceof Error ? error.message : "Unknown error"}. Proceeding with deployment.`
14637
+ };
14638
+ }
14639
+ }
14640
+ function formatDeniedActions(actions) {
14641
+ if (actions.length === 0) return "";
14642
+ const byService = {};
14643
+ for (const action of actions) {
14644
+ const [service, actionName] = action.split(":");
14645
+ if (!byService[service]) {
14646
+ byService[service] = [];
14647
+ }
14648
+ byService[service].push(actionName);
14649
+ }
14650
+ const lines = ["Missing permissions:"];
14651
+ for (const [service, serviceActions] of Object.entries(byService)) {
14652
+ lines.push(` ${service.toUpperCase()}:`);
14653
+ for (const action of serviceActions) {
14654
+ lines.push(` - ${service}:${action}`);
14655
+ }
14656
+ }
14657
+ lines.push("");
14658
+ lines.push("Run `wraps permissions --json` to see the full policy document.");
14659
+ return lines.join("\n");
14660
+ }
14661
+
14662
+ // src/commands/email/init.ts
14490
14663
  init_metadata();
14491
14664
  init_prompts();
14492
14665
  async function init2(options) {
@@ -14594,6 +14767,23 @@ ${pc18.yellow(pc18.bold("Configuration Warnings:"))}`);
14594
14767
  process.exit(0);
14595
14768
  }
14596
14769
  }
14770
+ if (!options.preview) {
14771
+ const iamCheckResult = await progress.execute(
14772
+ "Checking IAM permissions",
14773
+ async () => {
14774
+ const requiredActions = getRequiredActions(emailConfig);
14775
+ return checkIAMPermissions(identity.arn, requiredActions, region);
14776
+ }
14777
+ );
14778
+ if (iamCheckResult.skipped && iamCheckResult.skipReason) {
14779
+ progress.info(pc18.dim(iamCheckResult.skipReason));
14780
+ } else if (!iamCheckResult.success) {
14781
+ clack17.log.warn(
14782
+ pc18.yellow("Some IAM permissions may be missing. Deployment may fail.")
14783
+ );
14784
+ clack17.log.info(formatDeniedActions(iamCheckResult.deniedActions));
14785
+ }
14786
+ }
14597
14787
  const stackConfig = {
14598
14788
  provider,
14599
14789
  region,
@@ -14606,7 +14796,7 @@ ${pc18.yellow(pc18.bold("Configuration Warnings:"))}`);
14606
14796
  "Generating infrastructure preview",
14607
14797
  async () => {
14608
14798
  await ensurePulumiWorkDir();
14609
- const stack = await pulumi16.automation.LocalWorkspace.createOrSelectStack(
14799
+ const stack = await pulumi15.automation.LocalWorkspace.createOrSelectStack(
14610
14800
  {
14611
14801
  stackName: `wraps-${identity.accountId}-${region}`,
14612
14802
  projectName: "wraps-email",
@@ -14675,7 +14865,7 @@ ${pc18.yellow(pc18.bold("Configuration Warnings:"))}`);
14675
14865
  "Deploying infrastructure (this may take 2-3 minutes)",
14676
14866
  async () => {
14677
14867
  await ensurePulumiWorkDir();
14678
- const stack = await pulumi16.automation.LocalWorkspace.createOrSelectStack(
14868
+ const stack = await pulumi15.automation.LocalWorkspace.createOrSelectStack(
14679
14869
  {
14680
14870
  stackName: `wraps-${identity.accountId}-${region}`,
14681
14871
  projectName: "wraps-email",
@@ -14712,8 +14902,13 @@ ${pc18.yellow(pc18.bold("Configuration Warnings:"))}`);
14712
14902
  `wraps-${identity.accountId}-${region}`
14713
14903
  );
14714
14904
  await stack.setConfig("aws:region", { value: region });
14715
- const upResult = await stack.up({ onOutput: () => {
14716
- } });
14905
+ const upResult = await withTimeout(
14906
+ stack.up({ onOutput: () => {
14907
+ } }),
14908
+ // Suppress Pulumi output
14909
+ DEFAULT_PULUMI_TIMEOUT_MS,
14910
+ "Pulumi deployment"
14911
+ );
14717
14912
  const pulumiOutputs = upResult.outputs;
14718
14913
  return {
14719
14914
  roleArn: pulumiOutputs.roleArn?.value,
@@ -14891,7 +15086,7 @@ init_aws();
14891
15086
  init_fs();
14892
15087
  init_metadata();
14893
15088
  import * as clack18 from "@clack/prompts";
14894
- import * as pulumi17 from "@pulumi/pulumi";
15089
+ import * as pulumi16 from "@pulumi/pulumi";
14895
15090
  import pc19 from "picocolors";
14896
15091
  async function restore(options) {
14897
15092
  const startTime = Date.now();
@@ -14963,7 +15158,7 @@ ${pc19.bold("The following Wraps resources will be removed:")}
14963
15158
  const previewResult = await progress.execute(
14964
15159
  "Generating removal preview",
14965
15160
  async () => {
14966
- const stack = await pulumi17.automation.LocalWorkspace.selectStack(
15161
+ const stack = await pulumi16.automation.LocalWorkspace.selectStack(
14967
15162
  {
14968
15163
  stackName: pulumiStackName,
14969
15164
  projectName: "wraps-email",
@@ -15015,7 +15210,7 @@ ${pc19.bold("The following Wraps resources will be removed:")}
15015
15210
  if (!metadata.services.email?.pulumiStackName) {
15016
15211
  throw new Error("No Pulumi stack name found in metadata");
15017
15212
  }
15018
- const stack = await pulumi17.automation.LocalWorkspace.selectStack(
15213
+ const stack = await pulumi16.automation.LocalWorkspace.selectStack(
15019
15214
  {
15020
15215
  stackName: metadata.services.email.pulumiStackName,
15021
15216
  projectName: "wraps-email",
@@ -15069,7 +15264,7 @@ init_aws();
15069
15264
  init_fs();
15070
15265
  init_metadata();
15071
15266
  import * as clack19 from "@clack/prompts";
15072
- import * as pulumi18 from "@pulumi/pulumi";
15267
+ import * as pulumi17 from "@pulumi/pulumi";
15073
15268
  import pc20 from "picocolors";
15074
15269
  async function emailStatus(options) {
15075
15270
  const startTime = Date.now();
@@ -15105,7 +15300,7 @@ async function emailStatus(options) {
15105
15300
  let stackOutputs = {};
15106
15301
  try {
15107
15302
  await ensurePulumiWorkDir();
15108
- const stack = await pulumi18.automation.LocalWorkspace.selectStack({
15303
+ const stack = await pulumi17.automation.LocalWorkspace.selectStack({
15109
15304
  stackName: `wraps-${identity.accountId}-${region}`,
15110
15305
  workDir: getPulumiWorkDir()
15111
15306
  });
@@ -15182,7 +15377,7 @@ Run ${pc20.cyan("wraps email init")} to deploy email infrastructure.
15182
15377
  // src/commands/email/upgrade.ts
15183
15378
  init_esm_shims();
15184
15379
  import * as clack20 from "@clack/prompts";
15185
- import * as pulumi19 from "@pulumi/pulumi";
15380
+ import * as pulumi18 from "@pulumi/pulumi";
15186
15381
  import pc21 from "picocolors";
15187
15382
  init_events();
15188
15383
  init_costs();
@@ -16303,7 +16498,7 @@ ${pc21.bold("Cost Impact:")}`);
16303
16498
  "Generating upgrade preview",
16304
16499
  async () => {
16305
16500
  await ensurePulumiWorkDir();
16306
- const stack = await pulumi19.automation.LocalWorkspace.createOrSelectStack(
16501
+ const stack = await pulumi18.automation.LocalWorkspace.createOrSelectStack(
16307
16502
  {
16308
16503
  stackName: metadata.services.email?.pulumiStackName || `wraps-${identity.accountId}-${region}`,
16309
16504
  projectName: "wraps-email",
@@ -16381,7 +16576,7 @@ ${pc21.bold("Cost Impact:")}`);
16381
16576
  "Updating Wraps infrastructure (this may take 2-3 minutes)",
16382
16577
  async () => {
16383
16578
  await ensurePulumiWorkDir();
16384
- const stack = await pulumi19.automation.LocalWorkspace.createOrSelectStack(
16579
+ const stack = await pulumi18.automation.LocalWorkspace.createOrSelectStack(
16385
16580
  {
16386
16581
  stackName: metadata.services.email?.pulumiStackName || `wraps-${identity.accountId}-${region}`,
16387
16582
  projectName: "wraps-email",
@@ -17478,7 +17673,7 @@ function buildConsolePolicyDocument(emailConfig, smsConfig) {
17478
17673
  // src/commands/shared/dashboard.ts
17479
17674
  init_esm_shims();
17480
17675
  import * as clack24 from "@clack/prompts";
17481
- import * as pulumi20 from "@pulumi/pulumi";
17676
+ import * as pulumi19 from "@pulumi/pulumi";
17482
17677
  import getPort from "get-port";
17483
17678
  import open from "open";
17484
17679
  import pc26 from "picocolors";
@@ -20014,7 +20209,7 @@ async function dashboard(options) {
20014
20209
  try {
20015
20210
  await ensurePulumiWorkDir();
20016
20211
  try {
20017
- const emailStack = await pulumi20.automation.LocalWorkspace.selectStack({
20212
+ const emailStack = await pulumi19.automation.LocalWorkspace.selectStack({
20018
20213
  stackName: `wraps-${identity.accountId}-${region}`,
20019
20214
  workDir: getPulumiWorkDir()
20020
20215
  });
@@ -20022,7 +20217,7 @@ async function dashboard(options) {
20022
20217
  } catch (_emailError) {
20023
20218
  }
20024
20219
  try {
20025
- const smsStack = await pulumi20.automation.LocalWorkspace.selectStack({
20220
+ const smsStack = await pulumi19.automation.LocalWorkspace.selectStack({
20026
20221
  stackName: `wraps-sms-${identity.accountId}-${region}`,
20027
20222
  workDir: getPulumiWorkDir()
20028
20223
  });
@@ -20030,7 +20225,7 @@ async function dashboard(options) {
20030
20225
  } catch (_smsError) {
20031
20226
  }
20032
20227
  try {
20033
- const cdnStack = await pulumi20.automation.LocalWorkspace.selectStack({
20228
+ const cdnStack = await pulumi19.automation.LocalWorkspace.selectStack({
20034
20229
  stackName: `wraps-cdn-${identity.accountId}-${region}`,
20035
20230
  workDir: getPulumiWorkDir()
20036
20231
  });
@@ -20207,7 +20402,7 @@ init_events();
20207
20402
  init_aws();
20208
20403
  init_fs();
20209
20404
  import * as clack26 from "@clack/prompts";
20210
- import * as pulumi21 from "@pulumi/pulumi";
20405
+ import * as pulumi20 from "@pulumi/pulumi";
20211
20406
  import pc28 from "picocolors";
20212
20407
  async function status(_options) {
20213
20408
  const startTime = Date.now();
@@ -20223,7 +20418,7 @@ async function status(_options) {
20223
20418
  const services = [];
20224
20419
  try {
20225
20420
  await ensurePulumiWorkDir();
20226
- const emailStack = await pulumi21.automation.LocalWorkspace.selectStack({
20421
+ const emailStack = await pulumi20.automation.LocalWorkspace.selectStack({
20227
20422
  stackName: `wraps-${identity.accountId}-${region}`,
20228
20423
  workDir: getPulumiWorkDir()
20229
20424
  });
@@ -20242,7 +20437,7 @@ async function status(_options) {
20242
20437
  services.push({ name: "Email", status: "not_deployed" });
20243
20438
  }
20244
20439
  try {
20245
- const smsStack = await pulumi21.automation.LocalWorkspace.selectStack({
20440
+ const smsStack = await pulumi20.automation.LocalWorkspace.selectStack({
20246
20441
  stackName: `wraps-sms-${identity.accountId}-${region}`,
20247
20442
  workDir: getPulumiWorkDir()
20248
20443
  });
@@ -20302,13 +20497,13 @@ ${pc28.bold("Dashboard:")} ${pc28.blue("https://app.wraps.dev")}`);
20302
20497
  // src/commands/sms/destroy.ts
20303
20498
  init_esm_shims();
20304
20499
  import * as clack27 from "@clack/prompts";
20305
- import * as pulumi23 from "@pulumi/pulumi";
20500
+ import * as pulumi22 from "@pulumi/pulumi";
20306
20501
  import pc29 from "picocolors";
20307
20502
 
20308
20503
  // src/infrastructure/sms-stack.ts
20309
20504
  init_esm_shims();
20310
20505
  import * as aws15 from "@pulumi/aws";
20311
- import * as pulumi22 from "@pulumi/pulumi";
20506
+ import * as pulumi21 from "@pulumi/pulumi";
20312
20507
  async function roleExists3(roleName) {
20313
20508
  try {
20314
20509
  const { IAMClient: IAMClient3, GetRoleCommand: GetRoleCommand2 } = await import("@aws-sdk/client-iam");
@@ -20342,7 +20537,7 @@ async function tableExists2(tableName) {
20342
20537
  async function createSMSIAMRole(config2) {
20343
20538
  let assumeRolePolicy;
20344
20539
  if (config2.provider === "vercel" && config2.oidcProvider) {
20345
- assumeRolePolicy = pulumi22.interpolate`{
20540
+ assumeRolePolicy = pulumi21.interpolate`{
20346
20541
  "Version": "2012-10-17",
20347
20542
  "Statement": [
20348
20543
  {
@@ -20370,7 +20565,7 @@ async function createSMSIAMRole(config2) {
20370
20565
  ]
20371
20566
  }`;
20372
20567
  } else if (config2.provider === "aws") {
20373
- assumeRolePolicy = pulumi22.output(`{
20568
+ assumeRolePolicy = pulumi21.output(`{
20374
20569
  "Version": "2012-10-17",
20375
20570
  "Statement": [{
20376
20571
  "Effect": "Allow",
@@ -20725,7 +20920,7 @@ async function createSMSSNSResources(config2) {
20725
20920
  });
20726
20921
  new aws15.sqs.QueuePolicy("wraps-sms-events-queue-policy", {
20727
20922
  queueUrl: config2.queueUrl,
20728
- policy: pulumi22.all([config2.queueArn, topic.arn]).apply(
20923
+ policy: pulumi21.all([config2.queueArn, topic.arn]).apply(
20729
20924
  ([queueArn, topicArn2]) => JSON.stringify({
20730
20925
  Version: "2012-10-17",
20731
20926
  Statement: [
@@ -20824,7 +21019,7 @@ async function deploySMSLambdaFunction(config2) {
20824
21019
  });
20825
21020
  new aws15.iam.RolePolicy("wraps-sms-lambda-policy", {
20826
21021
  role: lambdaRole.name,
20827
- policy: pulumi22.all([config2.tableName, config2.queueArn]).apply(
21022
+ policy: pulumi21.all([config2.tableName, config2.queueArn]).apply(
20828
21023
  ([tableName, queueArn]) => JSON.stringify({
20829
21024
  Version: "2012-10-17",
20830
21025
  Statement: [
@@ -20861,7 +21056,7 @@ async function deploySMSLambdaFunction(config2) {
20861
21056
  runtime: "nodejs20.x",
20862
21057
  handler: "index.handler",
20863
21058
  role: lambdaRole.arn,
20864
- code: new pulumi22.asset.FileArchive(codeDir),
21059
+ code: new pulumi21.asset.FileArchive(codeDir),
20865
21060
  timeout: 300,
20866
21061
  // 5 minutes
20867
21062
  memorySize: 512,
@@ -21272,7 +21467,7 @@ async function smsDestroy(options) {
21272
21467
  const stackName = storedStackName || `wraps-sms-${identity.accountId}-${region}`;
21273
21468
  let stack;
21274
21469
  try {
21275
- stack = await pulumi23.automation.LocalWorkspace.selectStack({
21470
+ stack = await pulumi22.automation.LocalWorkspace.selectStack({
21276
21471
  stackName,
21277
21472
  workDir: getPulumiWorkDir()
21278
21473
  });
@@ -21329,7 +21524,7 @@ async function smsDestroy(options) {
21329
21524
  const stackName = storedStackName || `wraps-sms-${identity.accountId}-${region}`;
21330
21525
  let stack;
21331
21526
  try {
21332
- stack = await pulumi23.automation.LocalWorkspace.selectStack({
21527
+ stack = await pulumi22.automation.LocalWorkspace.selectStack({
21333
21528
  stackName,
21334
21529
  workDir: getPulumiWorkDir()
21335
21530
  });
@@ -21386,7 +21581,7 @@ Run ${pc29.cyan("wraps sms init")} to deploy infrastructure again.
21386
21581
  // src/commands/sms/init.ts
21387
21582
  init_esm_shims();
21388
21583
  import * as clack28 from "@clack/prompts";
21389
- import * as pulumi24 from "@pulumi/pulumi";
21584
+ import * as pulumi23 from "@pulumi/pulumi";
21390
21585
  import pc30 from "picocolors";
21391
21586
  init_events();
21392
21587
  init_aws();
@@ -22033,7 +22228,7 @@ ${pc30.yellow(pc30.bold("Important Notes:"))}`);
22033
22228
  "Deploying SMS infrastructure (this may take 2-3 minutes)",
22034
22229
  async () => {
22035
22230
  await ensurePulumiWorkDir();
22036
- const stack = await pulumi24.automation.LocalWorkspace.createOrSelectStack(
22231
+ const stack = await pulumi23.automation.LocalWorkspace.createOrSelectStack(
22037
22232
  {
22038
22233
  stackName: `wraps-sms-${identity.accountId}-${region}`,
22039
22234
  projectName: "wraps-sms",
@@ -22379,7 +22574,7 @@ init_aws();
22379
22574
  init_fs();
22380
22575
  init_metadata();
22381
22576
  import * as clack30 from "@clack/prompts";
22382
- import * as pulumi25 from "@pulumi/pulumi";
22577
+ import * as pulumi24 from "@pulumi/pulumi";
22383
22578
  import pc32 from "picocolors";
22384
22579
  function displaySMSStatus(options) {
22385
22580
  const lines = [];
@@ -22444,7 +22639,7 @@ Run ${pc32.cyan("wraps sms init")} to deploy SMS infrastructure.
22444
22639
  try {
22445
22640
  await ensurePulumiWorkDir();
22446
22641
  const stackName = metadata.services.sms.pulumiStackName || `wraps-sms-${identity.accountId}-${region}`;
22447
- const stack = await pulumi25.automation.LocalWorkspace.selectStack({
22642
+ const stack = await pulumi24.automation.LocalWorkspace.selectStack({
22448
22643
  stackName,
22449
22644
  workDir: getPulumiWorkDir()
22450
22645
  });
@@ -22483,7 +22678,7 @@ Run ${pc32.cyan("wraps sms init")} to deploy SMS infrastructure.
22483
22678
  // src/commands/sms/sync.ts
22484
22679
  init_esm_shims();
22485
22680
  import * as clack31 from "@clack/prompts";
22486
- import * as pulumi26 from "@pulumi/pulumi";
22681
+ import * as pulumi25 from "@pulumi/pulumi";
22487
22682
  import pc33 from "picocolors";
22488
22683
  init_events();
22489
22684
  init_aws();
@@ -22541,7 +22736,7 @@ Run ${pc33.cyan("wraps sms init")} to deploy SMS infrastructure first.
22541
22736
  outputs = await progress.execute("Syncing SMS infrastructure", async () => {
22542
22737
  await ensurePulumiWorkDir();
22543
22738
  const stackName = storedStackName || `wraps-sms-${identity.accountId}-${region}`;
22544
- const stack = await pulumi26.automation.LocalWorkspace.createOrSelectStack(
22739
+ const stack = await pulumi25.automation.LocalWorkspace.createOrSelectStack(
22545
22740
  {
22546
22741
  stackName,
22547
22742
  projectName: "wraps-sms",
@@ -22862,7 +23057,7 @@ Run ${pc34.cyan("wraps sms register")} to complete registration.
22862
23057
  // src/commands/sms/upgrade.ts
22863
23058
  init_esm_shims();
22864
23059
  import * as clack33 from "@clack/prompts";
22865
- import * as pulumi27 from "@pulumi/pulumi";
23060
+ import * as pulumi26 from "@pulumi/pulumi";
22866
23061
  import pc35 from "picocolors";
22867
23062
  init_events();
22868
23063
  init_aws();
@@ -23605,7 +23800,7 @@ ${pc35.bold("Cost Impact:")}`);
23605
23800
  "Updating SMS infrastructure (this may take 2-3 minutes)",
23606
23801
  async () => {
23607
23802
  await ensurePulumiWorkDir();
23608
- const stack = await pulumi27.automation.LocalWorkspace.createOrSelectStack(
23803
+ const stack = await pulumi26.automation.LocalWorkspace.createOrSelectStack(
23609
23804
  {
23610
23805
  stackName: metadata.services.sms?.pulumiStackName || `wraps-sms-${identity.accountId}-${region}`,
23611
23806
  projectName: "wraps-sms",
@@ -24270,6 +24465,20 @@ function printCompletionScript() {
24270
24465
 
24271
24466
  // src/cli.ts
24272
24467
  init_errors();
24468
+ var [nodeMajorVersion] = process.versions.node.split(".").map(Number);
24469
+ if (nodeMajorVersion < 20) {
24470
+ console.error(
24471
+ "\x1B[31mError: Wraps CLI requires Node.js 20 or higher.\x1B[0m"
24472
+ );
24473
+ console.error(`Current version: ${process.versions.node}`);
24474
+ console.error("");
24475
+ console.error("To upgrade Node.js:");
24476
+ console.error(" macOS/Linux (nvm): nvm install 20 && nvm use 20");
24477
+ console.error(" macOS (Homebrew): brew install node@20");
24478
+ console.error(" Windows: Download from https://nodejs.org/");
24479
+ console.error("");
24480
+ process.exit(1);
24481
+ }
24273
24482
  var __filename2 = fileURLToPath4(import.meta.url);
24274
24483
  var __dirname3 = dirname2(__filename2);
24275
24484
  var packageJson = JSON.parse(