@wraps.dev/cli 0.1.3 → 0.1.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
@@ -18,6 +18,205 @@ var init_esm_shims = __esm({
18
18
  }
19
19
  });
20
20
 
21
+ // src/utils/errors.ts
22
+ import * as clack from "@clack/prompts";
23
+ import pc from "picocolors";
24
+ function handleCLIError(error) {
25
+ console.error("");
26
+ if (error instanceof WrapsError) {
27
+ clack.log.error(error.message);
28
+ if (error.suggestion) {
29
+ console.log(`
30
+ ${pc.yellow("Suggestion:")}`);
31
+ console.log(` ${pc.white(error.suggestion)}
32
+ `);
33
+ }
34
+ if (error.docsUrl) {
35
+ console.log(`${pc.dim("Documentation:")}`);
36
+ console.log(` ${pc.blue(error.docsUrl)}
37
+ `);
38
+ }
39
+ process.exit(1);
40
+ }
41
+ clack.log.error("An unexpected error occurred");
42
+ console.error(error);
43
+ console.log(`
44
+ ${pc.dim("If this persists, please report at:")}`);
45
+ console.log(` ${pc.blue("https://github.com/wraps-team/wraps/issues")}
46
+ `);
47
+ process.exit(1);
48
+ }
49
+ var WrapsError, errors;
50
+ var init_errors = __esm({
51
+ "src/utils/errors.ts"() {
52
+ "use strict";
53
+ init_esm_shims();
54
+ WrapsError = class extends Error {
55
+ constructor(message, code, suggestion, docsUrl) {
56
+ super(message);
57
+ this.code = code;
58
+ this.suggestion = suggestion;
59
+ this.docsUrl = docsUrl;
60
+ this.name = "WrapsError";
61
+ }
62
+ };
63
+ errors = {
64
+ noAWSCredentials: () => new WrapsError(
65
+ "AWS credentials not found",
66
+ "NO_AWS_CREDENTIALS",
67
+ "Run: aws configure\nOr set AWS_PROFILE environment variable",
68
+ "https://docs.wraps.dev/setup/aws-credentials"
69
+ ),
70
+ stackExists: (stackName) => new WrapsError(
71
+ `Stack "${stackName}" already exists`,
72
+ "STACK_EXISTS",
73
+ `To update: wraps upgrade
74
+ To remove: wraps destroy --stack ${stackName}`,
75
+ "https://docs.wraps.dev/cli/upgrade"
76
+ ),
77
+ invalidRegion: (region) => new WrapsError(
78
+ `Invalid AWS region: ${region}`,
79
+ "INVALID_REGION",
80
+ "Use a valid AWS region like: us-east-1, eu-west-1, ap-southeast-1",
81
+ "https://docs.aws.amazon.com/general/latest/gr/rande.html"
82
+ ),
83
+ pulumiError: (message) => new WrapsError(
84
+ `Infrastructure deployment failed: ${message}`,
85
+ "PULUMI_ERROR",
86
+ "Check your AWS permissions and try again",
87
+ "https://docs.wraps.dev/troubleshooting"
88
+ ),
89
+ noStack: () => new WrapsError(
90
+ "No Wraps infrastructure found in this AWS account",
91
+ "NO_STACK",
92
+ "Run: wraps init\nTo deploy new infrastructure",
93
+ "https://docs.wraps.dev/cli/init"
94
+ ),
95
+ pulumiNotInstalled: () => new WrapsError(
96
+ "Pulumi CLI is not installed",
97
+ "PULUMI_NOT_INSTALLED",
98
+ "Install Pulumi:\n macOS: brew install pulumi/tap/pulumi\n Linux: curl -fsSL https://get.pulumi.com | sh\n Windows: choco install pulumi\n\nOr download from: https://www.pulumi.com/docs/install/",
99
+ "https://www.pulumi.com/docs/install/"
100
+ )
101
+ };
102
+ }
103
+ });
104
+
105
+ // src/utils/aws.ts
106
+ var aws_exports = {};
107
+ __export(aws_exports, {
108
+ checkRegion: () => checkRegion,
109
+ getAWSRegion: () => getAWSRegion,
110
+ isSESSandbox: () => isSESSandbox,
111
+ listSESDomains: () => listSESDomains,
112
+ validateAWSCredentials: () => validateAWSCredentials
113
+ });
114
+ import {
115
+ GetIdentityVerificationAttributesCommand,
116
+ ListIdentitiesCommand,
117
+ SESClient
118
+ } from "@aws-sdk/client-ses";
119
+ import { GetCallerIdentityCommand, STSClient } from "@aws-sdk/client-sts";
120
+ async function validateAWSCredentials() {
121
+ const sts = new STSClient({ region: "us-east-1" });
122
+ try {
123
+ const identity = await sts.send(new GetCallerIdentityCommand({}));
124
+ return {
125
+ accountId: identity.Account,
126
+ userId: identity.UserId,
127
+ arn: identity.Arn
128
+ };
129
+ } catch (_error) {
130
+ throw errors.noAWSCredentials();
131
+ }
132
+ }
133
+ async function checkRegion(region) {
134
+ const validRegions = [
135
+ "us-east-1",
136
+ "us-east-2",
137
+ "us-west-1",
138
+ "us-west-2",
139
+ "af-south-1",
140
+ "ap-east-1",
141
+ "ap-south-1",
142
+ "ap-northeast-1",
143
+ "ap-northeast-2",
144
+ "ap-northeast-3",
145
+ "ap-southeast-1",
146
+ "ap-southeast-2",
147
+ "ap-southeast-3",
148
+ "ca-central-1",
149
+ "eu-central-1",
150
+ "eu-west-1",
151
+ "eu-west-2",
152
+ "eu-west-3",
153
+ "eu-south-1",
154
+ "eu-north-1",
155
+ "me-south-1",
156
+ "sa-east-1"
157
+ ];
158
+ return validRegions.includes(region);
159
+ }
160
+ async function getAWSRegion() {
161
+ if (process.env.AWS_REGION) {
162
+ return process.env.AWS_REGION;
163
+ }
164
+ if (process.env.AWS_DEFAULT_REGION) {
165
+ return process.env.AWS_DEFAULT_REGION;
166
+ }
167
+ return "us-east-1";
168
+ }
169
+ async function listSESDomains(region) {
170
+ const ses = new SESClient({ region });
171
+ try {
172
+ const identitiesResponse = await ses.send(
173
+ new ListIdentitiesCommand({
174
+ IdentityType: "Domain"
175
+ })
176
+ );
177
+ const identities = identitiesResponse.Identities || [];
178
+ if (identities.length === 0) {
179
+ return [];
180
+ }
181
+ const attributesResponse = await ses.send(
182
+ new GetIdentityVerificationAttributesCommand({
183
+ Identities: identities
184
+ })
185
+ );
186
+ const attributes = attributesResponse.VerificationAttributes || {};
187
+ return identities.map((domain) => ({
188
+ domain,
189
+ verified: attributes[domain]?.VerificationStatus === "Success"
190
+ }));
191
+ } catch (error) {
192
+ console.error("Error listing SES domains:", error);
193
+ return [];
194
+ }
195
+ }
196
+ async function isSESSandbox(region) {
197
+ const ses = new SESClient({ region });
198
+ try {
199
+ await ses.send(
200
+ new ListIdentitiesCommand({
201
+ MaxItems: 1
202
+ })
203
+ );
204
+ return false;
205
+ } catch (error) {
206
+ if (error.name === "InvalidParameterValue") {
207
+ return true;
208
+ }
209
+ throw error;
210
+ }
211
+ }
212
+ var init_aws = __esm({
213
+ "src/utils/aws.ts"() {
214
+ "use strict";
215
+ init_esm_shims();
216
+ init_errors();
217
+ }
218
+ });
219
+
21
220
  // src/utils/costs.ts
