@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\nTo unlock, run:\n rm -rf ~/.wraps/pulumi/.pulumi/locks\n\nThen try your command again.",
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/fs.ts
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 join2 } from "path";
3554
+ import { join as join3 } from "path";
3322
3555
  function getWrapsDir() {
3323
- return join2(homedir2(), ".wraps");
3556
+ return join3(homedir2(), ".wraps");
3324
3557
  }
3325
3558
  function getPulumiWorkDir() {
3326
- return join2(getWrapsDir(), "pulumi");
3559
+ return join3(getWrapsDir(), "pulumi");
3327
3560
  }
3328
3561
  async function ensureWrapsDir() {
3329
3562
  const wrapsDir = getWrapsDir();
3330
- if (!existsSync2(wrapsDir)) {
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 (!existsSync2(pulumiDir)) {
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 existsSync3 } from "fs";
3372
- import { readFile, writeFile } from "fs/promises";
3373
- import { join as join3 } from "path";
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 join3(getWrapsDir(), "connections");
3648
+ return join4(getWrapsDir(), "connections");
3376
3649
  }
3377
3650
  function getMetadataPath(accountId, region) {
3378
- return join3(getConnectionsDir(), `${accountId}-${region}.json`);
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 (!existsSync3(connectionsDir)) {
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
- if (!existsSync3(metadataPath)) {
3412
- return null;
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
- try {
3415
- const content = await readFile(metadataPath, "utf-8");
3416
- const data = JSON.parse(content);
3417
- if (isLegacyMetadata(data)) {
3418
- const migrated = migrateLegacyMetadata(data);
3419
- await saveConnectionMetadata(migrated);
3420
- return migrated;
3421
- }
3422
- if (!data.version) {
3423
- data.version = "1.0.0";
3424
- await saveConnectionMetadata(data);
3425
- }
3426
- return data;
3427
- } catch (error) {
3428
- console.error("Error loading connection metadata:", error.message);
3429
- return null;
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 writeFile(metadataPath, content, "utf-8");
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 (existsSync3(metadataPath)) {
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 (!existsSync3(connectionsDir)) {
3789
+ if (!existsSync4(connectionsDir)) {
3453
3790
  return [];
3454
3791
  }
3455
3792
  try {
3456
- const { readdir } = await import("fs/promises");
3457
- const files = await readdir(connectionsDir);
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(join3(connectionsDir, file), "utf-8");
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 existsSync3(metadataPath);
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 existsSync4, mkdirSync } from "fs";
4233
+ import { existsSync as existsSync5, mkdirSync } from "fs";
3878
4234
  import { tmpdir } from "os";
3879
- import { dirname, join as join4 } from "path";
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 (existsSync4(join4(dir, "package.json"))) {
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 = join4(packageRoot, "dist", "lambda", functionName);
3932
- const distBundleMarker = join4(distLambdaPath, ".bundled");
3933
- if (existsSync4(distBundleMarker)) {
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 = join4(packageRoot, "lambda", functionName);
3937
- const lambdaBundleMarker = join4(lambdaPath, ".bundled");
3938
- if (existsSync4(lambdaBundleMarker)) {
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 = join4(lambdaPath, "index.ts");
3942
- if (!existsSync4(sourcePath)) {
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 = join4(tmpdir(), `wraps-lambda-${buildId}`);
3951
- if (!existsSync4(outdir)) {
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: join4(outdir, "index.mjs"),
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 join5 } from "path";
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
- let vercelConfig;
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
- vercelConfig = await promptVercelConfig();
17608
- } else if (metadata.provider === "vercel") {
17609
- vercelConfig = metadata.vercel;
17957
+ metadata.vercel = await promptVercelConfig();
17610
17958
  }
17611
- const stackConfig = {
17612
- provider: metadata.provider,
17613
- region,
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 = [...getBaseStatements()];
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
- vercelConfig = await promptVercelConfig();
18856
- } else if (metadata.provider === "vercel") {
18857
- vercelConfig = metadata.vercel;
19219
+ metadata.vercel = await promptVercelConfig();
18858
19220
  }
18859
- const stackConfig = {
18860
- provider: metadata.provider,
18861
- region,
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(join5(__dirname3, "../package.json"), "utf-8")
26492
+ readFileSync2(join6(__dirname3, "../package.json"), "utf-8")
26138
26493
  );
26139
26494
  var VERSION = packageJson.version;
26140
26495
  setupTabCompletion();