@wraps.dev/cli 2.15.1 → 2.17.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
@@ -65,6 +65,7 @@ var init_ci_detection = __esm({
65
65
  // src/utils/shared/s3-state.ts
66
66
  var s3_state_exports = {};
67
67
  __export(s3_state_exports, {
68
+ clearS3StackLocks: () => clearS3StackLocks,
68
69
  deleteMetadata: () => deleteMetadata,
69
70
  downloadMetadata: () => downloadMetadata,
70
71
  ensureStateBucket: () => ensureStateBucket,
@@ -196,6 +197,27 @@ async function deleteMetadata(bucketName, accountId, region) {
196
197
  })
197
198
  );
198
199
  }
200
+ async function clearS3StackLocks(accountId, region) {
201
+ const { S3Client: S3Client2, ListObjectsV2Command: ListObjectsV2Command2, DeleteObjectCommand } = await import("@aws-sdk/client-s3");
202
+ const client = new S3Client2({ region });
203
+ const bucketName = getStateBucketName(accountId, region);
204
+ const prefix = ".pulumi/locks/";
205
+ const response = await client.send(
206
+ new ListObjectsV2Command2({ Bucket: bucketName, Prefix: prefix })
207
+ );
208
+ const lockObjects = response.Contents ?? [];
209
+ if (lockObjects.length === 0) {
210
+ return 0;
211
+ }
212
+ for (const obj of lockObjects) {
213
+ if (obj.Key) {
214
+ await client.send(
215
+ new DeleteObjectCommand({ Bucket: bucketName, Key: obj.Key })
216
+ );
217
+ }
218
+ }
219
+ return lockObjects.length;
220
+ }
199
221
  async function downloadMetadata(bucketName, accountId, region) {
200
222
  const { S3Client: S3Client2, GetObjectCommand: GetObjectCommand2 } = await import("@aws-sdk/client-s3");
201
223
  const client = new S3Client2({ region });
@@ -302,8 +324,16 @@ var init_s3_state = __esm({
302
324
  });
303
325
 
304
326
  // src/utils/shared/fs.ts
327
+ var fs_exports = {};
328
+ __export(fs_exports, {
329
+ clearLocalStackLocks: () => clearLocalStackLocks,
330
+ ensurePulumiWorkDir: () => ensurePulumiWorkDir,
331
+ ensureWrapsDir: () => ensureWrapsDir,
332
+ getPulumiWorkDir: () => getPulumiWorkDir,
333
+ getWrapsDir: () => getWrapsDir
334
+ });
305
335
  import { existsSync as existsSync2 } from "fs";
306
- import { mkdir } from "fs/promises";
336
+ import { mkdir, readdir as readdir2, rm } from "fs/promises";
307
337
  import { homedir } from "os";
308
338
  import { join as join2 } from "path";
309
339
  function getWrapsDir() {
@@ -318,6 +348,27 @@ async function ensureWrapsDir() {
318
348
  await mkdir(wrapsDir, { recursive: true });
319
349
  }
320
350
  }
351
+ async function clearLocalStackLocks() {
352
+ const locksDir = join2(getPulumiWorkDir(), ".pulumi", "locks");
353
+ if (!existsSync2(locksDir)) {
354
+ return 0;
355
+ }
356
+ let count = 0;
357
+ async function walkAndDelete(dir) {
358
+ const entries = await readdir2(dir, { withFileTypes: true });
359
+ for (const entry of entries) {
360
+ const fullPath = join2(dir, entry.name);
361
+ if (entry.isDirectory()) {
362
+ await walkAndDelete(fullPath);
363
+ } else if (entry.name.endsWith(".json")) {
364
+ await rm(fullPath);
365
+ count++;
366
+ }
367
+ }
368
+ }
369
+ await walkAndDelete(locksDir);
370
+ return count;
371
+ }
321
372
  async function ensurePulumiWorkDir(options) {
322
373
  await ensureWrapsDir();
323
374
  const pulumiDir = getPulumiWorkDir();
@@ -3383,6 +3434,28 @@ async function promptDNSProvider(domain, availableProviders) {
3383
3434
  return provider;
3384
3435
  }
3385
3436
  async function promptInboundSubdomain(domain) {
3437
+ const choice = await clack6.select({
3438
+ message: `Where should ${pc7.cyan(domain)} receive inbound email?`,
3439
+ options: [
3440
+ {
3441
+ value: "",
3442
+ label: `${domain} (root domain)`,
3443
+ hint: `e.g., support@${domain}`
3444
+ },
3445
+ {
3446
+ value: "__subdomain__",
3447
+ label: "Use a subdomain",
3448
+ hint: `e.g., inbound.${domain}`
3449
+ }
3450
+ ]
3451
+ });
3452
+ if (clack6.isCancel(choice)) {
3453
+ clack6.cancel("Operation cancelled.");
3454
+ process.exit(0);
3455
+ }
3456
+ if (choice === "") {
3457
+ return "";
3458
+ }
3386
3459
  const subdomain = await clack6.text({
3387
3460
  message: `Subdomain for receiving emails (e.g., inbound \u2192 inbound.${domain}):`,
3388
3461
  placeholder: "inbound",
@@ -4296,8 +4369,8 @@ async function listConnections() {
4296
4369
  return [];
4297
4370
  }
4298
4371
  try {
4299
- const { readdir: readdir4 } = await import("fs/promises");
4300
- const files = await readdir4(connectionsDir);
4372
+ const { readdir: readdir5 } = await import("fs/promises");
4373
+ const files = await readdir5(connectionsDir);
4301
4374
  const connections = [];
4302
4375
  for (const file of files) {
4303
4376
  if (file.endsWith(".json")) {
@@ -4614,12 +4687,12 @@ function migrateInboundToMultiDomain(metadata) {
4614
4687
  return false;
4615
4688
  }
4616
4689
  const inbound = emailConfig.inbound;
4617
- const receivingDomain = inbound.receivingDomain || (inbound.subdomain && emailConfig.domain ? `${inbound.subdomain}.${emailConfig.domain}` : null);
4690
+ const receivingDomain = inbound.receivingDomain || (inbound.subdomain ? `${inbound.subdomain}.${emailConfig.domain}` : emailConfig.domain || null);
4618
4691
  if (!receivingDomain) {
4619
4692
  return false;
4620
4693
  }
4621
4694
  const parentDomain = emailConfig.domain || "";
4622
- const subdomain = inbound.subdomain || "inbound";
4695
+ const subdomain = inbound.subdomain ?? "";
4623
4696
  emailConfig.inboundDomains = [
4624
4697
  {
4625
4698
  subdomain,
@@ -10113,6 +10186,48 @@ async function previewWithResourceChanges(stack, options) {
10113
10186
  resourceChanges
10114
10187
  };
10115
10188
  }
10189
+ async function clearStackLocks(accountId, region) {
10190
+ const backendUrl = process.env.PULUMI_BACKEND_URL || "";
10191
+ if (backendUrl.startsWith("s3://")) {
10192
+ const { clearS3StackLocks: clearS3StackLocks2 } = await Promise.resolve().then(() => (init_s3_state(), s3_state_exports));
10193
+ return clearS3StackLocks2(accountId, region);
10194
+ }
10195
+ const { clearLocalStackLocks: clearLocalStackLocks2 } = await Promise.resolve().then(() => (init_fs(), fs_exports));
10196
+ return clearLocalStackLocks2();
10197
+ }
10198
+ async function withLockRetry(fn, options) {
10199
+ try {
10200
+ return await fn();
10201
+ } catch (error) {
10202
+ if (!(error instanceof Error)) {
10203
+ throw error;
10204
+ }
10205
+ const parsed = parsePulumiError(error);
10206
+ if (parsed.code !== "STACK_LOCKED") {
10207
+ throw error;
10208
+ }
10209
+ const clack51 = await import("@clack/prompts");
10210
+ const pc54 = (await import("picocolors")).default;
10211
+ if (options.autoConfirm) {
10212
+ clack51.log.warn(
10213
+ "Stack is locked from a previous interrupted run. Auto-clearing..."
10214
+ );
10215
+ } else {
10216
+ const shouldClear = await clack51.confirm({
10217
+ message: `Stack is locked from a previous interrupted run. ${pc54.yellow("Clear the stale lock and retry?")}`,
10218
+ initialValue: true
10219
+ });
10220
+ if (clack51.isCancel(shouldClear) || !shouldClear) {
10221
+ throw errors.stackLocked();
10222
+ }
10223
+ }
10224
+ const cleared = await clearStackLocks(options.accountId, options.region);
10225
+ clack51.log.info(
10226
+ `Cleared ${cleared} lock file${cleared === 1 ? "" : "s"}. Retrying...`
10227
+ );
10228
+ return fn();
10229
+ }
10230
+ }
10116
10231
 
10117
10232
  // src/commands/cdn/destroy.ts
10118
10233
  async function cdnDestroy(options) {
@@ -10303,8 +10418,12 @@ async function cdnDestroy(options) {
10303
10418
  } catch (_error) {
10304
10419
  throw new Error("No CDN infrastructure found to destroy");
10305
10420
  }
10306
- await stack.destroy({ onOutput: () => {
10307
- } });
10421
+ await withLockRetry(() => stack.destroy({ onOutput: () => {
10422
+ } }), {
10423
+ accountId: identity.accountId,
10424
+ region,
10425
+ autoConfirm: options.force
10426
+ });
10308
10427
  await stack.workspace.removeStack(stackName);
10309
10428
  }
10310
10429
  );
@@ -10319,10 +10438,6 @@ async function cdnDestroy(options) {
10319
10438
  }
10320
10439
  process.exit(0);
10321
10440
  }
10322
- if (msg.includes("stack is currently locked")) {
10323
- trackError("STACK_LOCKED", "storage destroy", { step: "destroy" });
10324
- throw errors.stackLocked();
10325
- }
10326
10441
  trackError("DESTROY_FAILED", "storage destroy", { step: "destroy" });
10327
10442
  clack9.log.error("CDN infrastructure destruction failed");
10328
10443
  throw error;
@@ -11712,8 +11827,11 @@ ${pc11.yellow(pc11.bold("Configuration Notes:"))}`);
11712
11827
  `wraps-cdn-${identity.accountId}-${region}`
11713
11828
  );
11714
11829
  await stack.setConfig("aws:region", { value: region });
11715
- const upResult = await stack.up({ onOutput: () => {
11716
- } });
11830
+ const upResult = await withLockRetry(
11831
+ () => stack.up({ onOutput: () => {
11832
+ } }),
11833
+ { accountId: identity.accountId, region, autoConfirm: options.yes }
11834
+ );
11717
11835
  const pulumiOutputs = upResult.outputs;
11718
11836
  return {
11719
11837
  roleArn: pulumiOutputs.roleArn?.value,
@@ -11739,10 +11857,6 @@ ${pc11.yellow(pc11.bold("Configuration Notes:"))}`);
11739
11857
  region,
11740
11858
  duration_ms: Date.now() - startTime
11741
11859
  });
11742
- if (msg.includes("stack is currently locked")) {
11743
- trackError("STACK_LOCKED", "storage:init", { step: "deploy" });
11744
- throw errors.stackLocked();
11745
- }
11746
11860
  trackError("DEPLOYMENT_FAILED", "storage:init", { step: "deploy" });
11747
11861
  throw new Error(`Pulumi deployment failed: ${msg}`);
11748
11862
  }
@@ -18190,11 +18304,14 @@ async function emailDestroy(options) {
18190
18304
  }
18191
18305
  await stack.refresh({ onOutput: () => {
18192
18306
  } });
18193
- await withTimeout(
18194
- stack.destroy({ onOutput: () => {
18195
- }, continueOnError: true }),
18196
- DEFAULT_PULUMI_TIMEOUT_MS,
18197
- "Pulumi destroy"
18307
+ await withLockRetry(
18308
+ () => withTimeout(
18309
+ stack.destroy({ onOutput: () => {
18310
+ }, continueOnError: true }),
18311
+ DEFAULT_PULUMI_TIMEOUT_MS,
18312
+ "Pulumi destroy"
18313
+ ),
18314
+ { accountId: identity.accountId, region, autoConfirm: options.force }
18198
18315
  );
18199
18316
  await stack.workspace.removeStack(stackName);
18200
18317
  }
@@ -18207,10 +18324,6 @@ async function emailDestroy(options) {
18207
18324
  await deleteConnectionMetadata(identity.accountId, region);
18208
18325
  process.exit(0);
18209
18326
  }
18210
- if (msg.includes("stack is currently locked")) {
18211
- trackError("STACK_LOCKED", "email destroy", { step: "destroy" });
18212
- throw errors.stackLocked();
18213
- }
18214
18327
  trackError("DESTROY_FAILED", "email destroy", { step: "destroy" });
18215
18328
  clack18.log.error("Email infrastructure destruction failed");
18216
18329
  destroyFailed = true;
@@ -19460,8 +19573,8 @@ async function inboundInit(options) {
19460
19573
  if (!domain) {
19461
19574
  throw errors.inboundRequiresOutbound();
19462
19575
  }
19463
- const subdomain = options.subdomain || (options.yes ? "inbound" : await promptInboundSubdomain(domain));
19464
- const receivingDomain = `${subdomain}.${domain}`;
19576
+ const subdomain = options.root ? "" : options.subdomain ?? (options.yes ? "inbound" : await promptInboundSubdomain(domain));
19577
+ const receivingDomain = subdomain ? `${subdomain}.${domain}` : domain;
19465
19578
  clack20.log.info(`Receiving domain: ${pc21.cyan(receivingDomain)}`);
19466
19579
  const webhookUrl = options.webhookUrl || (options.yes ? void 0 : await promptWebhookUrl());
19467
19580
  const webhookSecret = randomBytes3(32).toString("hex");
@@ -19525,22 +19638,25 @@ async function inboundInit(options) {
19525
19638
  );
19526
19639
  await stack.setConfig("aws:region", { value: region });
19527
19640
  const pulumiOutput = [];
19528
- await withTimeout(
19529
- stack.up({
19530
- onOutput: (msg) => {
19531
- pulumiOutput.push(msg);
19641
+ await withLockRetry(
19642
+ () => withTimeout(
19643
+ stack.up({
19644
+ onOutput: (msg) => {
19645
+ pulumiOutput.push(msg);
19646
+ }
19647
+ }),
19648
+ DEFAULT_PULUMI_TIMEOUT_MS,
19649
+ "Pulumi deployment"
19650
+ ).catch((error) => {
19651
+ if (pulumiOutput.length > 0) {
19652
+ const fullOutput = pulumiOutput.join("");
19653
+ clack20.log.error("Pulumi deployment output:");
19654
+ console.error(fullOutput);
19532
19655
  }
19656
+ throw error;
19533
19657
  }),
19534
- DEFAULT_PULUMI_TIMEOUT_MS,
19535
- "Pulumi deployment"
19536
- ).catch((error) => {
19537
- if (pulumiOutput.length > 0) {
19538
- const fullOutput = pulumiOutput.join("");
19539
- clack20.log.error("Pulumi deployment output:");
19540
- console.error(fullOutput);
19541
- }
19542
- throw error;
19543
- });
19658
+ { accountId: identity.accountId, region, autoConfirm: options.yes }
19659
+ );
19544
19660
  });
19545
19661
  await progress.execute("Creating SES receipt rules", async () => {
19546
19662
  await createReceiptRuleSet(region);
@@ -19766,11 +19882,14 @@ Deploy first: ${pc21.cyan("wraps email inbound init")}
19766
19882
  }
19767
19883
  );
19768
19884
  await stack.setConfig("aws:region", { value: region });
19769
- await withTimeout(
19770
- stack.up({ onOutput: () => {
19771
- } }),
19772
- DEFAULT_PULUMI_TIMEOUT_MS,
19773
- "Pulumi deployment"
19885
+ await withLockRetry(
19886
+ () => withTimeout(
19887
+ stack.up({ onOutput: () => {
19888
+ } }),
19889
+ DEFAULT_PULUMI_TIMEOUT_MS,
19890
+ "Pulumi deployment"
19891
+ ),
19892
+ { accountId: identity.accountId, region, autoConfirm: options.force }
19774
19893
  );
19775
19894
  });
19776
19895
  await progress.execute("Saving configuration", async () => {
@@ -19820,7 +19939,7 @@ Enable it: ${pc21.cyan("wraps email inbound init")}
19820
19939
  const inboundDomains = emailConfig.inboundDomains ?? [];
19821
19940
  const activeRuleSet = await getActiveReceiptRuleSet(region);
19822
19941
  const domainList = inboundDomains.length > 0 ? inboundDomains.map((d) => d.receivingDomain) : [
19823
- inbound.receivingDomain || `${inbound.subdomain}.${emailConfig.domain}`
19942
+ inbound.receivingDomain || (inbound.subdomain ? `${inbound.subdomain}.${emailConfig.domain}` : emailConfig.domain || "")
19824
19943
  ];
19825
19944
  if (isJsonMode()) {
19826
19945
  jsonSuccess("email.inbound.status", {
@@ -19883,7 +20002,7 @@ Enable it: ${pc21.cyan("wraps email inbound init")}
19883
20002
  const inbound = emailConfig.inbound;
19884
20003
  const inboundDomains = emailConfig.inboundDomains ?? [];
19885
20004
  const domainList = inboundDomains.length > 0 ? inboundDomains.map((d) => d.receivingDomain) : [
19886
- inbound.receivingDomain || `${inbound.subdomain}.${emailConfig.domain}`
20005
+ inbound.receivingDomain || (inbound.subdomain ? `${inbound.subdomain}.${emailConfig.domain}` : emailConfig.domain || "")
19887
20006
  ];
19888
20007
  let allPassed = true;
19889
20008
  const domainChecks = {};
@@ -19998,7 +20117,7 @@ Enable it: ${pc21.cyan("wraps email inbound init")}
19998
20117
  }
19999
20118
  const emailConfig = metadata.services.email.config;
20000
20119
  const inbound = emailConfig.inbound;
20001
- const receivingDomain = inbound.receivingDomain || `${inbound.subdomain}.${emailConfig.domain}`;
20120
+ const receivingDomain = inbound.receivingDomain || (inbound.subdomain ? `${inbound.subdomain}.${emailConfig.domain}` : emailConfig.domain || "");
20002
20121
  const bucketName = inbound.bucketName || `wraps-inbound-${identity.accountId}-${region}`;
20003
20122
  const testRecipient = `test@${receivingDomain}`;
20004
20123
  const testSubject = `Wraps Inbound Test - ${(/* @__PURE__ */ new Date()).toISOString()}`;
@@ -20176,8 +20295,8 @@ Deploy first: ${pc21.cyan("wraps email inbound init")}
20176
20295
  parentDomain = selected;
20177
20296
  }
20178
20297
  }
20179
- const subdomain = options.subdomain || (options.yes ? "inbound" : await promptInboundSubdomain(parentDomain));
20180
- const receivingDomain = `${subdomain}.${parentDomain}`;
20298
+ const subdomain = options.root ? "" : options.subdomain ?? (options.yes ? "inbound" : await promptInboundSubdomain(parentDomain));
20299
+ const receivingDomain = subdomain ? `${subdomain}.${parentDomain}` : parentDomain;
20181
20300
  const existingDomains = emailConfig.inboundDomains ?? [];
20182
20301
  if (existingDomains.some((d) => d.receivingDomain === receivingDomain)) {
20183
20302
  clack20.log.warn(
@@ -20828,12 +20947,19 @@ ${pc24.yellow(pc24.bold("Configuration Warnings:"))}`);
20828
20947
  `wraps-${identity.accountId}-${region}`
20829
20948
  );
20830
20949
  await stack.setConfig("aws:region", { value: region });
20831
- const upResult = await withTimeout(
20832
- stack.up({ onOutput: () => {
20833
- } }),
20834
- // Suppress Pulumi output
20835
- DEFAULT_PULUMI_TIMEOUT_MS,
20836
- "Pulumi deployment"
20950
+ const upResult = await withLockRetry(
20951
+ () => withTimeout(
20952
+ stack.up({ onOutput: () => {
20953
+ } }),
20954
+ // Suppress Pulumi output
20955
+ DEFAULT_PULUMI_TIMEOUT_MS,
20956
+ "Pulumi deployment"
20957
+ ),
20958
+ {
20959
+ accountId: identity.accountId,
20960
+ region,
20961
+ autoConfirm: options.yes || options.quick
20962
+ }
20837
20963
  );
20838
20964
  const pulumiOutputs = upResult.outputs;
20839
20965
  return {
@@ -20860,10 +20986,6 @@ ${pc24.yellow(pc24.bold("Configuration Warnings:"))}`);
20860
20986
  region,
20861
20987
  duration_ms: Date.now() - startTime
20862
20988
  });
20863
- if (msg.includes("stack is currently locked")) {
20864
- trackError("STACK_LOCKED", "email:init", { step: "deploy" });
20865
- throw errors.stackLocked();
20866
- }
20867
20989
  if (isPulumiError(error)) {
20868
20990
  const { code, iamAction, service } = parsePulumiError(error);
20869
20991
  trackError(`PULUMI_${code}`, "email:init", {
@@ -22028,8 +22150,8 @@ async function templatesInit(options) {
22028
22150
  const { homedir: homedir3 } = await import("os");
22029
22151
  const connectionsDir = join9(homedir3(), ".wraps", "connections");
22030
22152
  if (existsSync8(connectionsDir)) {
22031
- const { readdir: readdir4 } = await import("fs/promises");
22032
- const files = await readdir4(connectionsDir);
22153
+ const { readdir: readdir5 } = await import("fs/promises");
22154
+ const files = await readdir5(connectionsDir);
22033
22155
  if (files.length > 0) {
22034
22156
  const firstFile = files[0];
22035
22157
  const match = firstFile.match(/\d+-(.+)\.json$/);
@@ -22348,7 +22470,7 @@ import pc28 from "picocolors";
22348
22470
  // src/utils/email/template-compiler.ts
22349
22471
  init_esm_shims();
22350
22472
  import { existsSync as existsSync9 } from "fs";
22351
- import { mkdir as mkdir4, readdir as readdir2, writeFile as writeFile6 } from "fs/promises";
22473
+ import { mkdir as mkdir4, readdir as readdir3, writeFile as writeFile6 } from "fs/promises";
22352
22474
  import { join as join10 } from "path";
22353
22475
  async function loadWrapsConfig(wrapsDir) {
22354
22476
  const configPath = join10(wrapsDir, "wraps.config.ts");
@@ -22383,7 +22505,7 @@ async function loadWrapsConfig(wrapsDir) {
22383
22505
  return config2;
22384
22506
  }
22385
22507
  async function discoverTemplates(dir, filter) {
22386
- const entries = await readdir2(dir);
22508
+ const entries = await readdir3(dir);
22387
22509
  const templates = entries.filter(
22388
22510
  (f) => (f.endsWith(".tsx") || f.endsWith(".ts")) && !f.startsWith("_") && !f.endsWith(".d.ts")
22389
22511
  );
@@ -25659,8 +25781,8 @@ async function workflowsInit(options) {
25659
25781
  }
25660
25782
  const progress = new DeploymentProgress();
25661
25783
  if (existsSync13(workflowsDir) && !options.force) {
25662
- const { readdir: readdir4 } = await import("fs/promises");
25663
- const files = await readdir4(workflowsDir);
25784
+ const { readdir: readdir5 } = await import("fs/promises");
25785
+ const files = await readdir5(workflowsDir);
25664
25786
  const tsFiles = files.filter(
25665
25787
  (f) => f.endsWith(".ts") && !f.startsWith("_")
25666
25788
  );
@@ -25994,13 +26116,13 @@ function assignPositions(steps, transitions) {
25994
26116
  init_esm_shims();
25995
26117
  import { createHash as createHash2 } from "crypto";
25996
26118
  import { existsSync as existsSync14 } from "fs";
25997
- import { mkdir as mkdir8, readdir as readdir3, readFile as readFile8, writeFile as writeFile10 } from "fs/promises";
26119
+ import { mkdir as mkdir8, readdir as readdir4, readFile as readFile8, writeFile as writeFile10 } from "fs/promises";
25998
26120
  import { basename, join as join15 } from "path";
25999
26121
  async function discoverWorkflows(dir, filter) {
26000
26122
  if (!existsSync14(dir)) {
26001
26123
  return [];
26002
26124
  }
26003
- const entries = await readdir3(dir);
26125
+ const entries = await readdir4(dir);
26004
26126
  const workflows = entries.filter(
26005
26127
  (f) => (
26006
26128
  // Include .ts files only (not .tsx for workflows)
@@ -32975,7 +33097,6 @@ import * as pulumi26 from "@pulumi/pulumi";
32975
33097
  import pc43 from "picocolors";
32976
33098
  init_events();
32977
33099
  init_aws();
32978
- init_errors();
32979
33100
  init_fs();
32980
33101
  init_json_output();
32981
33102
  init_metadata();
@@ -33653,7 +33774,10 @@ ${pc43.yellow(pc43.bold("Important Notes:"))}`);
33653
33774
  }
33654
33775
  );
33655
33776
  await stack.setConfig("aws:region", { value: region });
33656
- const upResult = await stack.up({ onOutput: console.log });
33777
+ const upResult = await withLockRetry(
33778
+ () => stack.up({ onOutput: console.log }),
33779
+ { accountId: identity.accountId, region, autoConfirm: options.yes }
33780
+ );
33657
33781
  const pulumiOutputs = upResult.outputs;
33658
33782
  return {
33659
33783
  roleArn: pulumiOutputs.roleArn?.value,
@@ -33736,10 +33860,6 @@ ${pc43.yellow(pc43.bold("Important Notes:"))}`);
33736
33860
  duration_ms: Date.now() - startTime
33737
33861
  });
33738
33862
  const errorMessage = error instanceof Error ? error.message : String(error);
33739
- if (errorMessage.includes("stack is currently locked")) {
33740
- trackError("STACK_LOCKED", "sms:init", { step: "deploy" });
33741
- throw errors.stackLocked();
33742
- }
33743
33863
  trackError("DEPLOYMENT_FAILED", "sms:init", { step: "deploy" });
33744
33864
  throw new Error(`SMS deployment failed: ${errorMessage}`);
33745
33865
  }
@@ -36677,6 +36797,11 @@ args.options([
36677
36797
  name: "subdomain",
36678
36798
  description: "Subdomain for inbound email (e.g., inbound, support)",
36679
36799
  defaultValue: void 0
36800
+ },
36801
+ {
36802
+ name: "root",
36803
+ description: "Use root domain for inbound email (no subdomain)",
36804
+ defaultValue: false
36680
36805
  }
36681
36806
  ]);
36682
36807
  var flags = args.parse(process.argv);
@@ -36961,6 +37086,7 @@ Usage: ${pc53.cyan("wraps email verify --domain yourapp.com")}
36961
37086
  region: flags.region,
36962
37087
  subdomain: flags.domain,
36963
37088
  // reuse --domain flag for subdomain
37089
+ root: flags.root,
36964
37090
  yes: flags.yes,
36965
37091
  preview: flags.preview,
36966
37092
  json: flags.json
@@ -36995,6 +37121,7 @@ Usage: ${pc53.cyan("wraps email verify --domain yourapp.com")}
36995
37121
  await inboundAdd({
36996
37122
  region: flags.region,
36997
37123
  subdomain: flags.subdomain,
37124
+ root: flags.root,
36998
37125
  domain: flags.domain,
36999
37126
  yes: flags.yes,
37000
37127
  json: flags.json