22
221
  function estimateStorageSize(emailsPerMonth, retention, numEventTypes = 8) {
23
222
  const avgRecordSizeKB = 2;
@@ -1149,9 +1348,12 @@ var init_route53 = __esm({
1149
1348
 
1150
1349
  // src/cli.ts
1151
1350
  init_esm_shims();
1152
- import * as clack12 from "@clack/prompts";
1351
+ import { readFileSync } from "fs";
1352
+ import { dirname as dirname2, join as join4 } from "path";
1353
+ import { fileURLToPath as fileURLToPath4 } from "url";
1354
+ import * as clack13 from "@clack/prompts";
1153
1355
  import args from "args";
1154
- import pc12 from "picocolors";
1356
+ import pc13 from "picocolors";
1155
1357
 
1156
1358
  // src/commands/connect.ts
1157
1359
  init_esm_shims();
@@ -1333,7 +1535,10 @@ async function createIAMRole(config) {
1333
1535
  "dynamodb:Scan",
1334
1536
  "dynamodb:BatchGetItem"
1335
1537
  ],
1336
- Resource: "arn:aws:dynamodb:*:*:table/wraps-email-*"
1538
+ Resource: [
1539
+ "arn:aws:dynamodb:*:*:table/wraps-email-*",
1540
+ "arn:aws:dynamodb:*:*:table/wraps-email-*/index/*"
1541
+ ]
1337
1542
  });
1338
1543
  }
1339
1544
  if (config.emailConfig.eventTracking?.enabled) {
@@ -1729,143 +1934,8 @@ async function deployEmailStack(config) {
1729
1934
  };
1730
1935
  }
1731
1936
 
1732
- // src/utils/aws.ts
1733
- init_esm_shims();
1734
- import {
1735
- GetIdentityVerificationAttributesCommand,
1736
- ListIdentitiesCommand,
1737
- SESClient
1738
- } from "@aws-sdk/client-ses";
1739
- import { GetCallerIdentityCommand, STSClient } from "@aws-sdk/client-sts";
1740
-
1741
- // src/utils/errors.ts
1742
- init_esm_shims();
1743
- import * as clack from "@clack/prompts";
1744
- import pc from "picocolors";
1745
- var WrapsError = class extends Error {
1746
- constructor(message, code, suggestion, docsUrl) {
1747
- super(message);
1748
- this.code = code;
1749
- this.suggestion = suggestion;
1750
- this.docsUrl = docsUrl;
1751
- this.name = "WrapsError";
1752
- }
1753
- };
1754
- function handleCLIError(error) {
1755
- console.error("");
1756
- if (error instanceof WrapsError) {
1757
- clack.log.error(error.message);
1758
- if (error.suggestion) {
1759
- console.log(`
1760
- ${pc.yellow("Suggestion:")}`);
1761
- console.log(` ${pc.white(error.suggestion)}
1762
- `);
1763
- }
1764
- if (error.docsUrl) {
1765
- console.log(`${pc.dim("Documentation:")}`);
1766
- console.log(` ${pc.blue(error.docsUrl)}
1767
- `);
1768
- }
1769
- process.exit(1);
1770
- }
1771
- clack.log.error("An unexpected error occurred");
1772
- console.error(error);
1773
- console.log(`
1774
- ${pc.dim("If this persists, please report at:")}`);
1775
- console.log(` ${pc.blue("https://github.com/wraps-team/wraps/issues")}
1776
- `);
1777
- process.exit(1);
1778
- }
1779
- var errors = {
1780
- noAWSCredentials: () => new WrapsError(
1781
- "AWS credentials not found",
1782
- "NO_AWS_CREDENTIALS",
1783
- "Run: aws configure\nOr set AWS_PROFILE environment variable",
1784
- "https://docs.wraps.dev/setup/aws-credentials"
1785
- ),
1786
- stackExists: (stackName) => new WrapsError(
1787
- `Stack "${stackName}" already exists`,
1788
- "STACK_EXISTS",
1789
- `To update: wraps upgrade
1790
- To remove: wraps destroy --stack ${stackName}`,
1791
- "https://docs.wraps.dev/cli/upgrade"
1792
- ),
1793
- invalidRegion: (region) => new WrapsError(
1794
- `Invalid AWS region: ${region}`,
1795
- "INVALID_REGION",
1796
- "Use a valid AWS region like: us-east-1, eu-west-1, ap-southeast-1",
1797
- "https://docs.aws.amazon.com/general/latest/gr/rande.html"
1798
- ),
1799
- pulumiError: (message) => new WrapsError(
1800
- `Infrastructure deployment failed: ${message}`,
1801
- "PULUMI_ERROR",
1802
- "Check your AWS permissions and try again",
1803
- "https://docs.wraps.dev/troubleshooting"
1804
- ),
1805
- noStack: () => new WrapsError(
1806
- "No Wraps infrastructure found in this AWS account",
1807
- "NO_STACK",
1808
- "Run: wraps init\nTo deploy new infrastructure",
1809
- "https://docs.wraps.dev/cli/init"
1810
- ),
1811
- pulumiNotInstalled: () => new WrapsError(
1812
- "Pulumi CLI is not installed",
1813
- "PULUMI_NOT_INSTALLED",
1814
- "Install Pulumi:\n macOS: brew install pulumi/tap/pulumi\n Linux: curl -fsSL https://get.pulumi.com | sh\n Windows: choco install pulumi\n\nOr download from: https://www.pulumi.com/docs/install/",
1815
- "https://www.pulumi.com/docs/install/"
1816
- )
1817
- };
1818
-
1819
- // src/utils/aws.ts
1820
- async function validateAWSCredentials() {
1821
- const sts = new STSClient({ region: "us-east-1" });
1822
- try {
1823
- const identity = await sts.send(new GetCallerIdentityCommand({}));
1824
- return {
1825
- accountId: identity.Account,
1826
- userId: identity.UserId,
1827
- arn: identity.Arn
1828
- };
1829
- } catch (_error) {
1830
- throw errors.noAWSCredentials();
1831
- }
1832
- }
1833
- async function getAWSRegion() {
1834
- if (process.env.AWS_REGION) {
1835
- return process.env.AWS_REGION;
1836
- }
1837
- if (process.env.AWS_DEFAULT_REGION) {
1838
- return process.env.AWS_DEFAULT_REGION;
1839
- }
1840
- return "us-east-1";
1841
- }
1842
- async function listSESDomains(region) {
1843
- const ses = new SESClient({ region });
1844
- try {
1845
- const identitiesResponse = await ses.send(
1846
- new ListIdentitiesCommand({
1847
- IdentityType: "Domain"
1848
- })
1849
- );
1850
- const identities = identitiesResponse.Identities || [];
1851
- if (identities.length === 0) {
1852
- return [];
1853
- }
1854
- const attributesResponse = await ses.send(
1855
- new GetIdentityVerificationAttributesCommand({
1856
- Identities: identities
1857
- })
1858
- );
1859
- const attributes = attributesResponse.VerificationAttributes || {};
1860
- return identities.map((domain) => ({
1861
- domain,
1862
- verified: attributes[domain]?.VerificationStatus === "Success"
1863
- }));
1864
- } catch (error) {
1865
- console.error("Error listing SES domains:", error);
1866
- return [];
1867
- }
1868
- }
1937
+ // src/commands/connect.ts
1938
+ init_aws();
1869
1939
 
