@wraps.dev/cli 2.5.2 → 2.6.0
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
|
@@ -1028,7 +1028,7 @@ To remove: wraps destroy --stack ${stackName}`,
|
|
|
1028
1028
|
stackLocked: () => new WrapsError(
|
|
1029
1029
|
"The Pulumi stack is locked from a previous run",
|
|
1030
1030
|
"STACK_LOCKED",
|
|
1031
|
-
"This happens when a previous deployment was interrupted.\n\
|
|
1031
|
+
"This happens when a previous deployment was interrupted.\n\nFor local state, run:\n rm -rf ~/.wraps/pulumi/.pulumi/locks\n\nFor S3 state, delete the lock object in your wraps-state-* bucket under .pulumi/locks/\n\nThen try your command again.",
|
|
1032
1032
|
"https://wraps.dev/docs/guides/aws-setup/permissions/troubleshooting"
|
|
1033
1033
|
),
|
|
1034
1034
|
// SMS-specific errors
|
|
@@ -1168,6 +1168,24 @@ View required SES permissions:
|
|
|
1168
1168
|
"ROUTE53_PERMISSION_DENIED",
|
|
1169
1169
|
"Your IAM user/role needs Route53 permissions for automatic DNS management.\nRequired actions: ChangeResourceRecordSets, ListHostedZones\n\nThis is optional - you can add DNS records manually instead.",
|
|
1170
1170
|
"https://wraps.dev/docs/guides/aws-setup/permissions"
|
|
1171
|
+
),
|
|
1172
|
+
s3StateBucketCreationFailed: (bucketName) => new WrapsError(
|
|
1173
|
+
`Failed to create S3 state bucket: ${bucketName}`,
|
|
1174
|
+
"S3_STATE_BUCKET_CREATION_FAILED",
|
|
1175
|
+
"Ensure your IAM user/role has s3:CreateBucket, s3:PutBucketEncryption, s3:PutBucketVersioning permissions.\n\nTo use local-only state instead:\n export WRAPS_LOCAL_ONLY=1",
|
|
1176
|
+
"https://wraps.dev/docs/guides/aws-setup/permissions"
|
|
1177
|
+
),
|
|
1178
|
+
s3StateAccessDenied: () => new WrapsError(
|
|
1179
|
+
"Access denied to S3 state bucket",
|
|
1180
|
+
"S3_STATE_ACCESS_DENIED",
|
|
1181
|
+
"Ensure your IAM user/role has s3:GetObject, s3:PutObject, s3:ListBucket permissions on wraps-state-* buckets.\n\nTo use local-only state instead:\n export WRAPS_LOCAL_ONLY=1",
|
|
1182
|
+
"https://wraps.dev/docs/guides/aws-setup/permissions"
|
|
1183
|
+
),
|
|
1184
|
+
stateMigrationFailed: () => new WrapsError(
|
|
1185
|
+
"Failed to migrate Pulumi state to S3",
|
|
1186
|
+
"STATE_MIGRATION_FAILED",
|
|
1187
|
+
"The migration from local to S3 state storage failed.\nYour local state is still intact.\n\nTo skip migration and use local-only state:\n export WRAPS_LOCAL_ONLY=1",
|
|
1188
|
+
"https://wraps.dev/docs/guides/aws-setup/permissions"
|
|
1171
1189
|
)
|
|
1172
1190
|
};
|
|
1173
1191
|
}
|
|
@@ -3314,31 +3332,285 @@ var init_route53 = __esm({
|
|
|
3314
3332
|
}
|
|
3315
3333
|
});
|
|
3316
3334
|
|
|
3317
|
-
// src/utils/shared/
|
|
3335
|
+
// src/utils/shared/s3-state.ts
|
|
3336
|
+
var s3_state_exports = {};
|
|
3337
|
+
__export(s3_state_exports, {
|
|
3338
|
+
downloadMetadata: () => downloadMetadata,
|
|
3339
|
+
ensureStateBucket: () => ensureStateBucket,
|
|
3340
|
+
getS3BackendUrl: () => getS3BackendUrl,
|
|
3341
|
+
getStateBucketName: () => getStateBucketName,
|
|
3342
|
+
migrateLocalPulumiState: () => migrateLocalPulumiState,
|
|
3343
|
+
needsMigration: () => needsMigration,
|
|
3344
|
+
stateBucketExists: () => stateBucketExists,
|
|
3345
|
+
uploadMetadata: () => uploadMetadata
|
|
3346
|
+
});
|
|
3318
3347
|
import { existsSync as existsSync2 } from "fs";
|
|
3348
|
+
import { readdir, writeFile } from "fs/promises";
|
|
3349
|
+
import { join as join2 } from "path";
|
|
3350
|
+
function getStateBucketName(accountId, region) {
|
|
3351
|
+
return `wraps-state-${accountId}-${region}`;
|
|
3352
|
+
}
|
|
3353
|
+
function getS3BackendUrl(accountId, region) {
|
|
3354
|
+
return `s3://${getStateBucketName(accountId, region)}`;
|
|
3355
|
+
}
|
|
3356
|
+
async function stateBucketExists(accountId, region) {
|
|
3357
|
+
const { S3Client, HeadBucketCommand } = await import("@aws-sdk/client-s3");
|
|
3358
|
+
const client = new S3Client({ region });
|
|
3359
|
+
const bucketName = getStateBucketName(accountId, region);
|
|
3360
|
+
try {
|
|
3361
|
+
await client.send(new HeadBucketCommand({ Bucket: bucketName }));
|
|
3362
|
+
return true;
|
|
3363
|
+
} catch (error) {
|
|
3364
|
+
if (error.name === "NotFound" || error.name === "NoSuchBucket" || error.$metadata?.httpStatusCode === 404) {
|
|
3365
|
+
return false;
|
|
3366
|
+
}
|
|
3367
|
+
throw error;
|
|
3368
|
+
}
|
|
3369
|
+
}
|
|
3370
|
+
async function ensureStateBucket(accountId, region) {
|
|
3371
|
+
const {
|
|
3372
|
+
S3Client,
|
|
3373
|
+
HeadBucketCommand,
|
|
3374
|
+
CreateBucketCommand,
|
|
3375
|
+
PutBucketEncryptionCommand,
|
|
3376
|
+
PutBucketVersioningCommand,
|
|
3377
|
+
PutPublicAccessBlockCommand,
|
|
3378
|
+
PutBucketTaggingCommand
|
|
3379
|
+
} = await import("@aws-sdk/client-s3");
|
|
3380
|
+
const client = new S3Client({ region });
|
|
3381
|
+
const bucketName = getStateBucketName(accountId, region);
|
|
3382
|
+
try {
|
|
3383
|
+
await client.send(new HeadBucketCommand({ Bucket: bucketName }));
|
|
3384
|
+
return bucketName;
|
|
3385
|
+
} catch (error) {
|
|
3386
|
+
if (error.name !== "NotFound" && error.name !== "NoSuchBucket" && error.$metadata?.httpStatusCode !== 404) {
|
|
3387
|
+
throw error;
|
|
3388
|
+
}
|
|
3389
|
+
}
|
|
3390
|
+
const createParams = { Bucket: bucketName };
|
|
3391
|
+
if (region !== "us-east-1") {
|
|
3392
|
+
createParams.CreateBucketConfiguration = {
|
|
3393
|
+
LocationConstraint: region
|
|
3394
|
+
};
|
|
3395
|
+
}
|
|
3396
|
+
await client.send(new CreateBucketCommand(createParams));
|
|
3397
|
+
await client.send(
|
|
3398
|
+
new PutBucketEncryptionCommand({
|
|
3399
|
+
Bucket: bucketName,
|
|
3400
|
+
ServerSideEncryptionConfiguration: {
|
|
3401
|
+
Rules: [
|
|
3402
|
+
{
|
|
3403
|
+
ApplyServerSideEncryptionByDefault: {
|
|
3404
|
+
SSEAlgorithm: "AES256"
|
|
3405
|
+
}
|
|
3406
|
+
}
|
|
3407
|
+
]
|
|
3408
|
+
}
|
|
3409
|
+
})
|
|
3410
|
+
);
|
|
3411
|
+
await client.send(
|
|
3412
|
+
new PutBucketVersioningCommand({
|
|
3413
|
+
Bucket: bucketName,
|
|
3414
|
+
VersioningConfiguration: {
|
|
3415
|
+
Status: "Enabled"
|
|
3416
|
+
}
|
|
3417
|
+
})
|
|
3418
|
+
);
|
|
3419
|
+
await client.send(
|
|
3420
|
+
new PutPublicAccessBlockCommand({
|
|
3421
|
+
Bucket: bucketName,
|
|
3422
|
+
PublicAccessBlockConfiguration: {
|
|
3423
|
+
BlockPublicAcls: true,
|
|
3424
|
+
BlockPublicPolicy: true,
|
|
3425
|
+
IgnorePublicAcls: true,
|
|
3426
|
+
RestrictPublicBuckets: true
|
|
3427
|
+
}
|
|
3428
|
+
})
|
|
3429
|
+
);
|
|
3430
|
+
await client.send(
|
|
3431
|
+
new PutBucketTaggingCommand({
|
|
3432
|
+
Bucket: bucketName,
|
|
3433
|
+
Tagging: {
|
|
3434
|
+
TagSet: [
|
|
3435
|
+
{ Key: "ManagedBy", Value: "wraps-cli" },
|
|
3436
|
+
{ Key: "Purpose", Value: "state" }
|
|
3437
|
+
]
|
|
3438
|
+
}
|
|
3439
|
+
})
|
|
3440
|
+
);
|
|
3441
|
+
return bucketName;
|
|
3442
|
+
}
|
|
3443
|
+
async function uploadMetadata(bucketName, metadata) {
|
|
3444
|
+
const { S3Client, PutObjectCommand } = await import("@aws-sdk/client-s3");
|
|
3445
|
+
const client = new S3Client({ region: metadata.region });
|
|
3446
|
+
const key = `metadata/${metadata.accountId}-${metadata.region}.json`;
|
|
3447
|
+
await client.send(
|
|
3448
|
+
new PutObjectCommand({
|
|
3449
|
+
Bucket: bucketName,
|
|
3450
|
+
Key: key,
|
|
3451
|
+
Body: JSON.stringify(metadata, null, 2),
|
|
3452
|
+
ContentType: "application/json"
|
|
3453
|
+
})
|
|
3454
|
+
);
|
|
3455
|
+
}
|
|
3456
|
+
async function downloadMetadata(bucketName, accountId, region) {
|
|
3457
|
+
const { S3Client, GetObjectCommand } = await import("@aws-sdk/client-s3");
|
|
3458
|
+
const client = new S3Client({ region });
|
|
3459
|
+
const key = `metadata/${accountId}-${region}.json`;
|
|
3460
|
+
try {
|
|
3461
|
+
const response = await client.send(
|
|
3462
|
+
new GetObjectCommand({
|
|
3463
|
+
Bucket: bucketName,
|
|
3464
|
+
Key: key
|
|
3465
|
+
})
|
|
3466
|
+
);
|
|
3467
|
+
const body = await response.Body?.transformToString();
|
|
3468
|
+
if (!body) {
|
|
3469
|
+
return null;
|
|
3470
|
+
}
|
|
3471
|
+
return JSON.parse(body);
|
|
3472
|
+
} catch (error) {
|
|
3473
|
+
if (error.name === "NoSuchKey" || error.$metadata?.httpStatusCode === 404) {
|
|
3474
|
+
return null;
|
|
3475
|
+
}
|
|
3476
|
+
throw error;
|
|
3477
|
+
}
|
|
3478
|
+
}
|
|
3479
|
+
async function needsMigration(localPulumiDir, accountId, region) {
|
|
3480
|
+
const markerPath = join2(localPulumiDir, `.migrated-${accountId}-${region}`);
|
|
3481
|
+
if (existsSync2(markerPath)) {
|
|
3482
|
+
return false;
|
|
3483
|
+
}
|
|
3484
|
+
const stacksDir = join2(localPulumiDir, ".pulumi", "stacks");
|
|
3485
|
+
if (!existsSync2(stacksDir)) {
|
|
3486
|
+
return false;
|
|
3487
|
+
}
|
|
3488
|
+
try {
|
|
3489
|
+
const files = await readdir(stacksDir);
|
|
3490
|
+
const matchingStacks = files.filter(
|
|
3491
|
+
(f) => f.includes(accountId) && f.includes(region) && f.endsWith(".json")
|
|
3492
|
+
);
|
|
3493
|
+
return matchingStacks.length > 0;
|
|
3494
|
+
} catch {
|
|
3495
|
+
return false;
|
|
3496
|
+
}
|
|
3497
|
+
}
|
|
3498
|
+
async function migrateLocalPulumiState(localPulumiDir, bucketName, accountId, region) {
|
|
3499
|
+
const pulumi28 = await import("@pulumi/pulumi/automation/index.js");
|
|
3500
|
+
const stacksDir = join2(localPulumiDir, ".pulumi", "stacks");
|
|
3501
|
+
const files = await readdir(stacksDir);
|
|
3502
|
+
const stackFiles = files.filter(
|
|
3503
|
+
(f) => f.includes(accountId) && f.includes(region) && f.endsWith(".json")
|
|
3504
|
+
);
|
|
3505
|
+
for (const stackFile of stackFiles) {
|
|
3506
|
+
const stackName = stackFile.replace(".json", "");
|
|
3507
|
+
let projectName = "wraps-email";
|
|
3508
|
+
if (stackName.startsWith("wraps-sms-")) {
|
|
3509
|
+
projectName = "wraps-sms";
|
|
3510
|
+
} else if (stackName.startsWith("wraps-cdn-")) {
|
|
3511
|
+
projectName = "wraps-cdn";
|
|
3512
|
+
}
|
|
3513
|
+
try {
|
|
3514
|
+
const localStack = await pulumi28.LocalWorkspace.selectStack({
|
|
3515
|
+
stackName,
|
|
3516
|
+
workDir: localPulumiDir
|
|
3517
|
+
});
|
|
3518
|
+
const state = await localStack.exportStack();
|
|
3519
|
+
const s3Stack = await pulumi28.LocalWorkspace.createOrSelectStack(
|
|
3520
|
+
{
|
|
3521
|
+
stackName,
|
|
3522
|
+
projectName,
|
|
3523
|
+
program: async () => ({})
|
|
3524
|
+
},
|
|
3525
|
+
{
|
|
3526
|
+
workDir: localPulumiDir,
|
|
3527
|
+
envVars: {
|
|
3528
|
+
PULUMI_BACKEND_URL: `s3://${bucketName}`,
|
|
3529
|
+
PULUMI_CONFIG_PASSPHRASE: ""
|
|
3530
|
+
}
|
|
3531
|
+
}
|
|
3532
|
+
);
|
|
3533
|
+
await s3Stack.importStack(state);
|
|
3534
|
+
} catch (error) {
|
|
3535
|
+
console.error(
|
|
3536
|
+
`Warning: Failed to migrate stack ${stackName}: ${error.message}`
|
|
3537
|
+
);
|
|
3538
|
+
}
|
|
3539
|
+
}
|
|
3540
|
+
const markerPath = join2(localPulumiDir, `.migrated-${accountId}-${region}`);
|
|
3541
|
+
await writeFile(markerPath, (/* @__PURE__ */ new Date()).toISOString(), "utf-8");
|
|
3542
|
+
}
|
|
3543
|
+
var init_s3_state = __esm({
|
|
3544
|
+
"src/utils/shared/s3-state.ts"() {
|
|
3545
|
+
"use strict";
|
|
3546
|
+
init_esm_shims();
|
|
3547
|
+
}
|
|
3548
|
+
});
|
|
3549
|
+
|
|
3550
|
+
// src/utils/shared/fs.ts
|
|
3551
|
+
import { existsSync as existsSync3 } from "fs";
|
|
3319
3552
|
import { mkdir } from "fs/promises";
|
|
3320
3553
|
import { homedir as homedir2 } from "os";
|
|
3321
|
-
import { join as
|
|
3554
|
+
import { join as join3 } from "path";
|
|
3322
3555
|
function getWrapsDir() {
|
|
3323
|
-
return
|
|
3556
|
+
return join3(homedir2(), ".wraps");
|
|
3324
3557
|
}
|
|
3325
3558
|
function getPulumiWorkDir() {
|
|
3326
|
-
return
|
|
3559
|
+
return join3(getWrapsDir(), "pulumi");
|
|
3327
3560
|
}
|
|
3328
3561
|
async function ensureWrapsDir() {
|
|
3329
3562
|
const wrapsDir = getWrapsDir();
|
|
3330
|
-
if (!
|
|
3563
|
+
if (!existsSync3(wrapsDir)) {
|
|
3331
3564
|
await mkdir(wrapsDir, { recursive: true });
|
|
3332
3565
|
}
|
|
3333
3566
|
}
|
|
3334
|
-
async function ensurePulumiWorkDir() {
|
|
3567
|
+
async function ensurePulumiWorkDir(options) {
|
|
3335
3568
|
await ensureWrapsDir();
|
|
3336
3569
|
const pulumiDir = getPulumiWorkDir();
|
|
3337
|
-
if (!
|
|
3570
|
+
if (!existsSync3(pulumiDir)) {
|
|
3338
3571
|
await mkdir(pulumiDir, { recursive: true });
|
|
3339
3572
|
}
|
|
3340
|
-
process.env.PULUMI_BACKEND_URL = `file://${pulumiDir}`;
|
|
3341
3573
|
process.env.PULUMI_CONFIG_PASSPHRASE = "";
|
|
3574
|
+
const useS3 = options?.accountId && options?.region && process.env.WRAPS_LOCAL_ONLY !== "1";
|
|
3575
|
+
if (useS3) {
|
|
3576
|
+
try {
|
|
3577
|
+
const {
|
|
3578
|
+
ensureStateBucket: ensureStateBucket2,
|
|
3579
|
+
getS3BackendUrl: getS3BackendUrl2,
|
|
3580
|
+
needsMigration: needsMigration2,
|
|
3581
|
+
migrateLocalPulumiState: migrateLocalPulumiState2
|
|
3582
|
+
} = await Promise.resolve().then(() => (init_s3_state(), s3_state_exports));
|
|
3583
|
+
const bucketName = await ensureStateBucket2(
|
|
3584
|
+
options.accountId,
|
|
3585
|
+
options.region
|
|
3586
|
+
);
|
|
3587
|
+
const shouldMigrate = await needsMigration2(
|
|
3588
|
+
pulumiDir,
|
|
3589
|
+
options.accountId,
|
|
3590
|
+
options.region
|
|
3591
|
+
);
|
|
3592
|
+
if (shouldMigrate) {
|
|
3593
|
+
process.env.PULUMI_BACKEND_URL = `file://${pulumiDir}`;
|
|
3594
|
+
await migrateLocalPulumiState2(
|
|
3595
|
+
pulumiDir,
|
|
3596
|
+
bucketName,
|
|
3597
|
+
options.accountId,
|
|
3598
|
+
options.region
|
|
3599
|
+
);
|
|
3600
|
+
}
|
|
3601
|
+
process.env.PULUMI_BACKEND_URL = getS3BackendUrl2(
|
|
3602
|
+
options.accountId,
|
|
3603
|
+
options.region
|
|
3604
|
+
);
|
|
3605
|
+
return;
|
|
3606
|
+
} catch (error) {
|
|
3607
|
+
const clack38 = await import("@clack/prompts");
|
|
3608
|
+
clack38.log.warn(
|
|
3609
|
+
`S3 state backend unavailable (${error.message}). Using local state.`
|
|
3610
|
+
);
|
|
3611
|
+
}
|
|
3612
|
+
}
|
|
3613
|
+
process.env.PULUMI_BACKEND_URL = `file://${pulumiDir}`;
|
|
3342
3614
|
}
|
|
3343
3615
|
var init_fs = __esm({
|
|
3344
3616
|
"src/utils/shared/fs.ts"() {
|
|
@@ -3352,6 +3624,7 @@ var metadata_exports = {};
|
|
|
3352
3624
|
__export(metadata_exports, {
|
|
3353
3625
|
addServiceToConnection: () => addServiceToConnection,
|
|
3354
3626
|
applyConfigUpdates: () => applyConfigUpdates,
|
|
3627
|
+
buildEmailStackConfig: () => buildEmailStackConfig,
|
|
3355
3628
|
connectionExists: () => connectionExists,
|
|
3356
3629
|
createConnectionMetadata: () => createConnectionMetadata,
|
|
3357
3630
|
deleteConnectionMetadata: () => deleteConnectionMetadata,
|
|
@@ -3368,19 +3641,19 @@ __export(metadata_exports, {
|
|
|
3368
3641
|
updateServiceConfig: () => updateServiceConfig
|
|
3369
3642
|
});
|
|
3370
3643
|
import { randomBytes } from "crypto";
|
|
3371
|
-
import { existsSync as
|
|
3372
|
-
import { readFile, writeFile } from "fs/promises";
|
|
3373
|
-
import { join as
|
|
3644
|
+
import { existsSync as existsSync4 } from "fs";
|
|
3645
|
+
import { readFile, writeFile as writeFile2 } from "fs/promises";
|
|
3646
|
+
import { join as join4 } from "path";
|
|
3374
3647
|
function getConnectionsDir() {
|
|
3375
|
-
return
|
|
3648
|
+
return join4(getWrapsDir(), "connections");
|
|
3376
3649
|
}
|
|
3377
3650
|
function getMetadataPath(accountId, region) {
|
|
3378
|
-
return
|
|
3651
|
+
return join4(getConnectionsDir(), `${accountId}-${region}.json`);
|
|
3379
3652
|
}
|
|
3380
3653
|
async function ensureConnectionsDir() {
|
|
3381
3654
|
await ensureWrapsDir();
|
|
3382
3655
|
const connectionsDir = getConnectionsDir();
|
|
3383
|
-
if (!
|
|
3656
|
+
if (!existsSync4(connectionsDir)) {
|
|
3384
3657
|
const { mkdir: mkdir2 } = await import("fs/promises");
|
|
3385
3658
|
await mkdir2(connectionsDir, { recursive: true });
|
|
3386
3659
|
}
|
|
@@ -3408,57 +3681,121 @@ function isLegacyMetadata(data) {
|
|
|
3408
3681
|
}
|
|
3409
3682
|
async function loadConnectionMetadata(accountId, region) {
|
|
3410
3683
|
const metadataPath = getMetadataPath(accountId, region);
|
|
3411
|
-
|
|
3412
|
-
|
|
3684
|
+
let localData = null;
|
|
3685
|
+
if (existsSync4(metadataPath)) {
|
|
3686
|
+
try {
|
|
3687
|
+
const content = await readFile(metadataPath, "utf-8");
|
|
3688
|
+
const data = JSON.parse(content);
|
|
3689
|
+
if (isLegacyMetadata(data)) {
|
|
3690
|
+
const migrated = migrateLegacyMetadata(data);
|
|
3691
|
+
await saveConnectionMetadataLocal(migrated);
|
|
3692
|
+
localData = migrated;
|
|
3693
|
+
} else {
|
|
3694
|
+
if (!data.version) {
|
|
3695
|
+
data.version = "1.0.0";
|
|
3696
|
+
await saveConnectionMetadataLocal(data);
|
|
3697
|
+
}
|
|
3698
|
+
localData = data;
|
|
3699
|
+
}
|
|
3700
|
+
} catch (error) {
|
|
3701
|
+
console.error("Error loading connection metadata:", error.message);
|
|
3702
|
+
}
|
|
3413
3703
|
}
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
|
|
3428
|
-
|
|
3429
|
-
|
|
3704
|
+
if (process.env.WRAPS_LOCAL_ONLY !== "1") {
|
|
3705
|
+
try {
|
|
3706
|
+
const {
|
|
3707
|
+
stateBucketExists: stateBucketExists2,
|
|
3708
|
+
downloadMetadata: downloadMetadata2,
|
|
3709
|
+
uploadMetadata: uploadMetadata2,
|
|
3710
|
+
getStateBucketName: getStateBucketName2
|
|
3711
|
+
} = await Promise.resolve().then(() => (init_s3_state(), s3_state_exports));
|
|
3712
|
+
const bucketExists = await stateBucketExists2(accountId, region);
|
|
3713
|
+
if (bucketExists) {
|
|
3714
|
+
const bucketName = getStateBucketName2(accountId, region);
|
|
3715
|
+
const remoteData = await downloadMetadata2(
|
|
3716
|
+
bucketName,
|
|
3717
|
+
accountId,
|
|
3718
|
+
region
|
|
3719
|
+
);
|
|
3720
|
+
if (remoteData && localData) {
|
|
3721
|
+
if (remoteData.timestamp > localData.timestamp) {
|
|
3722
|
+
await saveConnectionMetadataLocal(remoteData);
|
|
3723
|
+
return remoteData;
|
|
3724
|
+
}
|
|
3725
|
+
if (localData.timestamp > remoteData.timestamp) {
|
|
3726
|
+
await uploadMetadata2(bucketName, localData).catch(() => {
|
|
3727
|
+
});
|
|
3728
|
+
return localData;
|
|
3729
|
+
}
|
|
3730
|
+
return localData;
|
|
3731
|
+
}
|
|
3732
|
+
if (remoteData && !localData) {
|
|
3733
|
+
await saveConnectionMetadataLocal(remoteData);
|
|
3734
|
+
return remoteData;
|
|
3735
|
+
}
|
|
3736
|
+
if (localData && !remoteData) {
|
|
3737
|
+
await uploadMetadata2(bucketName, localData).catch(() => {
|
|
3738
|
+
});
|
|
3739
|
+
}
|
|
3740
|
+
}
|
|
3741
|
+
} catch {
|
|
3742
|
+
}
|
|
3430
3743
|
}
|
|
3744
|
+
return localData;
|
|
3745
|
+
}
|
|
3746
|
+
async function saveConnectionMetadataLocal(metadata) {
|
|
3747
|
+
await ensureConnectionsDir();
|
|
3748
|
+
const metadataPath = getMetadataPath(metadata.accountId, metadata.region);
|
|
3749
|
+
const content = JSON.stringify(metadata, null, 2);
|
|
3750
|
+
await writeFile2(metadataPath, content, "utf-8");
|
|
3431
3751
|
}
|
|
3432
3752
|
async function saveConnectionMetadata(metadata) {
|
|
3433
3753
|
await ensureConnectionsDir();
|
|
3434
3754
|
const metadataPath = getMetadataPath(metadata.accountId, metadata.region);
|
|
3435
3755
|
try {
|
|
3436
3756
|
const content = JSON.stringify(metadata, null, 2);
|
|
3437
|
-
await
|
|
3757
|
+
await writeFile2(metadataPath, content, "utf-8");
|
|
3438
3758
|
} catch (error) {
|
|
3439
3759
|
console.error("Error saving connection metadata:", error.message);
|
|
3440
3760
|
throw error;
|
|
3441
3761
|
}
|
|
3762
|
+
if (process.env.WRAPS_LOCAL_ONLY !== "1") {
|
|
3763
|
+
try {
|
|
3764
|
+
const { stateBucketExists: stateBucketExists2, uploadMetadata: uploadMetadata2, getStateBucketName: getStateBucketName2 } = await Promise.resolve().then(() => (init_s3_state(), s3_state_exports));
|
|
3765
|
+
const bucketExists = await stateBucketExists2(
|
|
3766
|
+
metadata.accountId,
|
|
3767
|
+
metadata.region
|
|
3768
|
+
);
|
|
3769
|
+
if (bucketExists) {
|
|
3770
|
+
const bucketName = getStateBucketName2(
|
|
3771
|
+
metadata.accountId,
|
|
3772
|
+
metadata.region
|
|
3773
|
+
);
|
|
3774
|
+
await uploadMetadata2(bucketName, metadata);
|
|
3775
|
+
}
|
|
3776
|
+
} catch {
|
|
3777
|
+
}
|
|
3778
|
+
}
|
|
3442
3779
|
}
|
|
3443
3780
|
async function deleteConnectionMetadata(accountId, region) {
|
|
3444
3781
|
const metadataPath = getMetadataPath(accountId, region);
|
|
3445
|
-
if (
|
|
3782
|
+
if (existsSync4(metadataPath)) {
|
|
3446
3783
|
const { unlink } = await import("fs/promises");
|
|
3447
3784
|
await unlink(metadataPath);
|
|
3448
3785
|
}
|
|
3449
3786
|
}
|
|
3450
3787
|
async function listConnections() {
|
|
3451
3788
|
const connectionsDir = getConnectionsDir();
|
|
3452
|
-
if (!
|
|
3789
|
+
if (!existsSync4(connectionsDir)) {
|
|
3453
3790
|
return [];
|
|
3454
3791
|
}
|
|
3455
3792
|
try {
|
|
3456
|
-
const { readdir } = await import("fs/promises");
|
|
3457
|
-
const files = await
|
|
3793
|
+
const { readdir: readdir2 } = await import("fs/promises");
|
|
3794
|
+
const files = await readdir2(connectionsDir);
|
|
3458
3795
|
const connections = [];
|
|
3459
3796
|
for (const file of files) {
|
|
3460
3797
|
if (file.endsWith(".json")) {
|
|
3461
|
-
const content = await readFile(
|
|
3798
|
+
const content = await readFile(join4(connectionsDir, file), "utf-8");
|
|
3462
3799
|
try {
|
|
3463
3800
|
const metadata = JSON.parse(content);
|
|
3464
3801
|
connections.push(metadata);
|
|
@@ -3475,7 +3812,7 @@ async function listConnections() {
|
|
|
3475
3812
|
}
|
|
3476
3813
|
async function connectionExists(accountId, region) {
|
|
3477
3814
|
const metadataPath = getMetadataPath(accountId, region);
|
|
3478
|
-
return
|
|
3815
|
+
return existsSync4(metadataPath);
|
|
3479
3816
|
}
|
|
3480
3817
|
function createConnectionMetadata(accountId, region, provider, emailConfig, preset) {
|
|
3481
3818
|
return {
|
|
@@ -3665,6 +4002,25 @@ async function findConnectionsWithService(accountId, service) {
|
|
|
3665
4002
|
const accountConnections = await findConnectionsForAccount(accountId);
|
|
3666
4003
|
return accountConnections.filter((conn) => hasService(conn, service));
|
|
3667
4004
|
}
|
|
4005
|
+
function buildEmailStackConfig(metadata, region, overrides) {
|
|
4006
|
+
const emailService = metadata.services.email;
|
|
4007
|
+
let webhook;
|
|
4008
|
+
if (overrides && "webhook" in overrides) {
|
|
4009
|
+
webhook = overrides.webhook;
|
|
4010
|
+
} else if (emailService?.webhookSecret) {
|
|
4011
|
+
webhook = {
|
|
4012
|
+
awsAccountNumber: metadata.accountId,
|
|
4013
|
+
webhookSecret: emailService.webhookSecret
|
|
4014
|
+
};
|
|
4015
|
+
}
|
|
4016
|
+
return {
|
|
4017
|
+
provider: metadata.provider,
|
|
4018
|
+
region,
|
|
4019
|
+
vercel: metadata.vercel,
|
|
4020
|
+
emailConfig: overrides?.emailConfig ?? emailService.config,
|
|
4021
|
+
webhook
|
|
4022
|
+
};
|
|
4023
|
+
}
|
|
3668
4024
|
function generateWebhookSecret() {
|
|
3669
4025
|
return randomBytes(32).toString("hex");
|
|
3670
4026
|
}
|
|
@@ -3874,9 +4230,9 @@ __export(lambda_exports, {
|
|
|
3874
4230
|
getLambdaCode: () => getLambdaCode
|
|
3875
4231
|
});
|
|
3876
4232
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
3877
|
-
import { existsSync as
|
|
4233
|
+
import { existsSync as existsSync5, mkdirSync } from "fs";
|
|
3878
4234
|
import { tmpdir } from "os";
|
|
3879
|
-
import { dirname, join as
|
|
4235
|
+
import { dirname, join as join5 } from "path";
|
|
3880
4236
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
3881
4237
|
import * as aws8 from "@pulumi/aws";
|
|
3882
4238
|
import * as pulumi11 from "@pulumi/pulumi";
|
|
@@ -3885,7 +4241,7 @@ function getPackageRoot() {
|
|
|
3885
4241
|
const currentFile = fileURLToPath2(import.meta.url);
|
|
3886
4242
|
let dir = dirname(currentFile);
|
|
3887
4243
|
while (dir !== dirname(dir)) {
|
|
3888
|
-
if (
|
|
4244
|
+
if (existsSync5(join5(dir, "package.json"))) {
|
|
3889
4245
|
return dir;
|
|
3890
4246
|
}
|
|
3891
4247
|
dir = dirname(dir);
|
|
@@ -3928,18 +4284,18 @@ async function findEventSourceMapping(functionName, queueArn) {
|
|
|
3928
4284
|
}
|
|
3929
4285
|
async function getLambdaCode(functionName) {
|
|
3930
4286
|
const packageRoot = getPackageRoot();
|
|
3931
|
-
const distLambdaPath =
|
|
3932
|
-
const distBundleMarker =
|
|
3933
|
-
if (
|
|
4287
|
+
const distLambdaPath = join5(packageRoot, "dist", "lambda", functionName);
|
|
4288
|
+
const distBundleMarker = join5(distLambdaPath, ".bundled");
|
|
4289
|
+
if (existsSync5(distBundleMarker)) {
|
|
3934
4290
|
return distLambdaPath;
|
|
3935
4291
|
}
|
|
3936
|
-
const lambdaPath =
|
|
3937
|
-
const lambdaBundleMarker =
|
|
3938
|
-
if (
|
|
4292
|
+
const lambdaPath = join5(packageRoot, "lambda", functionName);
|
|
4293
|
+
const lambdaBundleMarker = join5(lambdaPath, ".bundled");
|
|
4294
|
+
if (existsSync5(lambdaBundleMarker)) {
|
|
3939
4295
|
return lambdaPath;
|
|
3940
4296
|
}
|
|
3941
|
-
const sourcePath =
|
|
3942
|
-
if (!
|
|
4297
|
+
const sourcePath = join5(lambdaPath, "index.ts");
|
|
4298
|
+
if (!existsSync5(sourcePath)) {
|
|
3943
4299
|
throw new Error(
|
|
3944
4300
|
`Lambda source not found: ${sourcePath}
|
|
3945
4301
|
This usually means the build process didn't complete successfully.
|
|
@@ -3947,8 +4303,8 @@ Try running: pnpm build`
|
|
|
3947
4303
|
);
|
|
3948
4304
|
}
|
|
3949
4305
|
const buildId = randomBytes2(8).toString("hex");
|
|
3950
|
-
const outdir =
|
|
3951
|
-
if (!
|
|
4306
|
+
const outdir = join5(tmpdir(), `wraps-lambda-${buildId}`);
|
|
4307
|
+
if (!existsSync5(outdir)) {
|
|
3952
4308
|
mkdirSync(outdir, { recursive: true });
|
|
3953
4309
|
}
|
|
3954
4310
|
await build({
|
|
@@ -3957,7 +4313,7 @@ Try running: pnpm build`
|
|
|
3957
4313
|
platform: "node",
|
|
3958
4314
|
target: "node24",
|
|
3959
4315
|
format: "esm",
|
|
3960
|
-
outfile:
|
|
4316
|
+
outfile: join5(outdir, "index.mjs"),
|
|
3961
4317
|
external: ["@aws-sdk/*"],
|
|
3962
4318
|
// AWS SDK v3 is included in Lambda runtime
|
|
3963
4319
|
minify: true,
|
|
@@ -5755,7 +6111,7 @@ var init_dynamodb_metrics = __esm({
|
|
|
5755
6111
|
// src/cli.ts
|
|
5756
6112
|
init_esm_shims();
|
|
5757
6113
|
import { readFileSync as readFileSync2 } from "fs";
|
|
5758
|
-
import { dirname as dirname2, join as
|
|
6114
|
+
import { dirname as dirname2, join as join6 } from "path";
|
|
5759
6115
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
5760
6116
|
import * as clack37 from "@clack/prompts";
|
|
5761
6117
|
import args from "args";
|
|
@@ -7340,7 +7696,7 @@ async function cdnDestroy(options) {
|
|
|
7340
7696
|
const previewResult = await progress.execute(
|
|
7341
7697
|
"Generating destruction preview",
|
|
7342
7698
|
async () => {
|
|
7343
|
-
await ensurePulumiWorkDir();
|
|
7699
|
+
await ensurePulumiWorkDir({ accountId: identity.accountId, region });
|
|
7344
7700
|
const stackName = storedStackName || `wraps-cdn-${identity.accountId}-${region}`;
|
|
7345
7701
|
let stack;
|
|
7346
7702
|
try {
|
|
@@ -7418,7 +7774,7 @@ async function cdnDestroy(options) {
|
|
|
7418
7774
|
await progress.execute(
|
|
7419
7775
|
"Destroying CDN infrastructure (this may take 2-3 minutes)",
|
|
7420
7776
|
async () => {
|
|
7421
|
-
await ensurePulumiWorkDir();
|
|
7777
|
+
await ensurePulumiWorkDir({ accountId: identity.accountId, region });
|
|
7422
7778
|
const stackName = storedStackName || `wraps-cdn-${identity.accountId}-${region}`;
|
|
7423
7779
|
let stack;
|
|
7424
7780
|
try {
|
|
@@ -8675,7 +9031,7 @@ ${pc8.yellow(pc8.bold("Configuration Notes:"))}`);
|
|
|
8675
9031
|
const previewResult = await progress.execute(
|
|
8676
9032
|
"Generating infrastructure preview",
|
|
8677
9033
|
async () => {
|
|
8678
|
-
await ensurePulumiWorkDir();
|
|
9034
|
+
await ensurePulumiWorkDir({ accountId: identity.accountId, region });
|
|
8679
9035
|
const stack = await pulumi4.automation.LocalWorkspace.createOrSelectStack(
|
|
8680
9036
|
{
|
|
8681
9037
|
stackName: `wraps-cdn-${identity.accountId}-${region}`,
|
|
@@ -8739,7 +9095,7 @@ ${pc8.yellow(pc8.bold("Configuration Notes:"))}`);
|
|
|
8739
9095
|
outputs = await progress.execute(
|
|
8740
9096
|
"Deploying CDN infrastructure (this may take 2-3 minutes)",
|
|
8741
9097
|
async () => {
|
|
8742
|
-
await ensurePulumiWorkDir();
|
|
9098
|
+
await ensurePulumiWorkDir({ accountId: identity.accountId, region });
|
|
8743
9099
|
const stack = await pulumi4.automation.LocalWorkspace.createOrSelectStack(
|
|
8744
9100
|
{
|
|
8745
9101
|
stackName: `wraps-cdn-${identity.accountId}-${region}`,
|
|
@@ -9108,7 +9464,7 @@ async function cdnStatus(options) {
|
|
|
9108
9464
|
}
|
|
9109
9465
|
let stackOutputs = {};
|
|
9110
9466
|
try {
|
|
9111
|
-
await ensurePulumiWorkDir();
|
|
9467
|
+
await ensurePulumiWorkDir({ accountId: identity.accountId, region });
|
|
9112
9468
|
const stack = await pulumi5.automation.LocalWorkspace.selectStack({
|
|
9113
9469
|
stackName: `wraps-cdn-${identity.accountId}-${region}`,
|
|
9114
9470
|
workDir: getPulumiWorkDir()
|
|
@@ -9286,7 +9642,7 @@ async function cdnSync(options) {
|
|
|
9286
9642
|
let existingCertArn;
|
|
9287
9643
|
if (cdnConfig.cdn.customDomain) {
|
|
9288
9644
|
try {
|
|
9289
|
-
await ensurePulumiWorkDir();
|
|
9645
|
+
await ensurePulumiWorkDir({ accountId: identity.accountId, region });
|
|
9290
9646
|
const checkStack = await pulumi6.automation.LocalWorkspace.selectStack({
|
|
9291
9647
|
stackName: `wraps-cdn-${identity.accountId}-${region}`,
|
|
9292
9648
|
workDir: getPulumiWorkDir()
|
|
@@ -9323,7 +9679,7 @@ async function cdnSync(options) {
|
|
|
9323
9679
|
};
|
|
9324
9680
|
try {
|
|
9325
9681
|
await progress.execute("Syncing CDN infrastructure", async () => {
|
|
9326
|
-
await ensurePulumiWorkDir();
|
|
9682
|
+
await ensurePulumiWorkDir({ accountId: identity.accountId, region });
|
|
9327
9683
|
const stack = await pulumi6.automation.LocalWorkspace.createOrSelectStack(
|
|
9328
9684
|
{
|
|
9329
9685
|
stackName: `wraps-cdn-${identity.accountId}-${region}`,
|
|
@@ -9440,7 +9796,7 @@ async function cdnUpgrade(options) {
|
|
|
9440
9796
|
const cdnConfig = cdnService.config;
|
|
9441
9797
|
let stackOutputs = {};
|
|
9442
9798
|
try {
|
|
9443
|
-
await ensurePulumiWorkDir();
|
|
9799
|
+
await ensurePulumiWorkDir({ accountId: identity.accountId, region });
|
|
9444
9800
|
const stack = await pulumi7.automation.LocalWorkspace.selectStack({
|
|
9445
9801
|
stackName: `wraps-cdn-${identity.accountId}-${region}`,
|
|
9446
9802
|
workDir: getPulumiWorkDir()
|
|
@@ -9577,7 +9933,7 @@ Ready to add custom domain: ${pc11.cyan(pendingDomain)}`);
|
|
|
9577
9933
|
await progress.execute(
|
|
9578
9934
|
"Updating CloudFront distribution (this may take 2-3 minutes)",
|
|
9579
9935
|
async () => {
|
|
9580
|
-
await ensurePulumiWorkDir();
|
|
9936
|
+
await ensurePulumiWorkDir({ accountId: identity.accountId, region });
|
|
9581
9937
|
const stack = await pulumi7.automation.LocalWorkspace.createOrSelectStack(
|
|
9582
9938
|
{
|
|
9583
9939
|
stackName: `wraps-cdn-${identity.accountId}-${region}`,
|
|
@@ -9747,7 +10103,7 @@ async function cdnVerify(options) {
|
|
|
9747
10103
|
}
|
|
9748
10104
|
let stackOutputs = {};
|
|
9749
10105
|
try {
|
|
9750
|
-
await ensurePulumiWorkDir();
|
|
10106
|
+
await ensurePulumiWorkDir({ accountId: identity.accountId, region });
|
|
9751
10107
|
const stack = await pulumi8.automation.LocalWorkspace.selectStack({
|
|
9752
10108
|
stackName: `wraps-cdn-${identity.accountId}-${region}`,
|
|
9753
10109
|
workDir: getPulumiWorkDir()
|
|
@@ -14052,22 +14408,13 @@ ${pc14.bold("Current Configuration:")}
|
|
|
14052
14408
|
process.exit(0);
|
|
14053
14409
|
}
|
|
14054
14410
|
}
|
|
14055
|
-
|
|
14056
|
-
if (metadata.provider === "vercel" && metadata.vercel) {
|
|
14057
|
-
vercelConfig = metadata.vercel;
|
|
14058
|
-
}
|
|
14059
|
-
const stackConfig = {
|
|
14060
|
-
provider: metadata.provider,
|
|
14061
|
-
region,
|
|
14062
|
-
vercel: vercelConfig,
|
|
14063
|
-
emailConfig: config2
|
|
14064
|
-
};
|
|
14411
|
+
const stackConfig = buildEmailStackConfig(metadata, region);
|
|
14065
14412
|
if (options.preview) {
|
|
14066
14413
|
try {
|
|
14067
14414
|
const previewResult = await progress.execute(
|
|
14068
14415
|
"Generating update preview",
|
|
14069
14416
|
async () => {
|
|
14070
|
-
await ensurePulumiWorkDir();
|
|
14417
|
+
await ensurePulumiWorkDir({ accountId: identity.accountId, region });
|
|
14071
14418
|
const stack = await pulumi12.automation.LocalWorkspace.createOrSelectStack(
|
|
14072
14419
|
{
|
|
14073
14420
|
stackName: metadata.services.email?.pulumiStackName || `wraps-${identity.accountId}-${region}`,
|
|
@@ -14131,7 +14478,7 @@ ${pc14.bold("Current Configuration:")}
|
|
|
14131
14478
|
outputs = await progress.execute(
|
|
14132
14479
|
"Updating Wraps infrastructure (this may take 2-3 minutes)",
|
|
14133
14480
|
async () => {
|
|
14134
|
-
await ensurePulumiWorkDir();
|
|
14481
|
+
await ensurePulumiWorkDir({ accountId: identity.accountId, region });
|
|
14135
14482
|
const stack = await pulumi12.automation.LocalWorkspace.createOrSelectStack(
|
|
14136
14483
|
{
|
|
14137
14484
|
stackName: metadata.services.email?.pulumiStackName || `wraps-${identity.accountId}-${region}`,
|
|
@@ -14568,7 +14915,7 @@ async function connect2(options) {
|
|
|
14568
14915
|
const previewResult = await progress.execute(
|
|
14569
14916
|
"Generating infrastructure preview",
|
|
14570
14917
|
async () => {
|
|
14571
|
-
await ensurePulumiWorkDir();
|
|
14918
|
+
await ensurePulumiWorkDir({ accountId: identity.accountId, region });
|
|
14572
14919
|
const stack = await pulumi13.automation.LocalWorkspace.createOrSelectStack(
|
|
14573
14920
|
{
|
|
14574
14921
|
stackName: `wraps-${identity.accountId}-${region}`,
|
|
@@ -14632,7 +14979,7 @@ async function connect2(options) {
|
|
|
14632
14979
|
outputs = await progress.execute(
|
|
14633
14980
|
"Deploying Wraps infrastructure (this may take 2-3 minutes)",
|
|
14634
14981
|
async () => {
|
|
14635
|
-
await ensurePulumiWorkDir();
|
|
14982
|
+
await ensurePulumiWorkDir({ accountId: identity.accountId, region });
|
|
14636
14983
|
const stack = await pulumi13.automation.LocalWorkspace.createOrSelectStack(
|
|
14637
14984
|
{
|
|
14638
14985
|
stackName: `wraps-${identity.accountId}-${region}`,
|
|
@@ -14928,7 +15275,7 @@ async function emailDestroy(options) {
|
|
|
14928
15275
|
const previewResult = await progress.execute(
|
|
14929
15276
|
"Generating destruction preview",
|
|
14930
15277
|
async () => {
|
|
14931
|
-
await ensurePulumiWorkDir();
|
|
15278
|
+
await ensurePulumiWorkDir({ accountId: identity.accountId, region });
|
|
14932
15279
|
const stackName = storedStackName || `wraps-${identity.accountId}-${region}`;
|
|
14933
15280
|
let stack;
|
|
14934
15281
|
try {
|
|
@@ -14999,7 +15346,7 @@ async function emailDestroy(options) {
|
|
|
14999
15346
|
await progress.execute(
|
|
15000
15347
|
"Destroying email infrastructure (this may take 2-3 minutes)",
|
|
15001
15348
|
async () => {
|
|
15002
|
-
await ensurePulumiWorkDir();
|
|
15349
|
+
await ensurePulumiWorkDir({ accountId: identity.accountId, region });
|
|
15003
15350
|
const stackName = storedStackName || `wraps-${identity.accountId}-${region}`;
|
|
15004
15351
|
let stack;
|
|
15005
15352
|
try {
|
|
@@ -15543,7 +15890,11 @@ var CORE_IAM_ACTIONS = [
|
|
|
15543
15890
|
"ses:CreateEmailIdentity",
|
|
15544
15891
|
"ses:DeleteEmailIdentity",
|
|
15545
15892
|
"ses:GetEmailIdentity",
|
|
15546
|
-
"ses:PutEmailIdentityDkimAttributes"
|
|
15893
|
+
"ses:PutEmailIdentityDkimAttributes",
|
|
15894
|
+
"s3:HeadBucket",
|
|
15895
|
+
"s3:CreateBucket",
|
|
15896
|
+
"s3:GetObject",
|
|
15897
|
+
"s3:PutObject"
|
|
15547
15898
|
];
|
|
15548
15899
|
var EVENT_TRACKING_ACTIONS = [
|
|
15549
15900
|
"events:CreateEventBus",
|
|
@@ -15804,7 +16155,7 @@ ${pc18.yellow(pc18.bold("Configuration Warnings:"))}`);
|
|
|
15804
16155
|
const previewResult = await progress.execute(
|
|
15805
16156
|
"Generating infrastructure preview",
|
|
15806
16157
|
async () => {
|
|
15807
|
-
await ensurePulumiWorkDir();
|
|
16158
|
+
await ensurePulumiWorkDir({ accountId: identity.accountId, region });
|
|
15808
16159
|
const stack = await pulumi15.automation.LocalWorkspace.createOrSelectStack(
|
|
15809
16160
|
{
|
|
15810
16161
|
stackName: `wraps-${identity.accountId}-${region}`,
|
|
@@ -15873,7 +16224,7 @@ ${pc18.yellow(pc18.bold("Configuration Warnings:"))}`);
|
|
|
15873
16224
|
outputs = await progress.execute(
|
|
15874
16225
|
"Deploying infrastructure (this may take 2-3 minutes)",
|
|
15875
16226
|
async () => {
|
|
15876
|
-
await ensurePulumiWorkDir();
|
|
16227
|
+
await ensurePulumiWorkDir({ accountId: identity.accountId, region });
|
|
15877
16228
|
const stack = await pulumi15.automation.LocalWorkspace.createOrSelectStack(
|
|
15878
16229
|
{
|
|
15879
16230
|
stackName: `wraps-${identity.accountId}-${region}`,
|
|
@@ -16406,7 +16757,7 @@ async function emailStatus(options) {
|
|
|
16406
16757
|
}
|
|
16407
16758
|
let stackOutputs = {};
|
|
16408
16759
|
try {
|
|
16409
|
-
await ensurePulumiWorkDir();
|
|
16760
|
+
await ensurePulumiWorkDir({ accountId: identity.accountId, region });
|
|
16410
16761
|
const stack = await pulumi17.automation.LocalWorkspace.selectStack({
|
|
16411
16762
|
stackName: `wraps-${identity.accountId}-${region}`,
|
|
16412
16763
|
workDir: getPulumiWorkDir()
|
|
@@ -17602,30 +17953,18 @@ ${pc21.bold("Cost Impact:")}`);
|
|
|
17602
17953
|
process.exit(0);
|
|
17603
17954
|
}
|
|
17604
17955
|
}
|
|
17605
|
-
let vercelConfig;
|
|
17606
17956
|
if (metadata.provider === "vercel" && !metadata.vercel) {
|
|
17607
|
-
|
|
17608
|
-
} else if (metadata.provider === "vercel") {
|
|
17609
|
-
vercelConfig = metadata.vercel;
|
|
17957
|
+
metadata.vercel = await promptVercelConfig();
|
|
17610
17958
|
}
|
|
17611
|
-
const stackConfig = {
|
|
17612
|
-
|
|
17613
|
-
|
|
17614
|
-
vercel: vercelConfig,
|
|
17615
|
-
emailConfig: updatedConfig,
|
|
17616
|
-
// Include webhook config if Wraps Dashboard is connected
|
|
17617
|
-
webhook: metadata.services.email?.webhookSecret ? {
|
|
17618
|
-
awsAccountNumber: metadata.accountId,
|
|
17619
|
-
// User's 12-digit AWS account ID
|
|
17620
|
-
webhookSecret: metadata.services.email.webhookSecret
|
|
17621
|
-
} : void 0
|
|
17622
|
-
};
|
|
17959
|
+
const stackConfig = buildEmailStackConfig(metadata, region, {
|
|
17960
|
+
emailConfig: updatedConfig
|
|
17961
|
+
});
|
|
17623
17962
|
if (options.preview) {
|
|
17624
17963
|
try {
|
|
17625
17964
|
const previewResult = await progress.execute(
|
|
17626
17965
|
"Generating upgrade preview",
|
|
17627
17966
|
async () => {
|
|
17628
|
-
await ensurePulumiWorkDir();
|
|
17967
|
+
await ensurePulumiWorkDir({ accountId: identity.accountId, region });
|
|
17629
17968
|
const stack = await pulumi18.automation.LocalWorkspace.createOrSelectStack(
|
|
17630
17969
|
{
|
|
17631
17970
|
stackName: metadata.services.email?.pulumiStackName || `wraps-${identity.accountId}-${region}`,
|
|
@@ -17703,7 +18042,7 @@ ${pc21.bold("Cost Impact:")}`);
|
|
|
17703
18042
|
outputs = await progress.execute(
|
|
17704
18043
|
"Updating Wraps infrastructure (this may take 2-3 minutes)",
|
|
17705
18044
|
async () => {
|
|
17706
|
-
await ensurePulumiWorkDir();
|
|
18045
|
+
await ensurePulumiWorkDir({ accountId: identity.accountId, region });
|
|
17707
18046
|
const stack = await pulumi18.automation.LocalWorkspace.createOrSelectStack(
|
|
17708
18047
|
{
|
|
17709
18048
|
stackName: metadata.services.email?.pulumiStackName || `wraps-${identity.accountId}-${region}`,
|
|
@@ -18322,6 +18661,26 @@ function getS3Statements() {
|
|
|
18322
18661
|
}
|
|
18323
18662
|
];
|
|
18324
18663
|
}
|
|
18664
|
+
function getS3StateStatements() {
|
|
18665
|
+
return [
|
|
18666
|
+
{
|
|
18667
|
+
Sid: "S3StateManagement",
|
|
18668
|
+
Effect: "Allow",
|
|
18669
|
+
Action: [
|
|
18670
|
+
"s3:CreateBucket",
|
|
18671
|
+
"s3:HeadBucket",
|
|
18672
|
+
"s3:PutBucketEncryption",
|
|
18673
|
+
"s3:PutBucketVersioning",
|
|
18674
|
+
"s3:PutPublicAccessBlock",
|
|
18675
|
+
"s3:PutBucketTagging",
|
|
18676
|
+
"s3:GetObject",
|
|
18677
|
+
"s3:PutObject",
|
|
18678
|
+
"s3:ListBucket"
|
|
18679
|
+
],
|
|
18680
|
+
Resource: ["arn:aws:s3:::wraps-state-*", "arn:aws:s3:::wraps-state-*/*"]
|
|
18681
|
+
}
|
|
18682
|
+
];
|
|
18683
|
+
}
|
|
18325
18684
|
function getCloudFrontStatements() {
|
|
18326
18685
|
return [
|
|
18327
18686
|
{
|
|
@@ -18396,7 +18755,10 @@ function getSMSStatements() {
|
|
|
18396
18755
|
];
|
|
18397
18756
|
}
|
|
18398
18757
|
function buildPolicy(service, preset) {
|
|
18399
|
-
const statements = [
|
|
18758
|
+
const statements = [
|
|
18759
|
+
...getBaseStatements(),
|
|
18760
|
+
...getS3StateStatements()
|
|
18761
|
+
];
|
|
18400
18762
|
if (!service || service === "email") {
|
|
18401
18763
|
statements.push(...getSESStatements());
|
|
18402
18764
|
if (!preset || preset === "production" || preset === "enterprise") {
|
|
@@ -18445,6 +18807,9 @@ ${pc23.dim("Service:")} ${pc23.cyan(serviceLabel)}`);
|
|
|
18445
18807
|
console.log(` ${pc23.green("+")} ${pc23.bold("IAM")} - Role management`);
|
|
18446
18808
|
console.log(` ${pc23.green("+")} ${pc23.bold("STS")} - Credential validation`);
|
|
18447
18809
|
console.log(` ${pc23.green("+")} ${pc23.bold("CloudWatch")} - Metrics access`);
|
|
18810
|
+
console.log(
|
|
18811
|
+
` ${pc23.green("+")} ${pc23.bold("S3")} - State management ${pc23.dim("(wraps-state-* buckets)")}`
|
|
18812
|
+
);
|
|
18448
18813
|
if (!service || service === "email") {
|
|
18449
18814
|
console.log(
|
|
18450
18815
|
` ${pc23.green("+")} ${pc23.bold("SES")} - Email sending & configuration`
|
|
@@ -18849,25 +19214,15 @@ Run ${pc24.cyan("wraps email init")} or ${pc24.cyan("wraps sms init")} first.
|
|
|
18849
19214
|
}
|
|
18850
19215
|
}
|
|
18851
19216
|
if (needsDeployment && hasEmail) {
|
|
18852
|
-
let vercelConfig;
|
|
18853
19217
|
if (metadata.provider === "vercel" && !metadata.vercel) {
|
|
18854
19218
|
progress.stop();
|
|
18855
|
-
|
|
18856
|
-
} else if (metadata.provider === "vercel") {
|
|
18857
|
-
vercelConfig = metadata.vercel;
|
|
19219
|
+
metadata.vercel = await promptVercelConfig();
|
|
18858
19220
|
}
|
|
18859
|
-
const stackConfig = {
|
|
18860
|
-
|
|
18861
|
-
|
|
18862
|
-
vercel: vercelConfig,
|
|
18863
|
-
emailConfig: metadata.services.email?.config,
|
|
18864
|
-
webhook: webhookSecret ? {
|
|
18865
|
-
awsAccountNumber: metadata.accountId,
|
|
18866
|
-
webhookSecret
|
|
18867
|
-
} : void 0
|
|
18868
|
-
};
|
|
19221
|
+
const stackConfig = buildEmailStackConfig(metadata, region, {
|
|
19222
|
+
webhook: webhookSecret ? { awsAccountNumber: metadata.accountId, webhookSecret } : void 0
|
|
19223
|
+
});
|
|
18869
19224
|
await progress.execute("Configuring event streaming", async () => {
|
|
18870
|
-
await ensurePulumiWorkDir();
|
|
19225
|
+
await ensurePulumiWorkDir({ accountId: identity.accountId, region });
|
|
18871
19226
|
const stack = await pulumi19.automation.LocalWorkspace.createOrSelectStack(
|
|
18872
19227
|
{
|
|
18873
19228
|
stackName: metadata.services.email?.pulumiStackName || `wraps-${identity.accountId}-${region}`,
|
|
@@ -21859,7 +22214,7 @@ async function dashboard(options) {
|
|
|
21859
22214
|
let smsStackOutputs = {};
|
|
21860
22215
|
let storageStackOutputs = {};
|
|
21861
22216
|
try {
|
|
21862
|
-
await ensurePulumiWorkDir();
|
|
22217
|
+
await ensurePulumiWorkDir({ accountId: identity.accountId, region });
|
|
21863
22218
|
try {
|
|
21864
22219
|
const emailStack = await pulumi20.automation.LocalWorkspace.selectStack({
|
|
21865
22220
|
stackName: `wraps-${identity.accountId}-${region}`,
|
|
@@ -22069,7 +22424,7 @@ async function status(_options) {
|
|
|
22069
22424
|
progress.info(`Region: ${pc29.cyan(region)}`);
|
|
22070
22425
|
const services = [];
|
|
22071
22426
|
try {
|
|
22072
|
-
await ensurePulumiWorkDir();
|
|
22427
|
+
await ensurePulumiWorkDir({ accountId: identity.accountId, region });
|
|
22073
22428
|
const emailStack = await pulumi21.automation.LocalWorkspace.selectStack({
|
|
22074
22429
|
stackName: `wraps-${identity.accountId}-${region}`,
|
|
22075
22430
|
workDir: getPulumiWorkDir()
|
|
@@ -23115,7 +23470,7 @@ async function smsDestroy(options) {
|
|
|
23115
23470
|
const previewResult = await progress.execute(
|
|
23116
23471
|
"Generating destruction preview",
|
|
23117
23472
|
async () => {
|
|
23118
|
-
await ensurePulumiWorkDir();
|
|
23473
|
+
await ensurePulumiWorkDir({ accountId: identity.accountId, region });
|
|
23119
23474
|
const stackName = storedStackName || `wraps-sms-${identity.accountId}-${region}`;
|
|
23120
23475
|
let stack;
|
|
23121
23476
|
try {
|
|
@@ -23172,7 +23527,7 @@ async function smsDestroy(options) {
|
|
|
23172
23527
|
await progress.execute(
|
|
23173
23528
|
"Destroying SMS infrastructure (this may take 2-3 minutes)",
|
|
23174
23529
|
async () => {
|
|
23175
|
-
await ensurePulumiWorkDir();
|
|
23530
|
+
await ensurePulumiWorkDir({ accountId: identity.accountId, region });
|
|
23176
23531
|
const stackName = storedStackName || `wraps-sms-${identity.accountId}-${region}`;
|
|
23177
23532
|
let stack;
|
|
23178
23533
|
try {
|
|
@@ -23879,7 +24234,7 @@ ${pc31.yellow(pc31.bold("Important Notes:"))}`);
|
|
|
23879
24234
|
outputs = await progress.execute(
|
|
23880
24235
|
"Deploying SMS infrastructure (this may take 2-3 minutes)",
|
|
23881
24236
|
async () => {
|
|
23882
|
-
await ensurePulumiWorkDir();
|
|
24237
|
+
await ensurePulumiWorkDir({ accountId: identity.accountId, region });
|
|
23883
24238
|
const stack = await pulumi24.automation.LocalWorkspace.createOrSelectStack(
|
|
23884
24239
|
{
|
|
23885
24240
|
stackName: `wraps-sms-${identity.accountId}-${region}`,
|
|
@@ -24289,7 +24644,7 @@ Run ${pc33.cyan("wraps sms init")} to deploy SMS infrastructure.
|
|
|
24289
24644
|
}
|
|
24290
24645
|
let stackOutputs = {};
|
|
24291
24646
|
try {
|
|
24292
|
-
await ensurePulumiWorkDir();
|
|
24647
|
+
await ensurePulumiWorkDir({ accountId: identity.accountId, region });
|
|
24293
24648
|
const stackName = metadata.services.sms.pulumiStackName || `wraps-sms-${identity.accountId}-${region}`;
|
|
24294
24649
|
const stack = await pulumi25.automation.LocalWorkspace.selectStack({
|
|
24295
24650
|
stackName,
|
|
@@ -24386,7 +24741,7 @@ Run ${pc34.cyan("wraps sms init")} to deploy SMS infrastructure first.
|
|
|
24386
24741
|
let outputs;
|
|
24387
24742
|
try {
|
|
24388
24743
|
outputs = await progress.execute("Syncing SMS infrastructure", async () => {
|
|
24389
|
-
await ensurePulumiWorkDir();
|
|
24744
|
+
await ensurePulumiWorkDir({ accountId: identity.accountId, region });
|
|
24390
24745
|
const stackName = storedStackName || `wraps-sms-${identity.accountId}-${region}`;
|
|
24391
24746
|
const stack = await pulumi26.automation.LocalWorkspace.createOrSelectStack(
|
|
24392
24747
|
{
|
|
@@ -25451,7 +25806,7 @@ ${pc36.bold("Cost Impact:")}`);
|
|
|
25451
25806
|
outputs = await progress.execute(
|
|
25452
25807
|
"Updating SMS infrastructure (this may take 2-3 minutes)",
|
|
25453
25808
|
async () => {
|
|
25454
|
-
await ensurePulumiWorkDir();
|
|
25809
|
+
await ensurePulumiWorkDir({ accountId: identity.accountId, region });
|
|
25455
25810
|
const stack = await pulumi27.automation.LocalWorkspace.createOrSelectStack(
|
|
25456
25811
|
{
|
|
25457
25812
|
stackName: metadata.services.sms?.pulumiStackName || `wraps-sms-${identity.accountId}-${region}`,
|
|
@@ -26134,7 +26489,7 @@ if (nodeMajorVersion < 20) {
|
|
|
26134
26489
|
var __filename2 = fileURLToPath4(import.meta.url);
|
|
26135
26490
|
var __dirname3 = dirname2(__filename2);
|
|
26136
26491
|
var packageJson = JSON.parse(
|
|
26137
|
-
readFileSync2(
|
|
26492
|
+
readFileSync2(join6(__dirname3, "../package.json"), "utf-8")
|
|
26138
26493
|
);
|
|
26139
26494
|
var VERSION = packageJson.version;
|
|
26140
26495
|
setupTabCompletion();
|