@wraps.dev/cli 2.11.5 → 2.11.6
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
|
@@ -1133,9 +1133,11 @@ var init_aws_detection = __esm({
|
|
|
1133
1133
|
var errors_exports = {};
|
|
1134
1134
|
__export(errors_exports, {
|
|
1135
1135
|
WrapsError: () => WrapsError,
|
|
1136
|
+
classifyDNSError: () => classifyDNSError,
|
|
1136
1137
|
errors: () => errors,
|
|
1137
1138
|
handleCLIError: () => handleCLIError,
|
|
1138
1139
|
isAWSError: () => isAWSError,
|
|
1140
|
+
isAWSNotFoundError: () => isAWSNotFoundError,
|
|
1139
1141
|
isPulumiError: () => isPulumiError,
|
|
1140
1142
|
parseAWSError: () => parseAWSError,
|
|
1141
1143
|
parsePulumiError: () => parsePulumiError,
|
|
@@ -1162,6 +1164,23 @@ function isAWSError(error) {
|
|
|
1162
1164
|
];
|
|
1163
1165
|
return awsErrorNames.includes(error.name) || "$metadata" in error;
|
|
1164
1166
|
}
|
|
1167
|
+
function classifyDNSError(error) {
|
|
1168
|
+
if (!(error instanceof Error)) {
|
|
1169
|
+
return "unknown";
|
|
1170
|
+
}
|
|
1171
|
+
const code = error.code;
|
|
1172
|
+
if (code === "ENOTFOUND" || code === "ENODATA") {
|
|
1173
|
+
return "missing";
|
|
1174
|
+
}
|
|
1175
|
+
if (code === "ETIMEOUT" || code === "ESERVFAIL" || code === "ECONNREFUSED") {
|
|
1176
|
+
return "network";
|
|
1177
|
+
}
|
|
1178
|
+
return "unknown";
|
|
1179
|
+
}
|
|
1180
|
+
function isAWSNotFoundError(error) {
|
|
1181
|
+
if (!(error instanceof Error)) return false;
|
|
1182
|
+
return error.name === "NotFoundException" || error.name === "NoSuchEntityException" || error.name === "NoSuchEntity" || error.name === "ResourceNotFoundException" || error.$metadata?.httpStatusCode === 404;
|
|
1183
|
+
}
|
|
1165
1184
|
function isPulumiError(error) {
|
|
1166
1185
|
if (!(error instanceof Error)) {
|
|
1167
1186
|
return false;
|
|
@@ -4444,6 +4463,100 @@ var init_metadata = __esm({
|
|
|
4444
4463
|
}
|
|
4445
4464
|
});
|
|
4446
4465
|
|
|
4466
|
+
// src/infrastructure/shared/resource-checks.ts
|
|
4467
|
+
async function roleExists(roleName) {
|
|
4468
|
+
try {
|
|
4469
|
+
const { IAMClient: IAMClient4, GetRoleCommand: GetRoleCommand3 } = await import("@aws-sdk/client-iam");
|
|
4470
|
+
const iam10 = new IAMClient4({
|
|
4471
|
+
region: process.env.AWS_REGION || "us-east-1"
|
|
4472
|
+
});
|
|
4473
|
+
await iam10.send(new GetRoleCommand3({ RoleName: roleName }));
|
|
4474
|
+
return true;
|
|
4475
|
+
} catch (error) {
|
|
4476
|
+
if (error instanceof Error && (error.name === "NoSuchEntityException" || error.Code === "NoSuchEntity" || error.Error?.Code === "NoSuchEntity")) {
|
|
4477
|
+
return false;
|
|
4478
|
+
}
|
|
4479
|
+
console.error("Error checking for existing IAM role:", error);
|
|
4480
|
+
return false;
|
|
4481
|
+
}
|
|
4482
|
+
}
|
|
4483
|
+
async function tableExists(tableName) {
|
|
4484
|
+
try {
|
|
4485
|
+
const { DynamoDBClient: DynamoDBClient6, DescribeTableCommand: DescribeTableCommand2 } = await import("@aws-sdk/client-dynamodb");
|
|
4486
|
+
const dynamodb3 = new DynamoDBClient6({
|
|
4487
|
+
region: process.env.AWS_REGION || "us-east-1"
|
|
4488
|
+
});
|
|
4489
|
+
await dynamodb3.send(new DescribeTableCommand2({ TableName: tableName }));
|
|
4490
|
+
return true;
|
|
4491
|
+
} catch (error) {
|
|
4492
|
+
if (error instanceof Error && error.name === "ResourceNotFoundException") {
|
|
4493
|
+
return false;
|
|
4494
|
+
}
|
|
4495
|
+
console.error("Error checking for existing DynamoDB table:", error);
|
|
4496
|
+
return false;
|
|
4497
|
+
}
|
|
4498
|
+
}
|
|
4499
|
+
async function sqsQueueExists(queueName) {
|
|
4500
|
+
try {
|
|
4501
|
+
const { SQSClient, GetQueueUrlCommand } = await import("@aws-sdk/client-sqs");
|
|
4502
|
+
const sqs5 = new SQSClient({
|
|
4503
|
+
region: process.env.AWS_REGION || "us-east-1"
|
|
4504
|
+
});
|
|
4505
|
+
const response = await sqs5.send(
|
|
4506
|
+
new GetQueueUrlCommand({ QueueName: queueName })
|
|
4507
|
+
);
|
|
4508
|
+
return response.QueueUrl || null;
|
|
4509
|
+
} catch {
|
|
4510
|
+
return null;
|
|
4511
|
+
}
|
|
4512
|
+
}
|
|
4513
|
+
async function snsTopicExists(topicName) {
|
|
4514
|
+
try {
|
|
4515
|
+
const { SNSClient: SNSClient2, ListTopicsCommand: ListTopicsCommand2 } = await import("@aws-sdk/client-sns");
|
|
4516
|
+
const sns3 = new SNSClient2({
|
|
4517
|
+
region: process.env.AWS_REGION || "us-east-1"
|
|
4518
|
+
});
|
|
4519
|
+
let nextToken;
|
|
4520
|
+
do {
|
|
4521
|
+
const response = await sns3.send(
|
|
4522
|
+
new ListTopicsCommand2({ NextToken: nextToken })
|
|
4523
|
+
);
|
|
4524
|
+
const found = response.Topics?.find(
|
|
4525
|
+
(t) => t.TopicArn?.endsWith(`:${topicName}`)
|
|
4526
|
+
);
|
|
4527
|
+
if (found?.TopicArn) {
|
|
4528
|
+
return found.TopicArn;
|
|
4529
|
+
}
|
|
4530
|
+
nextToken = response.NextToken;
|
|
4531
|
+
} while (nextToken);
|
|
4532
|
+
return null;
|
|
4533
|
+
} catch {
|
|
4534
|
+
return null;
|
|
4535
|
+
}
|
|
4536
|
+
}
|
|
4537
|
+
async function lambdaFunctionExists(functionName) {
|
|
4538
|
+
try {
|
|
4539
|
+
const { LambdaClient: LambdaClient2, GetFunctionCommand } = await import("@aws-sdk/client-lambda");
|
|
4540
|
+
const lambda4 = new LambdaClient2({
|
|
4541
|
+
region: process.env.AWS_REGION || "us-east-1"
|
|
4542
|
+
});
|
|
4543
|
+
await lambda4.send(new GetFunctionCommand({ FunctionName: functionName }));
|
|
4544
|
+
return true;
|
|
4545
|
+
} catch (error) {
|
|
4546
|
+
if (error instanceof Error && error.name === "ResourceNotFoundException") {
|
|
4547
|
+
return false;
|
|
4548
|
+
}
|
|
4549
|
+
console.error("Error checking for existing Lambda function:", error);
|
|
4550
|
+
return false;
|
|
4551
|
+
}
|
|
4552
|
+
}
|
|
4553
|
+
var init_resource_checks = __esm({
|
|
4554
|
+
"src/infrastructure/shared/resource-checks.ts"() {
|
|
4555
|
+
"use strict";
|
|
4556
|
+
init_esm_shims();
|
|
4557
|
+
}
|
|
4558
|
+
});
|
|
4559
|
+
|
|
4447
4560
|
// ../core/dist/index.js
|
|
4448
4561
|
var dist_exports = {};
|
|
4449
4562
|
__export(dist_exports, {
|
|
@@ -4661,22 +4774,6 @@ function getPackageRoot() {
|
|
|
4661
4774
|
}
|
|
4662
4775
|
throw new Error("Could not find package.json");
|
|
4663
4776
|
}
|
|
4664
|
-
async function lambdaFunctionExists(functionName) {
|
|
4665
|
-
try {
|
|
4666
|
-
const { LambdaClient: LambdaClient2, GetFunctionCommand } = await import("@aws-sdk/client-lambda");
|
|
4667
|
-
const lambda4 = new LambdaClient2({
|
|
4668
|
-
region: process.env.AWS_REGION || "us-east-1"
|
|
4669
|
-
});
|
|
4670
|
-
await lambda4.send(new GetFunctionCommand({ FunctionName: functionName }));
|
|
4671
|
-
return true;
|
|
4672
|
-
} catch (error) {
|
|
4673
|
-
if (error.name === "ResourceNotFoundException") {
|
|
4674
|
-
return false;
|
|
4675
|
-
}
|
|
4676
|
-
console.error("Error checking for existing Lambda function:", error);
|
|
4677
|
-
return false;
|
|
4678
|
-
}
|
|
4679
|
-
}
|
|
4680
4777
|
async function findEventSourceMapping(functionName, queueArn) {
|
|
4681
4778
|
try {
|
|
4682
4779
|
const { LambdaClient: LambdaClient2, ListEventSourceMappingsCommand } = await import("@aws-sdk/client-lambda");
|
|
@@ -4873,6 +4970,7 @@ var init_lambda = __esm({
|
|
|
4873
4970
|
"src/infrastructure/resources/lambda.ts"() {
|
|
4874
4971
|
"use strict";
|
|
4875
4972
|
init_esm_shims();
|
|
4973
|
+
init_resource_checks();
|
|
4876
4974
|
nodeBuiltins = builtinModules.flatMap((m) => [m, `node:${m}`]);
|
|
4877
4975
|
}
|
|
4878
4976
|
});
|
|
@@ -9146,12 +9244,13 @@ async function cdnDestroy(options) {
|
|
|
9146
9244
|
return;
|
|
9147
9245
|
} catch (error) {
|
|
9148
9246
|
progress.stop();
|
|
9149
|
-
|
|
9247
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
9248
|
+
if (msg.includes("No CDN infrastructure found")) {
|
|
9150
9249
|
clack9.log.warn("No CDN infrastructure found to preview");
|
|
9151
9250
|
process.exit(0);
|
|
9152
9251
|
}
|
|
9153
9252
|
trackError("PREVIEW_FAILED", "storage destroy", { step: "preview" });
|
|
9154
|
-
throw new Error(`Preview failed: ${
|
|
9253
|
+
throw new Error(`Preview failed: ${msg}`);
|
|
9155
9254
|
}
|
|
9156
9255
|
}
|
|
9157
9256
|
if (shouldCleanDNS && hostedZone && customDomain) {
|
|
@@ -9163,7 +9262,8 @@ async function cdnDestroy(options) {
|
|
|
9163
9262
|
}
|
|
9164
9263
|
);
|
|
9165
9264
|
} catch (error) {
|
|
9166
|
-
|
|
9265
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
9266
|
+
clack9.log.warn(`Could not delete DNS records: ${msg}`);
|
|
9167
9267
|
clack9.log.info("You may need to delete them manually from Route53");
|
|
9168
9268
|
}
|
|
9169
9269
|
}
|
|
@@ -9176,7 +9276,8 @@ async function cdnDestroy(options) {
|
|
|
9176
9276
|
}
|
|
9177
9277
|
);
|
|
9178
9278
|
} catch (error) {
|
|
9179
|
-
|
|
9279
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
9280
|
+
clack9.log.info(`Note: ${msg}`);
|
|
9180
9281
|
}
|
|
9181
9282
|
try {
|
|
9182
9283
|
await progress.execute(
|
|
@@ -9200,7 +9301,8 @@ async function cdnDestroy(options) {
|
|
|
9200
9301
|
);
|
|
9201
9302
|
} catch (error) {
|
|
9202
9303
|
progress.stop();
|
|
9203
|
-
|
|
9304
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
9305
|
+
if (msg.includes("No CDN infrastructure found")) {
|
|
9204
9306
|
clack9.log.warn("No CDN infrastructure found");
|
|
9205
9307
|
if (metadata) {
|
|
9206
9308
|
removeServiceFromConnection(metadata, "cdn");
|
|
@@ -9208,7 +9310,7 @@ async function cdnDestroy(options) {
|
|
|
9208
9310
|
}
|
|
9209
9311
|
process.exit(0);
|
|
9210
9312
|
}
|
|
9211
|
-
if (
|
|
9313
|
+
if (msg.includes("stack is currently locked")) {
|
|
9212
9314
|
trackError("STACK_LOCKED", "storage destroy", { step: "destroy" });
|
|
9213
9315
|
throw errors.stackLocked();
|
|
9214
9316
|
}
|
|
@@ -9647,6 +9749,9 @@ async function createCdnACMCertificate(config2) {
|
|
|
9647
9749
|
};
|
|
9648
9750
|
}
|
|
9649
9751
|
|
|
9752
|
+
// src/infrastructure/cdn-stack.ts
|
|
9753
|
+
init_resource_checks();
|
|
9754
|
+
|
|
9650
9755
|
// src/infrastructure/vercel-oidc.ts
|
|
9651
9756
|
init_esm_shims();
|
|
9652
9757
|
import * as aws2 from "@pulumi/aws";
|
|
@@ -9708,22 +9813,6 @@ async function createVercelOIDC(config2) {
|
|
|
9708
9813
|
}
|
|
9709
9814
|
|
|
9710
9815
|
// src/infrastructure/cdn-stack.ts
|
|
9711
|
-
async function roleExists(roleName) {
|
|
9712
|
-
try {
|
|
9713
|
-
const { IAMClient: IAMClient4, GetRoleCommand: GetRoleCommand3 } = await import("@aws-sdk/client-iam");
|
|
9714
|
-
const iam10 = new IAMClient4({
|
|
9715
|
-
region: process.env.AWS_REGION || "us-east-1"
|
|
9716
|
-
});
|
|
9717
|
-
await iam10.send(new GetRoleCommand3({ RoleName: roleName }));
|
|
9718
|
-
return true;
|
|
9719
|
-
} catch (error) {
|
|
9720
|
-
if (error.name === "NoSuchEntityException" || error.Code === "NoSuchEntity" || error.Error?.Code === "NoSuchEntity") {
|
|
9721
|
-
return false;
|
|
9722
|
-
}
|
|
9723
|
-
console.error("Error checking for existing IAM role:", error);
|
|
9724
|
-
return false;
|
|
9725
|
-
}
|
|
9726
|
-
}
|
|
9727
9816
|
async function createCdnIAMRole(config2) {
|
|
9728
9817
|
let assumeRolePolicy;
|
|
9729
9818
|
if (config2.provider === "vercel" && config2.oidcProvider) {
|
|
@@ -10492,10 +10581,11 @@ ${pc11.yellow(pc11.bold("Configuration Notes:"))}`);
|
|
|
10492
10581
|
return;
|
|
10493
10582
|
} catch (error) {
|
|
10494
10583
|
trackError("PREVIEW_FAILED", "storage:init", { step: "preview" });
|
|
10495
|
-
|
|
10584
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
10585
|
+
if (msg.includes("stack is currently locked")) {
|
|
10496
10586
|
throw errors.stackLocked();
|
|
10497
10587
|
}
|
|
10498
|
-
throw new Error(`Preview failed: ${
|
|
10588
|
+
throw new Error(`Preview failed: ${msg}`);
|
|
10499
10589
|
}
|
|
10500
10590
|
}
|
|
10501
10591
|
let outputs;
|
|
@@ -10559,18 +10649,19 @@ ${pc11.yellow(pc11.bold("Configuration Notes:"))}`);
|
|
|
10559
10649
|
}
|
|
10560
10650
|
);
|
|
10561
10651
|
} catch (error) {
|
|
10652
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
10562
10653
|
trackServiceInit("cdn", false, {
|
|
10563
10654
|
preset,
|
|
10564
10655
|
provider,
|
|
10565
10656
|
region,
|
|
10566
10657
|
duration_ms: Date.now() - startTime
|
|
10567
10658
|
});
|
|
10568
|
-
if (
|
|
10659
|
+
if (msg.includes("stack is currently locked")) {
|
|
10569
10660
|
trackError("STACK_LOCKED", "storage:init", { step: "deploy" });
|
|
10570
10661
|
throw errors.stackLocked();
|
|
10571
10662
|
}
|
|
10572
10663
|
trackError("DEPLOYMENT_FAILED", "storage:init", { step: "deploy" });
|
|
10573
|
-
throw new Error(`Pulumi deployment failed: ${
|
|
10664
|
+
throw new Error(`Pulumi deployment failed: ${msg}`);
|
|
10574
10665
|
}
|
|
10575
10666
|
if (metadata.services.cdn) {
|
|
10576
10667
|
metadata.services.cdn.pulumiStackName = `wraps-cdn-${identity.accountId}-${region}`;
|
|
@@ -10703,7 +10794,8 @@ ${pc11.yellow(pc11.bold("Configuration Notes:"))}`);
|
|
|
10703
10794
|
}
|
|
10704
10795
|
} catch (error) {
|
|
10705
10796
|
progress.stop();
|
|
10706
|
-
|
|
10797
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
10798
|
+
clack10.log.warn(`Could not manage DNS records: ${msg}`);
|
|
10707
10799
|
}
|
|
10708
10800
|
}
|
|
10709
10801
|
}
|
|
@@ -11127,13 +11219,14 @@ async function cdnSync(options) {
|
|
|
11127
11219
|
return result.summary;
|
|
11128
11220
|
});
|
|
11129
11221
|
} catch (error) {
|
|
11222
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
11130
11223
|
trackCommand("storage:sync", {
|
|
11131
11224
|
success: false,
|
|
11132
|
-
error:
|
|
11225
|
+
error: msg,
|
|
11133
11226
|
region,
|
|
11134
11227
|
duration_ms: Date.now() - startTime
|
|
11135
11228
|
});
|
|
11136
|
-
clack12.log.error(`Sync failed: ${
|
|
11229
|
+
clack12.log.error(`Sync failed: ${msg}`);
|
|
11137
11230
|
process.exit(1);
|
|
11138
11231
|
}
|
|
11139
11232
|
clack12.log.success(pc13.green("CDN infrastructure synced!"));
|
|
@@ -11257,7 +11350,8 @@ Current configuration:
|
|
|
11257
11350
|
certStatus = certResponse.Certificate?.Status || "UNKNOWN";
|
|
11258
11351
|
} catch (error) {
|
|
11259
11352
|
progress.fail("Failed to check certificate status");
|
|
11260
|
-
|
|
11353
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
11354
|
+
clack13.log.error(`Error: ${msg}`);
|
|
11261
11355
|
process.exit(1);
|
|
11262
11356
|
}
|
|
11263
11357
|
progress.stop();
|
|
@@ -11381,13 +11475,14 @@ Ready to add custom domain: ${pc14.cyan(pendingDomain)}`);
|
|
|
11381
11475
|
}
|
|
11382
11476
|
);
|
|
11383
11477
|
} catch (error) {
|
|
11478
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
11384
11479
|
trackCommand("storage:upgrade", {
|
|
11385
11480
|
success: false,
|
|
11386
|
-
error:
|
|
11481
|
+
error: msg,
|
|
11387
11482
|
region,
|
|
11388
11483
|
duration_ms: Date.now() - startTime
|
|
11389
11484
|
});
|
|
11390
|
-
clack13.log.error(`Upgrade failed: ${
|
|
11485
|
+
clack13.log.error(`Upgrade failed: ${msg}`);
|
|
11391
11486
|
process.exit(1);
|
|
11392
11487
|
}
|
|
11393
11488
|
if (metadata.services.cdn) {
|
|
@@ -14236,16 +14331,17 @@ async function check(options) {
|
|
|
14236
14331
|
process.exit(getExitCode(result.score.grade));
|
|
14237
14332
|
} catch (error) {
|
|
14238
14333
|
spinner8?.stop("Check failed");
|
|
14334
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
14239
14335
|
if (options.json) {
|
|
14240
|
-
console.log(JSON.stringify({ error:
|
|
14336
|
+
console.log(JSON.stringify({ error: msg }));
|
|
14241
14337
|
} else {
|
|
14242
|
-
clack15.log.error(
|
|
14338
|
+
clack15.log.error(msg);
|
|
14243
14339
|
}
|
|
14244
14340
|
const duration = Date.now() - startTime;
|
|
14245
14341
|
trackCommand("email:check", {
|
|
14246
14342
|
success: false,
|
|
14247
14343
|
duration_ms: duration,
|
|
14248
|
-
error:
|
|
14344
|
+
error: msg
|
|
14249
14345
|
});
|
|
14250
14346
|
process.exit(4);
|
|
14251
14347
|
}
|
|
@@ -14855,23 +14951,8 @@ async function createAlertingResources(config2) {
|
|
|
14855
14951
|
|
|
14856
14952
|
// src/infrastructure/resources/dynamodb.ts
|
|
14857
14953
|
init_esm_shims();
|
|
14954
|
+
init_resource_checks();
|
|
14858
14955
|
import * as aws5 from "@pulumi/aws";
|
|
14859
|
-
async function tableExists(tableName) {
|
|
14860
|
-
try {
|
|
14861
|
-
const { DynamoDBClient: DynamoDBClient6, DescribeTableCommand: DescribeTableCommand2 } = await import("@aws-sdk/client-dynamodb");
|
|
14862
|
-
const dynamodb3 = new DynamoDBClient6({
|
|
14863
|
-
region: process.env.AWS_REGION || "us-east-1"
|
|
14864
|
-
});
|
|
14865
|
-
await dynamodb3.send(new DescribeTableCommand2({ TableName: tableName }));
|
|
14866
|
-
return true;
|
|
14867
|
-
} catch (error) {
|
|
14868
|
-
if (error.name === "ResourceNotFoundException") {
|
|
14869
|
-
return false;
|
|
14870
|
-
}
|
|
14871
|
-
console.error("Error checking for existing DynamoDB table:", error);
|
|
14872
|
-
return false;
|
|
14873
|
-
}
|
|
14874
|
-
}
|
|
14875
14956
|
async function createDynamoDBTables(_config) {
|
|
14876
14957
|
const tableName = "wraps-email-history";
|
|
14877
14958
|
const exists = await tableExists(tableName);
|
|
@@ -15068,24 +15149,9 @@ async function createEventBridgeResources(config2) {
|
|
|
15068
15149
|
|
|
15069
15150
|
// src/infrastructure/resources/iam.ts
|
|
15070
15151
|
init_esm_shims();
|
|
15152
|
+
init_resource_checks();
|
|
15071
15153
|
import * as aws7 from "@pulumi/aws";
|
|
15072
15154
|
import * as pulumi10 from "@pulumi/pulumi";
|
|
15073
|
-
async function roleExists2(roleName) {
|
|
15074
|
-
try {
|
|
15075
|
-
const { IAMClient: IAMClient4, GetRoleCommand: GetRoleCommand3 } = await import("@aws-sdk/client-iam");
|
|
15076
|
-
const iam10 = new IAMClient4({
|
|
15077
|
-
region: process.env.AWS_REGION || "us-east-1"
|
|
15078
|
-
});
|
|
15079
|
-
await iam10.send(new GetRoleCommand3({ RoleName: roleName }));
|
|
15080
|
-
return true;
|
|
15081
|
-
} catch (error) {
|
|
15082
|
-
if (error.name === "NoSuchEntityException" || error.Code === "NoSuchEntity" || error.Error?.Code === "NoSuchEntity") {
|
|
15083
|
-
return false;
|
|
15084
|
-
}
|
|
15085
|
-
console.error("Error checking for existing IAM role:", error);
|
|
15086
|
-
return false;
|
|
15087
|
-
}
|
|
15088
|
-
}
|
|
15089
15155
|
async function createIAMRole(config2) {
|
|
15090
15156
|
let assumeRolePolicy;
|
|
15091
15157
|
if (config2.provider === "vercel" && config2.oidcProvider) {
|
|
@@ -15122,7 +15188,7 @@ async function createIAMRole(config2) {
|
|
|
15122
15188
|
throw new Error("Other providers not yet implemented");
|
|
15123
15189
|
}
|
|
15124
15190
|
const roleName = "wraps-email-role";
|
|
15125
|
-
const exists = await
|
|
15191
|
+
const exists = await roleExists(roleName);
|
|
15126
15192
|
const role = exists ? new aws7.iam.Role(
|
|
15127
15193
|
roleName,
|
|
15128
15194
|
{
|
|
@@ -15967,10 +16033,11 @@ ${pc17.bold("Current Configuration:")}
|
|
|
15967
16033
|
return;
|
|
15968
16034
|
} catch (error) {
|
|
15969
16035
|
trackError("PREVIEW_FAILED", "email:config", { step: "preview" });
|
|
15970
|
-
|
|
16036
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
16037
|
+
if (msg.includes("stack is currently locked")) {
|
|
15971
16038
|
throw errors.stackLocked();
|
|
15972
16039
|
}
|
|
15973
|
-
throw new Error(`Preview failed: ${
|
|
16040
|
+
throw new Error(`Preview failed: ${msg}`);
|
|
15974
16041
|
}
|
|
15975
16042
|
}
|
|
15976
16043
|
let outputs;
|
|
@@ -16028,16 +16095,17 @@ ${pc17.bold("Current Configuration:")}
|
|
|
16028
16095
|
}
|
|
16029
16096
|
);
|
|
16030
16097
|
} catch (error) {
|
|
16098
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
16031
16099
|
trackCommand("email:config", {
|
|
16032
16100
|
success: false,
|
|
16033
16101
|
duration_ms: Date.now() - startTime
|
|
16034
16102
|
});
|
|
16035
|
-
if (
|
|
16103
|
+
if (msg.includes("stack is currently locked")) {
|
|
16036
16104
|
trackError("STACK_LOCKED", "email:config", { step: "update" });
|
|
16037
16105
|
throw errors.stackLocked();
|
|
16038
16106
|
}
|
|
16039
16107
|
trackError("UPDATE_FAILED", "email:config", { step: "update" });
|
|
16040
|
-
throw new Error(`Pulumi update failed: ${
|
|
16108
|
+
throw new Error(`Pulumi update failed: ${msg}`);
|
|
16041
16109
|
}
|
|
16042
16110
|
metadata.timestamp = (/* @__PURE__ */ new Date()).toISOString();
|
|
16043
16111
|
await saveConnectionMetadata(metadata);
|
|
@@ -16468,10 +16536,11 @@ async function connect2(options) {
|
|
|
16468
16536
|
return;
|
|
16469
16537
|
} catch (error) {
|
|
16470
16538
|
trackError("PREVIEW_FAILED", "email:connect", { step: "preview" });
|
|
16471
|
-
|
|
16539
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
16540
|
+
if (msg.includes("stack is currently locked")) {
|
|
16472
16541
|
throw errors.stackLocked();
|
|
16473
16542
|
}
|
|
16474
|
-
throw new Error(`Preview failed: ${
|
|
16543
|
+
throw new Error(`Preview failed: ${msg}`);
|
|
16475
16544
|
}
|
|
16476
16545
|
}
|
|
16477
16546
|
let outputs;
|
|
@@ -16527,17 +16596,18 @@ async function connect2(options) {
|
|
|
16527
16596
|
}
|
|
16528
16597
|
);
|
|
16529
16598
|
} catch (error) {
|
|
16599
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
16530
16600
|
trackServiceInit("email", false, {
|
|
16531
16601
|
preset,
|
|
16532
16602
|
provider,
|
|
16533
16603
|
duration_ms: Date.now() - startTime
|
|
16534
16604
|
});
|
|
16535
|
-
if (
|
|
16605
|
+
if (msg.includes("stack is currently locked")) {
|
|
16536
16606
|
trackError("STACK_LOCKED", "email:connect", { step: "deploy" });
|
|
16537
16607
|
throw errors.stackLocked();
|
|
16538
16608
|
}
|
|
16539
16609
|
trackError("DEPLOYMENT_FAILED", "email:connect", { step: "deploy" });
|
|
16540
|
-
throw new Error(`Pulumi deployment failed: ${
|
|
16610
|
+
throw new Error(`Pulumi deployment failed: ${msg}`);
|
|
16541
16611
|
}
|
|
16542
16612
|
if (outputs.domain && outputs.dkimTokens && outputs.dkimTokens.length > 0) {
|
|
16543
16613
|
const { findHostedZone: findHostedZone2, createDNSRecords: createDNSRecords2 } = await Promise.resolve().then(() => (init_route53(), route53_exports));
|
|
@@ -16556,9 +16626,8 @@ async function connect2(options) {
|
|
|
16556
16626
|
);
|
|
16557
16627
|
progress.succeed("DNS records created in Route53");
|
|
16558
16628
|
} catch (error) {
|
|
16559
|
-
|
|
16560
|
-
|
|
16561
|
-
);
|
|
16629
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
16630
|
+
progress.fail(`Failed to create DNS records automatically: ${msg}`);
|
|
16562
16631
|
progress.info(
|
|
16563
16632
|
"You can manually add the required DNS records shown below"
|
|
16564
16633
|
);
|
|
@@ -16687,8 +16756,11 @@ async function getEmailIdentityInfo(domain, region) {
|
|
|
16687
16756
|
dkimTokens: response.DkimAttributes?.Tokens || [],
|
|
16688
16757
|
mailFromDomain: response.MailFromAttributes?.MailFromDomain
|
|
16689
16758
|
};
|
|
16690
|
-
} catch (
|
|
16691
|
-
|
|
16759
|
+
} catch (error) {
|
|
16760
|
+
if (isAWSError(error)) {
|
|
16761
|
+
return { dkimTokens: [] };
|
|
16762
|
+
}
|
|
16763
|
+
throw error;
|
|
16692
16764
|
}
|
|
16693
16765
|
}
|
|
16694
16766
|
async function emailDestroy(options) {
|
|
@@ -16783,7 +16855,7 @@ async function emailDestroy(options) {
|
|
|
16783
16855
|
stackName,
|
|
16784
16856
|
workDir: getPulumiWorkDir()
|
|
16785
16857
|
});
|
|
16786
|
-
} catch
|
|
16858
|
+
} catch {
|
|
16787
16859
|
throw new Error("No email infrastructure found to preview");
|
|
16788
16860
|
}
|
|
16789
16861
|
const result = await previewWithResourceChanges(stack, {
|
|
@@ -16817,12 +16889,13 @@ async function emailDestroy(options) {
|
|
|
16817
16889
|
return;
|
|
16818
16890
|
} catch (error) {
|
|
16819
16891
|
progress.stop();
|
|
16820
|
-
|
|
16892
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
16893
|
+
if (msg.includes("No email infrastructure found")) {
|
|
16821
16894
|
clack18.log.warn("No email infrastructure found to preview");
|
|
16822
16895
|
process.exit(0);
|
|
16823
16896
|
}
|
|
16824
16897
|
trackError("PREVIEW_FAILED", "email destroy", { step: "preview" });
|
|
16825
|
-
throw new Error(`Preview failed: ${
|
|
16898
|
+
throw new Error(`Preview failed: ${msg}`);
|
|
16826
16899
|
}
|
|
16827
16900
|
}
|
|
16828
16901
|
if (shouldCleanDNS && hostedZone && domain && dkimTokens.length > 0) {
|
|
@@ -16838,7 +16911,8 @@ async function emailDestroy(options) {
|
|
|
16838
16911
|
);
|
|
16839
16912
|
});
|
|
16840
16913
|
} catch (error) {
|
|
16841
|
-
|
|
16914
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
16915
|
+
clack18.log.warn(`Could not delete DNS records: ${msg}`);
|
|
16842
16916
|
clack18.log.info("You may need to delete them manually from Route53");
|
|
16843
16917
|
}
|
|
16844
16918
|
}
|
|
@@ -16854,7 +16928,7 @@ async function emailDestroy(options) {
|
|
|
16854
16928
|
stackName,
|
|
16855
16929
|
workDir: getPulumiWorkDir()
|
|
16856
16930
|
});
|
|
16857
|
-
} catch
|
|
16931
|
+
} catch {
|
|
16858
16932
|
throw new Error("No email infrastructure found to destroy");
|
|
16859
16933
|
}
|
|
16860
16934
|
await withTimeout(
|
|
@@ -16869,12 +16943,13 @@ async function emailDestroy(options) {
|
|
|
16869
16943
|
);
|
|
16870
16944
|
} catch (error) {
|
|
16871
16945
|
progress.stop();
|
|
16872
|
-
|
|
16946
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
16947
|
+
if (msg.includes("No email infrastructure found")) {
|
|
16873
16948
|
clack18.log.warn("No email infrastructure found");
|
|
16874
16949
|
await deleteConnectionMetadata(identity.accountId, region);
|
|
16875
16950
|
process.exit(0);
|
|
16876
16951
|
}
|
|
16877
|
-
if (
|
|
16952
|
+
if (msg.includes("stack is currently locked")) {
|
|
16878
16953
|
trackError("STACK_LOCKED", "email destroy", { step: "destroy" });
|
|
16879
16954
|
throw errors.stackLocked();
|
|
16880
16955
|
}
|
|
@@ -16919,6 +16994,7 @@ init_client();
|
|
|
16919
16994
|
init_events();
|
|
16920
16995
|
init_dns();
|
|
16921
16996
|
init_aws();
|
|
16997
|
+
init_errors();
|
|
16922
16998
|
init_metadata();
|
|
16923
16999
|
import { Resolver as Resolver2 } from "dns/promises";
|
|
16924
17000
|
import { GetEmailIdentityCommand as GetEmailIdentityCommand2, SESv2Client as SESv2Client3 } from "@aws-sdk/client-sesv2";
|
|
@@ -16945,16 +17021,19 @@ async function verifyDomain(options) {
|
|
|
16945
17021
|
);
|
|
16946
17022
|
dkimTokens = identity.DkimAttributes?.Tokens || [];
|
|
16947
17023
|
mailFromDomain = identity.MailFromAttributes?.MailFromDomain;
|
|
16948
|
-
} catch (
|
|
16949
|
-
|
|
16950
|
-
|
|
16951
|
-
|
|
16952
|
-
|
|
17024
|
+
} catch (error) {
|
|
17025
|
+
if (isAWSNotFoundError(error)) {
|
|
17026
|
+
progress.stop();
|
|
17027
|
+
clack19.log.error(`Domain ${options.domain} not found in SES`);
|
|
17028
|
+
console.log(
|
|
17029
|
+
`
|
|
16953
17030
|
Run ${pc20.cyan(`wraps email init --domain ${options.domain}`)} to add this domain.
|
|
16954
17031
|
`
|
|
16955
|
-
|
|
16956
|
-
|
|
16957
|
-
|
|
17032
|
+
);
|
|
17033
|
+
process.exit(1);
|
|
17034
|
+
return;
|
|
17035
|
+
}
|
|
17036
|
+
throw error;
|
|
16958
17037
|
}
|
|
16959
17038
|
const resolver = new Resolver2();
|
|
16960
17039
|
resolver.setServers(["8.8.8.8", "1.1.1.1"]);
|
|
@@ -16971,12 +17050,24 @@ Run ${pc20.cyan(`wraps email init --domain ${options.domain}`)} to add this doma
|
|
|
16971
17050
|
status: found ? "verified" : "incorrect",
|
|
16972
17051
|
records
|
|
16973
17052
|
});
|
|
16974
|
-
} catch (
|
|
16975
|
-
|
|
16976
|
-
|
|
16977
|
-
|
|
16978
|
-
|
|
16979
|
-
|
|
17053
|
+
} catch (error) {
|
|
17054
|
+
const dnsClass = classifyDNSError(error);
|
|
17055
|
+
if (dnsClass === "missing") {
|
|
17056
|
+
dnsResults.push({
|
|
17057
|
+
name: dkimRecord,
|
|
17058
|
+
type: "CNAME",
|
|
17059
|
+
status: "missing"
|
|
17060
|
+
});
|
|
17061
|
+
} else if (dnsClass === "network") {
|
|
17062
|
+
dnsResults.push({
|
|
17063
|
+
name: dkimRecord,
|
|
17064
|
+
type: "CNAME",
|
|
17065
|
+
status: "missing",
|
|
17066
|
+
records: ["DNS lookup failed (network issue)"]
|
|
17067
|
+
});
|
|
17068
|
+
} else {
|
|
17069
|
+
throw error;
|
|
17070
|
+
}
|
|
16980
17071
|
}
|
|
16981
17072
|
}
|
|
16982
17073
|
try {
|
|
@@ -16989,12 +17080,24 @@ Run ${pc20.cyan(`wraps email init --domain ${options.domain}`)} to add this doma
|
|
|
16989
17080
|
status: hasAmazonSES ? "verified" : spfRecord ? "incorrect" : "missing",
|
|
16990
17081
|
records: spfRecord ? [spfRecord] : void 0
|
|
16991
17082
|
});
|
|
16992
|
-
} catch (
|
|
16993
|
-
|
|
16994
|
-
|
|
16995
|
-
|
|
16996
|
-
|
|
16997
|
-
|
|
17083
|
+
} catch (error) {
|
|
17084
|
+
const dnsClass = classifyDNSError(error);
|
|
17085
|
+
if (dnsClass === "missing") {
|
|
17086
|
+
dnsResults.push({
|
|
17087
|
+
name: options.domain,
|
|
17088
|
+
type: "TXT (SPF)",
|
|
17089
|
+
status: "missing"
|
|
17090
|
+
});
|
|
17091
|
+
} else if (dnsClass === "network") {
|
|
17092
|
+
dnsResults.push({
|
|
17093
|
+
name: options.domain,
|
|
17094
|
+
type: "TXT (SPF)",
|
|
17095
|
+
status: "missing",
|
|
17096
|
+
records: ["DNS lookup failed (network issue)"]
|
|
17097
|
+
});
|
|
17098
|
+
} else {
|
|
17099
|
+
throw error;
|
|
17100
|
+
}
|
|
16998
17101
|
}
|
|
16999
17102
|
try {
|
|
17000
17103
|
const records = await resolver.resolveTxt(`_dmarc.${options.domain}`);
|
|
@@ -17005,12 +17108,24 @@ Run ${pc20.cyan(`wraps email init --domain ${options.domain}`)} to add this doma
|
|
|
17005
17108
|
status: dmarcRecord ? "verified" : "missing",
|
|
17006
17109
|
records: dmarcRecord ? [dmarcRecord] : void 0
|
|
17007
17110
|
});
|
|
17008
|
-
} catch (
|
|
17009
|
-
|
|
17010
|
-
|
|
17011
|
-
|
|
17012
|
-
|
|
17013
|
-
|
|
17111
|
+
} catch (error) {
|
|
17112
|
+
const dnsClass = classifyDNSError(error);
|
|
17113
|
+
if (dnsClass === "missing") {
|
|
17114
|
+
dnsResults.push({
|
|
17115
|
+
name: `_dmarc.${options.domain}`,
|
|
17116
|
+
type: "TXT (DMARC)",
|
|
17117
|
+
status: "missing"
|
|
17118
|
+
});
|
|
17119
|
+
} else if (dnsClass === "network") {
|
|
17120
|
+
dnsResults.push({
|
|
17121
|
+
name: `_dmarc.${options.domain}`,
|
|
17122
|
+
type: "TXT (DMARC)",
|
|
17123
|
+
status: "missing",
|
|
17124
|
+
records: ["DNS lookup failed (network issue)"]
|
|
17125
|
+
});
|
|
17126
|
+
} else {
|
|
17127
|
+
throw error;
|
|
17128
|
+
}
|
|
17014
17129
|
}
|
|
17015
17130
|
if (mailFromDomain) {
|
|
17016
17131
|
try {
|
|
@@ -17025,12 +17140,24 @@ Run ${pc20.cyan(`wraps email init --domain ${options.domain}`)} to add this doma
|
|
|
17025
17140
|
status: hasMx ? "verified" : mxRecords.length > 0 ? "incorrect" : "missing",
|
|
17026
17141
|
records: mxRecords.map((r) => `${r.priority} ${r.exchange}`)
|
|
17027
17142
|
});
|
|
17028
|
-
} catch (
|
|
17029
|
-
|
|
17030
|
-
|
|
17031
|
-
|
|
17032
|
-
|
|
17033
|
-
|
|
17143
|
+
} catch (error) {
|
|
17144
|
+
const dnsClass = classifyDNSError(error);
|
|
17145
|
+
if (dnsClass === "missing") {
|
|
17146
|
+
dnsResults.push({
|
|
17147
|
+
name: mailFromDomain,
|
|
17148
|
+
type: "MX",
|
|
17149
|
+
status: "missing"
|
|
17150
|
+
});
|
|
17151
|
+
} else if (dnsClass === "network") {
|
|
17152
|
+
dnsResults.push({
|
|
17153
|
+
name: mailFromDomain,
|
|
17154
|
+
type: "MX",
|
|
17155
|
+
status: "missing",
|
|
17156
|
+
records: ["DNS lookup failed (network issue)"]
|
|
17157
|
+
});
|
|
17158
|
+
} else {
|
|
17159
|
+
throw error;
|
|
17160
|
+
}
|
|
17034
17161
|
}
|
|
17035
17162
|
try {
|
|
17036
17163
|
const records = await resolver.resolveTxt(mailFromDomain);
|
|
@@ -17042,12 +17169,24 @@ Run ${pc20.cyan(`wraps email init --domain ${options.domain}`)} to add this doma
|
|
|
17042
17169
|
status: hasAmazonSES ? "verified" : spfRecord ? "incorrect" : "missing",
|
|
17043
17170
|
records: spfRecord ? [spfRecord] : void 0
|
|
17044
17171
|
});
|
|
17045
|
-
} catch (
|
|
17046
|
-
|
|
17047
|
-
|
|
17048
|
-
|
|
17049
|
-
|
|
17050
|
-
|
|
17172
|
+
} catch (error) {
|
|
17173
|
+
const dnsClass = classifyDNSError(error);
|
|
17174
|
+
if (dnsClass === "missing") {
|
|
17175
|
+
dnsResults.push({
|
|
17176
|
+
name: mailFromDomain,
|
|
17177
|
+
type: "TXT (SPF)",
|
|
17178
|
+
status: "missing"
|
|
17179
|
+
});
|
|
17180
|
+
} else if (dnsClass === "network") {
|
|
17181
|
+
dnsResults.push({
|
|
17182
|
+
name: mailFromDomain,
|
|
17183
|
+
type: "TXT (SPF)",
|
|
17184
|
+
status: "missing",
|
|
17185
|
+
records: ["DNS lookup failed (network issue)"]
|
|
17186
|
+
});
|
|
17187
|
+
} else {
|
|
17188
|
+
throw error;
|
|
17189
|
+
}
|
|
17051
17190
|
}
|
|
17052
17191
|
}
|
|
17053
17192
|
progress.stop();
|
|
@@ -17189,7 +17328,7 @@ Run ${pc20.cyan(`wraps email domains verify --domain ${domain}`)} to check verif
|
|
|
17189
17328
|
);
|
|
17190
17329
|
return;
|
|
17191
17330
|
} catch (error) {
|
|
17192
|
-
if (error
|
|
17331
|
+
if (!isAWSNotFoundError(error)) {
|
|
17193
17332
|
throw error;
|
|
17194
17333
|
}
|
|
17195
17334
|
}
|
|
@@ -17502,7 +17641,7 @@ ${pc20.bold("DNS Records to add:")}
|
|
|
17502
17641
|
} catch (error) {
|
|
17503
17642
|
progress.stop();
|
|
17504
17643
|
trackCommand("email:domains:get-dkim", { success: false });
|
|
17505
|
-
if (error
|
|
17644
|
+
if (isAWSNotFoundError(error)) {
|
|
17506
17645
|
clack19.log.error(`Domain ${options.domain} not found in SES`);
|
|
17507
17646
|
console.log(
|
|
17508
17647
|
`
|
|
@@ -17583,7 +17722,7 @@ Use ${pc20.cyan(`wraps email domains remove --domain ${options.domain} --force`)
|
|
|
17583
17722
|
} catch (error) {
|
|
17584
17723
|
progress.stop();
|
|
17585
17724
|
trackCommand("email:domains:remove", { success: false });
|
|
17586
|
-
if (error
|
|
17725
|
+
if (isAWSNotFoundError(error)) {
|
|
17587
17726
|
clack19.log.error(`Domain ${options.domain} not found in SES`);
|
|
17588
17727
|
process.exit(1);
|
|
17589
17728
|
return;
|
|
@@ -18682,10 +18821,11 @@ ${pc22.yellow(pc22.bold("Configuration Warnings:"))}`);
|
|
|
18682
18821
|
return;
|
|
18683
18822
|
} catch (error) {
|
|
18684
18823
|
trackError("PREVIEW_FAILED", "email:init", { step: "preview" });
|
|
18685
|
-
|
|
18824
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
18825
|
+
if (msg.includes("stack is currently locked")) {
|
|
18686
18826
|
throw errors.stackLocked();
|
|
18687
18827
|
}
|
|
18688
|
-
throw new Error(`Preview failed: ${
|
|
18828
|
+
throw new Error(`Preview failed: ${msg}`);
|
|
18689
18829
|
}
|
|
18690
18830
|
}
|
|
18691
18831
|
let outputs;
|
|
@@ -18756,13 +18896,14 @@ ${pc22.yellow(pc22.bold("Configuration Warnings:"))}`);
|
|
|
18756
18896
|
}
|
|
18757
18897
|
);
|
|
18758
18898
|
} catch (error) {
|
|
18899
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
18759
18900
|
trackServiceInit("email", false, {
|
|
18760
18901
|
preset,
|
|
18761
18902
|
provider,
|
|
18762
18903
|
region,
|
|
18763
18904
|
duration_ms: Date.now() - startTime
|
|
18764
18905
|
});
|
|
18765
|
-
if (
|
|
18906
|
+
if (msg.includes("stack is currently locked")) {
|
|
18766
18907
|
trackError("STACK_LOCKED", "email:init", { step: "deploy" });
|
|
18767
18908
|
throw errors.stackLocked();
|
|
18768
18909
|
}
|
|
@@ -18793,7 +18934,7 @@ ${pc22.yellow(pc22.bold("Configuration Warnings:"))}`);
|
|
|
18793
18934
|
}
|
|
18794
18935
|
}
|
|
18795
18936
|
trackError("DEPLOYMENT_FAILED", "email:init", { step: "deploy" });
|
|
18796
|
-
throw new Error(`Pulumi deployment failed: ${
|
|
18937
|
+
throw new Error(`Pulumi deployment failed: ${msg}`);
|
|
18797
18938
|
}
|
|
18798
18939
|
if (metadata.services.email) {
|
|
18799
18940
|
metadata.services.email.pulumiStackName = `wraps-${identity.accountId}-${region}`;
|
|
@@ -18891,7 +19032,8 @@ ${pc22.yellow(pc22.bold("Configuration Warnings:"))}`);
|
|
|
18891
19032
|
}
|
|
18892
19033
|
} catch (error) {
|
|
18893
19034
|
progress.stop();
|
|
18894
|
-
|
|
19035
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
19036
|
+
clack21.log.warn(`Could not manage DNS records: ${msg}`);
|
|
18895
19037
|
}
|
|
18896
19038
|
} else {
|
|
18897
19039
|
const recordData = {
|
|
@@ -19126,7 +19268,8 @@ ${pc23.bold("The following Wraps resources will be removed:")}
|
|
|
19126
19268
|
return;
|
|
19127
19269
|
} catch (error) {
|
|
19128
19270
|
trackError("PREVIEW_FAILED", "email:restore", { step: "preview" });
|
|
19129
|
-
|
|
19271
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
19272
|
+
throw new Error(`Preview failed: ${msg}`);
|
|
19130
19273
|
}
|
|
19131
19274
|
}
|
|
19132
19275
|
return;
|
|
@@ -19161,7 +19304,8 @@ ${pc23.bold("The following Wraps resources will be removed:")}
|
|
|
19161
19304
|
);
|
|
19162
19305
|
} catch (error) {
|
|
19163
19306
|
trackError("DESTROY_FAILED", "email:restore", { step: "destroy" });
|
|
19164
|
-
|
|
19307
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
19308
|
+
throw new Error(`Failed to destroy Pulumi stack: ${msg}`);
|
|
19165
19309
|
}
|
|
19166
19310
|
});
|
|
19167
19311
|
}
|
|
@@ -19188,6 +19332,7 @@ init_esm_shims();
|
|
|
19188
19332
|
init_client();
|
|
19189
19333
|
init_events();
|
|
19190
19334
|
init_aws();
|
|
19335
|
+
init_errors();
|
|
19191
19336
|
init_fs();
|
|
19192
19337
|
init_metadata();
|
|
19193
19338
|
import * as clack23 from "@clack/prompts";
|
|
@@ -19232,15 +19377,19 @@ async function emailStatus(options) {
|
|
|
19232
19377
|
workDir: getPulumiWorkDir()
|
|
19233
19378
|
});
|
|
19234
19379
|
stackOutputs = await stack.outputs();
|
|
19235
|
-
} catch (
|
|
19236
|
-
|
|
19237
|
-
|
|
19238
|
-
|
|
19239
|
-
|
|
19380
|
+
} catch (error) {
|
|
19381
|
+
if (error instanceof Error && (error.message.includes("no stack named") || error.message.includes("not found"))) {
|
|
19382
|
+
progress.stop();
|
|
19383
|
+
clack23.log.error("No email infrastructure found");
|
|
19384
|
+
console.log(
|
|
19385
|
+
`
|
|
19240
19386
|
Run ${pc24.cyan("wraps email init")} to deploy email infrastructure.
|
|
19241
19387
|
`
|
|
19242
|
-
|
|
19243
|
-
|
|
19388
|
+
);
|
|
19389
|
+
process.exit(1);
|
|
19390
|
+
return;
|
|
19391
|
+
}
|
|
19392
|
+
throw error;
|
|
19244
19393
|
}
|
|
19245
19394
|
const domains = await listSESDomains(region);
|
|
19246
19395
|
const { SESv2Client: SESv2Client6, GetEmailIdentityCommand: GetEmailIdentityCommand5 } = await import("@aws-sdk/client-sesv2");
|
|
@@ -19265,17 +19414,20 @@ Run ${pc24.cyan("wraps email init")} to deploy email infrastructure.
|
|
|
19265
19414
|
isPrimary: tracked?.isPrimary,
|
|
19266
19415
|
purpose: tracked?.purpose
|
|
19267
19416
|
};
|
|
19268
|
-
} catch (
|
|
19269
|
-
|
|
19270
|
-
|
|
19271
|
-
|
|
19272
|
-
|
|
19273
|
-
|
|
19274
|
-
|
|
19275
|
-
|
|
19276
|
-
|
|
19277
|
-
|
|
19278
|
-
|
|
19417
|
+
} catch (error) {
|
|
19418
|
+
if (isAWSError(error)) {
|
|
19419
|
+
return {
|
|
19420
|
+
domain: d.domain,
|
|
19421
|
+
status: d.verified ? "verified" : "pending",
|
|
19422
|
+
dkimTokens: void 0,
|
|
19423
|
+
mailFromDomain: void 0,
|
|
19424
|
+
mailFromStatus: void 0,
|
|
19425
|
+
managed: tracked?.managed,
|
|
19426
|
+
isPrimary: tracked?.isPrimary,
|
|
19427
|
+
purpose: tracked?.purpose
|
|
19428
|
+
};
|
|
19429
|
+
}
|
|
19430
|
+
throw error;
|
|
19279
19431
|
}
|
|
19280
19432
|
})
|
|
19281
19433
|
);
|
|
@@ -21899,11 +22051,12 @@ ${pc28.bold("Cost Impact:")}`);
|
|
|
21899
22051
|
});
|
|
21900
22052
|
return;
|
|
21901
22053
|
} catch (error) {
|
|
22054
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
21902
22055
|
trackError("PREVIEW_FAILED", "email:upgrade", { step: "preview" });
|
|
21903
|
-
if (
|
|
22056
|
+
if (msg.includes("stack is currently locked")) {
|
|
21904
22057
|
throw errors.stackLocked();
|
|
21905
22058
|
}
|
|
21906
|
-
throw new Error(`Preview failed: ${
|
|
22059
|
+
throw new Error(`Preview failed: ${msg}`);
|
|
21907
22060
|
}
|
|
21908
22061
|
}
|
|
21909
22062
|
let outputs;
|
|
@@ -21985,18 +22138,19 @@ ${pc28.bold("Cost Impact:")}`);
|
|
|
21985
22138
|
}
|
|
21986
22139
|
);
|
|
21987
22140
|
} catch (error) {
|
|
22141
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
21988
22142
|
trackServiceUpgrade("email", {
|
|
21989
22143
|
from_preset: metadata.services.email?.preset,
|
|
21990
22144
|
to_preset: newPreset,
|
|
21991
22145
|
action: typeof upgradeAction === "string" ? upgradeAction : void 0,
|
|
21992
22146
|
duration_ms: Date.now() - startTime
|
|
21993
22147
|
});
|
|
21994
|
-
if (
|
|
22148
|
+
if (msg.includes("stack is currently locked")) {
|
|
21995
22149
|
trackError("STACK_LOCKED", "email:upgrade", { step: "deploy" });
|
|
21996
22150
|
throw errors.stackLocked();
|
|
21997
22151
|
}
|
|
21998
22152
|
trackError("UPGRADE_FAILED", "email:upgrade", { step: "deploy" });
|
|
21999
|
-
throw new Error(`Pulumi upgrade failed: ${
|
|
22153
|
+
throw new Error(`Pulumi upgrade failed: ${msg}`);
|
|
22000
22154
|
}
|
|
22001
22155
|
let dnsAutoCreated = false;
|
|
22002
22156
|
if (outputs.domain && outputs.dkimTokens && outputs.dkimTokens.length > 0) {
|
|
@@ -22054,9 +22208,8 @@ ${pc28.bold("Cost Impact:")}`);
|
|
|
22054
22208
|
);
|
|
22055
22209
|
}
|
|
22056
22210
|
} catch (error) {
|
|
22057
|
-
|
|
22058
|
-
|
|
22059
|
-
);
|
|
22211
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
22212
|
+
progress.fail(`Failed to create DNS records automatically: ${msg}`);
|
|
22060
22213
|
progress.info(
|
|
22061
22214
|
"You can manually add the required DNS records shown below"
|
|
22062
22215
|
);
|
|
@@ -22195,9 +22348,8 @@ ${pc28.bold("Add these DNS records to your DNS provider:")}
|
|
|
22195
22348
|
}
|
|
22196
22349
|
}
|
|
22197
22350
|
} catch (error) {
|
|
22198
|
-
|
|
22199
|
-
|
|
22200
|
-
);
|
|
22351
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
22352
|
+
progress.fail(`Failed to create ACM validation record: ${msg}`);
|
|
22201
22353
|
}
|
|
22202
22354
|
}
|
|
22203
22355
|
}
|
|
@@ -24423,10 +24575,10 @@ var WRAPS_PLATFORM_ACCOUNT_ID = "905130073023";
|
|
|
24423
24575
|
async function updatePlatformRole(metadata, progress, externalId) {
|
|
24424
24576
|
const roleName = "wraps-console-access-role";
|
|
24425
24577
|
const iam10 = new IAMClient2({ region: "us-east-1" });
|
|
24426
|
-
let
|
|
24578
|
+
let roleExists2 = false;
|
|
24427
24579
|
try {
|
|
24428
24580
|
await iam10.send(new GetRoleCommand({ RoleName: roleName }));
|
|
24429
|
-
|
|
24581
|
+
roleExists2 = true;
|
|
24430
24582
|
} catch (error) {
|
|
24431
24583
|
const isNotFound = error instanceof Error && (error.name === "NoSuchEntityException" || error.name === "NoSuchEntity" || error.message.includes("NoSuchEntity"));
|
|
24432
24584
|
if (!isNotFound) {
|
|
@@ -24436,7 +24588,7 @@ async function updatePlatformRole(metadata, progress, externalId) {
|
|
|
24436
24588
|
const emailConfig = metadata.services.email?.config;
|
|
24437
24589
|
const smsConfig = metadata.services.sms?.config;
|
|
24438
24590
|
const policy = buildConsolePolicyDocument(emailConfig, smsConfig);
|
|
24439
|
-
if (
|
|
24591
|
+
if (roleExists2) {
|
|
24440
24592
|
await progress.execute("Updating platform access role", async () => {
|
|
24441
24593
|
await iam10.send(
|
|
24442
24594
|
new PutRolePolicyCommand({
|
|
@@ -24894,17 +25046,17 @@ Run ${pc33.cyan("wraps email init")} or ${pc33.cyan("wraps sms init")} first.
|
|
|
24894
25046
|
}
|
|
24895
25047
|
const roleName = "wraps-console-access-role";
|
|
24896
25048
|
const iam10 = new IAMClient2({ region: "us-east-1" });
|
|
24897
|
-
let
|
|
25049
|
+
let roleExists2 = false;
|
|
24898
25050
|
try {
|
|
24899
25051
|
await iam10.send(new GetRoleCommand({ RoleName: roleName }));
|
|
24900
|
-
|
|
25052
|
+
roleExists2 = true;
|
|
24901
25053
|
} catch (error) {
|
|
24902
25054
|
const isNotFound = error instanceof Error && (error.name === "NoSuchEntityException" || error.name === "NoSuchEntity" || error.message.includes("NoSuchEntity"));
|
|
24903
25055
|
if (!isNotFound) {
|
|
24904
25056
|
throw error;
|
|
24905
25057
|
}
|
|
24906
25058
|
}
|
|
24907
|
-
if (
|
|
25059
|
+
if (roleExists2) {
|
|
24908
25060
|
const emailConfig = metadata.services.email?.config;
|
|
24909
25061
|
const smsConfig = metadata.services.sms?.config;
|
|
24910
25062
|
const policy = buildConsolePolicyDocument(emailConfig, smsConfig);
|
|
@@ -25061,10 +25213,10 @@ Run ${pc35.cyan("wraps email init")} to deploy infrastructure first.
|
|
|
25061
25213
|
}
|
|
25062
25214
|
const roleName = "wraps-console-access-role";
|
|
25063
25215
|
const iam10 = new IAMClient3({ region: "us-east-1" });
|
|
25064
|
-
let
|
|
25216
|
+
let roleExists2 = false;
|
|
25065
25217
|
try {
|
|
25066
25218
|
await iam10.send(new GetRoleCommand2({ RoleName: roleName }));
|
|
25067
|
-
|
|
25219
|
+
roleExists2 = true;
|
|
25068
25220
|
} catch (error) {
|
|
25069
25221
|
const isNotFound = error instanceof Error && (error.name === "NoSuchEntityException" || error.name === "NoSuchEntity" || error.message.includes("NoSuchEntity"));
|
|
25070
25222
|
if (!isNotFound) {
|
|
@@ -25072,7 +25224,7 @@ Run ${pc35.cyan("wraps email init")} to deploy infrastructure first.
|
|
|
25072
25224
|
}
|
|
25073
25225
|
}
|
|
25074
25226
|
const externalId = metadata.platform?.externalId;
|
|
25075
|
-
if (!(
|
|
25227
|
+
if (!(roleExists2 || externalId)) {
|
|
25076
25228
|
progress.stop();
|
|
25077
25229
|
log31.warn(`IAM role ${pc35.cyan(roleName)} does not exist`);
|
|
25078
25230
|
console.log(
|
|
@@ -25084,7 +25236,7 @@ Run ${pc35.cyan("wraps email init")} to deploy infrastructure first.
|
|
|
25084
25236
|
);
|
|
25085
25237
|
process.exit(0);
|
|
25086
25238
|
}
|
|
25087
|
-
if (
|
|
25239
|
+
if (roleExists2) {
|
|
25088
25240
|
progress.info(`Found IAM role: ${pc35.cyan(roleName)}`);
|
|
25089
25241
|
} else {
|
|
25090
25242
|
progress.info(
|
|
@@ -25093,7 +25245,7 @@ Run ${pc35.cyan("wraps email init")} to deploy infrastructure first.
|
|
|
25093
25245
|
}
|
|
25094
25246
|
if (!options.force) {
|
|
25095
25247
|
progress.stop();
|
|
25096
|
-
const actionLabel =
|
|
25248
|
+
const actionLabel = roleExists2 ? "Update" : "Create";
|
|
25097
25249
|
const shouldContinue = await confirm13({
|
|
25098
25250
|
message: `${actionLabel} IAM role ${pc35.cyan(roleName)} with latest permissions?`,
|
|
25099
25251
|
initialValue: true
|
|
@@ -25112,7 +25264,7 @@ Run ${pc35.cyan("wraps email init")} to deploy infrastructure first.
|
|
|
25112
25264
|
const smsEnabled = !!smsConfig;
|
|
25113
25265
|
const smsSendingEnabled = smsConfig && smsConfig.sendingEnabled !== false;
|
|
25114
25266
|
const smsEventTracking = smsConfig?.eventTracking;
|
|
25115
|
-
if (!
|
|
25267
|
+
if (!roleExists2 && externalId) {
|
|
25116
25268
|
const WRAPS_PLATFORM_ACCOUNT_ID2 = "905130073023";
|
|
25117
25269
|
await progress.execute("Creating IAM role", async () => {
|
|
25118
25270
|
const trustPolicy = {
|
|
@@ -25165,7 +25317,7 @@ Run ${pc35.cyan("wraps email init")} to deploy infrastructure first.
|
|
|
25165
25317
|
});
|
|
25166
25318
|
}
|
|
25167
25319
|
progress.stop();
|
|
25168
|
-
const actionVerb =
|
|
25320
|
+
const actionVerb = roleExists2 ? "updated" : "created";
|
|
25169
25321
|
trackCommand("platform:update-role", {
|
|
25170
25322
|
success: true,
|
|
25171
25323
|
duration_ms: Date.now() - startTime,
|
|
@@ -28349,38 +28501,9 @@ import pc39 from "picocolors";
|
|
|
28349
28501
|
|
|
28350
28502
|
// src/infrastructure/sms-stack.ts
|
|
28351
28503
|
init_esm_shims();
|
|
28504
|
+
init_resource_checks();
|
|
28352
28505
|
import * as aws19 from "@pulumi/aws";
|
|
28353
28506
|
import * as pulumi24 from "@pulumi/pulumi";
|
|
28354
|
-
async function roleExists3(roleName) {
|
|
28355
|
-
try {
|
|
28356
|
-
const { IAMClient: IAMClient4, GetRoleCommand: GetRoleCommand3 } = await import("@aws-sdk/client-iam");
|
|
28357
|
-
const iam10 = new IAMClient4({
|
|
28358
|
-
region: process.env.AWS_REGION || "us-east-1"
|
|
28359
|
-
});
|
|
28360
|
-
await iam10.send(new GetRoleCommand3({ RoleName: roleName }));
|
|
28361
|
-
return true;
|
|
28362
|
-
} catch (error) {
|
|
28363
|
-
if (error.name === "NoSuchEntityException" || error.Code === "NoSuchEntity" || error.Error?.Code === "NoSuchEntity") {
|
|
28364
|
-
return false;
|
|
28365
|
-
}
|
|
28366
|
-
return false;
|
|
28367
|
-
}
|
|
28368
|
-
}
|
|
28369
|
-
async function tableExists2(tableName) {
|
|
28370
|
-
try {
|
|
28371
|
-
const { DynamoDBClient: DynamoDBClient6, DescribeTableCommand: DescribeTableCommand2 } = await import("@aws-sdk/client-dynamodb");
|
|
28372
|
-
const dynamodb3 = new DynamoDBClient6({
|
|
28373
|
-
region: process.env.AWS_REGION || "us-east-1"
|
|
28374
|
-
});
|
|
28375
|
-
await dynamodb3.send(new DescribeTableCommand2({ TableName: tableName }));
|
|
28376
|
-
return true;
|
|
28377
|
-
} catch (error) {
|
|
28378
|
-
if (error instanceof Error && "name" in error && error.name === "ResourceNotFoundException") {
|
|
28379
|
-
return false;
|
|
28380
|
-
}
|
|
28381
|
-
return false;
|
|
28382
|
-
}
|
|
28383
|
-
}
|
|
28384
28507
|
async function createSMSIAMRole(config2) {
|
|
28385
28508
|
let assumeRolePolicy;
|
|
28386
28509
|
if (config2.provider === "vercel" && config2.oidcProvider) {
|
|
@@ -28426,7 +28549,7 @@ async function createSMSIAMRole(config2) {
|
|
|
28426
28549
|
throw new Error("Other providers not yet implemented");
|
|
28427
28550
|
}
|
|
28428
28551
|
const roleName = "wraps-sms-role";
|
|
28429
|
-
const exists = await
|
|
28552
|
+
const exists = await roleExists(roleName);
|
|
28430
28553
|
const role = exists ? new aws19.iam.Role(
|
|
28431
28554
|
roleName,
|
|
28432
28555
|
{
|
|
@@ -28644,20 +28767,6 @@ async function createSMSPhoneNumber(phoneNumberType, optOutList) {
|
|
|
28644
28767
|
}
|
|
28645
28768
|
);
|
|
28646
28769
|
}
|
|
28647
|
-
async function sqsQueueExists(queueName) {
|
|
28648
|
-
try {
|
|
28649
|
-
const { SQSClient, GetQueueUrlCommand } = await import("@aws-sdk/client-sqs");
|
|
28650
|
-
const sqs5 = new SQSClient({
|
|
28651
|
-
region: process.env.AWS_REGION || "us-east-1"
|
|
28652
|
-
});
|
|
28653
|
-
const response = await sqs5.send(
|
|
28654
|
-
new GetQueueUrlCommand({ QueueName: queueName })
|
|
28655
|
-
);
|
|
28656
|
-
return response.QueueUrl || null;
|
|
28657
|
-
} catch {
|
|
28658
|
-
return null;
|
|
28659
|
-
}
|
|
28660
|
-
}
|
|
28661
28770
|
async function createSMSSQSResources() {
|
|
28662
28771
|
const dlqName = "wraps-sms-events-dlq";
|
|
28663
28772
|
const queueName = "wraps-sms-events";
|
|
@@ -28707,30 +28816,6 @@ async function createSMSSQSResources() {
|
|
|
28707
28816
|
});
|
|
28708
28817
|
return { queue, dlq };
|
|
28709
28818
|
}
|
|
28710
|
-
async function snsTopicExists(topicName) {
|
|
28711
|
-
try {
|
|
28712
|
-
const { SNSClient: SNSClient2, ListTopicsCommand: ListTopicsCommand2 } = await import("@aws-sdk/client-sns");
|
|
28713
|
-
const sns3 = new SNSClient2({
|
|
28714
|
-
region: process.env.AWS_REGION || "us-east-1"
|
|
28715
|
-
});
|
|
28716
|
-
let nextToken;
|
|
28717
|
-
do {
|
|
28718
|
-
const response = await sns3.send(
|
|
28719
|
-
new ListTopicsCommand2({ NextToken: nextToken })
|
|
28720
|
-
);
|
|
28721
|
-
const found = response.Topics?.find(
|
|
28722
|
-
(t) => t.TopicArn?.endsWith(`:${topicName}`)
|
|
28723
|
-
);
|
|
28724
|
-
if (found?.TopicArn) {
|
|
28725
|
-
return found.TopicArn;
|
|
28726
|
-
}
|
|
28727
|
-
nextToken = response.NextToken;
|
|
28728
|
-
} while (nextToken);
|
|
28729
|
-
return null;
|
|
28730
|
-
} catch {
|
|
28731
|
-
return null;
|
|
28732
|
-
}
|
|
28733
|
-
}
|
|
28734
28819
|
async function createSMSSNSResources(config2) {
|
|
28735
28820
|
const topicName = "wraps-sms-events";
|
|
28736
28821
|
const topicArn = await snsTopicExists(topicName);
|
|
@@ -28798,7 +28883,7 @@ async function createSMSSNSResources(config2) {
|
|
|
28798
28883
|
}
|
|
28799
28884
|
async function createSMSDynamoDBTable() {
|
|
28800
28885
|
const tableName = "wraps-sms-history";
|
|
28801
|
-
const exists = await
|
|
28886
|
+
const exists = await tableExists(tableName);
|
|
28802
28887
|
const tableConfig = {
|
|
28803
28888
|
name: tableName,
|
|
28804
28889
|
billingMode: "PAY_PER_REQUEST",
|
|
@@ -31734,18 +31819,19 @@ ${pc45.bold("Cost Impact:")}`);
|
|
|
31734
31819
|
});
|
|
31735
31820
|
}
|
|
31736
31821
|
} catch (error) {
|
|
31822
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
31737
31823
|
trackServiceUpgrade("sms", {
|
|
31738
31824
|
from_preset: metadata.services.sms?.preset,
|
|
31739
31825
|
to_preset: newPreset,
|
|
31740
31826
|
action: typeof upgradeAction === "string" ? upgradeAction : void 0,
|
|
31741
31827
|
duration_ms: Date.now() - startTime
|
|
31742
31828
|
});
|
|
31743
|
-
if (
|
|
31829
|
+
if (msg.includes("stack is currently locked")) {
|
|
31744
31830
|
trackError("STACK_LOCKED", "sms:upgrade", { step: "deploy" });
|
|
31745
31831
|
throw errors.stackLocked();
|
|
31746
31832
|
}
|
|
31747
31833
|
trackError("UPGRADE_FAILED", "sms:upgrade", { step: "deploy" });
|
|
31748
|
-
throw new Error(`SMS upgrade failed: ${
|
|
31834
|
+
throw new Error(`SMS upgrade failed: ${msg}`);
|
|
31749
31835
|
}
|
|
31750
31836
|
updateServiceConfig(metadata, "sms", updatedConfig);
|
|
31751
31837
|
if (metadata.services.sms) {
|