1870
1940
  // src/utils/fs.ts
1871
1941
  init_esm_shims();
@@ -2234,6 +2304,7 @@ init_prompts();
2234
2304
 
2235
2305
  // src/utils/pulumi.ts
2236
2306
  init_esm_shims();
2307
+ init_errors();
2237
2308
  import { exec } from "child_process";
2238
2309
  import { promisify } from "util";
2239
2310
  import * as automation2 from "@pulumi/pulumi/automation/index.js";
@@ -3802,6 +3873,7 @@ async function startConsoleServer(config) {
3802
3873
  }
3803
3874
 
3804
3875
  // src/commands/console.ts
3876
+ init_aws();
3805
3877
  async function runConsole(options) {
3806
3878
  clack5.intro(pc5.bold("Wraps Console"));
3807
3879
  const progress = new DeploymentProgress();
@@ -3853,6 +3925,7 @@ async function runConsole(options) {
3853
3925
 
3854
3926
  // src/commands/destroy.ts
3855
3927
  init_esm_shims();
3928
+ init_aws();
3856
3929
  import * as clack6 from "@clack/prompts";
3857
3930
  import * as pulumi7 from "@pulumi/pulumi";
3858
3931
  import pc6 from "picocolors";
@@ -3921,6 +3994,7 @@ init_esm_shims();
3921
3994
  import * as clack7 from "@clack/prompts";
3922
3995
  import * as pulumi8 from "@pulumi/pulumi";
3923
3996
  import pc7 from "picocolors";
3997
+ init_aws();
3924
3998
  init_costs();
3925
3999
  init_presets();
3926
4000
  init_prompts();
@@ -4127,6 +4201,7 @@ async function init(options) {
4127
4201
 
4128
4202
  // src/commands/restore.ts
4129
4203
  init_esm_shims();
4204
+ init_aws();
4130
4205
  import * as clack8 from "@clack/prompts";
4131
4206
  import * as pulumi9 from "@pulumi/pulumi";
4132
4207
  import pc8 from "picocolors";
@@ -4231,6 +4306,7 @@ ${pc8.green("\u2713")} ${pc8.bold("Infrastructure removed successfully!")}
4231
4306
 
4232
4307
  // src/commands/status.ts
4233
4308
  init_esm_shims();
4309
+ init_aws();
4234
4310
  import * as clack9 from "@clack/prompts";
4235
4311
  import * as pulumi10 from "@pulumi/pulumi";
4236
4312
  import pc9 from "picocolors";
@@ -4296,16 +4372,14 @@ Run ${pc9.cyan("wraps init")} to deploy infrastructure.
4296
4372
  });
4297
4373
  }
4298
4374
 
4299
- // src/commands/upgrade.ts
4375
+ // src/commands/update.ts
4300
4376
  init_esm_shims();
4301
4377
  import * as clack10 from "@clack/prompts";
4302
4378
  import * as pulumi11 from "@pulumi/pulumi";
4303
4379
  import pc10 from "picocolors";
