@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 +548 -228
- package/dist/cli.js.map +1 -1
- package/dist/lambda/event-processor/.bundled +1 -1
- package/dist/lambda/event-processor/index.mjs +1 -1
- package/package.json +1 -1
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
|
|
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
|
|
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:
|
|
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/
|
|
1733
|
-
|
|
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/
|
|
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
|
-
|
|
4305
|
-
|
|
4306
|
-
|
|
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
|
-
` ${
|
|
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(` ${
|
|
4624
|
+
console.log(` ${pc11.green("\u2713")} Bounce/Complaint Suppression`);
|
|
4360
4625
|
}
|
|
4361
4626
|
if (config.eventTracking?.enabled) {
|
|
4362
|
-
console.log(` ${
|
|
4627
|
+
console.log(` ${pc11.green("\u2713")} Event Tracking (EventBridge)`);
|
|
4363
4628
|
if (config.eventTracking.dynamoDBHistory) {
|
|
4364
4629
|
console.log(
|
|
4365
|
-
` ${
|
|
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(` ${
|
|
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: ${
|
|
4640
|
+
Estimated Cost: ${pc11.cyan(`~${formatCost(currentCostData.total.monthly)}/mo`)}`
|
|
4376
4641
|
);
|
|
4377
4642
|
console.log("");
|
|
4378
|
-
const upgradeAction = await
|
|
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 (
|
|
4414
|
-
|
|
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
|
-
|
|
4697
|
+
clack11.log.warn("Already on highest preset (Enterprise)");
|
|
4433
4698
|
process.exit(0);
|
|
4434
4699
|
}
|
|
4435
|
-
const selectedPreset = await
|
|
4700
|
+
const selectedPreset = await clack11.select({
|
|
4436
4701
|
message: "Select new preset:",
|
|
4437
4702
|
options: availablePresets
|
|
4438
4703
|
});
|
|
4439
|
-
if (
|
|
4440
|
-
|
|
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
|
-
|
|
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 (
|
|
4464
|
-
|
|
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
|
|
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 (
|
|
4499
|
-
|
|
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
|
|
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 (
|
|
4560
|
-
|
|
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
|
|
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 (
|
|
4580
|
-
|
|
4874
|
+
if (clack11.isCancel(confirmed)) {
|
|
4875
|
+
clack11.cancel("Upgrade cancelled.");
|
|
4581
4876
|
process.exit(0);
|
|
4582
4877
|
}
|
|
4583
4878
|
if (!confirmed) {
|
|
4584
|
-
|
|
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
|
-
${
|
|
4903
|
+
${pc11.bold("Cost Impact:")}`);
|
|
4609
4904
|
console.log(
|
|
4610
|
-
` Current: ${
|
|
4905
|
+
` Current: ${pc11.cyan(`${formatCost(currentCostData.total.monthly)}/mo`)}`
|
|
4611
4906
|
);
|
|
4612
4907
|
console.log(
|
|
4613
|
-
` New: ${
|
|
4908
|
+
` New: ${pc11.cyan(`${formatCost(newCostData.total.monthly)}/mo`)}`
|
|
4614
4909
|
);
|
|
4615
4910
|
if (costDiff > 0) {
|
|
4616
|
-
console.log(` Change: ${
|
|
4911
|
+
console.log(` Change: ${pc11.yellow(`+${formatCost(costDiff)}/mo`)}`);
|
|
4617
4912
|
} else if (costDiff < 0) {
|
|
4618
4913
|
console.log(
|
|
4619
|
-
` Change: ${
|
|
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
|
|
4919
|
+
const confirmed = await clack11.confirm({
|
|
4625
4920
|
message: "Proceed with upgrade?",
|
|
4626
4921
|
initialValue: true
|
|
4627
4922
|
});
|
|
4628
|
-
if (
|
|
4629
|
-
|
|
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
|
|
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
|
-
|
|
4992
|
+
clack11.log.error("Infrastructure upgrade failed");
|
|
4698
4993
|
if (error.message?.includes("stack is currently locked")) {
|
|
4699
|
-
|
|
4700
|
-
|
|
4701
|
-
|
|
4702
|
-
|
|
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
|
-
${
|
|
5022
|
+
${pc11.green("\u2713")} ${pc11.bold("Upgrade complete!")}
|
|
4728
5023
|
`);
|
|
4729
5024
|
if (upgradeAction === "preset" && newPreset) {
|
|
4730
5025
|
console.log(
|
|
4731
|
-
`Upgraded to ${
|
|
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 (${
|
|
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
|
|
4747
|
-
import
|
|
5042
|
+
import * as clack12 from "@clack/prompts";
|
|
5043
|
+
import pc12 from "picocolors";
|
|
4748
5044
|
async function verify(options) {
|
|
4749
|
-
|
|
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
|
-
|
|
5064
|
+
clack12.log.error(`Domain ${options.domain} not found in SES`);
|
|
4769
5065
|
console.log(
|
|
4770
5066
|
`
|
|
4771
|
-
Run ${
|
|
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
|
-
|
|
5130
|
+
clack12.note(
|
|
4835
5131
|
[
|
|
4836
|
-
`${
|
|
4837
|
-
`${
|
|
4838
|
-
`${
|
|
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 =
|
|
5143
|
+
statusColor = pc12.green;
|
|
4848
5144
|
} else if (record.status === "incorrect") {
|
|
4849
5145
|
statusIcon = "\u2717";
|
|
4850
|
-
statusColor =
|
|
5146
|
+
statusColor = pc12.red;
|
|
4851
5147
|
} else {
|
|
4852
5148
|
statusIcon = "\u2717";
|
|
4853
|
-
statusColor =
|
|
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
|
-
|
|
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
|
-
|
|
4865
|
-
|
|
5160
|
+
clack12.outro(
|
|
5161
|
+
pc12.green("\u2713 Domain is fully verified and ready to send emails!")
|
|
4866
5162
|
);
|
|
4867
5163
|
} else if (someIncorrect) {
|
|
4868
|
-
|
|
4869
|
-
|
|
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 ${
|
|
5169
|
+
Run ${pc12.cyan("wraps status")} to see the correct DNS records.
|
|
4874
5170
|
`
|
|
4875
5171
|
);
|
|
4876
5172
|
} else {
|
|
4877
|
-
|
|
4878
|
-
|
|
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
|
-
|
|
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(` ${
|
|
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
|
-
` ${
|
|
5230
|
+
` ${pc13.cyan("console")} Start local web dashboard for monitoring`
|
|
4921
5231
|
);
|
|
4922
5232
|
console.log(
|
|
4923
|
-
` ${
|
|
5233
|
+
` ${pc13.cyan("update")} Update infrastructure with latest CLI changes`
|
|
4924
5234
|
);
|
|
4925
5235
|
console.log(
|
|
4926
|
-
` ${
|
|
5236
|
+
` ${pc13.cyan("upgrade")} Add features to existing connection`
|
|
4927
5237
|
);
|
|
4928
|
-
console.log(` ${
|
|
5238
|
+
console.log(` ${pc13.cyan("status")} Show current infrastructure status`);
|
|
4929
5239
|
console.log(
|
|
4930
|
-
` ${
|
|
5240
|
+
` ${pc13.cyan("verify")} Verify domain DNS records and SES status`
|
|
4931
5241
|
);
|
|
4932
|
-
console.log(` ${
|
|
4933
|
-
console.log(` ${
|
|
4934
|
-
console.log(` ${
|
|
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
|
-
` ${
|
|
5248
|
+
` ${pc13.dim("--provider")} Hosting provider (vercel, aws, railway, other)`
|
|
4939
5249
|
);
|
|
4940
|
-
console.log(` ${
|
|
4941
|
-
console.log(` ${
|
|
4942
|
-
console.log(` ${
|
|
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 ${
|
|
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
|
-
|
|
5333
|
+
clack13.log.error("--domain flag is required");
|
|
5020
5334
|
console.log(
|
|
5021
5335
|
`
|
|
5022
|
-
Usage: ${
|
|
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
|
-
|
|
5385
|
+
clack13.log.error(`Unknown command: ${command}`);
|
|
5066
5386
|
console.log(
|
|
5067
5387
|
`
|
|
5068
|
-
Run ${
|
|
5388
|
+
Run ${pc13.cyan("wraps --help")} for available commands.
|
|
5069
5389
|
`
|
|
5070
5390
|
);
|
|
5071
5391
|
process.exit(1);
|