4304
- init_costs();
4305
- init_presets();
4306
- init_prompts();
4307
- async function upgrade(options) {
4308
- clack10.intro(pc10.bold("Wraps Upgrade - Enhance Your Email Infrastructure"));
4380
+ init_aws();
4381
+ async function update(options) {
4382
+ clack10.intro(pc10.bold("Wraps Update - Apply CLI Updates to Infrastructure"));
4309
4383
  const progress = new DeploymentProgress();
4310
4384
  const wasAutoInstalled = await progress.execute(
4311
4385
  "Checking Pulumi CLI installation",
@@ -4349,33 +4423,224 @@ ${pc10.bold("Current Configuration:")}
4349
4423
  }
4350
4424
  if (config.tracking?.enabled) {
4351
4425
  console.log(` ${pc10.green("\u2713")} Open & Click Tracking`);
4426
+ }
4427
+ if (config.suppressionList?.enabled) {
4428
+ console.log(` ${pc10.green("\u2713")} Bounce/Complaint Suppression`);
4429
+ }
4430
+ if (config.eventTracking?.enabled) {
4431
+ console.log(` ${pc10.green("\u2713")} Event Tracking (EventBridge)`);
4432
+ }
4433
+ if (config.dedicatedIp) {
4434
+ console.log(` ${pc10.green("\u2713")} Dedicated IP Address`);
4435
+ }
4436
+ console.log("");
4437
+ console.log(`${pc10.bold("What will be updated:")}
4438
+ `);
4439
+ console.log(
4440
+ ` ${pc10.cyan("\u2022")} Lambda function code (if event tracking enabled)`
4441
+ );
4442
+ console.log(
4443
+ ` ${pc10.cyan("\u2022")} EventBridge rules (if event tracking enabled)`
4444
+ );
4445
+ console.log(` ${pc10.cyan("\u2022")} IAM policies (security improvements)`);
4446
+ console.log(` ${pc10.cyan("\u2022")} SES configuration set (feature updates)`);
4447
+ console.log("");
4448
+ progress.info(
4449
+ "Your current configuration will be preserved - no features will be added or removed"
4450
+ );
4451
+ console.log("");
4452
+ if (!options.yes) {
4453
+ const confirmed = await clack10.confirm({
4454
+ message: "Proceed with update?",
4455
+ initialValue: true
4456
+ });
4457
+ if (clack10.isCancel(confirmed) || !confirmed) {
4458
+ clack10.cancel("Update cancelled.");
4459
+ process.exit(0);
4460
+ }
4461
+ }
4462
+ let vercelConfig;
4463
+ if (metadata.provider === "vercel" && metadata.vercel) {
4464
+ vercelConfig = metadata.vercel;
4465
+ }
4466
+ const stackConfig = {
4467
+ provider: metadata.provider,
4468
+ region,
4469
+ vercel: vercelConfig,
4470
+ emailConfig: config
4471
+ };
4472
+ let outputs;
4473
+ try {
4474
+ outputs = await progress.execute(
4475
+ "Updating Wraps infrastructure (this may take 2-3 minutes)",
4476
+ async () => {
4477
+ await ensurePulumiWorkDir();
4478
+ const stack = await pulumi11.automation.LocalWorkspace.createOrSelectStack(
4479
+ {
4480
+ stackName: metadata.pulumiStackName || `wraps-${identity.accountId}-${region}`,
4481
+ projectName: "wraps-email",
4482
+ program: async () => {
4483
+ const result = await deployEmailStack(stackConfig);
4484
+ return {
4485
+ roleArn: result.roleArn,
4486
+ configSetName: result.configSetName,
4487
+ tableName: result.tableName,
4488
+ region: result.region,
4489
+ lambdaFunctions: result.lambdaFunctions,
4490
+ domain: result.domain,
4491
+ dkimTokens: result.dkimTokens,
4492
+ customTrackingDomain: result.customTrackingDomain
4493
+ };
4494
+ }
4495
+ },
4496
+ {
4497
+ workDir: getPulumiWorkDir(),
4498
+ envVars: {
4499
+ PULUMI_CONFIG_PASSPHRASE: ""
4500
+ },
4501
+ secretsProvider: "passphrase"
4502
+ }
4503
+ );
4504
+ await stack.workspace.selectStack(
4505
+ metadata.pulumiStackName || `wraps-${identity.accountId}-${region}`
4506
+ );
4507
+ await stack.setConfig("aws:region", { value: region });
4508
+ const upResult = await stack.up({ onOutput: () => {
4509
+ } });
4510
+ const pulumiOutputs = upResult.outputs;
4511
+ return {
4512
+ roleArn: pulumiOutputs.roleArn?.value,
4513
+ configSetName: pulumiOutputs.configSetName?.value,
4514
+ tableName: pulumiOutputs.tableName?.value,
4515
+ region: pulumiOutputs.region?.value,
4516
+ lambdaFunctions: pulumiOutputs.lambdaFunctions?.value,
4517
+ domain: pulumiOutputs.domain?.value,
4518
+ dkimTokens: pulumiOutputs.dkimTokens?.value,
4519
+ customTrackingDomain: pulumiOutputs.customTrackingDomain?.value
4520
+ };
4521
+ }
4522
+ );
4523
+ } catch (error) {
4524
+ clack10.log.error("Infrastructure update failed");
4525
+ if (error.message?.includes("stack is currently locked")) {
4526
+ clack10.log.warn("\nThe Pulumi stack is locked from a previous run.");
4527
+ clack10.log.info("To fix this, run:");
4528
+ clack10.log.info(` ${pc10.cyan("rm -rf ~/.wraps/pulumi/.pulumi/locks")}`);
4529
+ clack10.log.info("\nThen try running wraps update again.");
4530
+ }
4531
+ throw new Error(`Pulumi update failed: ${error.message}`);
4532
+ }
4533
+ metadata.timestamp = (/* @__PURE__ */ new Date()).toISOString();
4534
+ await saveConnectionMetadata(metadata);
4535
+ progress.info("Connection metadata updated");
4536
+ displaySuccess({
4537
+ roleArn: outputs.roleArn,
4538
+ configSetName: outputs.configSetName,
4539
+ region: outputs.region,
4540
+ tableName: outputs.tableName,
4541
+ customTrackingDomain: outputs.customTrackingDomain
4542
+ });
4543
+ console.log(`
4544
+ ${pc10.green("\u2713")} ${pc10.bold("Update complete!")}
4545
+ `);
4546
+ console.log(
4547
+ "Infrastructure has been updated with the latest CLI improvements.\n"
4548
+ );
4549
+ console.log(`${pc10.bold("Next steps:")}
4550
+ `);
4551
+ console.log(
4552
+ ` ${pc10.cyan("1.")} No code changes needed - your existing SDK integration continues to work`
4553
+ );
4554
+ console.log(
4555
+ ` ${pc10.cyan("2.")} Check ${pc10.cyan("wraps status")} to verify all resources are healthy`
4556
+ );
4557
+ console.log(
4558
+ ` ${pc10.cyan("3.")} View analytics at ${pc10.cyan("wraps console")}
4559
+ `
4560
+ );
4561
+ }
4562
+
4563
+ // src/commands/upgrade.ts
4564
+ init_esm_shims();
4565
+ import * as clack11 from "@clack/prompts";
4566
+ import * as pulumi12 from "@pulumi/pulumi";
4567
+ import pc11 from "picocolors";
4568
+ init_aws();
4569
+ init_costs();
4570
+ init_presets();
4571
+ init_prompts();
4572
+ async function upgrade(options) {
4573
+ clack11.intro(pc11.bold("Wraps Upgrade - Enhance Your Email Infrastructure"));
4574
+ const progress = new DeploymentProgress();
4575
+ const wasAutoInstalled = await progress.execute(
4576
+ "Checking Pulumi CLI installation",
4577
+ async () => await ensurePulumiInstalled()
4578
+ );
4579
+ if (wasAutoInstalled) {
4580
+ progress.info("Pulumi CLI was automatically installed");
4581
+ }
4582
+ const identity = await progress.execute(
4583
+ "Validating AWS credentials",
4584
+ async () => validateAWSCredentials()
4585
+ );
4586
+ progress.info(`Connected to AWS account: ${pc11.cyan(identity.accountId)}`);
4587
+ let region = options.region;
4588
+ if (!region) {
4589
+ const defaultRegion = await getAWSRegion();
4590
+ region = defaultRegion;
4591
+ }
4592
+ const metadata = await loadConnectionMetadata(identity.accountId, region);
4593
+ if (!metadata) {
4594
+ clack11.log.error(
4595
+ `No Wraps connection found for account ${pc11.cyan(identity.accountId)} in region ${pc11.cyan(region)}`
4596
+ );
4597
+ clack11.log.info(
4598
+ `Use ${pc11.cyan("wraps init")} to create new infrastructure or ${pc11.cyan("wraps connect")} to connect existing.`
4599
+ );
4600
+ process.exit(1);
4601
+ }
4602
+ progress.info(`Found existing connection created: ${metadata.timestamp}`);
4603
+ console.log(`
4604
+ ${pc11.bold("Current Configuration:")}
4605
+ `);
4606
+ if (metadata.preset) {
4607
+ console.log(` Preset: ${pc11.cyan(metadata.preset)}`);
4608
+ } else {
4609
+ console.log(` Preset: ${pc11.cyan("custom")}`);
4610
+ }
4611
+ const config = metadata.emailConfig;
4612
+ if (config.domain) {
4613
+ console.log(` Sending Domain: ${pc11.cyan(config.domain)}`);
4614
+ }
4615
+ if (config.tracking?.enabled) {
4616
+ console.log(` ${pc11.green("\u2713")} Open & Click Tracking`);
4352
4617
  if (config.tracking.customRedirectDomain) {
4353
4618
  console.log(
4354
- ` ${pc10.dim("\u2514\u2500")} Custom domain: ${pc10.cyan(config.tracking.customRedirectDomain)}`
4619
+ ` ${pc11.dim("\u2514\u2500")} Custom domain: ${pc11.cyan(config.tracking.customRedirectDomain)}`
4355
4620
  );
4356
4621
  }
4357
4622
  }
4358
4623
  if (config.suppressionList?.enabled) {
4359
- console.log(` ${pc10.green("\u2713")} Bounce/Complaint Suppression`);
4624
+ console.log(` ${pc11.green("\u2713")} Bounce/Complaint Suppression`);
4360
4625
  }
4361
4626
  if (config.eventTracking?.enabled) {
4362
- console.log(` ${pc10.green("\u2713")} Event Tracking (EventBridge)`);
4627
+ console.log(` ${pc11.green("\u2713")} Event Tracking (EventBridge)`);
4363
4628
  if (config.eventTracking.dynamoDBHistory) {
4364
4629
  console.log(
4365
- ` ${pc10.dim("\u2514\u2500")} Email History: ${pc10.cyan(config.eventTracking.archiveRetention || "90days")}`
4630
+ ` ${pc11.dim("\u2514\u2500")} Email History: ${pc11.cyan(config.eventTracking.archiveRetention || "90days")}`
4366
4631
  );
4367
4632
  }
4368
4633
  }
4369
4634
  if (config.dedicatedIp) {
4370
- console.log(` ${pc10.green("\u2713")} Dedicated IP Address`);
4635
+ console.log(` ${pc11.green("\u2713")} Dedicated IP Address`);
4371
4636
  }
4372
4637
  const currentCostData = calculateCosts(config, 5e4);
4373
4638
  console.log(
4374
4639
  `
4375
- Estimated Cost: ${pc10.cyan(`~${formatCost(currentCostData.total.monthly)}/mo`)}`
4640
+ Estimated Cost: ${pc11.cyan(`~${formatCost(currentCostData.total.monthly)}/mo`)}`
4376
4641
  );
4377
4642
  console.log("");
4378
- const upgradeAction = await clack10.select({
4643
+ const upgradeAction = await clack11.select({
4379
4644
  message: "What would you like to do?",
4380
4645
  options: [
4381
4646
  {
@@ -4410,8 +4675,8 @@ ${pc10.bold("Current Configuration:")}
4410
4675
  }
4411
4676
  ]
4412
4677
  });
4413
- if (clack10.isCancel(upgradeAction)) {
4414
- clack10.cancel("Upgrade cancelled.");
4678
+ if (clack11.isCancel(upgradeAction)) {
4679
+ clack11.cancel("Upgrade cancelled.");
4415
4680
  process.exit(0);
4416
4681
  }
4417
4682
  let updatedConfig = { ...config };
@@ -4429,15 +4694,15 @@ ${pc10.bold("Current Configuration:")}
4429
4694
  disabled: currentPresetIdx >= 0 && idx <= currentPresetIdx ? "Current or lower tier" : void 0
4430
4695
  })).filter((p) => !p.disabled);
4431
4696
  if (availablePresets.length === 0) {
4432
- clack10.log.warn("Already on highest preset (Enterprise)");
4697
+ clack11.log.warn("Already on highest preset (Enterprise)");
4433
4698
  process.exit(0);
4434
4699
  }
4435
- const selectedPreset = await clack10.select({
4700
+ const selectedPreset = await clack11.select({
4436
4701
  message: "Select new preset:",
4437
4702
  options: availablePresets
4438
4703
  });
4439
- if (clack10.isCancel(selectedPreset)) {
4440
- clack10.cancel("Upgrade cancelled.");
4704
+ if (clack11.isCancel(selectedPreset)) {
4705
+ clack11.cancel("Upgrade cancelled.");
4441
4706
  process.exit(0);
4442
4707
  }
4443
4708
  const presetConfig = getPreset(selectedPreset);
@@ -4450,7 +4715,37 @@ ${pc10.bold("Current Configuration:")}
4450
4715
  break;
4451
4716
  }
4452
4717
  case "tracking-domain": {
4453
- const trackingDomain = await clack10.text({
4718
+ if (!config.domain) {
4719
+ clack11.log.error(
4720
+ "No sending domain configured. You must configure a sending domain before adding a custom tracking domain."
4721
+ );
4722
+ clack11.log.info(
4723
+ `Use ${pc11.cyan("wraps init")} to set up a sending domain first.`
4724
+ );
4725
+ process.exit(1);
4726
+ }
4727
+ const { listSESDomains: listSESDomains2 } = await Promise.resolve().then(() => (init_aws(), aws_exports));
4728
+ const domains = await progress.execute(
4729
+ "Checking domain verification status",
4730
+ async () => await listSESDomains2(region)
4731
+ );
4732
+ const sendingDomain = domains.find((d) => d.domain === config.domain);
4733
+ if (!sendingDomain?.verified) {
4734
+ clack11.log.error(
4735
+ `Sending domain ${pc11.cyan(config.domain)} is not verified.`
4736
+ );
4737
+ clack11.log.info(
4738
+ "You must verify your sending domain before adding a custom tracking domain."
4739
+ );
4740
+ clack11.log.info(
4741
+ `Use ${pc11.cyan("wraps verify")} to check DNS records and complete verification.`
4742
+ );
4743
+ process.exit(1);
4744
+ }
4745
+ progress.info(
4746
+ `Sending domain ${pc11.cyan(config.domain)} is verified ${pc11.green("\u2713")}`
4747
+ );
4748
+ const trackingDomain = await clack11.text({
4454
4749
  message: "Custom tracking redirect domain:",
4455
4750
  placeholder: "track.yourdomain.com",
4456
4751
  initialValue: config.tracking?.customRedirectDomain || "",
@@ -4460,8 +4755,8 @@ ${pc10.bold("Current Configuration:")}
4460
4755
  }
4461
4756
  }
4462
4757
  });
4463
- if (clack10.isCancel(trackingDomain)) {
4464
- clack10.cancel("Upgrade cancelled.");
4758
+ if (clack11.isCancel(trackingDomain)) {
4759
+ clack11.cancel("Upgrade cancelled.");
4465
4760
  process.exit(0);
4466
4761
  }
4467
4762
  updatedConfig = {
@@ -4476,7 +4771,7 @@ ${pc10.bold("Current Configuration:")}
4476
4771
  break;
4477
4772
  }
4478
4773
  case "retention": {
4479
- const retention = await clack10.select({
4774
+ const retention = await clack11.select({
4480
4775
  message: "Email history retention period:",
4481
4776
  options: [
4482
4777
  { value: "7days", label: "7 days", hint: "Minimal storage cost" },
@@ -4495,8 +4790,8 @@ ${pc10.bold("Current Configuration:")}
4495
4790
  ],
4496
4791
  initialValue: config.eventTracking?.archiveRetention || "90days"
4497
4792
  });
4498
- if (clack10.isCancel(retention)) {
4499
- clack10.cancel("Upgrade cancelled.");
4793
+ if (clack11.isCancel(retention)) {
4794
+ clack11.cancel("Upgrade cancelled.");
4500
4795
  process.exit(0);
4501
4796
  }
4502
4797
  updatedConfig = {
@@ -4512,7 +4807,7 @@ ${pc10.bold("Current Configuration:")}
4512
4807
  break;
4513
4808
  }
4514
4809
  case "events": {
4515
- const selectedEvents = await clack10.multiselect({
4810
+ const selectedEvents = await clack11.multiselect({
4516
4811
  message: "Select SES event types to track:",
4517
4812
  options: [
4518
4813
  { value: "SEND", label: "Send", hint: "Email sent to SES" },
@@ -4556,8 +4851,8 @@ ${pc10.bold("Current Configuration:")}
4556
4851
  ],
4557
4852
  required: true
4558
4853
  });
4559
- if (clack10.isCancel(selectedEvents)) {
4560
- clack10.cancel("Upgrade cancelled.");
4854
+ if (clack11.isCancel(selectedEvents)) {
4855
+ clack11.cancel("Upgrade cancelled.");
4561
4856
  process.exit(0);
4562
4857
  }
4563
4858
  updatedConfig = {
@@ -4572,16 +4867,16 @@ ${pc10.bold("Current Configuration:")}
4572
4867
  break;
4573
4868
  }
4574
4869
  case "dedicated-ip": {
4575
- const confirmed = await clack10.confirm({
4870
+ const confirmed = await clack11.confirm({
4576
4871
  message: "Enable dedicated IP? (Requires 100k+ emails/day, adds ~$50-100/mo)",
4577
4872
  initialValue: false
4578
4873
  });
4579
- if (clack10.isCancel(confirmed)) {
4580
- clack10.cancel("Upgrade cancelled.");
4874
+ if (clack11.isCancel(confirmed)) {
4875
+ clack11.cancel("Upgrade cancelled.");
4581
4876
  process.exit(0);
4582
4877
  }
4583
4878
  if (!confirmed) {
4584
- clack10.log.info("Dedicated IP not enabled.");
4879
+ clack11.log.info("Dedicated IP not enabled.");
4585
4880
  process.exit(0);
4586
4881
  }
4587
4882
  updatedConfig = {
@@ -4605,28 +4900,28 @@ ${pc10.bold("Current Configuration:")}
4605
4900
  const newCostData = calculateCosts(updatedConfig, 5e4);
4606
4901
  const costDiff = newCostData.total.monthly - currentCostData.total.monthly;
4607
4902
  console.log(`
4608
- ${pc10.bold("Cost Impact:")}`);
4903
+ ${pc11.bold("Cost Impact:")}`);
4609
4904
  console.log(
4610
- ` Current: ${pc10.cyan(`${formatCost(currentCostData.total.monthly)}/mo`)}`
4905
+ ` Current: ${pc11.cyan(`${formatCost(currentCostData.total.monthly)}/mo`)}`
4611
4906
  );
4612
4907
  console.log(
4613
- ` New: ${pc10.cyan(`${formatCost(newCostData.total.monthly)}/mo`)}`
4908
+ ` New: ${pc11.cyan(`${formatCost(newCostData.total.monthly)}/mo`)}`
4614
4909
  );
4615
4910
  if (costDiff > 0) {
4616
- console.log(` Change: ${pc10.yellow(`+${formatCost(costDiff)}/mo`)}`);
4911
+ console.log(` Change: ${pc11.yellow(`+${formatCost(costDiff)}/mo`)}`);
4617
4912
  } else if (costDiff < 0) {
4618
4913
  console.log(
4619
- ` Change: ${pc10.green(`${formatCost(Math.abs(costDiff))}/mo`)}`
4914
+ ` Change: ${pc11.green(`${formatCost(Math.abs(costDiff))}/mo`)}`
4620
4915
  );
4621
4916
  }
4622
4917
  console.log("");
4623
4918
  if (!options.yes) {
4624
- const confirmed = await clack10.confirm({
4919
+ const confirmed = await clack11.confirm({
4625
4920
  message: "Proceed with upgrade?",
4626
4921
  initialValue: true
4627
4922
  });
4628
- if (clack10.isCancel(confirmed) || !confirmed) {
4629
- clack10.cancel("Upgrade cancelled.");
4923
+ if (clack11.isCancel(confirmed) || !confirmed) {
4924
+ clack11.cancel("Upgrade cancelled.");
4630
4925
  process.exit(0);
4631
4926
  }
4632
4927
  }
@@ -4648,7 +4943,7 @@ ${pc10.bold("Cost Impact:")}`);
4648
4943
  "Updating Wraps infrastructure (this may take 2-3 minutes)",
4649
4944
  async () => {
4650
4945
  await ensurePulumiWorkDir();
4651
- const stack = await pulumi11.automation.LocalWorkspace.createOrSelectStack(
4946
+ const stack = await pulumi12.automation.LocalWorkspace.createOrSelectStack(
4652
4947
  {
4653
4948
  stackName: metadata.pulumiStackName || `wraps-${identity.accountId}-${region}`,
4654
4949
  projectName: "wraps-email",
@@ -4694,12 +4989,12 @@ ${pc10.bold("Cost Impact:")}`);
4694
4989
  }
4695
4990
  );
4696
4991
  } catch (error) {
4697
- clack10.log.error("Infrastructure upgrade failed");
4992
+ clack11.log.error("Infrastructure upgrade failed");
4698
4993
  if (error.message?.includes("stack is currently locked")) {
4699
- clack10.log.warn("\nThe Pulumi stack is locked from a previous run.");
4700
- clack10.log.info("To fix this, run:");
4701
- clack10.log.info(` ${pc10.cyan("rm -rf ~/.wraps/pulumi/.pulumi/locks")}`);
4702
- clack10.log.info("\nThen try running wraps upgrade again.");
4994
+ clack11.log.warn("\nThe Pulumi stack is locked from a previous run.");
4995
+ clack11.log.info("To fix this, run:");
4996
+ clack11.log.info(` ${pc11.cyan("rm -rf ~/.wraps/pulumi/.pulumi/locks")}`);
4997
+ clack11.log.info("\nThen try running wraps upgrade again.");
4703
4998
  }
4704
4999
  throw new Error(`Pulumi upgrade failed: ${error.message}`);
4705
5000
  }
@@ -4724,16 +5019,16 @@ ${pc10.bold("Cost Impact:")}`);
4724
5019
  customTrackingDomain: outputs.customTrackingDomain
4725
5020
  });
4726
5021
  console.log(`
4727
- ${pc10.green("\u2713")} ${pc10.bold("Upgrade complete!")}
5022
+ ${pc11.green("\u2713")} ${pc11.bold("Upgrade complete!")}
4728
5023
  `);
4729
5024
  if (upgradeAction === "preset" && newPreset) {
4730
5025
  console.log(
4731
- `Upgraded to ${pc10.cyan(newPreset)} preset (${pc10.green(formatCost(newCostData.total.monthly) + "/mo")})
5026
+ `Upgraded to ${pc11.cyan(newPreset)} preset (${pc11.green(formatCost(newCostData.total.monthly) + "/mo")})
4732
5027
  `
4733
5028
  );
4734
5029
  } else {
4735
5030
  console.log(
4736
- `Updated configuration (${pc10.green(formatCost(newCostData.total.monthly) + "/mo")})
5031
+ `Updated configuration (${pc11.green(formatCost(newCostData.total.monthly) + "/mo")})
4737
5032
  `
4738
5033
  );
4739
5034
  }
@@ -4741,12 +5036,13 @@ ${pc10.green("\u2713")} ${pc10.bold("Upgrade complete!")}
4741
5036
 
4742
5037
  // src/commands/verify.ts
4743
5038
  init_esm_shims();
5039
+ init_aws();
4744
5040
  import { Resolver } from "dns/promises";
4745
5041
  import { GetEmailIdentityCommand as GetEmailIdentityCommand3, SESv2Client as SESv2Client3 } from "@aws-sdk/client-sesv2";
4746
- import * as clack11 from "@clack/prompts";
4747
- import pc11 from "picocolors";
5042
+ import * as clack12 from "@clack/prompts";
5043
+ import pc12 from "picocolors";
4748
5044
  async function verify(options) {
4749
- clack11.intro(pc11.bold(`Verifying ${options.domain}`));
5045
+ clack12.intro(pc12.bold(`Verifying ${options.domain}`));
4750
5046
  const progress = new DeploymentProgress();
4751
5047
  const region = await getAWSRegion();
4752
5048
  const sesClient = new SESv2Client3({ region });
@@ -4765,10 +5061,10 @@ async function verify(options) {
4765
5061
  dkimTokens = identity.DkimAttributes?.Tokens || [];
4766
5062
  } catch (_error) {
4767
5063
  progress.stop();
4768
- clack11.log.error(`Domain ${options.domain} not found in SES`);
5064
+ clack12.log.error(`Domain ${options.domain} not found in SES`);
4769
5065
  console.log(
4770
5066
  `
4771
- Run ${pc11.cyan(`wraps init --domain ${options.domain}`)} to add this domain.
5067
+ Run ${pc12.cyan(`wraps init --domain ${options.domain}`)} to add this domain.
4772
5068
  `
4773
5069
  );
4774
5070
  process.exit(1);
@@ -4831,11 +5127,11 @@ Run ${pc11.cyan(`wraps init --domain ${options.domain}`)} to add this domain.
4831
5127
  progress.stop();
4832
5128
  const verificationStatus = identity.VerifiedForSendingStatus ? "verified" : "pending";
4833
5129
  const dkimStatus = identity.DkimAttributes?.Status || "PENDING";
4834
- clack11.note(
5130
+ clack12.note(
4835
5131
  [
4836
- `${pc11.bold("Domain:")} ${options.domain}`,
4837
- `${pc11.bold("Verification Status:")} ${verificationStatus === "verified" ? pc11.green("\u2713 Verified") : pc11.yellow("\u23F1 Pending")}`,
4838
- `${pc11.bold("DKIM Status:")} ${dkimStatus === "SUCCESS" ? pc11.green("\u2713 Success") : pc11.yellow(`\u23F1 ${dkimStatus}`)}`
5132
+ `${pc12.bold("Domain:")} ${options.domain}`,
5133
+ `${pc12.bold("Verification Status:")} ${verificationStatus === "verified" ? pc12.green("\u2713 Verified") : pc12.yellow("\u23F1 Pending")}`,
5134
+ `${pc12.bold("DKIM Status:")} ${dkimStatus === "SUCCESS" ? pc12.green("\u2713 Success") : pc12.yellow(`\u23F1 ${dkimStatus}`)}`
4839
5135
  ].join("\n"),
4840
5136
  "SES Status"
4841
5137
  );
@@ -4844,38 +5140,38 @@ Run ${pc11.cyan(`wraps init --domain ${options.domain}`)} to add this domain.
4844
5140
  let statusColor;
4845
5141
  if (record.status === "verified") {
4846
5142
  statusIcon = "\u2713";
4847
- statusColor = pc11.green;
5143
+ statusColor = pc12.green;
4848
5144
  } else if (record.status === "incorrect") {
4849
5145
  statusIcon = "\u2717";
4850
- statusColor = pc11.red;
5146
+ statusColor = pc12.red;
4851
5147
  } else {
4852
5148
  statusIcon = "\u2717";
4853
- statusColor = pc11.red;
5149
+ statusColor = pc12.red;
4854
5150
  }
4855
5151
  const recordInfo = record.records ? ` \u2192 ${record.records.join(", ")}` : "";
4856
5152
  return ` ${statusColor(statusIcon)} ${record.name} (${record.type}) ${statusColor(
4857
5153
  record.status
4858
5154
  )}${recordInfo}`;
4859
5155
  });
4860
- clack11.note(dnsLines.join("\n"), "DNS Records");
5156
+ clack12.note(dnsLines.join("\n"), "DNS Records");
4861
5157
  const allVerified = dnsResults.every((r) => r.status === "verified");
4862
5158
  const someIncorrect = dnsResults.some((r) => r.status === "incorrect");
4863
5159
  if (verificationStatus === "verified" && allVerified) {
4864
- clack11.outro(
4865
- pc11.green("\u2713 Domain is fully verified and ready to send emails!")
5160
+ clack12.outro(
5161
+ pc12.green("\u2713 Domain is fully verified and ready to send emails!")
4866
5162
  );
4867
5163
  } else if (someIncorrect) {
4868
- clack11.outro(
4869
- pc11.red("\u2717 Some DNS records are incorrect. Please update them.")
5164
+ clack12.outro(
5165
+ pc12.red("\u2717 Some DNS records are incorrect. Please update them.")
4870
5166
  );
4871
5167
  console.log(
4872
5168
  `
4873
- Run ${pc11.cyan("wraps status")} to see the correct DNS records.
5169
+ Run ${pc12.cyan("wraps status")} to see the correct DNS records.
4874
5170
  `
4875
5171
  );
4876
5172
  } else {
4877
- clack11.outro(
4878
- pc11.yellow("\u23F1 Waiting for DNS propagation and SES verification")
5173
+ clack12.outro(
5174
+ pc12.yellow("\u23F1 Waiting for DNS propagation and SES verification")
4879
5175
  );
4880
5176
  console.log("\nDNS records can take up to 48 hours to propagate.");
4881
5177
  console.log(
@@ -4909,44 +5205,62 @@ function printCompletionScript() {
4909
5205
  }
4910
5206
 
4911
5207
  // src/cli.ts
5208
+ init_errors();
5209
+ var __filename2 = fileURLToPath4(import.meta.url);
5210
+ var __dirname3 = dirname2(__filename2);
5211
+ var packageJson = JSON.parse(
5212
+ readFileSync(join4(__dirname3, "../package.json"), "utf-8")
5213
+ );
5214
+ var VERSION = packageJson.version;
4912
5215
  setupTabCompletion();
5216
+ function showVersion() {
5217
+ console.log(`wraps v${VERSION}`);
5218
+ process.exit(0);
5219
+ }
4913
5220
  function showHelp() {
4914
- clack12.intro(pc12.bold("WRAPS CLI"));
5221
+ clack13.intro(pc13.bold(`WRAPS CLI v${VERSION}`));
4915
5222
  console.log("Deploy email infrastructure to your AWS account\n");
4916
5223
  console.log("Usage: wraps <command> [options]\n");
4917
5224
  console.log("Commands:");
4918
- console.log(` ${pc12.cyan("init")} Deploy new email infrastructure`);
5225
+ console.log(` ${pc13.cyan("init")} Deploy new email infrastructure`);
5226
+ console.log(
5227
+ ` ${pc13.cyan("connect")} Connect to existing AWS SES infrastructure`
5228
+ );
4919
5229
  console.log(
4920
- ` ${pc12.cyan("connect")} Connect to existing AWS SES infrastructure`
5230
+ ` ${pc13.cyan("console")} Start local web dashboard for monitoring`
4921
5231
  );
4922
5232
  console.log(
4923
- ` ${pc12.cyan("console")} Start local web dashboard for monitoring`
5233
+ ` ${pc13.cyan("update")} Update infrastructure with latest CLI changes`
4924
5234
  );
4925
5235
  console.log(
4926
- ` ${pc12.cyan("upgrade")} Add features to existing connection`
5236
+ ` ${pc13.cyan("upgrade")} Add features to existing connection`
4927
5237
  );
4928
- console.log(` ${pc12.cyan("status")} Show current infrastructure status`);
5238
+ console.log(` ${pc13.cyan("status")} Show current infrastructure status`);
4929
5239
  console.log(
4930
- ` ${pc12.cyan("verify")} Verify domain DNS records and SES status`
5240
+ ` ${pc13.cyan("verify")} Verify domain DNS records and SES status`
4931
5241
  );
4932
- console.log(` ${pc12.cyan("restore")} Restore original AWS configuration`);
4933
- console.log(` ${pc12.cyan("destroy")} Remove all deployed infrastructure`);
4934
- console.log(` ${pc12.cyan("completion")} Generate shell completion script
5242
+ console.log(` ${pc13.cyan("restore")} Restore original AWS configuration`);
5243
+ console.log(` ${pc13.cyan("destroy")} Remove all deployed infrastructure`);
5244
+ console.log(` ${pc13.cyan("completion")} Generate shell completion script
4935
5245
  `);
4936
5246
  console.log("Options:");
4937
5247
  console.log(
4938
- ` ${pc12.dim("--provider")} Hosting provider (vercel, aws, railway, other)`
5248
+ ` ${pc13.dim("--provider")} Hosting provider (vercel, aws, railway, other)`
4939
5249
  );
4940
- console.log(` ${pc12.dim("--region")} AWS region`);
4941
- console.log(` ${pc12.dim("--domain")} Domain to verify`);
4942
- console.log(` ${pc12.dim("--account")} AWS account ID or alias
5250
+ console.log(` ${pc13.dim("--region")} AWS region`);
5251
+ console.log(` ${pc13.dim("--domain")} Domain to verify`);
5252
+ console.log(` ${pc13.dim("--account")} AWS account ID or alias`);
5253
+ console.log(` ${pc13.dim("--version, -v")} Show version number
4943
5254
  `);
4944
5255
  console.log(
4945
- `Run ${pc12.cyan("wraps <command> --help")} for more information on a command.
5256
+ `Run ${pc13.cyan("wraps <command> --help")} for more information on a command.
4946
5257
  `
4947
5258
  );
4948
5259
  process.exit(0);
4949
5260
  }
5261
+ if (process.argv.includes("--version") || process.argv.includes("-v")) {
5262
+ showVersion();
5263
+ }
4950
5264
  if (process.argv.includes("--help") || process.argv.includes("-h")) {
4951
5265
  showHelp();
4952
5266
  }
@@ -5016,10 +5330,10 @@ async function run() {
5016
5330
  break;
5017
5331
  case "verify":
5018
5332
  if (!flags.domain) {
5019
- clack12.log.error("--domain flag is required");
5333
+ clack13.log.error("--domain flag is required");
5020
5334
  console.log(
5021
5335
  `
5022
- Usage: ${pc12.cyan("wraps verify --domain yourapp.com")}
5336
+ Usage: ${pc13.cyan("wraps verify --domain yourapp.com")}
5023
5337
  `
5024
5338
  );
5025
5339
  process.exit(1);
@@ -5041,6 +5355,12 @@ Usage: ${pc12.cyan("wraps verify --domain yourapp.com")}
5041
5355
  noOpen: flags.noOpen
5042
5356
  });
5043
5357
  break;
5358
+ case "update":
5359
+ await update({
5360
+ region: flags.region,
5361
+ yes: flags.yes
5362
+ });
5363
+ break;
5044
5364
  case "upgrade":
5045
5365
  await upgrade({
5046
5366
  region: flags.region,
@@ -5062,10 +5382,10 @@ Usage: ${pc12.cyan("wraps verify --domain yourapp.com")}
5062
5382
  printCompletionScript();
5063
5383
  break;
5064
5384
  default:
5065
- clack12.log.error(`Unknown command: ${command}`);
5385
+ clack13.log.error(`Unknown command: ${command}`);
5066
5386
  console.log(
5067
5387
  `
5068
- Run ${pc12.cyan("wraps --help")} for available commands.
5388
+ Run ${pc13.cyan("wraps --help")} for available commands.
5069
5389
  `
5070
5390
  );
5071
5391
  process.exit(1);