@wraps.dev/cli 2.3.5 → 2.5.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
@@ -1989,9 +1989,11 @@ __export(prompts_exports, {
1989
1989
  getAvailableFeatures: () => getAvailableFeatures,
1990
1990
  promptConfigPreset: () => promptConfigPreset,
1991
1991
  promptConflictResolution: () => promptConflictResolution,
1992
+ promptContinueManualDNS: () => promptContinueManualDNS,
1992
1993
  promptCustomConfig: () => promptCustomConfig,
1993
1994
  promptDNSConfirmation: () => promptDNSConfirmation,
1994
1995
  promptDNSManagement: () => promptDNSManagement,
1996
+ promptDNSProvider: () => promptDNSProvider,
1995
1997
  promptDomain: () => promptDomain,
1996
1998
  promptEmailArchiving: () => promptEmailArchiving,
1997
1999
  promptEstimatedVolume: () => promptEstimatedVolume,
@@ -2713,6 +2715,58 @@ async function promptDNSManagement(domain) {
2713
2715
  }
2714
2716
  return manage;
2715
2717
  }
2718
+ async function promptDNSProvider(domain, availableProviders) {
2719
+ const options = availableProviders.map((p) => {
2720
+ let label;
2721
+ let hint;
2722
+ switch (p.provider) {
2723
+ case "route53":
2724
+ label = "AWS Route53";
2725
+ hint = p.detected ? "Hosted zone detected" : "Requires Route53 hosted zone";
2726
+ break;
2727
+ case "vercel":
2728
+ label = "Vercel DNS";
2729
+ hint = p.detected ? "Token found" : "Requires VERCEL_TOKEN";
2730
+ break;
2731
+ case "cloudflare":
2732
+ label = "Cloudflare";
2733
+ hint = p.detected ? "Token found" : "Requires CLOUDFLARE_API_TOKEN";
2734
+ break;
2735
+ case "manual":
2736
+ label = "Manual";
2737
+ hint = "I'll add DNS records myself";
2738
+ break;
2739
+ }
2740
+ if (p.detected && p.provider !== "manual") {
2741
+ label = `${label} (Recommended)`;
2742
+ }
2743
+ return {
2744
+ value: p.provider,
2745
+ label,
2746
+ hint: p.hint || hint
2747
+ };
2748
+ });
2749
+ const provider = await clack3.select({
2750
+ message: `Where do you manage DNS for ${pc4.cyan(domain)}?`,
2751
+ options
2752
+ });
2753
+ if (clack3.isCancel(provider)) {
2754
+ clack3.cancel("Operation cancelled.");
2755
+ process.exit(0);
2756
+ }
2757
+ return provider;
2758
+ }
2759
+ async function promptContinueManualDNS() {
2760
+ const continueManual = await clack3.confirm({
2761
+ message: "Continue with manual DNS setup?",
2762
+ initialValue: true
2763
+ });
2764
+ if (clack3.isCancel(continueManual)) {
2765
+ clack3.cancel("Operation cancelled.");
2766
+ process.exit(0);
2767
+ }
2768
+ return continueManual;
2769
+ }
2716
2770
  var DNS_CATEGORY_LABELS, DNS_STATUS_SYMBOLS;
2717
2771
  var init_prompts = __esm({
2718
2772
  "src/utils/shared/prompts.ts"() {
@@ -4433,185 +4487,1108 @@ var init_mail_manager = __esm({
4433
4487
  }
4434
4488
  });
4435
4489
 
4436
- // src/utils/shared/assume-role.ts
4437
- var assume_role_exports = {};
4438
- __export(assume_role_exports, {
4439
- assumeRole: () => assumeRole
4440
- });
4441
- import { AssumeRoleCommand, STSClient as STSClient3 } from "@aws-sdk/client-sts";
4442
- async function assumeRole(roleArn, region, sessionName = "wraps-console") {
4443
- const sts = new STSClient3({ region });
4444
- const response = await sts.send(
4445
- new AssumeRoleCommand({
4446
- RoleArn: roleArn,
4447
- RoleSessionName: sessionName,
4448
- DurationSeconds: 3600
4449
- // 1 hour
4450
- })
4451
- );
4452
- if (!response.Credentials) {
4453
- throw new Error("Failed to assume role: No credentials returned");
4454
- }
4455
- return {
4456
- accessKeyId: response.Credentials.AccessKeyId,
4457
- secretAccessKey: response.Credentials.SecretAccessKey,
4458
- sessionToken: response.Credentials.SessionToken,
4459
- expiration: response.Credentials.Expiration
4460
- };
4461
- }
4462
- var init_assume_role = __esm({
4463
- "src/utils/shared/assume-role.ts"() {
4490
+ // src/utils/dns/cloudflare.ts
4491
+ var CLOUDFLARE_API_BASE, CloudflareDNSClient;
4492
+ var init_cloudflare = __esm({
4493
+ "src/utils/dns/cloudflare.ts"() {
4464
4494
  "use strict";
4465
4495
  init_esm_shims();
4466
- }
4467
- });
4468
-
4469
- // src/utils/archive.ts
4470
- import {
4471
- GetArchiveMessageCommand,
4472
- GetArchiveSearchResultsCommand,
4473
- MailManagerClient as MailManagerClient2,
4474
- StartArchiveSearchCommand
4475
- } from "@aws-sdk/client-mailmanager";
4476
- import DOMPurify from "isomorphic-dompurify";
4477
- import { simpleParser } from "mailparser";
4478
- function extractArchiveId(archiveArnOrId) {
4479
- if (archiveArnOrId.startsWith("arn:")) {
4480
- const parts = archiveArnOrId.split("/");
4481
- return parts.at(-1);
4482
- }
4483
- return archiveArnOrId;
4484
- }
4485
- async function getArchivedEmail(archiveArnOrId, searchCriteria, region) {
4486
- const client = new MailManagerClient2({ region });
4487
- const archiveId = extractArchiveId(archiveArnOrId);
4488
- const searchTime = searchCriteria.timestamp || /* @__PURE__ */ new Date();
4489
- const dayBefore = new Date(searchTime.getTime() - 24 * 60 * 60 * 1e3);
4490
- const dayAfter = new Date(searchTime.getTime() + 24 * 60 * 60 * 1e3);
4491
- const filters = [];
4492
- if (searchCriteria.from) {
4493
- filters.push({
4494
- StringExpression: {
4495
- Evaluate: {
4496
- Attribute: "FROM"
4497
- },
4498
- Operator: "CONTAINS",
4499
- Values: [searchCriteria.from]
4496
+ CLOUDFLARE_API_BASE = "https://api.cloudflare.com/client/v4";
4497
+ CloudflareDNSClient = class {
4498
+ zoneId;
4499
+ apiToken;
4500
+ constructor(zoneId, apiToken) {
4501
+ this.zoneId = zoneId;
4502
+ this.apiToken = apiToken;
4503
+ }
4504
+ async request(endpoint, method = "GET", body) {
4505
+ const response = await fetch(
4506
+ `${CLOUDFLARE_API_BASE}/zones/${this.zoneId}${endpoint}`,
4507
+ {
4508
+ method,
4509
+ headers: {
4510
+ Authorization: `Bearer ${this.apiToken}`,
4511
+ "Content-Type": "application/json"
4512
+ },
4513
+ body: body ? JSON.stringify(body) : void 0
4514
+ }
4515
+ );
4516
+ return response.json();
4500
4517
  }
4501
- });
4502
- }
4503
- if (searchCriteria.to) {
4504
- filters.push({
4505
- StringExpression: {
4506
- Evaluate: {
4507
- Attribute: "TO"
4508
- },
4509
- Operator: "CONTAINS",
4510
- Values: [searchCriteria.to]
4518
+ async createRecord(name, type, content, priority) {
4519
+ const body = {
4520
+ name,
4521
+ type,
4522
+ content,
4523
+ ttl: 1800,
4524
+ proxied: false
4525
+ // Must not be proxied for email records
4526
+ };
4527
+ if (priority !== void 0) {
4528
+ body.priority = priority;
4529
+ }
4530
+ const result = await this.request(
4531
+ "/dns_records",
4532
+ "POST",
4533
+ body
4534
+ );
4535
+ return result.success;
4511
4536
  }
4512
- });
4513
- }
4514
- if (searchCriteria.subject) {
4515
- filters.push({
4516
- StringExpression: {
4517
- Evaluate: {
4518
- Attribute: "SUBJECT"
4519
- },
4520
- Operator: "CONTAINS",
4521
- Values: [searchCriteria.subject]
4537
+ async findRecord(name, type) {
4538
+ const result = await this.request(
4539
+ `/dns_records?name=${encodeURIComponent(name)}&type=${type}`
4540
+ );
4541
+ if (result.success && result.result.length > 0) {
4542
+ return result.result[0];
4543
+ }
4544
+ return null;
4522
4545
  }
4523
- });
4524
- }
4525
- if (filters.length === 0) {
4526
- throw new Error(
4527
- "At least one search criterion (from, to, or subject) is required"
4528
- );
4529
- }
4530
- const searchCommand = new StartArchiveSearchCommand({
4531
- ArchiveId: archiveId,
4532
- FromTimestamp: dayBefore,
4533
- ToTimestamp: dayAfter,
4534
- Filters: {
4535
- Include: filters
4536
- },
4537
- MaxResults: 10
4538
- // Get a few results in case there are multiple matches
4539
- });
4540
- const searchResponse = await client.send(searchCommand);
4541
- const searchId = searchResponse.SearchId;
4542
- if (!searchId) {
4543
- throw new Error("Failed to start archive search");
4544
- }
4545
- let archivedMessageId;
4546
- let attempts = 0;
4547
- const maxAttempts = 20;
4548
- const pollInterval = 1e3;
4549
- await new Promise((resolve) => setTimeout(resolve, 1e3));
4550
- while (attempts < maxAttempts) {
4551
- try {
4552
- const resultsCommand = new GetArchiveSearchResultsCommand({
4553
- SearchId: searchId
4554
- });
4555
- const resultsResponse = await client.send(resultsCommand);
4556
- if (resultsResponse.Rows && resultsResponse.Rows.length > 0) {
4557
- archivedMessageId = resultsResponse.Rows[0].ArchivedMessageId;
4558
- break;
4546
+ async deleteRecord(recordId) {
4547
+ const result = await this.request(
4548
+ `/dns_records/${recordId}`,
4549
+ "DELETE"
4550
+ );
4551
+ return result.success;
4559
4552
  }
4560
- if (resultsResponse.Rows && resultsResponse.Rows.length === 0) {
4561
- break;
4553
+ async createEmailRecords(data) {
4554
+ const { domain, dkimTokens, mailFromDomain, region } = data;
4555
+ const errors2 = [];
4556
+ let recordsCreated = 0;
4557
+ try {
4558
+ for (const token of dkimTokens) {
4559
+ const name = `${token}._domainkey.${domain}`;
4560
+ const success = await this.createRecord(
4561
+ name,
4562
+ "CNAME",
4563
+ `${token}.dkim.amazonses.com`
4564
+ );
4565
+ if (success) {
4566
+ recordsCreated++;
4567
+ } else {
4568
+ errors2.push(`Failed to create DKIM record: ${name}`);
4569
+ }
4570
+ }
4571
+ const spfSuccess = await this.createRecord(
4572
+ domain,
4573
+ "TXT",
4574
+ "v=spf1 include:amazonses.com ~all"
4575
+ );
4576
+ if (spfSuccess) {
4577
+ recordsCreated++;
4578
+ } else {
4579
+ errors2.push(`Failed to create SPF record for ${domain}`);
4580
+ }
4581
+ const dmarcSuccess = await this.createRecord(
4582
+ `_dmarc.${domain}`,
4583
+ "TXT",
4584
+ `v=DMARC1; p=quarantine; rua=mailto:postmaster@${mailFromDomain || domain}`
4585
+ );
4586
+ if (dmarcSuccess) {
4587
+ recordsCreated++;
4588
+ } else {
4589
+ errors2.push(`Failed to create DMARC record for ${domain}`);
4590
+ }
4591
+ if (mailFromDomain) {
4592
+ const mxSuccess = await this.createRecord(
4593
+ mailFromDomain,
4594
+ "MX",
4595
+ `feedback-smtp.${region}.amazonses.com`,
4596
+ 10
4597
+ );
4598
+ if (mxSuccess) {
4599
+ recordsCreated++;
4600
+ } else {
4601
+ errors2.push(`Failed to create MX record for ${mailFromDomain}`);
4602
+ }
4603
+ const mailFromSpfSuccess = await this.createRecord(
4604
+ mailFromDomain,
4605
+ "TXT",
4606
+ "v=spf1 include:amazonses.com ~all"
4607
+ );
4608
+ if (mailFromSpfSuccess) {
4609
+ recordsCreated++;
4610
+ } else {
4611
+ errors2.push(`Failed to create SPF record for ${mailFromDomain}`);
4612
+ }
4613
+ }
4614
+ return {
4615
+ success: errors2.length === 0,
4616
+ recordsCreated,
4617
+ errors: errors2.length > 0 ? errors2 : void 0
4618
+ };
4619
+ } catch (error) {
4620
+ return {
4621
+ success: false,
4622
+ recordsCreated,
4623
+ errors: [
4624
+ ...errors2,
4625
+ error instanceof Error ? error.message : "Unknown error"
4626
+ ]
4627
+ };
4628
+ }
4562
4629
  }
4563
- } catch (error) {
4564
- if (error instanceof Error && error.name === "ConflictException" && error.message.includes("still in progress")) {
4565
- console.log(`Search still in progress, attempt ${attempts + 1}...`);
4566
- } else {
4567
- throw error;
4630
+ async deleteEmailRecords(data) {
4631
+ const { domain, dkimTokens, mailFromDomain } = data;
4632
+ const errors2 = [];
4633
+ let recordsCreated = 0;
4634
+ try {
4635
+ for (const token of dkimTokens) {
4636
+ const name = `${token}._domainkey.${domain}`;
4637
+ const record = await this.findRecord(name, "CNAME");
4638
+ if (record) {
4639
+ const success = await this.deleteRecord(record.id);
4640
+ if (success) {
4641
+ recordsCreated++;
4642
+ } else {
4643
+ errors2.push(`Failed to delete DKIM record: ${name}`);
4644
+ }
4645
+ }
4646
+ }
4647
+ const dmarcRecord = await this.findRecord(`_dmarc.${domain}`, "TXT");
4648
+ if (dmarcRecord) {
4649
+ const success = await this.deleteRecord(dmarcRecord.id);
4650
+ if (success) {
4651
+ recordsCreated++;
4652
+ } else {
4653
+ errors2.push("Failed to delete DMARC record");
4654
+ }
4655
+ }
4656
+ if (mailFromDomain) {
4657
+ const mxRecord = await this.findRecord(mailFromDomain, "MX");
4658
+ if (mxRecord) {
4659
+ const success = await this.deleteRecord(mxRecord.id);
4660
+ if (success) {
4661
+ recordsCreated++;
4662
+ } else {
4663
+ errors2.push(`Failed to delete MX record for ${mailFromDomain}`);
4664
+ }
4665
+ }
4666
+ const spfRecord = await this.findRecord(mailFromDomain, "TXT");
4667
+ if (spfRecord) {
4668
+ const success = await this.deleteRecord(spfRecord.id);
4669
+ if (success) {
4670
+ recordsCreated++;
4671
+ } else {
4672
+ errors2.push(`Failed to delete SPF record for ${mailFromDomain}`);
4673
+ }
4674
+ }
4675
+ }
4676
+ return {
4677
+ success: errors2.length === 0,
4678
+ recordsCreated,
4679
+ errors: errors2.length > 0 ? errors2 : void 0
4680
+ };
4681
+ } catch (error) {
4682
+ return {
4683
+ success: false,
4684
+ recordsCreated,
4685
+ errors: [
4686
+ ...errors2,
4687
+ error instanceof Error ? error.message : "Unknown error"
4688
+ ]
4689
+ };
4690
+ }
4568
4691
  }
4569
- }
4570
- await new Promise((resolve) => setTimeout(resolve, pollInterval));
4571
- attempts++;
4572
- }
4573
- if (!archivedMessageId) {
4574
- throw new Error(
4575
- "Email not found in archive with the provided search criteria. It may have been sent before archiving was enabled."
4576
- );
4577
- }
4578
- const command = new GetArchiveMessageCommand({
4579
- ArchivedMessageId: archivedMessageId
4580
- });
4581
- const response = await client.send(command);
4582
- if (!response.MessageDownloadLink) {
4583
- throw new Error("No download link available for archived message");
4584
- }
4585
- const emailResponse = await fetch(response.MessageDownloadLink);
4586
- if (!emailResponse.ok) {
4587
- throw new Error(`Failed to download email: ${emailResponse.statusText}`);
4588
- }
4589
- const emailRaw = await emailResponse.text();
4590
- const parsed = await simpleParser(emailRaw);
4591
- const attachments = parsed.attachments?.map((att) => ({
4592
- filename: att.filename,
4593
- contentType: att.contentType,
4594
- size: att.size
4595
- })) || [];
4596
- const headers = {};
4597
- if (parsed.headers) {
4598
- for (const [key, value] of parsed.headers) {
4599
- if (value instanceof Date) {
4600
- headers[key] = value.toISOString();
4601
- } else if (typeof value === "string") {
4602
- headers[key] = value;
4603
- } else if (Array.isArray(value) && value.every((v) => typeof v === "string")) {
4604
- headers[key] = value;
4605
- } else {
4606
- headers[key] = JSON.stringify(value);
4692
+ async verifyRecords(data) {
4693
+ const { domain, dkimTokens, mailFromDomain, region } = data;
4694
+ const missing = [];
4695
+ const incorrect = [];
4696
+ for (const token of dkimTokens) {
4697
+ const name = `${token}._domainkey.${domain}`;
4698
+ const expectedValue = `${token}.dkim.amazonses.com`;
4699
+ const record = await this.findRecord(name, "CNAME");
4700
+ if (!record) {
4701
+ missing.push(`DKIM: ${name}`);
4702
+ } else if (record.content !== expectedValue) {
4703
+ incorrect.push(`DKIM: ${name} (expected ${expectedValue})`);
4704
+ }
4705
+ }
4706
+ const spfRecord = await this.findRecord(domain, "TXT");
4707
+ if (!spfRecord) {
4708
+ missing.push(`SPF: ${domain}`);
4709
+ } else if (!spfRecord.content.includes("include:amazonses.com")) {
4710
+ incorrect.push(`SPF: ${domain} (missing amazonses.com include)`);
4711
+ }
4712
+ const dmarcRecord = await this.findRecord(`_dmarc.${domain}`, "TXT");
4713
+ if (!dmarcRecord) {
4714
+ missing.push(`DMARC: _dmarc.${domain}`);
4715
+ }
4716
+ if (mailFromDomain) {
4717
+ const mxRecord = await this.findRecord(mailFromDomain, "MX");
4718
+ if (!mxRecord) {
4719
+ missing.push(`MX: ${mailFromDomain}`);
4720
+ } else if (!mxRecord.content.includes(`feedback-smtp.${region}.amazonses.com`)) {
4721
+ incorrect.push(`MX: ${mailFromDomain}`);
4722
+ }
4723
+ const mailFromSpf = await this.findRecord(mailFromDomain, "TXT");
4724
+ if (!mailFromSpf) {
4725
+ missing.push(`SPF: ${mailFromDomain}`);
4726
+ }
4727
+ }
4728
+ return {
4729
+ verified: missing.length === 0 && incorrect.length === 0,
4730
+ missing,
4731
+ incorrect
4732
+ };
4607
4733
  }
4608
- }
4734
+ };
4609
4735
  }
4610
- const getAddressText = (addr) => {
4611
- if (!addr) {
4612
- return "";
4613
- }
4614
- if (Array.isArray(addr)) {
4736
+ });
4737
+
4738
+ // src/utils/dns/vercel.ts
4739
+ var VERCEL_API_BASE, VercelDNSClient;
4740
+ var init_vercel = __esm({
4741
+ "src/utils/dns/vercel.ts"() {
4742
+ "use strict";
4743
+ init_esm_shims();
4744
+ VERCEL_API_BASE = "https://api.vercel.com";
4745
+ VercelDNSClient = class {
4746
+ domain;
4747
+ apiToken;
4748
+ teamId;
4749
+ constructor(domain, apiToken, teamId) {
4750
+ this.domain = domain;
4751
+ this.apiToken = apiToken;
4752
+ this.teamId = teamId;
4753
+ }
4754
+ get teamParam() {
4755
+ return this.teamId ? `&teamId=${this.teamId}` : "";
4756
+ }
4757
+ async request(endpoint, method = "GET", body) {
4758
+ const url = `${VERCEL_API_BASE}${endpoint}${endpoint.includes("?") ? "&" : "?"}${this.teamParam.slice(1)}`;
4759
+ const response = await fetch(url, {
4760
+ method,
4761
+ headers: {
4762
+ Authorization: `Bearer ${this.apiToken}`,
4763
+ "Content-Type": "application/json"
4764
+ },
4765
+ body: body ? JSON.stringify(body) : void 0
4766
+ });
4767
+ return response.json();
4768
+ }
4769
+ async createRecord(name, type, value, mxPriority) {
4770
+ const relativeName = name === this.domain ? "@" : name.replace(`.${this.domain}`, "");
4771
+ const body = {
4772
+ name: relativeName,
4773
+ type,
4774
+ value,
4775
+ ttl: 1800
4776
+ };
4777
+ if (mxPriority !== void 0) {
4778
+ body.mxPriority = mxPriority;
4779
+ }
4780
+ const result = await this.request(
4781
+ `/v2/domains/${this.domain}/records`,
4782
+ "POST",
4783
+ body
4784
+ );
4785
+ return !result.error;
4786
+ }
4787
+ async findRecord(name, type) {
4788
+ const result = await this.request(
4789
+ `/v4/domains/${this.domain}/records`
4790
+ );
4791
+ if (result.error || !result.records) {
4792
+ return null;
4793
+ }
4794
+ const relativeName = name === this.domain ? "@" : name.replace(`.${this.domain}`, "");
4795
+ return result.records.find(
4796
+ (r) => (r.name === relativeName || r.name === name) && r.type === type
4797
+ ) || null;
4798
+ }
4799
+ async deleteRecord(recordId) {
4800
+ const result = await this.request(
4801
+ `/v2/domains/${this.domain}/records/${recordId}`,
4802
+ "DELETE"
4803
+ );
4804
+ return !result.error;
4805
+ }
4806
+ async createEmailRecords(data) {
4807
+ const { domain, dkimTokens, mailFromDomain, region } = data;
4808
+ const errors2 = [];
4809
+ let recordsCreated = 0;
4810
+ try {
4811
+ for (const token of dkimTokens) {
4812
+ const name = `${token}._domainkey.${domain}`;
4813
+ const success = await this.createRecord(
4814
+ name,
4815
+ "CNAME",
4816
+ `${token}.dkim.amazonses.com`
4817
+ );
4818
+ if (success) {
4819
+ recordsCreated++;
4820
+ } else {
4821
+ errors2.push(`Failed to create DKIM record: ${name}`);
4822
+ }
4823
+ }
4824
+ const spfSuccess = await this.createRecord(
4825
+ domain,
4826
+ "TXT",
4827
+ "v=spf1 include:amazonses.com ~all"
4828
+ );
4829
+ if (spfSuccess) {
4830
+ recordsCreated++;
4831
+ } else {
4832
+ errors2.push(`Failed to create SPF record for ${domain}`);
4833
+ }
4834
+ const dmarcSuccess = await this.createRecord(
4835
+ `_dmarc.${domain}`,
4836
+ "TXT",
4837
+ `v=DMARC1; p=quarantine; rua=mailto:postmaster@${mailFromDomain || domain}`
4838
+ );
4839
+ if (dmarcSuccess) {
4840
+ recordsCreated++;
4841
+ } else {
4842
+ errors2.push(`Failed to create DMARC record for ${domain}`);
4843
+ }
4844
+ if (mailFromDomain) {
4845
+ const mxSuccess = await this.createRecord(
4846
+ mailFromDomain,
4847
+ "MX",
4848
+ `feedback-smtp.${region}.amazonses.com`,
4849
+ 10
4850
+ );
4851
+ if (mxSuccess) {
4852
+ recordsCreated++;
4853
+ } else {
4854
+ errors2.push(`Failed to create MX record for ${mailFromDomain}`);
4855
+ }
4856
+ const mailFromSpfSuccess = await this.createRecord(
4857
+ mailFromDomain,
4858
+ "TXT",
4859
+ "v=spf1 include:amazonses.com ~all"
4860
+ );
4861
+ if (mailFromSpfSuccess) {
4862
+ recordsCreated++;
4863
+ } else {
4864
+ errors2.push(`Failed to create SPF record for ${mailFromDomain}`);
4865
+ }
4866
+ }
4867
+ return {
4868
+ success: errors2.length === 0,
4869
+ recordsCreated,
4870
+ errors: errors2.length > 0 ? errors2 : void 0
4871
+ };
4872
+ } catch (error) {
4873
+ return {
4874
+ success: false,
4875
+ recordsCreated,
4876
+ errors: [
4877
+ ...errors2,
4878
+ error instanceof Error ? error.message : "Unknown error"
4879
+ ]
4880
+ };
4881
+ }
4882
+ }
4883
+ async deleteEmailRecords(data) {
4884
+ const { domain, dkimTokens, mailFromDomain } = data;
4885
+ const errors2 = [];
4886
+ let recordsCreated = 0;
4887
+ try {
4888
+ for (const token of dkimTokens) {
4889
+ const name = `${token}._domainkey.${domain}`;
4890
+ const record = await this.findRecord(name, "CNAME");
4891
+ if (record) {
4892
+ const success = await this.deleteRecord(record.id);
4893
+ if (success) {
4894
+ recordsCreated++;
4895
+ } else {
4896
+ errors2.push(`Failed to delete DKIM record: ${name}`);
4897
+ }
4898
+ }
4899
+ }
4900
+ const dmarcRecord = await this.findRecord(`_dmarc.${domain}`, "TXT");
4901
+ if (dmarcRecord) {
4902
+ const success = await this.deleteRecord(dmarcRecord.id);
4903
+ if (success) {
4904
+ recordsCreated++;
4905
+ } else {
4906
+ errors2.push("Failed to delete DMARC record");
4907
+ }
4908
+ }
4909
+ if (mailFromDomain) {
4910
+ const mxRecord = await this.findRecord(mailFromDomain, "MX");
4911
+ if (mxRecord) {
4912
+ const success = await this.deleteRecord(mxRecord.id);
4913
+ if (success) {
4914
+ recordsCreated++;
4915
+ } else {
4916
+ errors2.push(`Failed to delete MX record for ${mailFromDomain}`);
4917
+ }
4918
+ }
4919
+ const spfRecord = await this.findRecord(mailFromDomain, "TXT");
4920
+ if (spfRecord) {
4921
+ const success = await this.deleteRecord(spfRecord.id);
4922
+ if (success) {
4923
+ recordsCreated++;
4924
+ } else {
4925
+ errors2.push(`Failed to delete SPF record for ${mailFromDomain}`);
4926
+ }
4927
+ }
4928
+ }
4929
+ return {
4930
+ success: errors2.length === 0,
4931
+ recordsCreated,
4932
+ errors: errors2.length > 0 ? errors2 : void 0
4933
+ };
4934
+ } catch (error) {
4935
+ return {
4936
+ success: false,
4937
+ recordsCreated,
4938
+ errors: [
4939
+ ...errors2,
4940
+ error instanceof Error ? error.message : "Unknown error"
4941
+ ]
4942
+ };
4943
+ }
4944
+ }
4945
+ async verifyRecords(data) {
4946
+ const { domain, dkimTokens, mailFromDomain, region } = data;
4947
+ const missing = [];
4948
+ const incorrect = [];
4949
+ for (const token of dkimTokens) {
4950
+ const name = `${token}._domainkey.${domain}`;
4951
+ const expectedValue = `${token}.dkim.amazonses.com`;
4952
+ const record = await this.findRecord(name, "CNAME");
4953
+ if (!record) {
4954
+ missing.push(`DKIM: ${name}`);
4955
+ } else if (record.value !== expectedValue) {
4956
+ incorrect.push(`DKIM: ${name} (expected ${expectedValue})`);
4957
+ }
4958
+ }
4959
+ const spfRecord = await this.findRecord(domain, "TXT");
4960
+ if (!spfRecord) {
4961
+ missing.push(`SPF: ${domain}`);
4962
+ } else if (!spfRecord.value.includes("include:amazonses.com")) {
4963
+ incorrect.push(`SPF: ${domain} (missing amazonses.com include)`);
4964
+ }
4965
+ const dmarcRecord = await this.findRecord(`_dmarc.${domain}`, "TXT");
4966
+ if (!dmarcRecord) {
4967
+ missing.push(`DMARC: _dmarc.${domain}`);
4968
+ }
4969
+ if (mailFromDomain) {
4970
+ const mxRecord = await this.findRecord(mailFromDomain, "MX");
4971
+ if (!mxRecord) {
4972
+ missing.push(`MX: ${mailFromDomain}`);
4973
+ } else if (!mxRecord.value.includes(`feedback-smtp.${region}.amazonses.com`)) {
4974
+ incorrect.push(`MX: ${mailFromDomain}`);
4975
+ }
4976
+ const mailFromSpf = await this.findRecord(mailFromDomain, "TXT");
4977
+ if (!mailFromSpf) {
4978
+ missing.push(`SPF: ${mailFromDomain}`);
4979
+ }
4980
+ }
4981
+ return {
4982
+ verified: missing.length === 0 && incorrect.length === 0,
4983
+ missing,
4984
+ incorrect
4985
+ };
4986
+ }
4987
+ };
4988
+ }
4989
+ });
4990
+
4991
+ // src/utils/dns/create-records.ts
4992
+ function buildEmailDNSRecords(data) {
4993
+ const { domain, dkimTokens, mailFromDomain, region } = data;
4994
+ const records = [];
4995
+ for (const token of dkimTokens) {
4996
+ records.push({
4997
+ name: `${token}._domainkey.${domain}`,
4998
+ type: "CNAME",
4999
+ value: `${token}.dkim.amazonses.com`,
5000
+ category: "dkim"
5001
+ });
5002
+ }
5003
+ records.push({
5004
+ name: domain,
5005
+ type: "TXT",
5006
+ value: "v=spf1 include:amazonses.com ~all",
5007
+ category: "spf"
5008
+ });
5009
+ const dmarcRuaDomain = mailFromDomain || domain;
5010
+ records.push({
5011
+ name: `_dmarc.${domain}`,
5012
+ type: "TXT",
5013
+ value: `v=DMARC1; p=quarantine; rua=mailto:postmaster@${dmarcRuaDomain}`,
5014
+ category: "dmarc"
5015
+ });
5016
+ if (mailFromDomain) {
5017
+ records.push({
5018
+ name: mailFromDomain,
5019
+ type: "MX",
5020
+ value: `feedback-smtp.${region}.amazonses.com`,
5021
+ priority: 10,
5022
+ category: "mailfrom_mx"
5023
+ });
5024
+ records.push({
5025
+ name: mailFromDomain,
5026
+ type: "TXT",
5027
+ value: "v=spf1 include:amazonses.com ~all",
5028
+ category: "mailfrom_spf"
5029
+ });
5030
+ }
5031
+ return records;
5032
+ }
5033
+ function formatDNSRecordsForDisplay(records) {
5034
+ return records.map((r) => ({
5035
+ name: r.name,
5036
+ type: r.type,
5037
+ value: r.priority ? `${r.priority} ${r.value}` : r.value
5038
+ }));
5039
+ }
5040
+ async function createDNSRecordsForProvider(credentials, data, selectedCategories) {
5041
+ switch (credentials.provider) {
5042
+ case "route53": {
5043
+ const categories = selectedCategories || /* @__PURE__ */ new Set([
5044
+ "dkim",
5045
+ "spf",
5046
+ "dmarc",
5047
+ "mailfrom_mx",
5048
+ "mailfrom_spf"
5049
+ ]);
5050
+ try {
5051
+ await createSelectedDNSRecords(
5052
+ credentials.hostedZoneId,
5053
+ data.domain,
5054
+ data.dkimTokens,
5055
+ data.region,
5056
+ categories,
5057
+ void 0,
5058
+ // customTrackingDomain - not used here
5059
+ data.mailFromDomain
5060
+ );
5061
+ let recordsCreated = 0;
5062
+ if (categories.has("dkim")) recordsCreated += data.dkimTokens.length;
5063
+ if (categories.has("spf")) recordsCreated += 1;
5064
+ if (categories.has("dmarc")) recordsCreated += 1;
5065
+ if (data.mailFromDomain) {
5066
+ if (categories.has("mailfrom_mx")) recordsCreated += 1;
5067
+ if (categories.has("mailfrom_spf")) recordsCreated += 1;
5068
+ }
5069
+ return {
5070
+ success: true,
5071
+ recordsCreated
5072
+ };
5073
+ } catch (error) {
5074
+ return {
5075
+ success: false,
5076
+ recordsCreated: 0,
5077
+ errors: [error instanceof Error ? error.message : "Unknown error"]
5078
+ };
5079
+ }
5080
+ }
5081
+ case "vercel": {
5082
+ const client = new VercelDNSClient(
5083
+ data.domain,
5084
+ credentials.token,
5085
+ credentials.teamId
5086
+ );
5087
+ return client.createEmailRecords(data);
5088
+ }
5089
+ case "cloudflare": {
5090
+ const client = new CloudflareDNSClient(
5091
+ credentials.zoneId,
5092
+ credentials.token
5093
+ );
5094
+ return client.createEmailRecords(data);
5095
+ }
5096
+ case "manual": {
5097
+ return {
5098
+ success: true,
5099
+ recordsCreated: 0
5100
+ };
5101
+ }
5102
+ }
5103
+ }
5104
+ function getDNSProviderDisplayName(provider) {
5105
+ switch (provider) {
5106
+ case "route53":
5107
+ return "AWS Route53";
5108
+ case "vercel":
5109
+ return "Vercel DNS";
5110
+ case "cloudflare":
5111
+ return "Cloudflare";
5112
+ case "manual":
5113
+ return "Manual";
5114
+ }
5115
+ }
5116
+ function getDNSProviderTokenUrl(provider) {
5117
+ switch (provider) {
5118
+ case "vercel":
5119
+ return "https://vercel.com/account/tokens";
5120
+ case "cloudflare":
5121
+ return "https://dash.cloudflare.com/profile/api-tokens";
5122
+ }
5123
+ }
5124
+ var init_create_records = __esm({
5125
+ "src/utils/dns/create-records.ts"() {
5126
+ "use strict";
5127
+ init_esm_shims();
5128
+ init_route53();
5129
+ init_cloudflare();
5130
+ init_vercel();
5131
+ }
5132
+ });
5133
+
5134
+ // src/utils/dns/credentials.ts
5135
+ function getDNSProviderEnvVars(provider) {
5136
+ switch (provider) {
5137
+ case "route53":
5138
+ return [];
5139
+ // Uses AWS credentials from environment/config
5140
+ case "vercel":
5141
+ return ["VERCEL_TOKEN"];
5142
+ case "cloudflare":
5143
+ return ["CLOUDFLARE_API_TOKEN"];
5144
+ case "manual":
5145
+ return [];
5146
+ }
5147
+ }
5148
+ function getDNSProviderOptionalEnvVars(provider) {
5149
+ switch (provider) {
5150
+ case "vercel":
5151
+ return ["VERCEL_TEAM_ID"];
5152
+ case "cloudflare":
5153
+ return ["CLOUDFLARE_ZONE_ID"];
5154
+ default:
5155
+ return [];
5156
+ }
5157
+ }
5158
+ function hasVercelToken() {
5159
+ return !!process.env.VERCEL_TOKEN;
5160
+ }
5161
+ function hasCloudflareToken() {
5162
+ return !!process.env.CLOUDFLARE_API_TOKEN;
5163
+ }
5164
+ async function validateVercelCredentials(token) {
5165
+ try {
5166
+ const response = await fetch("https://api.vercel.com/v2/user", {
5167
+ headers: {
5168
+ Authorization: `Bearer ${token}`
5169
+ }
5170
+ });
5171
+ return response.ok;
5172
+ } catch {
5173
+ return false;
5174
+ }
5175
+ }
5176
+ async function checkVercelDomain(token, domain, teamId) {
5177
+ try {
5178
+ const teamParam = teamId ? `&teamId=${teamId}` : "";
5179
+ const response = await fetch(
5180
+ `https://api.vercel.com/v5/domains/${domain}?${teamParam}`,
5181
+ {
5182
+ headers: {
5183
+ Authorization: `Bearer ${token}`
5184
+ }
5185
+ }
5186
+ );
5187
+ return response.ok;
5188
+ } catch {
5189
+ return false;
5190
+ }
5191
+ }
5192
+ async function validateCloudflareCredentials(token) {
5193
+ try {
5194
+ const response = await fetch(
5195
+ "https://api.cloudflare.com/client/v4/user/tokens/verify",
5196
+ {
5197
+ headers: {
5198
+ Authorization: `Bearer ${token}`
5199
+ }
5200
+ }
5201
+ );
5202
+ const data = await response.json();
5203
+ return data.success === true;
5204
+ } catch {
5205
+ return false;
5206
+ }
5207
+ }
5208
+ async function findCloudflareZoneId(token, domain) {
5209
+ try {
5210
+ const response = await fetch(
5211
+ `https://api.cloudflare.com/client/v4/zones?name=${encodeURIComponent(domain)}`,
5212
+ {
5213
+ headers: {
5214
+ Authorization: `Bearer ${token}`,
5215
+ "Content-Type": "application/json"
5216
+ }
5217
+ }
5218
+ );
5219
+ const data = await response.json();
5220
+ if (data.success && data.result.length > 0) {
5221
+ return data.result[0].id;
5222
+ }
5223
+ const parts = domain.split(".");
5224
+ if (parts.length > 2) {
5225
+ const parentDomain = parts.slice(-2).join(".");
5226
+ const parentResponse = await fetch(
5227
+ `https://api.cloudflare.com/client/v4/zones?name=${encodeURIComponent(parentDomain)}`,
5228
+ {
5229
+ headers: {
5230
+ Authorization: `Bearer ${token}`,
5231
+ "Content-Type": "application/json"
5232
+ }
5233
+ }
5234
+ );
5235
+ const parentData = await parentResponse.json();
5236
+ if (parentData.success && parentData.result.length > 0) {
5237
+ return parentData.result[0].id;
5238
+ }
5239
+ }
5240
+ return null;
5241
+ } catch {
5242
+ return null;
5243
+ }
5244
+ }
5245
+ async function getDNSCredentials(provider, domain, region) {
5246
+ switch (provider) {
5247
+ case "route53": {
5248
+ const hostedZone = await findHostedZone(domain, region);
5249
+ if (hostedZone) {
5250
+ return {
5251
+ valid: true,
5252
+ credentials: { provider: "route53", hostedZoneId: hostedZone.id }
5253
+ };
5254
+ }
5255
+ return {
5256
+ valid: false,
5257
+ error: `No Route53 hosted zone found for ${domain}`
5258
+ };
5259
+ }
5260
+ case "vercel": {
5261
+ const token = process.env.VERCEL_TOKEN;
5262
+ if (!token) {
5263
+ return {
5264
+ valid: false,
5265
+ error: "VERCEL_TOKEN environment variable is not set"
5266
+ };
5267
+ }
5268
+ const isValid = await validateVercelCredentials(token);
5269
+ if (!isValid) {
5270
+ return {
5271
+ valid: false,
5272
+ error: "Invalid VERCEL_TOKEN - authentication failed"
5273
+ };
5274
+ }
5275
+ const teamId = process.env.VERCEL_TEAM_ID;
5276
+ const hasDomain = await checkVercelDomain(token, domain, teamId);
5277
+ if (!hasDomain) {
5278
+ return {
5279
+ valid: false,
5280
+ error: `Domain ${domain} not found in Vercel DNS`
5281
+ };
5282
+ }
5283
+ return {
5284
+ valid: true,
5285
+ credentials: { provider: "vercel", token, teamId }
5286
+ };
5287
+ }
5288
+ case "cloudflare": {
5289
+ const token = process.env.CLOUDFLARE_API_TOKEN;
5290
+ if (!token) {
5291
+ return {
5292
+ valid: false,
5293
+ error: "CLOUDFLARE_API_TOKEN environment variable is not set"
5294
+ };
5295
+ }
5296
+ const isValid = await validateCloudflareCredentials(token);
5297
+ if (!isValid) {
5298
+ return {
5299
+ valid: false,
5300
+ error: "Invalid CLOUDFLARE_API_TOKEN - authentication failed"
5301
+ };
5302
+ }
5303
+ let zoneId = process.env.CLOUDFLARE_ZONE_ID;
5304
+ if (!zoneId) {
5305
+ const detectedZoneId = await findCloudflareZoneId(token, domain);
5306
+ zoneId = detectedZoneId ?? void 0;
5307
+ }
5308
+ if (!zoneId) {
5309
+ return {
5310
+ valid: false,
5311
+ error: `Could not find Cloudflare zone for ${domain}. Set CLOUDFLARE_ZONE_ID if the domain uses a different zone.`
5312
+ };
5313
+ }
5314
+ return {
5315
+ valid: true,
5316
+ credentials: { provider: "cloudflare", token, zoneId }
5317
+ };
5318
+ }
5319
+ case "manual":
5320
+ return {
5321
+ valid: true,
5322
+ credentials: { provider: "manual" }
5323
+ };
5324
+ }
5325
+ }
5326
+ async function detectAvailableDNSProviders(domain, region) {
5327
+ const providers = [];
5328
+ const hostedZone = await findHostedZone(domain, region);
5329
+ providers.push({
5330
+ provider: "route53",
5331
+ detected: !!hostedZone,
5332
+ hint: hostedZone ? "Hosted zone detected" : void 0
5333
+ });
5334
+ const vercelToken = process.env.VERCEL_TOKEN;
5335
+ if (vercelToken) {
5336
+ const teamId = process.env.VERCEL_TEAM_ID;
5337
+ const hasDomain = await checkVercelDomain(vercelToken, domain, teamId);
5338
+ providers.push({
5339
+ provider: "vercel",
5340
+ detected: hasDomain,
5341
+ hint: hasDomain ? "Domain found in Vercel DNS" : "Token found"
5342
+ });
5343
+ } else {
5344
+ providers.push({
5345
+ provider: "vercel",
5346
+ detected: false
5347
+ });
5348
+ }
5349
+ const cfToken = process.env.CLOUDFLARE_API_TOKEN;
5350
+ if (cfToken) {
5351
+ const zoneId = process.env.CLOUDFLARE_ZONE_ID || await findCloudflareZoneId(cfToken, domain);
5352
+ providers.push({
5353
+ provider: "cloudflare",
5354
+ detected: !!zoneId,
5355
+ hint: zoneId ? "Zone found" : "Token found"
5356
+ });
5357
+ } else {
5358
+ providers.push({
5359
+ provider: "cloudflare",
5360
+ detected: false
5361
+ });
5362
+ }
5363
+ providers.push({
5364
+ provider: "manual",
5365
+ detected: true,
5366
+ hint: "I'll add DNS records myself"
5367
+ });
5368
+ return providers.sort((a, b) => {
5369
+ if (a.provider === "manual") return 1;
5370
+ if (b.provider === "manual") return -1;
5371
+ if (a.detected && !b.detected) return -1;
5372
+ if (!a.detected && b.detected) return 1;
5373
+ return 0;
5374
+ });
5375
+ }
5376
+ var init_credentials = __esm({
5377
+ "src/utils/dns/credentials.ts"() {
5378
+ "use strict";
5379
+ init_esm_shims();
5380
+ init_route53();
5381
+ }
5382
+ });
5383
+
5384
+ // src/utils/dns/index.ts
5385
+ var dns_exports = {};
5386
+ __export(dns_exports, {
5387
+ CloudflareDNSClient: () => CloudflareDNSClient,
5388
+ VercelDNSClient: () => VercelDNSClient,
5389
+ buildEmailDNSRecords: () => buildEmailDNSRecords,
5390
+ createDNSRecordsForProvider: () => createDNSRecordsForProvider,
5391
+ detectAvailableDNSProviders: () => detectAvailableDNSProviders,
5392
+ findCloudflareZoneId: () => findCloudflareZoneId,
5393
+ formatDNSRecordsForDisplay: () => formatDNSRecordsForDisplay,
5394
+ getDNSCredentials: () => getDNSCredentials,
5395
+ getDNSProviderDisplayName: () => getDNSProviderDisplayName,
5396
+ getDNSProviderEnvVars: () => getDNSProviderEnvVars,
5397
+ getDNSProviderOptionalEnvVars: () => getDNSProviderOptionalEnvVars,
5398
+ getDNSProviderTokenUrl: () => getDNSProviderTokenUrl,
5399
+ hasCloudflareToken: () => hasCloudflareToken,
5400
+ hasVercelToken: () => hasVercelToken
5401
+ });
5402
+ var init_dns = __esm({
5403
+ "src/utils/dns/index.ts"() {
5404
+ "use strict";
5405
+ init_esm_shims();
5406
+ init_cloudflare();
5407
+ init_create_records();
5408
+ init_credentials();
5409
+ init_vercel();
5410
+ }
5411
+ });
5412
+
5413
+ // src/utils/shared/assume-role.ts
5414
+ var assume_role_exports = {};
5415
+ __export(assume_role_exports, {
5416
+ assumeRole: () => assumeRole
5417
+ });
5418
+ import { AssumeRoleCommand, STSClient as STSClient3 } from "@aws-sdk/client-sts";
5419
+ async function assumeRole(roleArn, region, sessionName = "wraps-console") {
5420
+ const sts = new STSClient3({ region });
5421
+ const response = await sts.send(
5422
+ new AssumeRoleCommand({
5423
+ RoleArn: roleArn,
5424
+ RoleSessionName: sessionName,
5425
+ DurationSeconds: 3600
5426
+ // 1 hour
5427
+ })
5428
+ );
5429
+ if (!response.Credentials) {
5430
+ throw new Error("Failed to assume role: No credentials returned");
5431
+ }
5432
+ return {
5433
+ accessKeyId: response.Credentials.AccessKeyId,
5434
+ secretAccessKey: response.Credentials.SecretAccessKey,
5435
+ sessionToken: response.Credentials.SessionToken,
5436
+ expiration: response.Credentials.Expiration
5437
+ };
5438
+ }
5439
+ var init_assume_role = __esm({
5440
+ "src/utils/shared/assume-role.ts"() {
5441
+ "use strict";
5442
+ init_esm_shims();
5443
+ }
5444
+ });
5445
+
5446
+ // src/utils/archive.ts
5447
+ import {
5448
+ GetArchiveMessageCommand,
5449
+ GetArchiveSearchResultsCommand,
5450
+ MailManagerClient as MailManagerClient2,
5451
+ StartArchiveSearchCommand
5452
+ } from "@aws-sdk/client-mailmanager";
5453
+ import DOMPurify from "isomorphic-dompurify";
5454
+ import { simpleParser } from "mailparser";
5455
+ function extractArchiveId(archiveArnOrId) {
5456
+ if (archiveArnOrId.startsWith("arn:")) {
5457
+ const parts = archiveArnOrId.split("/");
5458
+ return parts.at(-1);
5459
+ }
5460
+ return archiveArnOrId;
5461
+ }
5462
+ async function getArchivedEmail(archiveArnOrId, searchCriteria, region) {
5463
+ const client = new MailManagerClient2({ region });
5464
+ const archiveId = extractArchiveId(archiveArnOrId);
5465
+ const searchTime = searchCriteria.timestamp || /* @__PURE__ */ new Date();
5466
+ const dayBefore = new Date(searchTime.getTime() - 24 * 60 * 60 * 1e3);
5467
+ const dayAfter = new Date(searchTime.getTime() + 24 * 60 * 60 * 1e3);
5468
+ const filters = [];
5469
+ if (searchCriteria.from) {
5470
+ filters.push({
5471
+ StringExpression: {
5472
+ Evaluate: {
5473
+ Attribute: "FROM"
5474
+ },
5475
+ Operator: "CONTAINS",
5476
+ Values: [searchCriteria.from]
5477
+ }
5478
+ });
5479
+ }
5480
+ if (searchCriteria.to) {
5481
+ filters.push({
5482
+ StringExpression: {
5483
+ Evaluate: {
5484
+ Attribute: "TO"
5485
+ },
5486
+ Operator: "CONTAINS",
5487
+ Values: [searchCriteria.to]
5488
+ }
5489
+ });
5490
+ }
5491
+ if (searchCriteria.subject) {
5492
+ filters.push({
5493
+ StringExpression: {
5494
+ Evaluate: {
5495
+ Attribute: "SUBJECT"
5496
+ },
5497
+ Operator: "CONTAINS",
5498
+ Values: [searchCriteria.subject]
5499
+ }
5500
+ });
5501
+ }
5502
+ if (filters.length === 0) {
5503
+ throw new Error(
5504
+ "At least one search criterion (from, to, or subject) is required"
5505
+ );
5506
+ }
5507
+ const searchCommand = new StartArchiveSearchCommand({
5508
+ ArchiveId: archiveId,
5509
+ FromTimestamp: dayBefore,
5510
+ ToTimestamp: dayAfter,
5511
+ Filters: {
5512
+ Include: filters
5513
+ },
5514
+ MaxResults: 10
5515
+ // Get a few results in case there are multiple matches
5516
+ });
5517
+ const searchResponse = await client.send(searchCommand);
5518
+ const searchId = searchResponse.SearchId;
5519
+ if (!searchId) {
5520
+ throw new Error("Failed to start archive search");
5521
+ }
5522
+ let archivedMessageId;
5523
+ let attempts = 0;
5524
+ const maxAttempts = 20;
5525
+ const pollInterval = 1e3;
5526
+ await new Promise((resolve) => setTimeout(resolve, 1e3));
5527
+ while (attempts < maxAttempts) {
5528
+ try {
5529
+ const resultsCommand = new GetArchiveSearchResultsCommand({
5530
+ SearchId: searchId
5531
+ });
5532
+ const resultsResponse = await client.send(resultsCommand);
5533
+ if (resultsResponse.Rows && resultsResponse.Rows.length > 0) {
5534
+ archivedMessageId = resultsResponse.Rows[0].ArchivedMessageId;
5535
+ break;
5536
+ }
5537
+ if (resultsResponse.Rows && resultsResponse.Rows.length === 0) {
5538
+ break;
5539
+ }
5540
+ } catch (error) {
5541
+ if (error instanceof Error && error.name === "ConflictException" && error.message.includes("still in progress")) {
5542
+ console.log(`Search still in progress, attempt ${attempts + 1}...`);
5543
+ } else {
5544
+ throw error;
5545
+ }
5546
+ }
5547
+ await new Promise((resolve) => setTimeout(resolve, pollInterval));
5548
+ attempts++;
5549
+ }
5550
+ if (!archivedMessageId) {
5551
+ throw new Error(
5552
+ "Email not found in archive with the provided search criteria. It may have been sent before archiving was enabled."
5553
+ );
5554
+ }
5555
+ const command = new GetArchiveMessageCommand({
5556
+ ArchivedMessageId: archivedMessageId
5557
+ });
5558
+ const response = await client.send(command);
5559
+ if (!response.MessageDownloadLink) {
5560
+ throw new Error("No download link available for archived message");
5561
+ }
5562
+ const emailResponse = await fetch(response.MessageDownloadLink);
5563
+ if (!emailResponse.ok) {
5564
+ throw new Error(`Failed to download email: ${emailResponse.statusText}`);
5565
+ }
5566
+ const emailRaw = await emailResponse.text();
5567
+ const parsed = await simpleParser(emailRaw);
5568
+ const attachments = parsed.attachments?.map((att) => ({
5569
+ filename: att.filename,
5570
+ contentType: att.contentType,
5571
+ size: att.size
5572
+ })) || [];
5573
+ const headers = {};
5574
+ if (parsed.headers) {
5575
+ for (const [key, value] of parsed.headers) {
5576
+ if (value instanceof Date) {
5577
+ headers[key] = value.toISOString();
5578
+ } else if (typeof value === "string") {
5579
+ headers[key] = value;
5580
+ } else if (Array.isArray(value) && value.every((v) => typeof v === "string")) {
5581
+ headers[key] = value;
5582
+ } else {
5583
+ headers[key] = JSON.stringify(value);
5584
+ }
5585
+ }
5586
+ }
5587
+ const getAddressText = (addr) => {
5588
+ if (!addr) {
5589
+ return "";
5590
+ }
5591
+ if (Array.isArray(addr)) {
4615
5592
  return addr.map((a) => a.text).join(", ");
4616
5593
  }
4617
5594
  return addr.text || "";
@@ -4758,7 +5735,7 @@ import { dirname as dirname2, join as join5 } from "path";
4758
5735
  import { fileURLToPath as fileURLToPath4 } from "url";
4759
5736
  import * as clack37 from "@clack/prompts";
4760
5737
  import args from "args";
4761
- import pc39 from "picocolors";
5738
+ import pc40 from "picocolors";
4762
5739
 
4763
5740
  // src/commands/aws/doctor.ts
4764
5741
  init_esm_shims();
@@ -6885,8 +7862,8 @@ init_esm_shims();
6885
7862
  import * as aws2 from "@pulumi/aws";
6886
7863
  async function getExistingOIDCProviderArn(url) {
6887
7864
  try {
6888
- const { IAMClient: IAMClient3, ListOpenIDConnectProvidersCommand } = await import("@aws-sdk/client-iam");
6889
- const iam8 = new IAMClient3({
7865
+ const { IAMClient: IAMClient4, ListOpenIDConnectProvidersCommand } = await import("@aws-sdk/client-iam");
7866
+ const iam8 = new IAMClient4({
6890
7867
  region: process.env.AWS_REGION || "us-east-1"
6891
7868
  });
6892
7869
  const response = await iam8.send(new ListOpenIDConnectProvidersCommand({}));
@@ -6943,11 +7920,11 @@ async function createVercelOIDC(config2) {
6943
7920
  // src/infrastructure/cdn-stack.ts
6944
7921
  async function roleExists(roleName) {
6945
7922
  try {
6946
- const { IAMClient: IAMClient3, GetRoleCommand: GetRoleCommand2 } = await import("@aws-sdk/client-iam");
6947
- const iam8 = new IAMClient3({
7923
+ const { IAMClient: IAMClient4, GetRoleCommand: GetRoleCommand3 } = await import("@aws-sdk/client-iam");
7924
+ const iam8 = new IAMClient4({
6948
7925
  region: process.env.AWS_REGION || "us-east-1"
6949
7926
  });
6950
- await iam8.send(new GetRoleCommand2({ RoleName: roleName }));
7927
+ await iam8.send(new GetRoleCommand3({ RoleName: roleName }));
6951
7928
  return true;
6952
7929
  } catch (error) {
6953
7930
  if (error.name === "NoSuchEntityException" || error.Code === "NoSuchEntity" || error.Error?.Code === "NoSuchEntity") {
@@ -12300,11 +13277,11 @@ import * as aws7 from "@pulumi/aws";
12300
13277
  import * as pulumi10 from "@pulumi/pulumi";
12301
13278
  async function roleExists2(roleName) {
12302
13279
  try {
12303
- const { IAMClient: IAMClient3, GetRoleCommand: GetRoleCommand2 } = await import("@aws-sdk/client-iam");
12304
- const iam8 = new IAMClient3({
13280
+ const { IAMClient: IAMClient4, GetRoleCommand: GetRoleCommand3 } = await import("@aws-sdk/client-iam");
13281
+ const iam8 = new IAMClient4({
12305
13282
  region: process.env.AWS_REGION || "us-east-1"
12306
13283
  });
12307
- await iam8.send(new GetRoleCommand2({ RoleName: roleName }));
13284
+ await iam8.send(new GetRoleCommand3({ RoleName: roleName }));
12308
13285
  return true;
12309
13286
  } catch (error) {
12310
13287
  if (error.name === "NoSuchEntityException" || error.Code === "NoSuchEntity" || error.Error?.Code === "NoSuchEntity") {
@@ -12689,8 +13666,8 @@ function convertToSMTPPassword2(secretAccessKey, region) {
12689
13666
  }
12690
13667
  async function userExists(userName) {
12691
13668
  try {
12692
- const { IAMClient: IAMClient3, GetUserCommand } = await import("@aws-sdk/client-iam");
12693
- const iam8 = new IAMClient3({
13669
+ const { IAMClient: IAMClient4, GetUserCommand } = await import("@aws-sdk/client-iam");
13670
+ const iam8 = new IAMClient4({
12694
13671
  region: process.env.AWS_REGION || "us-east-1"
12695
13672
  });
12696
13673
  await iam8.send(new GetUserCommand({ UserName: userName }));
@@ -14585,8 +15562,8 @@ function getRequiredActions(config2) {
14585
15562
  }
14586
15563
  async function checkIAMPermissions(userArn, actions, region) {
14587
15564
  try {
14588
- const { IAMClient: IAMClient3, SimulatePrincipalPolicyCommand } = await import("@aws-sdk/client-iam");
14589
- const client = new IAMClient3({ region });
15565
+ const { IAMClient: IAMClient4, SimulatePrincipalPolicyCommand } = await import("@aws-sdk/client-iam");
15566
+ const client = new IAMClient4({ region });
14590
15567
  const batchSize = 100;
14591
15568
  const batches = [];
14592
15569
  for (let i = 0; i < actions.length; i += batchSize) {
@@ -14978,51 +15955,149 @@ ${pc18.yellow(pc18.bold("Configuration Warnings:"))}`);
14978
15955
  await saveConnectionMetadata(metadata);
14979
15956
  progress.info("Connection metadata saved for upgrade and restore capability");
14980
15957
  let dnsAutoCreated = false;
15958
+ let dnsProvider;
14981
15959
  if (outputs.domain && outputs.dkimTokens && outputs.dkimTokens.length > 0) {
14982
- const { findHostedZone: findHostedZone2, previewDNSChanges: previewDNSChanges2, createSelectedDNSRecords: createSelectedDNSRecords2 } = await Promise.resolve().then(() => (init_route53(), route53_exports));
14983
- const { promptDNSManagement: promptDNSManagement2, promptDNSConfirmation: promptDNSConfirmation2 } = await Promise.resolve().then(() => (init_prompts(), prompts_exports));
14984
- const hostedZone = await findHostedZone2(outputs.domain, region);
14985
- if (hostedZone) {
14986
- const manageDNS = await promptDNSManagement2(outputs.domain);
14987
- if (manageDNS) {
14988
- try {
14989
- progress.start("Checking existing DNS records");
14990
- const dnsPreview = await previewDNSChanges2(
14991
- hostedZone.id,
14992
- outputs.domain,
14993
- outputs.dkimTokens,
14994
- region,
14995
- outputs.customTrackingDomain,
14996
- outputs.mailFromDomain
14997
- );
14998
- progress.stop();
14999
- const { shouldCreate, selectedCategories } = await promptDNSConfirmation2(dnsPreview);
15000
- if (shouldCreate && selectedCategories.size > 0) {
15001
- progress.start("Creating selected DNS records in Route53");
15002
- await createSelectedDNSRecords2(
15003
- hostedZone.id,
15960
+ const {
15961
+ detectAvailableDNSProviders: detectAvailableDNSProviders2,
15962
+ getDNSCredentials: getDNSCredentials2,
15963
+ createDNSRecordsForProvider: createDNSRecordsForProvider2,
15964
+ getDNSProviderDisplayName: getDNSProviderDisplayName2,
15965
+ getDNSProviderTokenUrl: getDNSProviderTokenUrl2,
15966
+ buildEmailDNSRecords: buildEmailDNSRecords2
15967
+ } = await Promise.resolve().then(() => (init_dns(), dns_exports));
15968
+ const {
15969
+ promptDNSProvider: promptDNSProvider2,
15970
+ promptDNSConfirmation: promptDNSConfirmation2,
15971
+ promptContinueManualDNS: promptContinueManualDNS2
15972
+ } = await Promise.resolve().then(() => (init_prompts(), prompts_exports));
15973
+ const { previewDNSChanges: previewDNSChanges2 } = await Promise.resolve().then(() => (init_route53(), route53_exports));
15974
+ progress.start("Detecting DNS providers");
15975
+ const availableProviders = await detectAvailableDNSProviders2(
15976
+ outputs.domain,
15977
+ region
15978
+ );
15979
+ progress.stop();
15980
+ const selectedProvider = await promptDNSProvider2(
15981
+ outputs.domain,
15982
+ availableProviders
15983
+ );
15984
+ dnsProvider = selectedProvider;
15985
+ if (selectedProvider !== "manual") {
15986
+ progress.start(
15987
+ `Validating ${getDNSProviderDisplayName2(selectedProvider)} credentials`
15988
+ );
15989
+ const credentialResult2 = await getDNSCredentials2(
15990
+ selectedProvider,
15991
+ outputs.domain,
15992
+ region
15993
+ );
15994
+ progress.stop();
15995
+ if (credentialResult2.valid && credentialResult2.credentials) {
15996
+ const credentials = credentialResult2.credentials;
15997
+ if (credentials.provider === "route53") {
15998
+ try {
15999
+ progress.start("Checking existing DNS records");
16000
+ const dnsPreview = await previewDNSChanges2(
16001
+ credentials.hostedZoneId,
15004
16002
  outputs.domain,
15005
16003
  outputs.dkimTokens,
15006
16004
  region,
15007
- selectedCategories,
15008
16005
  outputs.customTrackingDomain,
15009
16006
  outputs.mailFromDomain
15010
16007
  );
16008
+ progress.stop();
16009
+ const { shouldCreate, selectedCategories } = await promptDNSConfirmation2(dnsPreview);
16010
+ if (shouldCreate && selectedCategories.size > 0) {
16011
+ progress.start("Creating selected DNS records in Route53");
16012
+ const result = await createDNSRecordsForProvider2(
16013
+ credentials,
16014
+ {
16015
+ domain: outputs.domain,
16016
+ dkimTokens: outputs.dkimTokens,
16017
+ mailFromDomain: outputs.mailFromDomain,
16018
+ region
16019
+ },
16020
+ selectedCategories
16021
+ );
16022
+ if (result.success) {
16023
+ progress.succeed(
16024
+ `Created ${selectedCategories.size} DNS record group(s) in Route53`
16025
+ );
16026
+ dnsAutoCreated = true;
16027
+ } else {
16028
+ progress.fail("Failed to create some DNS records");
16029
+ if (result.errors) {
16030
+ for (const error of result.errors) {
16031
+ clack17.log.warn(error);
16032
+ }
16033
+ }
16034
+ }
16035
+ } else {
16036
+ clack17.log.info(
16037
+ "Skipping DNS record creation. You can add them manually."
16038
+ );
16039
+ }
16040
+ } catch (error) {
16041
+ progress.stop();
16042
+ clack17.log.warn(`Could not manage DNS records: ${error.message}`);
16043
+ }
16044
+ } else {
16045
+ const recordData = {
16046
+ domain: outputs.domain,
16047
+ dkimTokens: outputs.dkimTokens,
16048
+ mailFromDomain: outputs.mailFromDomain,
16049
+ region
16050
+ };
16051
+ const records = buildEmailDNSRecords2(recordData);
16052
+ clack17.log.info(pc18.bold("DNS records to create:"));
16053
+ for (const record of records) {
16054
+ clack17.log.info(
16055
+ pc18.dim(` ${record.type} ${record.name} \u2192 ${record.value}`)
16056
+ );
16057
+ }
16058
+ progress.start(
16059
+ `Creating DNS records in ${getDNSProviderDisplayName2(credentials.provider)}`
16060
+ );
16061
+ const result = await createDNSRecordsForProvider2(
16062
+ credentials,
16063
+ recordData
16064
+ );
16065
+ if (result.success) {
15011
16066
  progress.succeed(
15012
- `Created ${selectedCategories.size} DNS record group(s) in Route53`
16067
+ `Created ${result.recordsCreated} DNS records in ${getDNSProviderDisplayName2(credentials.provider)}`
15013
16068
  );
15014
16069
  dnsAutoCreated = true;
15015
16070
  } else {
15016
- clack17.log.info(
15017
- "Skipping DNS record creation. You can add them manually."
15018
- );
16071
+ progress.fail("Failed to create some DNS records");
16072
+ if (result.errors) {
16073
+ for (const error of result.errors) {
16074
+ clack17.log.warn(error);
16075
+ }
16076
+ }
15019
16077
  }
15020
- } catch (error) {
15021
- progress.stop();
15022
- clack17.log.warn(`Could not manage DNS records: ${error.message}`);
16078
+ }
16079
+ } else {
16080
+ clack17.log.warn(
16081
+ credentialResult2.error || "Could not validate credentials"
16082
+ );
16083
+ if (selectedProvider === "vercel" || selectedProvider === "cloudflare") {
16084
+ clack17.log.info(
16085
+ `Set the ${selectedProvider === "vercel" ? "VERCEL_TOKEN" : "CLOUDFLARE_API_TOKEN"} environment variable to enable automatic DNS management.`
16086
+ );
16087
+ clack17.log.info(
16088
+ `You can create a token at: ${pc18.cyan(getDNSProviderTokenUrl2(selectedProvider))}`
16089
+ );
16090
+ }
16091
+ const continueManual = await promptContinueManualDNS2();
16092
+ if (continueManual) {
16093
+ dnsProvider = "manual";
15023
16094
  }
15024
16095
  }
15025
16096
  }
16097
+ if (dnsProvider && metadata.services.email) {
16098
+ metadata.services.email.dnsProvider = dnsProvider;
16099
+ await saveConnectionMetadata(metadata);
16100
+ }
15026
16101
  }
15027
16102
  const dnsRecords = [];
15028
16103
  if (outputs.domain && outputs.dkimTokens && outputs.dkimTokens.length > 0 && !dnsAutoCreated) {
@@ -15380,6 +16455,8 @@ import * as clack20 from "@clack/prompts";
15380
16455
  import * as pulumi18 from "@pulumi/pulumi";
15381
16456
  import pc21 from "picocolors";
15382
16457
  init_events();
16458
+ init_create_records();
16459
+ init_credentials();
15383
16460
  init_costs();
15384
16461
  init_presets();
15385
16462
  init_aws();
@@ -15820,35 +16897,54 @@ ${pc21.bold("Current Configuration:")}
15820
16897
  "This ensures all tracking links use secure HTTPS connections."
15821
16898
  )
15822
16899
  );
15823
- const { findHostedZone: findHostedZone2 } = await Promise.resolve().then(() => (init_route53(), route53_exports));
15824
- const hostedZone = await progress.execute(
15825
- "Checking for Route53 hosted zone",
15826
- async () => await findHostedZone2(trackingDomain || config2.domain, region)
15827
- );
15828
- if (hostedZone) {
15829
- progress.info(
15830
- `Found Route53 hosted zone: ${pc21.cyan(hostedZone.name)} ${pc21.green("\u2713")}`
15831
- );
15832
- clack20.log.info(
15833
- pc21.dim(
15834
- "DNS records (SSL certificate validation + CloudFront) will be created automatically."
15835
- )
15836
- );
16900
+ let trackingDnsProvider = metadata.services.email?.dnsProvider;
16901
+ let canAutomateDNS = false;
16902
+ if (trackingDnsProvider) {
16903
+ canAutomateDNS = trackingDnsProvider !== "manual";
16904
+ if (canAutomateDNS) {
16905
+ progress.info(
16906
+ `Will use ${pc21.cyan(getDNSProviderDisplayName(trackingDnsProvider))} for DNS records ${pc21.green("\u2713")}`
16907
+ );
16908
+ }
15837
16909
  } else {
15838
- clack20.log.warn(
15839
- `No Route53 hosted zone found for ${pc21.cyan(trackingDomain || config2.domain)}`
15840
- );
15841
- clack20.log.info(
15842
- pc21.dim(
15843
- "You'll need to manually create DNS records for SSL certificate validation and CloudFront."
16910
+ const availableProviders = await progress.execute(
16911
+ "Detecting available DNS providers",
16912
+ async () => await detectAvailableDNSProviders(
16913
+ trackingDomain || config2.domain,
16914
+ region
15844
16915
  )
15845
16916
  );
15846
- clack20.log.info(
15847
- pc21.dim("DNS record details will be shown after deployment.")
16917
+ const detectedProvider = availableProviders.find(
16918
+ (p) => p.detected && p.provider !== "manual"
15848
16919
  );
16920
+ if (detectedProvider) {
16921
+ trackingDnsProvider = detectedProvider.provider;
16922
+ canAutomateDNS = true;
16923
+ progress.info(
16924
+ `Found ${pc21.cyan(getDNSProviderDisplayName(detectedProvider.provider))} ${pc21.green("\u2713")}`
16925
+ );
16926
+ clack20.log.info(
16927
+ pc21.dim(
16928
+ "DNS records (SSL certificate validation + CloudFront) will be created automatically."
16929
+ )
16930
+ );
16931
+ } else {
16932
+ canAutomateDNS = false;
16933
+ clack20.log.warn(
16934
+ `No automatic DNS provider detected for ${pc21.cyan(trackingDomain || config2.domain)}`
16935
+ );
16936
+ clack20.log.info(
16937
+ pc21.dim(
16938
+ "You'll need to manually create DNS records for SSL certificate validation and CloudFront."
16939
+ )
16940
+ );
16941
+ clack20.log.info(
16942
+ pc21.dim("DNS record details will be shown after deployment.")
16943
+ );
16944
+ }
15849
16945
  }
15850
16946
  const confirmHttps = await clack20.confirm({
15851
- message: hostedZone ? "Proceed with automatic HTTPS setup?" : "Proceed with manual HTTPS setup (requires DNS configuration)?",
16947
+ message: canAutomateDNS ? "Proceed with automatic HTTPS setup?" : "Proceed with manual HTTPS setup (requires DNS configuration)?",
15852
16948
  initialValue: true
15853
16949
  });
15854
16950
  if (clack20.isCancel(confirmHttps) || !confirmHttps) {
@@ -16660,31 +17756,103 @@ ${pc21.bold("Cost Impact:")}`);
16660
17756
  trackError("UPGRADE_FAILED", "email:upgrade", { step: "deploy" });
16661
17757
  throw new Error(`Pulumi upgrade failed: ${error.message}`);
16662
17758
  }
17759
+ let dnsAutoCreated = false;
16663
17760
  if (outputs.domain && outputs.dkimTokens && outputs.dkimTokens.length > 0) {
16664
- const { findHostedZone: findHostedZone2, createDNSRecords: createDNSRecords2 } = await Promise.resolve().then(() => (init_route53(), route53_exports));
16665
- const hostedZone = await findHostedZone2(outputs.domain, region);
16666
- if (hostedZone) {
16667
- try {
16668
- progress.start("Creating DNS records in Route53");
16669
- const mailFromDomain = updatedConfig.mailFromDomain || `mail.${outputs.domain}`;
16670
- await createDNSRecords2(
16671
- hostedZone.id,
17761
+ let dnsProvider = metadata.services.email?.dnsProvider;
17762
+ if (!dnsProvider) {
17763
+ const availableProviders = await progress.execute(
17764
+ "Detecting available DNS providers",
17765
+ async () => await detectAvailableDNSProviders(outputs.domain, region)
17766
+ );
17767
+ const detectedProvider = availableProviders.find(
17768
+ (p) => p.detected && p.provider !== "manual"
17769
+ );
17770
+ if (detectedProvider) {
17771
+ dnsProvider = await promptDNSProvider(
16672
17772
  outputs.domain,
16673
- outputs.dkimTokens,
16674
- region,
16675
- outputs.customTrackingDomain,
16676
- mailFromDomain,
16677
- outputs.cloudFrontDomain
16678
- );
16679
- progress.succeed("DNS records created in Route53");
16680
- } catch (error) {
16681
- progress.fail(
16682
- `Failed to create DNS records automatically: ${error.message}`
17773
+ availableProviders
16683
17774
  );
16684
- progress.info(
16685
- "You can manually add the required DNS records shown below"
17775
+ if (dnsProvider && dnsProvider !== "manual" && metadata.services.email) {
17776
+ metadata.services.email.dnsProvider = dnsProvider;
17777
+ }
17778
+ }
17779
+ }
17780
+ if (dnsProvider && dnsProvider !== "manual") {
17781
+ const credResult = await progress.execute(
17782
+ `Validating ${getDNSProviderDisplayName(dnsProvider)} credentials`,
17783
+ async () => await getDNSCredentials(dnsProvider, outputs.domain, region)
17784
+ );
17785
+ if (credResult.valid && credResult.credentials) {
17786
+ const mailFromDomain = updatedConfig.mailFromDomain || `mail.${outputs.domain}`;
17787
+ const dnsData = {
17788
+ domain: outputs.domain,
17789
+ dkimTokens: outputs.dkimTokens,
17790
+ mailFromDomain,
17791
+ region
17792
+ };
17793
+ try {
17794
+ progress.start(
17795
+ `Creating DNS records in ${getDNSProviderDisplayName(dnsProvider)}`
17796
+ );
17797
+ const result = await createDNSRecordsForProvider(
17798
+ credResult.credentials,
17799
+ dnsData
17800
+ );
17801
+ if (result.success) {
17802
+ progress.succeed(
17803
+ `Created ${result.recordsCreated} DNS records in ${getDNSProviderDisplayName(dnsProvider)}`
17804
+ );
17805
+ dnsAutoCreated = true;
17806
+ } else {
17807
+ progress.fail(
17808
+ `Failed to create some DNS records: ${result.errors?.join(", ")}`
17809
+ );
17810
+ progress.info(
17811
+ "You can manually add the required DNS records shown below"
17812
+ );
17813
+ }
17814
+ } catch (error) {
17815
+ progress.fail(
17816
+ `Failed to create DNS records automatically: ${error.message}`
17817
+ );
17818
+ progress.info(
17819
+ "You can manually add the required DNS records shown below"
17820
+ );
17821
+ }
17822
+ } else {
17823
+ clack20.log.warn(
17824
+ credResult.error || `Unable to validate ${getDNSProviderDisplayName(dnsProvider)} credentials`
16686
17825
  );
17826
+ if (dnsProvider === "vercel" || dnsProvider === "cloudflare") {
17827
+ clack20.log.info(
17828
+ `Set ${dnsProvider === "vercel" ? "VERCEL_TOKEN" : "CLOUDFLARE_API_TOKEN"} to enable automatic DNS management.`
17829
+ );
17830
+ clack20.log.info(
17831
+ `You can create a token at: ${pc21.cyan(getDNSProviderTokenUrl(dnsProvider))}`
17832
+ );
17833
+ }
17834
+ }
17835
+ }
17836
+ if (!dnsAutoCreated) {
17837
+ const mailFromDomain = updatedConfig.mailFromDomain || `mail.${outputs.domain}`;
17838
+ const dnsData = {
17839
+ domain: outputs.domain,
17840
+ dkimTokens: outputs.dkimTokens,
17841
+ mailFromDomain,
17842
+ region
17843
+ };
17844
+ const dnsRecords = buildEmailDNSRecords(dnsData);
17845
+ const displayRecords = formatDNSRecordsForDisplay(dnsRecords);
17846
+ console.log(
17847
+ `
17848
+ ${pc21.bold("Add these DNS records to your DNS provider:")}
17849
+ `
17850
+ );
17851
+ for (const record of displayRecords) {
17852
+ console.log(` ${pc21.cyan(record.type)} ${record.name}`);
17853
+ console.log(` ${pc21.dim(record.value)}`);
16687
17854
  }
17855
+ console.log("");
16688
17856
  }
16689
17857
  }
16690
17858
  updateEmailConfig(metadata, updatedConfig);
@@ -17257,127 +18425,578 @@ ${pc23.dim("Service:")} ${pc23.cyan(serviceLabel)}`);
17257
18425
  ` ${pc23.green("+")} ${pc23.bold("SQS")} - Event queuing (Production+)`
17258
18426
  );
17259
18427
  console.log(
17260
- ` ${pc23.green("+")} ${pc23.bold("Lambda")} - Event processing (Production+)`
18428
+ ` ${pc23.green("+")} ${pc23.bold("Lambda")} - Event processing (Production+)`
18429
+ );
18430
+ console.log(
18431
+ ` ${pc23.green("+")} ${pc23.bold("DynamoDB")} - Email history (Production+)`
18432
+ );
18433
+ }
18434
+ console.log(
18435
+ ` ${pc23.yellow("?")} ${pc23.bold("Route53")} - Auto DNS ${pc23.dim("(optional)")}`
18436
+ );
18437
+ console.log(
18438
+ ` ${pc23.yellow("?")} ${pc23.bold("IAM OIDC")} - Vercel integration ${pc23.dim("(if using Vercel)")}`
18439
+ );
18440
+ }
18441
+ if (!service || service === "sms") {
18442
+ console.log(
18443
+ ` ${pc23.green("+")} ${pc23.bold("SMS Voice")} - SMS sending & management`
18444
+ );
18445
+ console.log(` ${pc23.green("+")} ${pc23.bold("DynamoDB")} - Message history`);
18446
+ console.log(` ${pc23.green("+")} ${pc23.bold("Lambda")} - Event processing`);
18447
+ }
18448
+ if (!service || service === "cdn") {
18449
+ console.log(` ${pc23.green("+")} ${pc23.bold("S3")} - Asset storage`);
18450
+ console.log(
18451
+ ` ${pc23.green("+")} ${pc23.bold("CloudFront")} - CDN distribution`
18452
+ );
18453
+ console.log(` ${pc23.green("+")} ${pc23.bold("ACM")} - SSL certificates`);
18454
+ console.log(
18455
+ ` ${pc23.yellow("?")} ${pc23.bold("Route53")} - DNS management ${pc23.dim("(optional)")}`
18456
+ );
18457
+ }
18458
+ console.log(`
18459
+ ${pc23.dim("Get full IAM policy JSON:")}`);
18460
+ console.log(` ${pc23.cyan("wraps permissions --json")}`);
18461
+ if (service) {
18462
+ console.log(`
18463
+ ${pc23.dim("Get permissions for all services:")}`);
18464
+ console.log(` ${pc23.cyan("wraps permissions")}`);
18465
+ }
18466
+ console.log(`
18467
+ ${pc23.dim("Documentation:")}`);
18468
+ console.log(
18469
+ ` ${pc23.blue("https://wraps.dev/docs/guides/aws-setup/permissions")}
18470
+ `
18471
+ );
18472
+ }
18473
+ async function permissions(options) {
18474
+ const startTime = Date.now();
18475
+ trackCommand("permissions", {
18476
+ json: options.json,
18477
+ preset: options.preset,
18478
+ service: options.service
18479
+ });
18480
+ const policy = buildPolicy(options.service, options.preset);
18481
+ if (options.json) {
18482
+ console.log(JSON.stringify(policy, null, 2));
18483
+ } else {
18484
+ displaySummary(options.service, options.preset);
18485
+ console.log(pc23.bold("Quick Setup:\n"));
18486
+ console.log("1. Copy the IAM policy:");
18487
+ console.log(
18488
+ ` ${pc23.cyan("wraps permissions --json > wraps-policy.json")}
18489
+ `
18490
+ );
18491
+ console.log("2. Create the policy in AWS Console:");
18492
+ console.log(" IAM > Policies > Create Policy > JSON\n");
18493
+ console.log("3. Attach to your IAM user/role\n");
18494
+ clack22.outro(
18495
+ pc23.green("Run with --json to get the full IAM policy document")
18496
+ );
18497
+ }
18498
+ trackCommand("permissions", {
18499
+ success: true,
18500
+ duration_ms: Date.now() - startTime
18501
+ });
18502
+ }
18503
+
18504
+ // src/commands/platform/connect.ts
18505
+ init_esm_shims();
18506
+ import {
18507
+ GetRoleCommand,
18508
+ IAMClient as IAMClient2,
18509
+ PutRolePolicyCommand
18510
+ } from "@aws-sdk/client-iam";
18511
+ import { confirm as confirm11, intro as intro21, isCancel as isCancel16, log as log21, outro as outro18, select as select12 } from "@clack/prompts";
18512
+ import * as pulumi19 from "@pulumi/pulumi";
18513
+ import pc24 from "picocolors";
18514
+ init_events();
18515
+ init_aws();
18516
+ init_fs();
18517
+ init_metadata();
18518
+ init_prompts();
18519
+ function buildConsolePolicyDocument(emailConfig, smsConfig) {
18520
+ const statements = [];
18521
+ statements.push({
18522
+ Effect: "Allow",
18523
+ Action: [
18524
+ "ses:GetAccount",
18525
+ "ses:GetSendStatistics",
18526
+ "ses:ListIdentities",
18527
+ "ses:GetIdentityVerificationAttributes",
18528
+ "ses:ListEmailIdentities",
18529
+ "ses:GetEmailIdentity",
18530
+ "ses:GetConfigurationSet",
18531
+ "ses:GetConfigurationSetEventDestinations",
18532
+ "cloudwatch:GetMetricData",
18533
+ "cloudwatch:GetMetricStatistics"
18534
+ ],
18535
+ Resource: "*"
18536
+ });
18537
+ statements.push({
18538
+ Effect: "Allow",
18539
+ Action: [
18540
+ "ses:GetTemplate",
18541
+ "ses:ListTemplates",
18542
+ "ses:CreateTemplate",
18543
+ "ses:UpdateTemplate",
18544
+ "ses:DeleteTemplate",
18545
+ "ses:TestRenderTemplate"
18546
+ ],
18547
+ Resource: "*"
18548
+ });
18549
+ const sendingEnabled = !emailConfig || emailConfig.sendingEnabled !== false;
18550
+ if (sendingEnabled) {
18551
+ statements.push({
18552
+ Effect: "Allow",
18553
+ Action: [
18554
+ "ses:SendEmail",
18555
+ "ses:SendRawEmail",
18556
+ "ses:SendTemplatedEmail",
18557
+ "ses:SendBulkTemplatedEmail",
18558
+ "ses:SendBulkEmail"
18559
+ ],
18560
+ Resource: "*"
18561
+ });
18562
+ }
18563
+ const eventTracking = emailConfig?.eventTracking;
18564
+ if (eventTracking?.dynamoDBHistory) {
18565
+ statements.push({
18566
+ Effect: "Allow",
18567
+ Action: [
18568
+ "dynamodb:PutItem",
18569
+ "dynamodb:GetItem",
18570
+ "dynamodb:Query",
18571
+ "dynamodb:Scan",
18572
+ "dynamodb:BatchGetItem",
18573
+ "dynamodb:DescribeTable"
18574
+ ],
18575
+ Resource: [
18576
+ "arn:aws:dynamodb:*:*:table/wraps-email-*",
18577
+ "arn:aws:dynamodb:*:*:table/wraps-email-*/index/*"
18578
+ ]
18579
+ });
18580
+ }
18581
+ if (eventTracking?.enabled) {
18582
+ statements.push({
18583
+ Effect: "Allow",
18584
+ Action: ["events:PutEvents", "events:DescribeEventBus"],
18585
+ Resource: "arn:aws:events:*:*:event-bus/wraps-email-*"
18586
+ });
18587
+ }
18588
+ if (eventTracking?.enabled) {
18589
+ statements.push({
18590
+ Effect: "Allow",
18591
+ Action: [
18592
+ "sqs:SendMessage",
18593
+ "sqs:ReceiveMessage",
18594
+ "sqs:DeleteMessage",
18595
+ "sqs:GetQueueAttributes"
18596
+ ],
18597
+ Resource: "arn:aws:sqs:*:*:wraps-email-*"
18598
+ });
18599
+ }
18600
+ const emailArchiving = emailConfig?.emailArchiving;
18601
+ if (emailArchiving?.enabled) {
18602
+ statements.push({
18603
+ Effect: "Allow",
18604
+ Action: [
18605
+ "ses:StartArchiveSearch",
18606
+ "ses:GetArchiveSearchResults",
18607
+ "ses:GetArchiveMessage",
18608
+ "ses:GetArchiveMessageContent",
18609
+ "ses:GetArchive",
18610
+ "ses:ListArchives",
18611
+ "ses:StartArchiveExport",
18612
+ "ses:GetArchiveExport"
18613
+ ],
18614
+ Resource: "arn:aws:ses:*:*:mailmanager-archive/*"
18615
+ });
18616
+ }
18617
+ if (smsConfig) {
18618
+ statements.push({
18619
+ Effect: "Allow",
18620
+ Action: [
18621
+ "sms-voice:DescribeAccountAttributes",
18622
+ "sms-voice:DescribeSpendLimits",
18623
+ "sms-voice:DescribeConfigurationSets",
18624
+ "sms-voice:DescribeOptOutLists",
18625
+ "sms-voice:DescribeOptedOutNumbers",
18626
+ "sms-voice:DescribePools",
18627
+ "sms-voice:DescribePhoneNumbers",
18628
+ "sms-voice:DescribeProtectConfigurations",
18629
+ "sms-voice:DescribeRegistrations",
18630
+ "sms-voice:DescribeRegistrationAttachments",
18631
+ "sms-voice:DescribeRegistrationFieldDefinitions",
18632
+ "sms-voice:DescribeRegistrationFieldValues",
18633
+ "sms-voice:DescribeRegistrationSectionDefinitions",
18634
+ "sms-voice:DescribeRegistrationVersions"
18635
+ ],
18636
+ Resource: "*"
18637
+ });
18638
+ const smsSendingEnabled = smsConfig.sendingEnabled !== false;
18639
+ if (smsSendingEnabled) {
18640
+ statements.push({
18641
+ Effect: "Allow",
18642
+ Action: ["sms-voice:SendTextMessage", "sms-voice:SendMediaMessage"],
18643
+ Resource: "*"
18644
+ });
18645
+ }
18646
+ const smsEventTracking = smsConfig.eventTracking;
18647
+ if (smsEventTracking?.dynamoDBHistory) {
18648
+ statements.push({
18649
+ Effect: "Allow",
18650
+ Action: [
18651
+ "dynamodb:GetItem",
18652
+ "dynamodb:Query",
18653
+ "dynamodb:Scan",
18654
+ "dynamodb:BatchGetItem",
18655
+ "dynamodb:DescribeTable"
18656
+ ],
18657
+ Resource: [
18658
+ "arn:aws:dynamodb:*:*:table/wraps-sms-*",
18659
+ "arn:aws:dynamodb:*:*:table/wraps-sms-*/index/*"
18660
+ ]
18661
+ });
18662
+ }
18663
+ if (smsEventTracking?.enabled) {
18664
+ statements.push({
18665
+ Effect: "Allow",
18666
+ Action: ["sns:GetTopicAttributes", "sns:ListSubscriptionsByTopic"],
18667
+ Resource: "arn:aws:sns:*:*:wraps-sms-*"
18668
+ });
18669
+ }
18670
+ }
18671
+ return {
18672
+ Version: "2012-10-17",
18673
+ Statement: statements
18674
+ };
18675
+ }
18676
+ async function connect3(options) {
18677
+ const startTime = Date.now();
18678
+ intro21(pc24.bold("Connect to Wraps Platform"));
18679
+ const progress = new DeploymentProgress();
18680
+ try {
18681
+ const wasAutoInstalled = await progress.execute(
18682
+ "Checking Pulumi CLI installation",
18683
+ async () => await ensurePulumiInstalled()
18684
+ );
18685
+ if (wasAutoInstalled) {
18686
+ progress.info("Pulumi CLI was automatically installed");
18687
+ }
18688
+ const identity = await progress.execute(
18689
+ "Validating AWS credentials",
18690
+ async () => validateAWSCredentials()
18691
+ );
18692
+ progress.info(`Connected to AWS account: ${pc24.cyan(identity.accountId)}`);
18693
+ let region = options.region;
18694
+ if (!region) {
18695
+ region = await getAWSRegion();
18696
+ }
18697
+ const metadata = await loadConnectionMetadata(identity.accountId, region);
18698
+ if (!metadata) {
18699
+ progress.stop();
18700
+ log21.error(
18701
+ `No Wraps deployment found for account ${pc24.cyan(identity.accountId)} in region ${pc24.cyan(region)}`
18702
+ );
18703
+ console.log(
18704
+ `
18705
+ Run ${pc24.cyan("wraps email init")} to deploy infrastructure first.
18706
+ `
17261
18707
  );
18708
+ process.exit(1);
18709
+ }
18710
+ const hasEmail = !!metadata.services.email?.config;
18711
+ const hasSms = !!metadata.services.sms?.config;
18712
+ if (!(hasEmail || hasSms)) {
18713
+ progress.stop();
18714
+ log21.error("No services deployed in this region.");
17262
18715
  console.log(
17263
- ` ${pc23.green("+")} ${pc23.bold("DynamoDB")} - Email history (Production+)`
18716
+ `
18717
+ Run ${pc24.cyan("wraps email init")} or ${pc24.cyan("wraps sms init")} first.
18718
+ `
17264
18719
  );
18720
+ process.exit(1);
18721
+ }
18722
+ progress.info(
18723
+ `Found services: ${[hasEmail && "email", hasSms && "sms"].filter(Boolean).join(", ")}`
18724
+ );
18725
+ let webhookSecret;
18726
+ let needsDeployment = false;
18727
+ if (hasEmail) {
18728
+ const emailConfig = metadata.services.email.config;
18729
+ const existingSecret = metadata.services.email.webhookSecret;
18730
+ if (!emailConfig.eventTracking?.enabled) {
18731
+ progress.stop();
18732
+ log21.warn(
18733
+ "Event tracking must be enabled to connect to the Wraps Platform."
18734
+ );
18735
+ log21.info(
18736
+ "Enabling event tracking will allow SES events to be streamed to the dashboard."
18737
+ );
18738
+ const enableEventTracking = await confirm11({
18739
+ message: "Enable event tracking now?",
18740
+ initialValue: true
18741
+ });
18742
+ if (isCancel16(enableEventTracking) || !enableEventTracking) {
18743
+ outro18("Platform connection cancelled.");
18744
+ process.exit(0);
18745
+ }
18746
+ metadata.services.email.config = {
18747
+ ...emailConfig,
18748
+ eventTracking: {
18749
+ enabled: true,
18750
+ eventBridge: true,
18751
+ events: [
18752
+ "SEND",
18753
+ "DELIVERY",
18754
+ "OPEN",
18755
+ "CLICK",
18756
+ "BOUNCE",
18757
+ "COMPLAINT"
18758
+ ],
18759
+ dynamoDBHistory: emailConfig.eventTracking?.dynamoDBHistory ?? false,
18760
+ archiveRetention: emailConfig.eventTracking?.archiveRetention ?? "90days"
18761
+ }
18762
+ };
18763
+ needsDeployment = true;
18764
+ }
18765
+ if (existingSecret) {
18766
+ progress.stop();
18767
+ log21.info(
18768
+ `Already connected to Wraps Platform (AWS Account: ${pc24.cyan(metadata.accountId)})`
18769
+ );
18770
+ const action = await select12({
18771
+ message: "What would you like to do?",
18772
+ options: [
18773
+ {
18774
+ value: "keep",
18775
+ label: "Keep current connection",
18776
+ hint: "Continue with existing webhook secret"
18777
+ },
18778
+ {
18779
+ value: "regenerate",
18780
+ label: "Regenerate webhook secret",
18781
+ hint: "Create new secret (requires update in dashboard)"
18782
+ },
18783
+ {
18784
+ value: "disconnect",
18785
+ label: "Disconnect from platform",
18786
+ hint: "Stop sending events to Wraps"
18787
+ }
18788
+ ]
18789
+ });
18790
+ if (isCancel16(action)) {
18791
+ outro18("Operation cancelled");
18792
+ process.exit(0);
18793
+ }
18794
+ if (action === "keep") {
18795
+ webhookSecret = existingSecret;
18796
+ } else if (action === "disconnect") {
18797
+ const confirmDisconnect = await confirm11({
18798
+ message: "Are you sure? Events will no longer be sent to the Wraps Platform.",
18799
+ initialValue: false
18800
+ });
18801
+ if (isCancel16(confirmDisconnect) || !confirmDisconnect) {
18802
+ outro18("Disconnect cancelled");
18803
+ process.exit(0);
18804
+ }
18805
+ metadata.services.email.webhookSecret = void 0;
18806
+ needsDeployment = true;
18807
+ webhookSecret = void 0;
18808
+ } else {
18809
+ webhookSecret = generateWebhookSecret();
18810
+ metadata.services.email.webhookSecret = webhookSecret;
18811
+ needsDeployment = true;
18812
+ }
18813
+ } else {
18814
+ webhookSecret = generateWebhookSecret();
18815
+ metadata.services.email.webhookSecret = webhookSecret;
18816
+ needsDeployment = true;
18817
+ }
18818
+ }
18819
+ if (needsDeployment && hasEmail) {
18820
+ let vercelConfig;
18821
+ if (metadata.provider === "vercel" && !metadata.vercel) {
18822
+ progress.stop();
18823
+ vercelConfig = await promptVercelConfig();
18824
+ } else if (metadata.provider === "vercel") {
18825
+ vercelConfig = metadata.vercel;
18826
+ }
18827
+ const stackConfig = {
18828
+ provider: metadata.provider,
18829
+ region,
18830
+ vercel: vercelConfig,
18831
+ emailConfig: metadata.services.email.config,
18832
+ webhook: webhookSecret ? {
18833
+ awsAccountNumber: metadata.accountId,
18834
+ webhookSecret
18835
+ } : void 0
18836
+ };
18837
+ await progress.execute("Configuring event streaming", async () => {
18838
+ await ensurePulumiWorkDir();
18839
+ const stack = await pulumi19.automation.LocalWorkspace.createOrSelectStack(
18840
+ {
18841
+ stackName: metadata.services.email?.pulumiStackName || `wraps-${identity.accountId}-${region}`,
18842
+ projectName: "wraps-email",
18843
+ program: async () => {
18844
+ const result = await deployEmailStack(stackConfig);
18845
+ return {
18846
+ roleArn: result.roleArn,
18847
+ configSetName: result.configSetName,
18848
+ tableName: result.tableName,
18849
+ region: result.region
18850
+ };
18851
+ }
18852
+ },
18853
+ {
18854
+ workDir: getPulumiWorkDir(),
18855
+ envVars: {
18856
+ PULUMI_CONFIG_PASSPHRASE: "",
18857
+ AWS_REGION: region
18858
+ },
18859
+ secretsProvider: "passphrase"
18860
+ }
18861
+ );
18862
+ await stack.setConfig("aws:region", { value: region });
18863
+ await stack.refresh({ onOutput: () => {
18864
+ } });
18865
+ await stack.up({ onOutput: () => {
18866
+ } });
18867
+ });
18868
+ progress.succeed("Event streaming configured");
18869
+ } else if (!needsDeployment && hasEmail && webhookSecret) {
18870
+ progress.succeed("Event streaming already configured");
18871
+ }
18872
+ const roleName = "wraps-console-access-role";
18873
+ const iam8 = new IAMClient2({ region: "us-east-1" });
18874
+ let roleExists4 = false;
18875
+ try {
18876
+ await iam8.send(new GetRoleCommand({ RoleName: roleName }));
18877
+ roleExists4 = true;
18878
+ } catch (error) {
18879
+ if (error && typeof error === "object" && "name" in error && error.name !== "NoSuchEntity") {
18880
+ throw error;
18881
+ }
18882
+ }
18883
+ if (roleExists4) {
18884
+ const emailConfig = metadata.services.email?.config;
18885
+ const smsConfig = metadata.services.sms?.config;
18886
+ const policy = buildConsolePolicyDocument(emailConfig, smsConfig);
18887
+ await progress.execute("Updating platform access role", async () => {
18888
+ await iam8.send(
18889
+ new PutRolePolicyCommand({
18890
+ RoleName: roleName,
18891
+ PolicyName: "wraps-console-access-policy",
18892
+ PolicyDocument: JSON.stringify(policy, null, 2)
18893
+ })
18894
+ );
18895
+ });
18896
+ progress.succeed("Platform access role updated");
18897
+ } else {
18898
+ progress.info(
18899
+ `IAM role ${pc24.cyan(roleName)} will be created when you add your AWS account in the dashboard`
18900
+ );
18901
+ }
18902
+ await saveConnectionMetadata(metadata);
18903
+ progress.stop();
18904
+ outro18(pc24.green("Platform connection complete!"));
18905
+ if (webhookSecret && needsDeployment) {
18906
+ console.log(`
18907
+ ${pc24.bold("Webhook Secret")} ${pc24.dim("(save this!)")}`);
18908
+ console.log(pc24.dim("\u2500".repeat(60)));
18909
+ console.log(` ${pc24.cyan(webhookSecret)}`);
18910
+ console.log(pc24.dim("\u2500".repeat(60)));
18911
+ } else if (metadata.services.email?.webhookSecret && !needsDeployment) {
18912
+ console.log(`
18913
+ ${pc24.bold("Existing Webhook Secret:")}`);
18914
+ console.log(pc24.dim("\u2500".repeat(60)));
18915
+ console.log(` ${pc24.cyan(metadata.services.email.webhookSecret)}`);
18916
+ console.log(pc24.dim("\u2500".repeat(60)));
17265
18917
  }
17266
- console.log(
17267
- ` ${pc23.yellow("?")} ${pc23.bold("Route53")} - Auto DNS ${pc23.dim("(optional)")}`
17268
- );
17269
- console.log(
17270
- ` ${pc23.yellow("?")} ${pc23.bold("IAM OIDC")} - Vercel integration ${pc23.dim("(if using Vercel)")}`
17271
- );
17272
- }
17273
- if (!service || service === "sms") {
17274
- console.log(
17275
- ` ${pc23.green("+")} ${pc23.bold("SMS Voice")} - SMS sending & management`
17276
- );
17277
- console.log(` ${pc23.green("+")} ${pc23.bold("DynamoDB")} - Message history`);
17278
- console.log(` ${pc23.green("+")} ${pc23.bold("Lambda")} - Event processing`);
17279
- }
17280
- if (!service || service === "cdn") {
17281
- console.log(` ${pc23.green("+")} ${pc23.bold("S3")} - Asset storage`);
17282
- console.log(
17283
- ` ${pc23.green("+")} ${pc23.bold("CloudFront")} - CDN distribution`
17284
- );
17285
- console.log(` ${pc23.green("+")} ${pc23.bold("ACM")} - SSL certificates`);
17286
- console.log(
17287
- ` ${pc23.yellow("?")} ${pc23.bold("Route53")} - DNS management ${pc23.dim("(optional)")}`
17288
- );
17289
- }
17290
- console.log(`
17291
- ${pc23.dim("Get full IAM policy JSON:")}`);
17292
- console.log(` ${pc23.cyan("wraps permissions --json")}`);
17293
- if (service) {
17294
18918
  console.log(`
17295
- ${pc23.dim("Get permissions for all services:")}`);
17296
- console.log(` ${pc23.cyan("wraps permissions")}`);
17297
- }
17298
- console.log(`
17299
- ${pc23.dim("Documentation:")}`);
17300
- console.log(
17301
- ` ${pc23.blue("https://wraps.dev/docs/guides/aws-setup/permissions")}
17302
- `
17303
- );
17304
- }
17305
- async function permissions(options) {
17306
- const startTime = Date.now();
17307
- trackCommand("permissions", {
17308
- json: options.json,
17309
- preset: options.preset,
17310
- service: options.service
17311
- });
17312
- const policy = buildPolicy(options.service, options.preset);
17313
- if (options.json) {
17314
- console.log(JSON.stringify(policy, null, 2));
17315
- } else {
17316
- displaySummary(options.service, options.preset);
17317
- console.log(pc23.bold("Quick Setup:\n"));
17318
- console.log("1. Copy the IAM policy:");
18919
+ ${pc24.bold("Next Steps:")}`);
18920
+ console.log(` 1. Go to ${pc24.cyan("https://app.wraps.dev/settings/aws")}`);
18921
+ console.log(` 2. Add your AWS account: ${pc24.cyan(identity.accountId)}`);
18922
+ if (webhookSecret) {
18923
+ console.log(" 3. Paste the webhook secret shown above");
18924
+ }
18925
+ console.log();
17319
18926
  console.log(
17320
- ` ${pc23.cyan("wraps permissions --json > wraps-policy.json")}
17321
- `
17322
- );
17323
- console.log("2. Create the policy in AWS Console:");
17324
- console.log(" IAM > Policies > Create Policy > JSON\n");
17325
- console.log("3. Attach to your IAM user/role\n");
17326
- clack22.outro(
17327
- pc23.green("Run with --json to get the full IAM policy document")
18927
+ pc24.dim(
18928
+ "Events from your AWS infrastructure will stream to the dashboard."
18929
+ )
17328
18930
  );
18931
+ console.log();
18932
+ const duration = Date.now() - startTime;
18933
+ trackCommand("platform:connect", {
18934
+ success: true,
18935
+ duration_ms: duration
18936
+ });
18937
+ } catch (error) {
18938
+ progress.stop();
18939
+ const duration = Date.now() - startTime;
18940
+ const errorCode = error instanceof Error ? error.name : "UNKNOWN_ERROR";
18941
+ trackError(errorCode, "platform:connect", {
18942
+ message: error instanceof Error ? error.message : String(error)
18943
+ });
18944
+ trackCommand("platform:connect", {
18945
+ success: false,
18946
+ duration_ms: duration
18947
+ });
18948
+ throw error;
17329
18949
  }
17330
- trackCommand("permissions", {
17331
- success: true,
17332
- duration_ms: Date.now() - startTime
17333
- });
17334
18950
  }
17335
18951
 
17336
18952
  // src/commands/platform/index.ts
17337
18953
  init_esm_shims();
17338
18954
  import * as clack23 from "@clack/prompts";
17339
- import pc24 from "picocolors";
18955
+ import pc25 from "picocolors";
17340
18956
  async function platform() {
17341
- clack23.intro(pc24.bold("Wraps Platform"));
18957
+ clack23.intro(pc25.bold("Wraps Platform"));
17342
18958
  console.log();
17343
18959
  console.log(
17344
18960
  " The Wraps Platform extends the free CLI with hosted features:"
17345
18961
  );
17346
18962
  console.log();
17347
- console.log(` ${pc24.bold("Features:")}`);
17348
- console.log(` ${pc24.green("\u2713")} Visual email template editor`);
17349
- console.log(` ${pc24.green("\u2713")} Broadcast campaigns & scheduling`);
17350
- console.log(` ${pc24.green("\u2713")} Contact management & segments`);
17351
- console.log(` ${pc24.green("\u2713")} Workflow automations`);
17352
- console.log(` ${pc24.green("\u2713")} Analytics dashboard`);
17353
- console.log(` ${pc24.green("\u2713")} Team collaboration`);
18963
+ console.log(` ${pc25.bold("Features:")}`);
18964
+ console.log(` ${pc25.green("\u2713")} Visual email template editor`);
18965
+ console.log(` ${pc25.green("\u2713")} Broadcast campaigns & scheduling`);
18966
+ console.log(` ${pc25.green("\u2713")} Contact management & segments`);
18967
+ console.log(` ${pc25.green("\u2713")} Workflow automations`);
18968
+ console.log(` ${pc25.green("\u2713")} Analytics dashboard`);
18969
+ console.log(` ${pc25.green("\u2713")} Team collaboration`);
17354
18970
  console.log();
17355
- console.log(` ${pc24.bold("Pricing:")}`);
17356
- console.log(` ${pc24.cyan("Starter")} $10/mo 5,000 contacts`);
17357
- console.log(` ${pc24.cyan("Growth")} $25/mo 25,000 contacts`);
17358
- console.log(` ${pc24.cyan("Scale")} $50/mo 100,000 contacts`);
17359
- console.log(` ${pc24.cyan("Enterprise")} Custom Unlimited contacts`);
18971
+ console.log(` ${pc25.bold("Pricing:")}`);
18972
+ console.log(` ${pc25.cyan("Starter")} $10/mo 5,000 contacts`);
18973
+ console.log(` ${pc25.cyan("Growth")} $25/mo 25,000 contacts`);
18974
+ console.log(` ${pc25.cyan("Scale")} $50/mo 100,000 contacts`);
18975
+ console.log(` ${pc25.cyan("Enterprise")} Custom Unlimited contacts`);
17360
18976
  console.log();
17361
18977
  console.log(
17362
- pc24.dim(" + AWS costs at $0.10 per 1,000 emails (paid directly to AWS)")
18978
+ pc25.dim(" + AWS costs at $0.10 per 1,000 emails (paid directly to AWS)")
17363
18979
  );
17364
18980
  console.log();
17365
18981
  console.log(
17366
- ` ${pc24.bold("Learn more:")} ${pc24.cyan("https://wraps.dev/platform")}`
18982
+ ` ${pc25.bold("Learn more:")} ${pc25.cyan("https://wraps.dev/platform")}`
17367
18983
  );
17368
18984
  console.log();
17369
- console.log(pc24.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
18985
+ console.log(pc25.dim("\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
17370
18986
  console.log();
17371
- console.log(` ${pc24.bold("Platform Commands:")}`);
18987
+ console.log(` ${pc25.bold("Platform Commands:")}`);
17372
18988
  console.log();
17373
18989
  console.log(
17374
- ` ${pc24.cyan("wraps platform update-role")} Update IAM permissions for hosted dashboard`
18990
+ ` ${pc25.cyan("wraps platform connect")} Connect infrastructure to Wraps Platform`
18991
+ );
18992
+ console.log(
18993
+ ` ${pc25.cyan("wraps platform update-role")} Update IAM permissions for dashboard`
17375
18994
  );
17376
18995
  console.log();
17377
18996
  console.log(
17378
- pc24.dim(" Run this after deploying new infrastructure to grant the hosted")
18997
+ pc25.dim(" The connect command sets up event streaming and IAM permissions")
17379
18998
  );
17380
- console.log(pc24.dim(" dashboard access to your AWS resources."));
18999
+ console.log(pc25.dim(" in one step. Run it after deploying infrastructure."));
17381
19000
  console.log();
17382
19001
  }
17383
19002
 
@@ -17385,11 +19004,11 @@ async function platform() {
17385
19004
  init_esm_shims();
17386
19005
  init_aws();
17387
19006
  init_metadata();
17388
- import { GetRoleCommand, IAMClient as IAMClient2 } from "@aws-sdk/client-iam";
17389
- import { confirm as confirm11, intro as intro22, isCancel as isCancel16, log as log21, outro as outro18 } from "@clack/prompts";
17390
- import pc25 from "picocolors";
19007
+ import { GetRoleCommand as GetRoleCommand2, IAMClient as IAMClient3 } from "@aws-sdk/client-iam";
19008
+ import { confirm as confirm12, intro as intro23, isCancel as isCancel17, log as log22, outro as outro19 } from "@clack/prompts";
19009
+ import pc26 from "picocolors";
17391
19010
  async function updateRole(options) {
17392
- intro22(pc25.bold("Update Platform Access Role"));
19011
+ intro23(pc26.bold("Update Platform Access Role"));
17393
19012
  const progress = new DeploymentProgress();
17394
19013
  const identity = await progress.execute(
17395
19014
  "Validating AWS credentials",
@@ -17399,21 +19018,21 @@ async function updateRole(options) {
17399
19018
  const metadata = await loadConnectionMetadata(identity.accountId, region);
17400
19019
  if (!metadata) {
17401
19020
  progress.stop();
17402
- log21.error(
17403
- `No Wraps deployment found for account ${pc25.cyan(identity.accountId)} in region ${pc25.cyan(region)}`
19021
+ log22.error(
19022
+ `No Wraps deployment found for account ${pc26.cyan(identity.accountId)} in region ${pc26.cyan(region)}`
17404
19023
  );
17405
19024
  console.log(
17406
19025
  `
17407
- Run ${pc25.cyan("wraps email init")} to deploy infrastructure first.
19026
+ Run ${pc26.cyan("wraps email init")} to deploy infrastructure first.
17408
19027
  `
17409
19028
  );
17410
19029
  process.exit(1);
17411
19030
  }
17412
19031
  const roleName = "wraps-console-access-role";
17413
- const iam8 = new IAMClient2({ region: "us-east-1" });
19032
+ const iam8 = new IAMClient3({ region: "us-east-1" });
17414
19033
  let roleExists4 = false;
17415
19034
  try {
17416
- await iam8.send(new GetRoleCommand({ RoleName: roleName }));
19035
+ await iam8.send(new GetRoleCommand2({ RoleName: roleName }));
17417
19036
  roleExists4 = true;
17418
19037
  } catch (error) {
17419
19038
  if (error && typeof error === "object" && "name" in error && error.name !== "NoSuchEntity") {
@@ -17422,7 +19041,7 @@ Run ${pc25.cyan("wraps email init")} to deploy infrastructure first.
17422
19041
  }
17423
19042
  if (!roleExists4) {
17424
19043
  progress.stop();
17425
- log21.warn(`IAM role ${pc25.cyan(roleName)} does not exist`);
19044
+ log22.warn(`IAM role ${pc26.cyan(roleName)} does not exist`);
17426
19045
  console.log(
17427
19046
  "\nThis role is created when you connect AWS accounts through the Wraps Platform."
17428
19047
  );
@@ -17431,21 +19050,21 @@ Run ${pc25.cyan("wraps email init")} to deploy infrastructure first.
17431
19050
  );
17432
19051
  process.exit(0);
17433
19052
  }
17434
- progress.info(`Found IAM role: ${pc25.cyan(roleName)}`);
19053
+ progress.info(`Found IAM role: ${pc26.cyan(roleName)}`);
17435
19054
  if (!options.force) {
17436
19055
  progress.stop();
17437
- const shouldContinue = await confirm11({
17438
- message: `Update IAM role ${pc25.cyan(roleName)} with latest permissions?`,
19056
+ const shouldContinue = await confirm12({
19057
+ message: `Update IAM role ${pc26.cyan(roleName)} with latest permissions?`,
17439
19058
  initialValue: true
17440
19059
  });
17441
- if (isCancel16(shouldContinue) || !shouldContinue) {
17442
- outro18("Update cancelled");
19060
+ if (isCancel17(shouldContinue) || !shouldContinue) {
19061
+ outro19("Update cancelled");
17443
19062
  process.exit(0);
17444
19063
  }
17445
19064
  }
17446
19065
  const emailConfig = metadata.services.email?.config;
17447
19066
  const smsConfig = metadata.services.sms?.config;
17448
- const policy = buildConsolePolicyDocument(emailConfig, smsConfig);
19067
+ const policy = buildConsolePolicyDocument2(emailConfig, smsConfig);
17449
19068
  const sendingEnabled = !emailConfig || emailConfig.sendingEnabled !== false;
17450
19069
  const eventTracking = emailConfig?.eventTracking;
17451
19070
  const emailArchiving = emailConfig?.emailArchiving;
@@ -17453,9 +19072,9 @@ Run ${pc25.cyan("wraps email init")} to deploy infrastructure first.
17453
19072
  const smsSendingEnabled = smsConfig && smsConfig.sendingEnabled !== false;
17454
19073
  const smsEventTracking = smsConfig?.eventTracking;
17455
19074
  await progress.execute("Updating IAM role permissions", async () => {
17456
- const { PutRolePolicyCommand } = await import("@aws-sdk/client-iam");
19075
+ const { PutRolePolicyCommand: PutRolePolicyCommand2 } = await import("@aws-sdk/client-iam");
17457
19076
  await iam8.send(
17458
- new PutRolePolicyCommand({
19077
+ new PutRolePolicyCommand2({
17459
19078
  RoleName: roleName,
17460
19079
  PolicyName: "wraps-console-access-policy",
17461
19080
  PolicyDocument: JSON.stringify(policy, null, 2)
@@ -17463,52 +19082,52 @@ Run ${pc25.cyan("wraps email init")} to deploy infrastructure first.
17463
19082
  );
17464
19083
  });
17465
19084
  progress.stop();
17466
- outro18(pc25.green("\u2713 Platform access role updated successfully"));
19085
+ outro19(pc26.green("\u2713 Platform access role updated successfully"));
17467
19086
  console.log(`
17468
- ${pc25.bold("Updated Permissions:")}`);
19087
+ ${pc26.bold("Updated Permissions:")}`);
17469
19088
  console.log(`
17470
- ${pc25.bold(pc25.cyan("Email:"))}`);
19089
+ ${pc26.bold(pc26.cyan("Email:"))}`);
17471
19090
  console.log(
17472
- ` ${pc25.green("\u2713")} SES metrics and identity verification (always enabled)`
19091
+ ` ${pc26.green("\u2713")} SES metrics and identity verification (always enabled)`
17473
19092
  );
17474
- console.log(` ${pc25.green("\u2713")} SES template management (always enabled)`);
19093
+ console.log(` ${pc26.green("\u2713")} SES template management (always enabled)`);
17475
19094
  if (sendingEnabled) {
17476
- console.log(` ${pc25.green("\u2713")} Email sending via SES`);
19095
+ console.log(` ${pc26.green("\u2713")} Email sending via SES`);
17477
19096
  }
17478
19097
  if (eventTracking?.dynamoDBHistory) {
17479
19098
  console.log(
17480
- ` ${pc25.green("\u2713")} DynamoDB read access (including DescribeTable)`
19099
+ ` ${pc26.green("\u2713")} DynamoDB read access (including DescribeTable)`
17481
19100
  );
17482
19101
  }
17483
19102
  if (eventTracking?.enabled) {
17484
- console.log(` ${pc25.green("\u2713")} EventBridge and SQS access`);
19103
+ console.log(` ${pc26.green("\u2713")} EventBridge and SQS access`);
17485
19104
  }
17486
19105
  if (emailArchiving?.enabled) {
17487
- console.log(` ${pc25.green("\u2713")} Mail Manager Archive access`);
19106
+ console.log(` ${pc26.green("\u2713")} Mail Manager Archive access`);
17488
19107
  }
17489
19108
  if (smsEnabled) {
17490
19109
  console.log(`
17491
- ${pc25.bold(pc25.cyan("SMS:"))}`);
19110
+ ${pc26.bold(pc26.cyan("SMS:"))}`);
17492
19111
  console.log(
17493
- ` ${pc25.green("\u2713")} SMS Voice V2 read access (phone numbers, config, registrations)`
19112
+ ` ${pc26.green("\u2713")} SMS Voice V2 read access (phone numbers, config, registrations)`
17494
19113
  );
17495
19114
  if (smsSendingEnabled) {
17496
- console.log(` ${pc25.green("\u2713")} SMS sending via SMS Voice V2`);
19115
+ console.log(` ${pc26.green("\u2713")} SMS sending via SMS Voice V2`);
17497
19116
  }
17498
19117
  if (smsEventTracking?.dynamoDBHistory) {
17499
- console.log(` ${pc25.green("\u2713")} DynamoDB read access for SMS history`);
19118
+ console.log(` ${pc26.green("\u2713")} DynamoDB read access for SMS history`);
17500
19119
  }
17501
19120
  if (smsEventTracking?.enabled) {
17502
- console.log(` ${pc25.green("\u2713")} SNS topic access for SMS events`);
19121
+ console.log(` ${pc26.green("\u2713")} SNS topic access for SMS events`);
17503
19122
  }
17504
19123
  }
17505
19124
  console.log(
17506
19125
  `
17507
- ${pc25.dim("The Wraps Platform will now have updated permissions for feature detection.")}
19126
+ ${pc26.dim("The Wraps Platform will now have updated permissions for feature detection.")}
17508
19127
  `
17509
19128
  );
17510
19129
  }
17511
- function buildConsolePolicyDocument(emailConfig, smsConfig) {
19130
+ function buildConsolePolicyDocument2(emailConfig, smsConfig) {
17512
19131
  const statements = [];
17513
19132
  statements.push({
17514
19133
  Effect: "Allow",
@@ -17673,10 +19292,10 @@ function buildConsolePolicyDocument(emailConfig, smsConfig) {
17673
19292
  // src/commands/shared/dashboard.ts
17674
19293
  init_esm_shims();
17675
19294
  import * as clack24 from "@clack/prompts";
17676
- import * as pulumi19 from "@pulumi/pulumi";
19295
+ import * as pulumi20 from "@pulumi/pulumi";
17677
19296
  import getPort from "get-port";
17678
19297
  import open from "open";
17679
- import pc26 from "picocolors";
19298
+ import pc27 from "picocolors";
17680
19299
 
17681
19300
  // src/console/server.ts
17682
19301
  init_esm_shims();
@@ -18765,13 +20384,13 @@ function createMetricsRouter(config2) {
18765
20384
  const router = createRouter4();
18766
20385
  router.get("/stream", async (req, res) => {
18767
20386
  const connectionId = randomUUID().slice(0, 8);
18768
- const log34 = (msg, data) => {
20387
+ const log35 = (msg, data) => {
18769
20388
  console.log(JSON.stringify({ connectionId, msg, ...data }));
18770
20389
  };
18771
20390
  res.setHeader("Content-Type", "text/event-stream");
18772
20391
  res.setHeader("Cache-Control", "no-cache");
18773
20392
  res.setHeader("Connection", "keep-alive");
18774
- log34("SSE connected");
20393
+ log35("SSE connected");
18775
20394
  res.write('data: {"type":"connected"}\n\n');
18776
20395
  const { startTime, endTime } = req.query;
18777
20396
  const getTimeRange = () => ({
@@ -18781,7 +20400,7 @@ function createMetricsRouter(config2) {
18781
20400
  const sendMetrics = async () => {
18782
20401
  try {
18783
20402
  const timeRange = getTimeRange();
18784
- log34("Fetching metrics", {
20403
+ log35("Fetching metrics", {
18785
20404
  start: timeRange.start.toISOString(),
18786
20405
  end: timeRange.end.toISOString()
18787
20406
  });
@@ -18794,7 +20413,7 @@ function createMetricsRouter(config2) {
18794
20413
  ),
18795
20414
  fetchSendQuota(config2.roleArn, config2.region)
18796
20415
  ]);
18797
- log34("Metrics fetched successfully");
20416
+ log35("Metrics fetched successfully");
18798
20417
  const data = {
18799
20418
  type: "metrics",
18800
20419
  timestamp: Date.now(),
@@ -18824,7 +20443,7 @@ function createMetricsRouter(config2) {
18824
20443
  const interval = setInterval(sendMetrics, 6e4);
18825
20444
  req.on("close", () => {
18826
20445
  clearInterval(interval);
18827
- log34("SSE disconnected");
20446
+ log35("SSE disconnected");
18828
20447
  });
18829
20448
  });
18830
20449
  router.get("/snapshot", async (_req, res) => {
@@ -20066,9 +21685,9 @@ function createUserRouter(config2) {
20066
21685
  if (config2.roleArn) {
20067
21686
  console.log("[User API] Attempting to fetch account alias via IAM");
20068
21687
  const { assumeRole: assumeRole2 } = await Promise.resolve().then(() => (init_assume_role(), assume_role_exports));
20069
- const { IAMClient: IAMClient3, ListAccountAliasesCommand } = await import("@aws-sdk/client-iam");
21688
+ const { IAMClient: IAMClient4, ListAccountAliasesCommand } = await import("@aws-sdk/client-iam");
20070
21689
  const credentials = await assumeRole2(config2.roleArn, region);
20071
- const iamClient = new IAMClient3({ region, credentials });
21690
+ const iamClient = new IAMClient4({ region, credentials });
20072
21691
  const response = await iamClient.send(
20073
21692
  new ListAccountAliasesCommand({})
20074
21693
  );
@@ -20196,7 +21815,7 @@ init_aws();
20196
21815
  init_fs();
20197
21816
  init_metadata();
20198
21817
  async function dashboard(options) {
20199
- clack24.intro(pc26.bold("Wraps Dashboard"));
21818
+ clack24.intro(pc27.bold("Wraps Dashboard"));
20200
21819
  const progress = new DeploymentProgress();
20201
21820
  const identity = await progress.execute(
20202
21821
  "Validating AWS credentials",
@@ -20209,7 +21828,7 @@ async function dashboard(options) {
20209
21828
  try {
20210
21829
  await ensurePulumiWorkDir();
20211
21830
  try {
20212
- const emailStack = await pulumi19.automation.LocalWorkspace.selectStack({
21831
+ const emailStack = await pulumi20.automation.LocalWorkspace.selectStack({
20213
21832
  stackName: `wraps-${identity.accountId}-${region}`,
20214
21833
  workDir: getPulumiWorkDir()
20215
21834
  });
@@ -20217,7 +21836,7 @@ async function dashboard(options) {
20217
21836
  } catch (_emailError) {
20218
21837
  }
20219
21838
  try {
20220
- const smsStack = await pulumi19.automation.LocalWorkspace.selectStack({
21839
+ const smsStack = await pulumi20.automation.LocalWorkspace.selectStack({
20221
21840
  stackName: `wraps-sms-${identity.accountId}-${region}`,
20222
21841
  workDir: getPulumiWorkDir()
20223
21842
  });
@@ -20225,7 +21844,7 @@ async function dashboard(options) {
20225
21844
  } catch (_smsError) {
20226
21845
  }
20227
21846
  try {
20228
- const cdnStack = await pulumi19.automation.LocalWorkspace.selectStack({
21847
+ const cdnStack = await pulumi20.automation.LocalWorkspace.selectStack({
20229
21848
  stackName: `wraps-cdn-${identity.accountId}-${region}`,
20230
21849
  workDir: getPulumiWorkDir()
20231
21850
  });
@@ -20239,7 +21858,7 @@ async function dashboard(options) {
20239
21858
  progress.stop();
20240
21859
  clack24.log.error("No Wraps infrastructure found");
20241
21860
  console.log(
20242
- `\\nRun ${pc26.cyan("wraps email init")}, ${pc26.cyan("wraps sms init")}, or ${pc26.cyan("wraps storage init")} to deploy infrastructure first.\\n`
21861
+ `\\nRun ${pc27.cyan("wraps email init")}, ${pc27.cyan("wraps sms init")}, or ${pc27.cyan("wraps storage init")} to deploy infrastructure first.\\n`
20243
21862
  );
20244
21863
  process.exit(1);
20245
21864
  }
@@ -20282,7 +21901,7 @@ async function dashboard(options) {
20282
21901
  progress.stop();
20283
21902
  clack24.log.success("Starting dashboard server...");
20284
21903
  console.log(
20285
- `${pc26.dim("Using current AWS credentials (no role assumption)")}\\n`
21904
+ `${pc27.dim("Using current AWS credentials (no role assumption)")}\\n`
20286
21905
  );
20287
21906
  const { url } = await startConsoleServer({
20288
21907
  port,
@@ -20313,8 +21932,8 @@ async function dashboard(options) {
20313
21932
  cdnCustomDomain,
20314
21933
  cdnCertificateArn
20315
21934
  });
20316
- console.log(`\\n${pc26.bold("Dashboard:")} ${pc26.cyan(url)}`);
20317
- console.log(`${pc26.dim("Press Ctrl+C to stop")}\\n`);
21935
+ console.log(`\\n${pc27.bold("Dashboard:")} ${pc27.cyan(url)}`);
21936
+ console.log(`${pc27.dim("Press Ctrl+C to stop")}\\n`);
20318
21937
  getTelemetryClient().showFooterOnce();
20319
21938
  if (!options.noOpen) {
20320
21939
  await open(url);
@@ -20333,9 +21952,9 @@ init_esm_shims();
20333
21952
  init_aws();
20334
21953
  init_metadata();
20335
21954
  import * as clack25 from "@clack/prompts";
20336
- import pc27 from "picocolors";
21955
+ import pc28 from "picocolors";
20337
21956
  async function destroy(options) {
20338
- clack25.intro(pc27.bold("Wraps Infrastructure Teardown"));
21957
+ clack25.intro(pc28.bold("Wraps Infrastructure Teardown"));
20339
21958
  const spinner6 = clack25.spinner();
20340
21959
  spinner6.start("Validating AWS credentials");
20341
21960
  let identity;
@@ -20356,14 +21975,14 @@ async function destroy(options) {
20356
21975
  clack25.log.warn("No Wraps services found in this region");
20357
21976
  console.log(
20358
21977
  `
20359
- Run ${pc27.cyan("wraps email init")} to deploy infrastructure.
21978
+ Run ${pc28.cyan("wraps email init")} to deploy infrastructure.
20360
21979
  `
20361
21980
  );
20362
21981
  process.exit(0);
20363
21982
  }
20364
21983
  if (deployedServices.length === 1) {
20365
21984
  const service = deployedServices[0];
20366
- clack25.log.info(`Found ${pc27.cyan(service)} service deployed`);
21985
+ clack25.log.info(`Found ${pc28.cyan(service)} service deployed`);
20367
21986
  if (service === "email") {
20368
21987
  await emailDestroy(options);
20369
21988
  return;
@@ -20392,7 +22011,7 @@ Run ${pc27.cyan("wraps email init")} to deploy infrastructure.
20392
22011
  await emailDestroy(options);
20393
22012
  }
20394
22013
  if (serviceToDestroy === "all") {
20395
- clack25.outro(pc27.green("All Wraps infrastructure has been removed"));
22014
+ clack25.outro(pc28.green("All Wraps infrastructure has been removed"));
20396
22015
  }
20397
22016
  }
20398
22017
 
@@ -20402,23 +22021,23 @@ init_events();
20402
22021
  init_aws();
20403
22022
  init_fs();
20404
22023
  import * as clack26 from "@clack/prompts";
20405
- import * as pulumi20 from "@pulumi/pulumi";
20406
- import pc28 from "picocolors";
22024
+ import * as pulumi21 from "@pulumi/pulumi";
22025
+ import pc29 from "picocolors";
20407
22026
  async function status(_options) {
20408
22027
  const startTime = Date.now();
20409
22028
  const progress = new DeploymentProgress();
20410
- clack26.intro(pc28.bold("Wraps Infrastructure Status"));
22029
+ clack26.intro(pc29.bold("Wraps Infrastructure Status"));
20411
22030
  const identity = await progress.execute(
20412
22031
  "Loading infrastructure status",
20413
22032
  async () => validateAWSCredentials()
20414
22033
  );
20415
- progress.info(`AWS Account: ${pc28.cyan(identity.accountId)}`);
22034
+ progress.info(`AWS Account: ${pc29.cyan(identity.accountId)}`);
20416
22035
  const region = await getAWSRegion();
20417
- progress.info(`Region: ${pc28.cyan(region)}`);
22036
+ progress.info(`Region: ${pc29.cyan(region)}`);
20418
22037
  const services = [];
20419
22038
  try {
20420
22039
  await ensurePulumiWorkDir();
20421
- const emailStack = await pulumi20.automation.LocalWorkspace.selectStack({
22040
+ const emailStack = await pulumi21.automation.LocalWorkspace.selectStack({
20422
22041
  stackName: `wraps-${identity.accountId}-${region}`,
20423
22042
  workDir: getPulumiWorkDir()
20424
22043
  });
@@ -20437,7 +22056,7 @@ async function status(_options) {
20437
22056
  services.push({ name: "Email", status: "not_deployed" });
20438
22057
  }
20439
22058
  try {
20440
- const smsStack = await pulumi20.automation.LocalWorkspace.selectStack({
22059
+ const smsStack = await pulumi21.automation.LocalWorkspace.selectStack({
20441
22060
  stackName: `wraps-sms-${identity.accountId}-${region}`,
20442
22061
  workDir: getPulumiWorkDir()
20443
22062
  });
@@ -20460,32 +22079,32 @@ async function status(_options) {
20460
22079
  clack26.note(
20461
22080
  services.map((s) => {
20462
22081
  if (s.status === "deployed") {
20463
- const details = s.details ? pc28.dim(` (${s.details})`) : "";
20464
- return ` ${pc28.green("\u2713")} ${s.name}${details}`;
22082
+ const details = s.details ? pc29.dim(` (${s.details})`) : "";
22083
+ return ` ${pc29.green("\u2713")} ${s.name}${details}`;
20465
22084
  }
20466
- return ` ${pc28.dim("\u25CB")} ${s.name} ${pc28.dim("(not deployed)")}`;
22085
+ return ` ${pc29.dim("\u25CB")} ${s.name} ${pc29.dim("(not deployed)")}`;
20467
22086
  }).join("\n"),
20468
22087
  "Services"
20469
22088
  );
20470
22089
  const hasDeployedServices = services.some((s) => s.status === "deployed");
20471
22090
  if (hasDeployedServices) {
20472
22091
  console.log(`
20473
- ${pc28.bold("Details:")}`);
22092
+ ${pc29.bold("Details:")}`);
20474
22093
  if (services.find((s) => s.name === "Email")?.status === "deployed") {
20475
- console.log(` ${pc28.dim("Email:")} ${pc28.cyan("wraps email status")}`);
22094
+ console.log(` ${pc29.dim("Email:")} ${pc29.cyan("wraps email status")}`);
20476
22095
  }
20477
22096
  if (services.find((s) => s.name === "SMS")?.status === "deployed") {
20478
- console.log(` ${pc28.dim("SMS:")} ${pc28.cyan("wraps sms status")}`);
22097
+ console.log(` ${pc29.dim("SMS:")} ${pc29.cyan("wraps sms status")}`);
20479
22098
  }
20480
22099
  } else {
20481
22100
  console.log(`
20482
- ${pc28.bold("Get started:")}`);
20483
- console.log(` ${pc28.dim("Deploy email:")} ${pc28.cyan("wraps email init")}`);
20484
- console.log(` ${pc28.dim("Deploy SMS:")} ${pc28.cyan("wraps sms init")}`);
22101
+ ${pc29.bold("Get started:")}`);
22102
+ console.log(` ${pc29.dim("Deploy email:")} ${pc29.cyan("wraps email init")}`);
22103
+ console.log(` ${pc29.dim("Deploy SMS:")} ${pc29.cyan("wraps sms init")}`);
20485
22104
  }
20486
22105
  console.log(`
20487
- ${pc28.bold("Dashboard:")} ${pc28.blue("https://app.wraps.dev")}`);
20488
- console.log(`${pc28.bold("Docs:")} ${pc28.blue("https://wraps.dev/docs")}
22106
+ ${pc29.bold("Dashboard:")} ${pc29.blue("https://app.wraps.dev")}`);
22107
+ console.log(`${pc29.bold("Docs:")} ${pc29.blue("https://wraps.dev/docs")}
20489
22108
  `);
20490
22109
  trackCommand("status", {
20491
22110
  success: true,
@@ -20497,20 +22116,20 @@ ${pc28.bold("Dashboard:")} ${pc28.blue("https://app.wraps.dev")}`);
20497
22116
  // src/commands/sms/destroy.ts
20498
22117
  init_esm_shims();
20499
22118
  import * as clack27 from "@clack/prompts";
20500
- import * as pulumi22 from "@pulumi/pulumi";
20501
- import pc29 from "picocolors";
22119
+ import * as pulumi23 from "@pulumi/pulumi";
22120
+ import pc30 from "picocolors";
20502
22121
 
20503
22122
  // src/infrastructure/sms-stack.ts
20504
22123
  init_esm_shims();
20505
22124
  import * as aws15 from "@pulumi/aws";
20506
- import * as pulumi21 from "@pulumi/pulumi";
22125
+ import * as pulumi22 from "@pulumi/pulumi";
20507
22126
  async function roleExists3(roleName) {
20508
22127
  try {
20509
- const { IAMClient: IAMClient3, GetRoleCommand: GetRoleCommand2 } = await import("@aws-sdk/client-iam");
20510
- const iam8 = new IAMClient3({
22128
+ const { IAMClient: IAMClient4, GetRoleCommand: GetRoleCommand3 } = await import("@aws-sdk/client-iam");
22129
+ const iam8 = new IAMClient4({
20511
22130
  region: process.env.AWS_REGION || "us-east-1"
20512
22131
  });
20513
- await iam8.send(new GetRoleCommand2({ RoleName: roleName }));
22132
+ await iam8.send(new GetRoleCommand3({ RoleName: roleName }));
20514
22133
  return true;
20515
22134
  } catch (error) {
20516
22135
  if (error.name === "NoSuchEntityException" || error.Code === "NoSuchEntity" || error.Error?.Code === "NoSuchEntity") {
@@ -20537,7 +22156,7 @@ async function tableExists2(tableName) {
20537
22156
  async function createSMSIAMRole(config2) {
20538
22157
  let assumeRolePolicy;
20539
22158
  if (config2.provider === "vercel" && config2.oidcProvider) {
20540
- assumeRolePolicy = pulumi21.interpolate`{
22159
+ assumeRolePolicy = pulumi22.interpolate`{
20541
22160
  "Version": "2012-10-17",
20542
22161
  "Statement": [
20543
22162
  {
@@ -20565,7 +22184,7 @@ async function createSMSIAMRole(config2) {
20565
22184
  ]
20566
22185
  }`;
20567
22186
  } else if (config2.provider === "aws") {
20568
- assumeRolePolicy = pulumi21.output(`{
22187
+ assumeRolePolicy = pulumi22.output(`{
20569
22188
  "Version": "2012-10-17",
20570
22189
  "Statement": [{
20571
22190
  "Effect": "Allow",
@@ -20920,7 +22539,7 @@ async function createSMSSNSResources(config2) {
20920
22539
  });
20921
22540
  new aws15.sqs.QueuePolicy("wraps-sms-events-queue-policy", {
20922
22541
  queueUrl: config2.queueUrl,
20923
- policy: pulumi21.all([config2.queueArn, topic.arn]).apply(
22542
+ policy: pulumi22.all([config2.queueArn, topic.arn]).apply(
20924
22543
  ([queueArn, topicArn2]) => JSON.stringify({
20925
22544
  Version: "2012-10-17",
20926
22545
  Statement: [
@@ -21019,7 +22638,7 @@ async function deploySMSLambdaFunction(config2) {
21019
22638
  });
21020
22639
  new aws15.iam.RolePolicy("wraps-sms-lambda-policy", {
21021
22640
  role: lambdaRole.name,
21022
- policy: pulumi21.all([config2.tableName, config2.queueArn]).apply(
22641
+ policy: pulumi22.all([config2.tableName, config2.queueArn]).apply(
21023
22642
  ([tableName, queueArn]) => JSON.stringify({
21024
22643
  Version: "2012-10-17",
21025
22644
  Statement: [
@@ -21056,7 +22675,7 @@ async function deploySMSLambdaFunction(config2) {
21056
22675
  runtime: "nodejs20.x",
21057
22676
  handler: "index.handler",
21058
22677
  role: lambdaRole.arn,
21059
- code: new pulumi21.asset.FileArchive(codeDir),
22678
+ code: new pulumi22.asset.FileArchive(codeDir),
21060
22679
  timeout: 300,
21061
22680
  // 5 minutes
21062
22681
  memorySize: 512,
@@ -21296,18 +22915,18 @@ async function createSMSProtectConfigurationWithSDK(configurationSetName, region
21296
22915
  const existing = await client.send(
21297
22916
  new DescribeProtectConfigurationsCommand({})
21298
22917
  );
21299
- for (const pc40 of existing.ProtectConfigurations || []) {
21300
- if (!(pc40.ProtectConfigurationArn && pc40.ProtectConfigurationId)) {
22918
+ for (const pc41 of existing.ProtectConfigurations || []) {
22919
+ if (!(pc41.ProtectConfigurationArn && pc41.ProtectConfigurationId)) {
21301
22920
  continue;
21302
22921
  }
21303
22922
  const tagsResponse = await client.send(
21304
22923
  new ListTagsForResourceCommand({
21305
- ResourceArn: pc40.ProtectConfigurationArn
22924
+ ResourceArn: pc41.ProtectConfigurationArn
21306
22925
  })
21307
22926
  );
21308
22927
  const nameTag = tagsResponse.Tags?.find((t) => t.Key === "Name");
21309
22928
  if (nameTag?.Value === protectConfigName) {
21310
- existingProtectConfigId = pc40.ProtectConfigurationId;
22929
+ existingProtectConfigId = pc41.ProtectConfigurationId;
21311
22930
  break;
21312
22931
  }
21313
22932
  }
@@ -21394,13 +23013,13 @@ async function deleteSMSProtectConfigurationWithSDK(region) {
21394
23013
  new DescribeProtectConfigurationsCommand({})
21395
23014
  );
21396
23015
  if (existing.ProtectConfigurations) {
21397
- for (const pc40 of existing.ProtectConfigurations) {
21398
- if (!(pc40.ProtectConfigurationArn && pc40.ProtectConfigurationId)) {
23016
+ for (const pc41 of existing.ProtectConfigurations) {
23017
+ if (!(pc41.ProtectConfigurationArn && pc41.ProtectConfigurationId)) {
21399
23018
  continue;
21400
23019
  }
21401
23020
  const tagsResponse = await client.send(
21402
23021
  new ListTagsForResourceCommand({
21403
- ResourceArn: pc40.ProtectConfigurationArn
23022
+ ResourceArn: pc41.ProtectConfigurationArn
21404
23023
  })
21405
23024
  );
21406
23025
  const isWrapsManaged = tagsResponse.Tags?.some(
@@ -21409,7 +23028,7 @@ async function deleteSMSProtectConfigurationWithSDK(region) {
21409
23028
  if (isWrapsManaged) {
21410
23029
  await client.send(
21411
23030
  new DeleteProtectConfigurationCommand({
21412
- ProtectConfigurationId: pc40.ProtectConfigurationId
23031
+ ProtectConfigurationId: pc41.ProtectConfigurationId
21413
23032
  })
21414
23033
  );
21415
23034
  }
@@ -21428,7 +23047,7 @@ init_metadata();
21428
23047
  async function smsDestroy(options) {
21429
23048
  const startTime = Date.now();
21430
23049
  clack27.intro(
21431
- pc29.bold(
23050
+ pc30.bold(
21432
23051
  options.preview ? "SMS Infrastructure Destruction Preview" : "SMS Infrastructure Teardown"
21433
23052
  )
21434
23053
  );
@@ -21448,7 +23067,7 @@ async function smsDestroy(options) {
21448
23067
  }
21449
23068
  if (!(options.force || options.preview)) {
21450
23069
  const confirmed = await clack27.confirm({
21451
- message: pc29.red(
23070
+ message: pc30.red(
21452
23071
  "Are you sure you want to destroy all SMS infrastructure?"
21453
23072
  ),
21454
23073
  initialValue: false
@@ -21467,7 +23086,7 @@ async function smsDestroy(options) {
21467
23086
  const stackName = storedStackName || `wraps-sms-${identity.accountId}-${region}`;
21468
23087
  let stack;
21469
23088
  try {
21470
- stack = await pulumi22.automation.LocalWorkspace.selectStack({
23089
+ stack = await pulumi23.automation.LocalWorkspace.selectStack({
21471
23090
  stackName,
21472
23091
  workDir: getPulumiWorkDir()
21473
23092
  });
@@ -21487,7 +23106,7 @@ async function smsDestroy(options) {
21487
23106
  commandName: "wraps sms destroy"
21488
23107
  });
21489
23108
  clack27.outro(
21490
- pc29.green("Preview complete. Run without --preview to destroy.")
23109
+ pc30.green("Preview complete. Run without --preview to destroy.")
21491
23110
  );
21492
23111
  trackServiceRemoved("sms", {
21493
23112
  preview: true,
@@ -21524,7 +23143,7 @@ async function smsDestroy(options) {
21524
23143
  const stackName = storedStackName || `wraps-sms-${identity.accountId}-${region}`;
21525
23144
  let stack;
21526
23145
  try {
21527
- stack = await pulumi22.automation.LocalWorkspace.selectStack({
23146
+ stack = await pulumi23.automation.LocalWorkspace.selectStack({
21528
23147
  stackName,
21529
23148
  workDir: getPulumiWorkDir()
21530
23149
  });
@@ -21560,16 +23179,16 @@ async function smsDestroy(options) {
21560
23179
  await saveConnectionMetadata(metadata);
21561
23180
  }
21562
23181
  progress.stop();
21563
- clack27.outro(pc29.green("SMS infrastructure has been removed"));
23182
+ clack27.outro(pc30.green("SMS infrastructure has been removed"));
21564
23183
  console.log(`
21565
- ${pc29.bold("Cleaned up:")}`);
21566
- console.log(` ${pc29.green("\u2713")} Phone number released`);
21567
- console.log(` ${pc29.green("\u2713")} Configuration set deleted`);
21568
- console.log(` ${pc29.green("\u2713")} Event processing infrastructure removed`);
21569
- console.log(` ${pc29.green("\u2713")} IAM role deleted`);
23184
+ ${pc30.bold("Cleaned up:")}`);
23185
+ console.log(` ${pc30.green("\u2713")} Phone number released`);
23186
+ console.log(` ${pc30.green("\u2713")} Configuration set deleted`);
23187
+ console.log(` ${pc30.green("\u2713")} Event processing infrastructure removed`);
23188
+ console.log(` ${pc30.green("\u2713")} IAM role deleted`);
21570
23189
  console.log(
21571
23190
  `
21572
- Run ${pc29.cyan("wraps sms init")} to deploy infrastructure again.
23191
+ Run ${pc30.cyan("wraps sms init")} to deploy infrastructure again.
21573
23192
  `
21574
23193
  );
21575
23194
  trackServiceRemoved("sms", {
@@ -21581,8 +23200,8 @@ Run ${pc29.cyan("wraps sms init")} to deploy infrastructure again.
21581
23200
  // src/commands/sms/init.ts
21582
23201
  init_esm_shims();
21583
23202
  import * as clack28 from "@clack/prompts";
21584
- import * as pulumi23 from "@pulumi/pulumi";
21585
- import pc30 from "picocolors";
23203
+ import * as pulumi24 from "@pulumi/pulumi";
23204
+ import pc31 from "picocolors";
21586
23205
  init_events();
21587
23206
  init_aws();
21588
23207
  init_errors();
@@ -22106,7 +23725,7 @@ async function promptEstimatedSMSVolume() {
22106
23725
  }
22107
23726
  async function init3(options) {
22108
23727
  const startTime = Date.now();
22109
- clack28.intro(pc30.bold("Wraps SMS Infrastructure Setup"));
23728
+ clack28.intro(pc31.bold("Wraps SMS Infrastructure Setup"));
22110
23729
  const progress = new DeploymentProgress();
22111
23730
  const wasAutoInstalled = await progress.execute(
22112
23731
  "Checking Pulumi CLI installation",
@@ -22119,7 +23738,7 @@ async function init3(options) {
22119
23738
  "Validating AWS credentials",
22120
23739
  async () => validateAWSCredentials()
22121
23740
  );
22122
- progress.info(`Connected to AWS account: ${pc30.cyan(identity.accountId)}`);
23741
+ progress.info(`Connected to AWS account: ${pc31.cyan(identity.accountId)}`);
22123
23742
  const provider = options.provider || await promptProvider();
22124
23743
  const region = options.region || await promptRegion(await getAWSRegion());
22125
23744
  let vercelConfig;
@@ -22132,9 +23751,9 @@ async function init3(options) {
22132
23751
  );
22133
23752
  if (existingConnection?.services?.sms) {
22134
23753
  clack28.log.warn(
22135
- `SMS already configured for account ${pc30.cyan(identity.accountId)} in region ${pc30.cyan(region)}`
23754
+ `SMS already configured for account ${pc31.cyan(identity.accountId)} in region ${pc31.cyan(region)}`
22136
23755
  );
22137
- clack28.log.info(`Use ${pc30.cyan("wraps sms status")} to view current setup`);
23756
+ clack28.log.info(`Use ${pc31.cyan("wraps sms status")} to view current setup`);
22138
23757
  process.exit(0);
22139
23758
  }
22140
23759
  let preset = options.preset;
@@ -22176,7 +23795,7 @@ async function init3(options) {
22176
23795
  }
22177
23796
  progress.info(
22178
23797
  `
22179
- ${pc30.bold("Fraud Protection")} - Block SMS to countries where you don't do business`
23798
+ ${pc31.bold("Fraud Protection")} - Block SMS to countries where you don't do business`
22180
23799
  );
22181
23800
  const allowedCountries = await promptAllowedCountries();
22182
23801
  let aitFiltering = false;
@@ -22198,13 +23817,13 @@ ${pc30.bold("Fraud Protection")} - Block SMS to countries where you don't do bus
22198
23817
  };
22199
23818
  const estimatedVolume = await promptEstimatedSMSVolume();
22200
23819
  progress.info(`
22201
- ${pc30.bold("Cost Estimate:")}`);
23820
+ ${pc31.bold("Cost Estimate:")}`);
22202
23821
  const costSummary = getSMSCostSummary(smsConfig, estimatedVolume);
22203
23822
  clack28.log.info(costSummary);
22204
23823
  const warnings = validateSMSConfig(smsConfig);
22205
23824
  if (warnings.length > 0) {
22206
23825
  progress.info(`
22207
- ${pc30.yellow(pc30.bold("Important Notes:"))}`);
23826
+ ${pc31.yellow(pc31.bold("Important Notes:"))}`);
22208
23827
  for (const warning of warnings) {
22209
23828
  clack28.log.warn(warning);
22210
23829
  }
@@ -22228,7 +23847,7 @@ ${pc30.yellow(pc30.bold("Important Notes:"))}`);
22228
23847
  "Deploying SMS infrastructure (this may take 2-3 minutes)",
22229
23848
  async () => {
22230
23849
  await ensurePulumiWorkDir();
22231
- const stack = await pulumi23.automation.LocalWorkspace.createOrSelectStack(
23850
+ const stack = await pulumi24.automation.LocalWorkspace.createOrSelectStack(
22232
23851
  {
22233
23852
  stackName: `wraps-sms-${identity.accountId}-${region}`,
22234
23853
  projectName: "wraps-sms",
@@ -22334,47 +23953,47 @@ ${pc30.yellow(pc30.bold("Important Notes:"))}`);
22334
23953
  await saveConnectionMetadata(metadata);
22335
23954
  progress.info("Connection metadata saved");
22336
23955
  console.log("\n");
22337
- clack28.log.success(pc30.green(pc30.bold("SMS infrastructure deployed!")));
23956
+ clack28.log.success(pc31.green(pc31.bold("SMS infrastructure deployed!")));
22338
23957
  console.log("\n");
22339
23958
  clack28.note(
22340
23959
  [
22341
- `${pc30.bold("Phone Number:")} ${pc30.cyan(outputs.phoneNumber || "Provisioning...")}`,
22342
- `${pc30.bold("Phone Type:")} ${pc30.cyan(smsConfig.phoneNumberType || "simulator")}`,
22343
- `${pc30.bold("Config Set:")} ${pc30.cyan(outputs.configSetName || "wraps-sms-config")}`,
22344
- `${pc30.bold("Region:")} ${pc30.cyan(outputs.region)}`,
22345
- outputs.tableName ? `${pc30.bold("History Table:")} ${pc30.cyan(outputs.tableName)}` : "",
23960
+ `${pc31.bold("Phone Number:")} ${pc31.cyan(outputs.phoneNumber || "Provisioning...")}`,
23961
+ `${pc31.bold("Phone Type:")} ${pc31.cyan(smsConfig.phoneNumberType || "simulator")}`,
23962
+ `${pc31.bold("Config Set:")} ${pc31.cyan(outputs.configSetName || "wraps-sms-config")}`,
23963
+ `${pc31.bold("Region:")} ${pc31.cyan(outputs.region)}`,
23964
+ outputs.tableName ? `${pc31.bold("History Table:")} ${pc31.cyan(outputs.tableName)}` : "",
22346
23965
  "",
22347
- pc30.dim("IAM Role:"),
22348
- pc30.dim(` ${outputs.roleArn}`)
23966
+ pc31.dim("IAM Role:"),
23967
+ pc31.dim(` ${outputs.roleArn}`)
22349
23968
  ].filter(Boolean).join("\n"),
22350
23969
  "SMS Infrastructure"
22351
23970
  );
22352
23971
  const nextSteps = [];
22353
23972
  if (smsConfig.phoneNumberType === "toll-free") {
22354
23973
  nextSteps.push(
22355
- `${pc30.cyan("wraps sms register")} - Submit toll-free registration (required before sending)`
23974
+ `${pc31.cyan("wraps sms register")} - Submit toll-free registration (required before sending)`
22356
23975
  );
22357
23976
  }
22358
23977
  nextSteps.push(
22359
- `${pc30.cyan("wraps sms test --to +1234567890")} - Send a test message`
23978
+ `${pc31.cyan("wraps sms test --to +1234567890")} - Send a test message`
22360
23979
  );
22361
- nextSteps.push(`${pc30.cyan("wraps sms status")} - View SMS configuration`);
23980
+ nextSteps.push(`${pc31.cyan("wraps sms status")} - View SMS configuration`);
22362
23981
  console.log("\n");
22363
- clack28.log.info(pc30.bold("Next steps:"));
23982
+ clack28.log.info(pc31.bold("Next steps:"));
22364
23983
  for (const step of nextSteps) {
22365
23984
  console.log(` ${step}`);
22366
23985
  }
22367
23986
  console.log("\n");
22368
- clack28.log.info(pc30.bold("SDK Usage:"));
22369
- console.log(pc30.dim(" npm install @wraps.dev/sms"));
23987
+ clack28.log.info(pc31.bold("SDK Usage:"));
23988
+ console.log(pc31.dim(" npm install @wraps.dev/sms"));
22370
23989
  console.log("");
22371
- console.log(pc30.dim(" import { Wraps } from '@wraps.dev/sms';"));
22372
- console.log(pc30.dim(" const wraps = new Wraps();"));
22373
- console.log(pc30.dim(" await wraps.sms.send({"));
22374
- console.log(pc30.dim(" to: '+14155551234',"));
22375
- console.log(pc30.dim(" message: 'Your code is 123456',"));
22376
- console.log(pc30.dim(" });"));
22377
- clack28.outro(pc30.green("Setup complete!"));
23990
+ console.log(pc31.dim(" import { Wraps } from '@wraps.dev/sms';"));
23991
+ console.log(pc31.dim(" const wraps = new Wraps();"));
23992
+ console.log(pc31.dim(" await wraps.sms.send({"));
23993
+ console.log(pc31.dim(" to: '+14155551234',"));
23994
+ console.log(pc31.dim(" message: 'Your code is 123456',"));
23995
+ console.log(pc31.dim(" });"));
23996
+ clack28.outro(pc31.green("Setup complete!"));
22378
23997
  const duration = Date.now() - startTime;
22379
23998
  const enabledFeatures = [];
22380
23999
  if (smsConfig.tracking?.enabled) {
@@ -22408,7 +24027,7 @@ init_esm_shims();
22408
24027
  init_aws();
22409
24028
  init_metadata();
22410
24029
  import * as clack29 from "@clack/prompts";
22411
- import pc31 from "picocolors";
24030
+ import pc32 from "picocolors";
22412
24031
  async function getPhoneNumberDetails(region) {
22413
24032
  const { PinpointSMSVoiceV2Client: PinpointSMSVoiceV2Client5, DescribePhoneNumbersCommand: DescribePhoneNumbersCommand2 } = await import("@aws-sdk/client-pinpoint-sms-voice-v2");
22414
24033
  const client = new PinpointSMSVoiceV2Client5({ region });
@@ -22446,7 +24065,7 @@ async function getRegistrationStatus(region, registrationId) {
22446
24065
  }
22447
24066
  }
22448
24067
  async function smsRegister(options) {
22449
- clack29.intro(pc31.bold("Wraps SMS - Toll-Free Registration"));
24068
+ clack29.intro(pc32.bold("Wraps SMS - Toll-Free Registration"));
22450
24069
  const progress = new DeploymentProgress();
22451
24070
  const identity = await progress.execute(
22452
24071
  "Validating AWS credentials",
@@ -22459,7 +24078,7 @@ async function smsRegister(options) {
22459
24078
  const metadata = await loadConnectionMetadata(identity.accountId, region);
22460
24079
  if (!metadata?.services?.sms) {
22461
24080
  clack29.log.error("No SMS infrastructure found.");
22462
- clack29.log.info(`Run ${pc31.cyan("wraps sms init")} first.`);
24081
+ clack29.log.info(`Run ${pc32.cyan("wraps sms init")} first.`);
22463
24082
  process.exit(1);
22464
24083
  }
22465
24084
  const phoneDetails = await progress.execute(
@@ -22472,11 +24091,11 @@ async function smsRegister(options) {
22472
24091
  }
22473
24092
  console.log("");
22474
24093
  console.log(
22475
- `${pc31.bold("Phone Number:")} ${pc31.cyan(phoneDetails.phoneNumber)}`
24094
+ `${pc32.bold("Phone Number:")} ${pc32.cyan(phoneDetails.phoneNumber)}`
22476
24095
  );
22477
- console.log(`${pc31.bold("Type:")} ${pc31.cyan(phoneDetails.type)}`);
24096
+ console.log(`${pc32.bold("Type:")} ${pc32.cyan(phoneDetails.type)}`);
22478
24097
  console.log(
22479
- `${pc31.bold("Status:")} ${phoneDetails.status === "ACTIVE" ? pc31.green(phoneDetails.status) : pc31.yellow(phoneDetails.status)}`
24098
+ `${pc32.bold("Status:")} ${phoneDetails.status === "ACTIVE" ? pc32.green(phoneDetails.status) : pc32.yellow(phoneDetails.status)}`
22480
24099
  );
22481
24100
  if (phoneDetails.registrationId) {
22482
24101
  const regStatus = await getRegistrationStatus(
@@ -22484,7 +24103,7 @@ async function smsRegister(options) {
22484
24103
  phoneDetails.registrationId
22485
24104
  );
22486
24105
  if (regStatus) {
22487
- console.log(`${pc31.bold("Registration:")} ${pc31.cyan(regStatus)}`);
24106
+ console.log(`${pc32.bold("Registration:")} ${pc32.cyan(regStatus)}`);
22488
24107
  }
22489
24108
  }
22490
24109
  console.log("");
@@ -22503,21 +24122,21 @@ async function smsRegister(options) {
22503
24122
  clack29.log.info(`Your ${phoneDetails.type} number should be ready to use.`);
22504
24123
  process.exit(0);
22505
24124
  }
22506
- console.log(pc31.bold("Toll-Free Registration Required"));
24125
+ console.log(pc32.bold("Toll-Free Registration Required"));
22507
24126
  console.log("");
22508
24127
  console.log(
22509
- pc31.dim("To send SMS at scale, you must register your toll-free number.")
24128
+ pc32.dim("To send SMS at scale, you must register your toll-free number.")
22510
24129
  );
22511
- console.log(pc31.dim("This process typically takes 1-15 business days."));
24130
+ console.log(pc32.dim("This process typically takes 1-15 business days."));
22512
24131
  console.log("");
22513
- console.log(pc31.bold("You'll need to provide:"));
22514
- console.log(` ${pc31.dim("\u2022")} Business name and address`);
24132
+ console.log(pc32.bold("You'll need to provide:"));
24133
+ console.log(` ${pc32.dim("\u2022")} Business name and address`);
22515
24134
  console.log(
22516
- ` ${pc31.dim("\u2022")} Use case description (what messages you're sending)`
24135
+ ` ${pc32.dim("\u2022")} Use case description (what messages you're sending)`
22517
24136
  );
22518
- console.log(` ${pc31.dim("\u2022")} Sample messages (2-3 examples)`);
22519
- console.log(` ${pc31.dim("\u2022")} How users opt-in to receive messages`);
22520
- console.log(` ${pc31.dim("\u2022")} Expected monthly message volume`);
24137
+ console.log(` ${pc32.dim("\u2022")} Sample messages (2-3 examples)`);
24138
+ console.log(` ${pc32.dim("\u2022")} How users opt-in to receive messages`);
24139
+ console.log(` ${pc32.dim("\u2022")} Expected monthly message volume`);
22521
24140
  console.log("");
22522
24141
  const openConsole = await clack29.confirm({
22523
24142
  message: "Open AWS Console to start registration?",
@@ -22542,29 +24161,29 @@ async function smsRegister(options) {
22542
24161
  } catch {
22543
24162
  clack29.log.info("Open this URL in your browser:");
22544
24163
  console.log(`
22545
- ${pc31.cyan(consoleUrl)}
24164
+ ${pc32.cyan(consoleUrl)}
22546
24165
  `);
22547
24166
  }
22548
24167
  }
22549
24168
  console.log("");
22550
- console.log(pc31.bold("Next Steps:"));
24169
+ console.log(pc32.bold("Next Steps:"));
22551
24170
  console.log(
22552
- ` 1. Click ${pc31.cyan("Create registration")} in the AWS Console`
24171
+ ` 1. Click ${pc32.cyan("Create registration")} in the AWS Console`
22553
24172
  );
22554
- console.log(` 2. Select ${pc31.cyan("Toll-free number registration")}`);
24173
+ console.log(` 2. Select ${pc32.cyan("Toll-free number registration")}`);
22555
24174
  console.log(" 3. Fill out the business information form");
22556
24175
  console.log(" 4. Submit and wait for approval (1-15 business days)");
22557
24176
  console.log("");
22558
24177
  console.log(
22559
- pc31.dim("Once approved, run `wraps sms sync` to complete setup.")
24178
+ pc32.dim("Once approved, run `wraps sms sync` to complete setup.")
22560
24179
  );
22561
24180
  } else {
22562
24181
  const consoleUrl = `https://${region}.console.aws.amazon.com/sms-voice/home?region=${region}#/registrations`;
22563
24182
  console.log("");
22564
24183
  console.log("When you're ready, go to:");
22565
- console.log(` ${pc31.cyan(consoleUrl)}`);
24184
+ console.log(` ${pc32.cyan(consoleUrl)}`);
22566
24185
  }
22567
- clack29.outro(pc31.dim("Good luck with your registration!"));
24186
+ clack29.outro(pc32.dim("Good luck with your registration!"));
22568
24187
  }
22569
24188
 
22570
24189
  // src/commands/sms/status.ts
@@ -22574,51 +24193,51 @@ init_aws();
22574
24193
  init_fs();
22575
24194
  init_metadata();
22576
24195
  import * as clack30 from "@clack/prompts";
22577
- import * as pulumi24 from "@pulumi/pulumi";
22578
- import pc32 from "picocolors";
24196
+ import * as pulumi25 from "@pulumi/pulumi";
24197
+ import pc33 from "picocolors";
22579
24198
  function displaySMSStatus(options) {
22580
24199
  const lines = [];
22581
- lines.push(pc32.bold(pc32.green("SMS Infrastructure Active")));
24200
+ lines.push(pc33.bold(pc33.green("SMS Infrastructure Active")));
22582
24201
  lines.push("");
22583
- lines.push(pc32.bold("Phone Number"));
24202
+ lines.push(pc33.bold("Phone Number"));
22584
24203
  if (options.phoneNumber) {
22585
- lines.push(` Number: ${pc32.cyan(options.phoneNumber)}`);
24204
+ lines.push(` Number: ${pc33.cyan(options.phoneNumber)}`);
22586
24205
  } else {
22587
- lines.push(` Number: ${pc32.yellow("Provisioning...")}`);
24206
+ lines.push(` Number: ${pc33.yellow("Provisioning...")}`);
22588
24207
  }
22589
- lines.push(` Type: ${pc32.cyan(options.phoneNumberType || "simulator")}`);
24208
+ lines.push(` Type: ${pc33.cyan(options.phoneNumberType || "simulator")}`);
22590
24209
  lines.push("");
22591
- lines.push(pc32.bold("Configuration"));
22592
- lines.push(` Region: ${pc32.cyan(options.region)}`);
24210
+ lines.push(pc33.bold("Configuration"));
24211
+ lines.push(` Region: ${pc33.cyan(options.region)}`);
22593
24212
  if (options.preset) {
22594
- lines.push(` Preset: ${pc32.cyan(options.preset)}`);
24213
+ lines.push(` Preset: ${pc33.cyan(options.preset)}`);
22595
24214
  }
22596
24215
  if (options.configSetName) {
22597
- lines.push(` Config Set: ${pc32.cyan(options.configSetName)}`);
24216
+ lines.push(` Config Set: ${pc33.cyan(options.configSetName)}`);
22598
24217
  }
22599
24218
  lines.push("");
22600
- lines.push(pc32.bold("Features"));
24219
+ lines.push(pc33.bold("Features"));
22601
24220
  lines.push(
22602
- ` Event Tracking: ${options.eventTracking ? pc32.green("Enabled") : pc32.dim("Disabled")}`
24221
+ ` Event Tracking: ${options.eventTracking ? pc33.green("Enabled") : pc33.dim("Disabled")}`
22603
24222
  );
22604
24223
  if (options.tableName) {
22605
- lines.push(` Message History: ${pc32.green("Enabled")}`);
22606
- lines.push(` Table: ${pc32.dim(options.tableName)}`);
24224
+ lines.push(` Message History: ${pc33.green("Enabled")}`);
24225
+ lines.push(` Table: ${pc33.dim(options.tableName)}`);
22607
24226
  }
22608
24227
  if (options.queueUrl) {
22609
- lines.push(` Event Queue: ${pc32.green("Enabled")}`);
24228
+ lines.push(` Event Queue: ${pc33.green("Enabled")}`);
22610
24229
  }
22611
24230
  lines.push("");
22612
24231
  if (options.roleArn) {
22613
- lines.push(pc32.bold("IAM Role"));
22614
- lines.push(` ${pc32.dim(options.roleArn)}`);
24232
+ lines.push(pc33.bold("IAM Role"));
24233
+ lines.push(` ${pc33.dim(options.roleArn)}`);
22615
24234
  }
22616
24235
  clack30.note(lines.join("\n"), "SMS Status");
22617
24236
  }
22618
24237
  async function smsStatus(_options) {
22619
24238
  const startTime = Date.now();
22620
24239
  const progress = new DeploymentProgress();
22621
- clack30.intro(pc32.bold("Wraps SMS Status"));
24240
+ clack30.intro(pc33.bold("Wraps SMS Status"));
22622
24241
  const identity = await progress.execute(
22623
24242
  "Loading SMS infrastructure status",
22624
24243
  async () => validateAWSCredentials()
@@ -22630,7 +24249,7 @@ async function smsStatus(_options) {
22630
24249
  clack30.log.error("No SMS infrastructure found");
22631
24250
  console.log(
22632
24251
  `
22633
- Run ${pc32.cyan("wraps sms init")} to deploy SMS infrastructure.
24252
+ Run ${pc33.cyan("wraps sms init")} to deploy SMS infrastructure.
22634
24253
  `
22635
24254
  );
22636
24255
  process.exit(1);
@@ -22639,7 +24258,7 @@ Run ${pc32.cyan("wraps sms init")} to deploy SMS infrastructure.
22639
24258
  try {
22640
24259
  await ensurePulumiWorkDir();
22641
24260
  const stackName = metadata.services.sms.pulumiStackName || `wraps-sms-${identity.accountId}-${region}`;
22642
- const stack = await pulumi24.automation.LocalWorkspace.selectStack({
24261
+ const stack = await pulumi25.automation.LocalWorkspace.selectStack({
22643
24262
  stackName,
22644
24263
  workDir: getPulumiWorkDir()
22645
24264
  });
@@ -22661,25 +24280,25 @@ Run ${pc32.cyan("wraps sms init")} to deploy SMS infrastructure.
22661
24280
  preset: metadata.services.sms.preset
22662
24281
  });
22663
24282
  console.log("");
22664
- clack30.log.info(pc32.bold("Commands:"));
24283
+ clack30.log.info(pc33.bold("Commands:"));
22665
24284
  console.log(
22666
- ` ${pc32.cyan("wraps sms test --to +1234567890")} - Send a test message`
24285
+ ` ${pc33.cyan("wraps sms test --to +1234567890")} - Send a test message`
22667
24286
  );
22668
- console.log(` ${pc32.cyan("wraps sms destroy")} - Remove SMS infrastructure`);
24287
+ console.log(` ${pc33.cyan("wraps sms destroy")} - Remove SMS infrastructure`);
22669
24288
  trackCommand("sms:status", {
22670
24289
  success: true,
22671
24290
  phone_type: smsConfig?.phoneNumberType,
22672
24291
  event_tracking: smsConfig?.eventTracking?.enabled,
22673
24292
  duration_ms: Date.now() - startTime
22674
24293
  });
22675
- clack30.outro(pc32.dim("SMS infrastructure is ready"));
24294
+ clack30.outro(pc33.dim("SMS infrastructure is ready"));
22676
24295
  }
22677
24296
 
22678
24297
  // src/commands/sms/sync.ts
22679
24298
  init_esm_shims();
22680
24299
  import * as clack31 from "@clack/prompts";
22681
- import * as pulumi25 from "@pulumi/pulumi";
22682
- import pc33 from "picocolors";
24300
+ import * as pulumi26 from "@pulumi/pulumi";
24301
+ import pc34 from "picocolors";
22683
24302
  init_events();
22684
24303
  init_aws();
22685
24304
  init_errors();
@@ -22687,7 +24306,7 @@ init_fs();
22687
24306
  init_metadata();
22688
24307
  async function smsSync(options) {
22689
24308
  const startTime = Date.now();
22690
- clack31.intro(pc33.bold("Wraps SMS Infrastructure Sync"));
24309
+ clack31.intro(pc34.bold("Wraps SMS Infrastructure Sync"));
22691
24310
  const progress = new DeploymentProgress();
22692
24311
  const identity = await progress.execute(
22693
24312
  "Validating AWS credentials",
@@ -22701,7 +24320,7 @@ async function smsSync(options) {
22701
24320
  clack31.log.error("No SMS infrastructure found to sync");
22702
24321
  console.log(
22703
24322
  `
22704
- Run ${pc33.cyan("wraps sms init")} to deploy SMS infrastructure first.
24323
+ Run ${pc34.cyan("wraps sms init")} to deploy SMS infrastructure first.
22705
24324
  `
22706
24325
  );
22707
24326
  process.exit(1);
@@ -22710,10 +24329,10 @@ Run ${pc33.cyan("wraps sms init")} to deploy SMS infrastructure first.
22710
24329
  const storedStackName = smsService.pulumiStackName;
22711
24330
  progress.info("Found existing SMS configuration");
22712
24331
  progress.info(
22713
- `Phone type: ${pc33.cyan(smsConfig.phoneNumberType || "simulator")}`
24332
+ `Phone type: ${pc34.cyan(smsConfig.phoneNumberType || "simulator")}`
22714
24333
  );
22715
24334
  progress.info(
22716
- `Event tracking: ${pc33.cyan(smsConfig.eventTracking?.enabled ? "enabled" : "disabled")}`
24335
+ `Event tracking: ${pc34.cyan(smsConfig.eventTracking?.enabled ? "enabled" : "disabled")}`
22717
24336
  );
22718
24337
  if (!options.yes) {
22719
24338
  const confirmed = await clack31.confirm({
@@ -22736,7 +24355,7 @@ Run ${pc33.cyan("wraps sms init")} to deploy SMS infrastructure first.
22736
24355
  outputs = await progress.execute("Syncing SMS infrastructure", async () => {
22737
24356
  await ensurePulumiWorkDir();
22738
24357
  const stackName = storedStackName || `wraps-sms-${identity.accountId}-${region}`;
22739
- const stack = await pulumi25.automation.LocalWorkspace.createOrSelectStack(
24358
+ const stack = await pulumi26.automation.LocalWorkspace.createOrSelectStack(
22740
24359
  {
22741
24360
  stackName,
22742
24361
  projectName: "wraps-sms",
@@ -22835,7 +24454,7 @@ Run ${pc33.cyan("wraps sms init")} to deploy SMS infrastructure first.
22835
24454
  }
22836
24455
  progress.stop();
22837
24456
  console.log("\n");
22838
- clack31.log.success(pc33.green("SMS infrastructure synced successfully!"));
24457
+ clack31.log.success(pc34.green("SMS infrastructure synced successfully!"));
22839
24458
  const changes = [];
22840
24459
  if (outputs.lambdaFunctions?.length) {
22841
24460
  changes.push("Lambda functions updated");
@@ -22843,13 +24462,13 @@ Run ${pc33.cyan("wraps sms init")} to deploy SMS infrastructure first.
22843
24462
  changes.push("SDK resources verified");
22844
24463
  console.log("");
22845
24464
  for (const change of changes) {
22846
- console.log(` ${pc33.green("\u2713")} ${change}`);
24465
+ console.log(` ${pc34.green("\u2713")} ${change}`);
22847
24466
  }
22848
24467
  trackCommand("sms:sync", {
22849
24468
  success: true,
22850
24469
  duration_ms: Date.now() - startTime
22851
24470
  });
22852
- clack31.outro(pc33.green("Sync complete!"));
24471
+ clack31.outro(pc34.green("Sync complete!"));
22853
24472
  }
22854
24473
 
22855
24474
  // src/commands/sms/test.ts
@@ -22862,7 +24481,7 @@ import {
22862
24481
  SendTextMessageCommand
22863
24482
  } from "@aws-sdk/client-pinpoint-sms-voice-v2";
22864
24483
  import * as clack32 from "@clack/prompts";
22865
- import pc34 from "picocolors";
24484
+ import pc35 from "picocolors";
22866
24485
  function isValidPhoneNumber(phone) {
22867
24486
  return /^\+[1-9]\d{1,14}$/.test(phone);
22868
24487
  }
@@ -22879,7 +24498,7 @@ var SIMULATOR_DESTINATIONS = [
22879
24498
  async function smsTest(options) {
22880
24499
  const startTime = Date.now();
22881
24500
  const progress = new DeploymentProgress();
22882
- clack32.intro(pc34.bold("Wraps SMS Test"));
24501
+ clack32.intro(pc35.bold("Wraps SMS Test"));
22883
24502
  const identity = await progress.execute(
22884
24503
  "Validating AWS credentials",
22885
24504
  async () => validateAWSCredentials()
@@ -22891,7 +24510,7 @@ async function smsTest(options) {
22891
24510
  clack32.log.error("No SMS infrastructure found");
22892
24511
  console.log(
22893
24512
  `
22894
- Run ${pc34.cyan("wraps sms init")} to deploy SMS infrastructure.
24513
+ Run ${pc35.cyan("wraps sms init")} to deploy SMS infrastructure.
22895
24514
  `
22896
24515
  );
22897
24516
  process.exit(1);
@@ -22998,16 +24617,16 @@ Run ${pc34.cyan("wraps sms init")} to deploy SMS infrastructure.
22998
24617
  );
22999
24618
  progress.stop();
23000
24619
  console.log("\n");
23001
- clack32.log.success(pc34.green("Test SMS sent successfully!"));
24620
+ clack32.log.success(pc35.green("Test SMS sent successfully!"));
23002
24621
  console.log("");
23003
24622
  clack32.note(
23004
24623
  [
23005
- `${pc34.bold("Message ID:")} ${pc34.cyan(messageId || "unknown")}`,
23006
- `${pc34.bold("To:")} ${pc34.cyan(toNumber)}`,
23007
- `${pc34.bold("Message:")} ${message}`,
23008
- `${pc34.bold("Type:")} ${pc34.cyan(smsConfig?.phoneNumberType || "simulator")}`,
24624
+ `${pc35.bold("Message ID:")} ${pc35.cyan(messageId || "unknown")}`,
24625
+ `${pc35.bold("To:")} ${pc35.cyan(toNumber)}`,
24626
+ `${pc35.bold("Message:")} ${message}`,
24627
+ `${pc35.bold("Type:")} ${pc35.cyan(smsConfig?.phoneNumberType || "simulator")}`,
23009
24628
  "",
23010
- pc34.dim(
24629
+ pc35.dim(
23011
24630
  smsConfig?.phoneNumberType === "simulator" ? "Note: Simulator messages are not actually delivered" : "Check your phone for the message!"
23012
24631
  )
23013
24632
  ].join("\n"),
@@ -23016,7 +24635,7 @@ Run ${pc34.cyan("wraps sms init")} to deploy SMS infrastructure.
23016
24635
  if (smsConfig?.eventTracking?.enabled) {
23017
24636
  console.log("");
23018
24637
  clack32.log.info(
23019
- pc34.dim("Event tracking is enabled. Check DynamoDB for delivery status.")
24638
+ pc35.dim("Event tracking is enabled. Check DynamoDB for delivery status.")
23020
24639
  );
23021
24640
  }
23022
24641
  trackCommand("sms:test", {
@@ -23025,7 +24644,7 @@ Run ${pc34.cyan("wraps sms init")} to deploy SMS infrastructure.
23025
24644
  message_length: message?.length,
23026
24645
  duration_ms: Date.now() - startTime
23027
24646
  });
23028
- clack32.outro(pc34.green("Test complete!"));
24647
+ clack32.outro(pc35.green("Test complete!"));
23029
24648
  } catch (error) {
23030
24649
  progress.stop();
23031
24650
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -23039,7 +24658,7 @@ Run ${pc34.cyan("wraps sms init")} to deploy SMS infrastructure.
23039
24658
  clack32.log.error("Toll-free number registration is not complete");
23040
24659
  console.log(
23041
24660
  `
23042
- Run ${pc34.cyan("wraps sms register")} to complete registration.
24661
+ Run ${pc35.cyan("wraps sms register")} to complete registration.
23043
24662
  `
23044
24663
  );
23045
24664
  } else if (errorMessage.includes("opt-out")) {
@@ -23057,8 +24676,8 @@ Run ${pc34.cyan("wraps sms register")} to complete registration.
23057
24676
  // src/commands/sms/upgrade.ts
23058
24677
  init_esm_shims();
23059
24678
  import * as clack33 from "@clack/prompts";
23060
- import * as pulumi26 from "@pulumi/pulumi";
23061
- import pc35 from "picocolors";
24679
+ import * as pulumi27 from "@pulumi/pulumi";
24680
+ import pc36 from "picocolors";
23062
24681
  init_events();
23063
24682
  init_aws();
23064
24683
  init_errors();
@@ -23068,7 +24687,7 @@ init_prompts();
23068
24687
  async function smsUpgrade(options) {
23069
24688
  const startTime = Date.now();
23070
24689
  let upgradeAction = "";
23071
- clack33.intro(pc35.bold("Wraps SMS Upgrade - Enhance Your SMS Infrastructure"));
24690
+ clack33.intro(pc36.bold("Wraps SMS Upgrade - Enhance Your SMS Infrastructure"));
23072
24691
  const progress = new DeploymentProgress();
23073
24692
  const wasAutoInstalled = await progress.execute(
23074
24693
  "Checking Pulumi CLI installation",
@@ -23081,7 +24700,7 @@ async function smsUpgrade(options) {
23081
24700
  "Validating AWS credentials",
23082
24701
  async () => validateAWSCredentials()
23083
24702
  );
23084
- progress.info(`Connected to AWS account: ${pc35.cyan(identity.accountId)}`);
24703
+ progress.info(`Connected to AWS account: ${pc36.cyan(identity.accountId)}`);
23085
24704
  let region = options.region;
23086
24705
  if (!region) {
23087
24706
  const defaultRegion = await getAWSRegion();
@@ -23090,34 +24709,34 @@ async function smsUpgrade(options) {
23090
24709
  const metadata = await loadConnectionMetadata(identity.accountId, region);
23091
24710
  if (!metadata) {
23092
24711
  clack33.log.error(
23093
- `No Wraps connection found for account ${pc35.cyan(identity.accountId)} in region ${pc35.cyan(region)}`
24712
+ `No Wraps connection found for account ${pc36.cyan(identity.accountId)} in region ${pc36.cyan(region)}`
23094
24713
  );
23095
24714
  clack33.log.info(
23096
- `Use ${pc35.cyan("wraps sms init")} to create new infrastructure.`
24715
+ `Use ${pc36.cyan("wraps sms init")} to create new infrastructure.`
23097
24716
  );
23098
24717
  process.exit(1);
23099
24718
  }
23100
24719
  if (!metadata.services.sms) {
23101
24720
  clack33.log.error("No SMS infrastructure found");
23102
24721
  clack33.log.info(
23103
- `Use ${pc35.cyan("wraps sms init")} to deploy SMS infrastructure.`
24722
+ `Use ${pc36.cyan("wraps sms init")} to deploy SMS infrastructure.`
23104
24723
  );
23105
24724
  process.exit(1);
23106
24725
  }
23107
24726
  progress.info(`Found existing connection created: ${metadata.timestamp}`);
23108
24727
  console.log(`
23109
- ${pc35.bold("Current Configuration:")}
24728
+ ${pc36.bold("Current Configuration:")}
23110
24729
  `);
23111
24730
  if (metadata.services.sms.preset) {
23112
- console.log(` Preset: ${pc35.cyan(metadata.services.sms.preset)}`);
24731
+ console.log(` Preset: ${pc36.cyan(metadata.services.sms.preset)}`);
23113
24732
  } else {
23114
- console.log(` Preset: ${pc35.cyan("custom")}`);
24733
+ console.log(` Preset: ${pc36.cyan("custom")}`);
23115
24734
  }
23116
24735
  const config2 = metadata.services.sms.config;
23117
24736
  if (!config2) {
23118
24737
  clack33.log.error("No SMS configuration found in metadata");
23119
24738
  clack33.log.info(
23120
- `Use ${pc35.cyan("wraps sms init")} to create new infrastructure.`
24739
+ `Use ${pc36.cyan("wraps sms init")} to create new infrastructure.`
23121
24740
  );
23122
24741
  process.exit(1);
23123
24742
  }
@@ -23129,45 +24748,45 @@ ${pc35.bold("Current Configuration:")}
23129
24748
  "short-code": "Short code ($995+/mo, 100+ MPS)"
23130
24749
  };
23131
24750
  console.log(
23132
- ` Phone Type: ${pc35.cyan(phoneTypeLabels2[config2.phoneNumberType] || config2.phoneNumberType)}`
24751
+ ` Phone Type: ${pc36.cyan(phoneTypeLabels2[config2.phoneNumberType] || config2.phoneNumberType)}`
23133
24752
  );
23134
24753
  }
23135
24754
  if (config2.tracking?.enabled) {
23136
- console.log(` ${pc35.green("\u2713")} Delivery Tracking`);
24755
+ console.log(` ${pc36.green("\u2713")} Delivery Tracking`);
23137
24756
  if (config2.tracking.linkTracking) {
23138
- console.log(` ${pc35.dim("\u2514\u2500")} Link click tracking enabled`);
24757
+ console.log(` ${pc36.dim("\u2514\u2500")} Link click tracking enabled`);
23139
24758
  }
23140
24759
  }
23141
24760
  if (config2.eventTracking?.enabled) {
23142
- console.log(` ${pc35.green("\u2713")} Event Tracking (SNS)`);
24761
+ console.log(` ${pc36.green("\u2713")} Event Tracking (SNS)`);
23143
24762
  if (config2.eventTracking.dynamoDBHistory) {
23144
24763
  console.log(
23145
- ` ${pc35.dim("\u2514\u2500")} Message History: ${pc35.cyan(config2.eventTracking.archiveRetention || "90days")}`
24764
+ ` ${pc36.dim("\u2514\u2500")} Message History: ${pc36.cyan(config2.eventTracking.archiveRetention || "90days")}`
23146
24765
  );
23147
24766
  }
23148
24767
  }
23149
24768
  if (config2.messageArchiving?.enabled) {
23150
24769
  console.log(
23151
- ` ${pc35.green("\u2713")} Message Archiving (${config2.messageArchiving.retention})`
24770
+ ` ${pc36.green("\u2713")} Message Archiving (${config2.messageArchiving.retention})`
23152
24771
  );
23153
24772
  }
23154
24773
  if (config2.optOutManagement) {
23155
- console.log(` ${pc35.green("\u2713")} Opt-out Management`);
24774
+ console.log(` ${pc36.green("\u2713")} Opt-out Management`);
23156
24775
  }
23157
24776
  if (config2.protectConfiguration?.enabled) {
23158
24777
  const countries = config2.protectConfiguration.allowedCountries?.join(", ") || "US";
23159
- console.log(` ${pc35.green("\u2713")} Fraud Protection`);
23160
- console.log(` ${pc35.dim("\u2514\u2500")} Allowed countries: ${pc35.cyan(countries)}`);
24778
+ console.log(` ${pc36.green("\u2713")} Fraud Protection`);
24779
+ console.log(` ${pc36.dim("\u2514\u2500")} Allowed countries: ${pc36.cyan(countries)}`);
23161
24780
  if (config2.protectConfiguration.aitFiltering) {
23162
- console.log(` ${pc35.dim("\u2514\u2500")} AIT filtering: ${pc35.cyan("enabled")}`);
24781
+ console.log(` ${pc36.dim("\u2514\u2500")} AIT filtering: ${pc36.cyan("enabled")}`);
23163
24782
  }
23164
24783
  } else {
23165
- console.log(` ${pc35.dim("\u25CB")} Fraud Protection (not configured)`);
24784
+ console.log(` ${pc36.dim("\u25CB")} Fraud Protection (not configured)`);
23166
24785
  }
23167
24786
  const currentCostData = calculateSMSCosts(config2, 1e4);
23168
24787
  console.log(
23169
24788
  `
23170
- Estimated Cost: ${pc35.cyan(`~${formatCost3(currentCostData.total.monthly)}/mo`)}`
24789
+ Estimated Cost: ${pc36.cyan(`~${formatCost3(currentCostData.total.monthly)}/mo`)}`
23171
24790
  );
23172
24791
  console.log("");
23173
24792
  const phoneTypeLabels = {
@@ -23274,22 +24893,22 @@ ${pc35.bold("Current Configuration:")}
23274
24893
  if (selectedType === "toll-free") {
23275
24894
  console.log(
23276
24895
  `
23277
- ${pc35.yellow("\u26A0")} ${pc35.bold("Toll-free Registration Required")}
24896
+ ${pc36.yellow("\u26A0")} ${pc36.bold("Toll-free Registration Required")}
23278
24897
  `
23279
24898
  );
23280
24899
  console.log(
23281
- pc35.dim("Toll-free numbers require carrier registration before")
24900
+ pc36.dim("Toll-free numbers require carrier registration before")
23282
24901
  );
23283
24902
  console.log(
23284
- pc35.dim("they can send messages at scale. After deployment:\n")
24903
+ pc36.dim("they can send messages at scale. After deployment:\n")
23285
24904
  );
23286
24905
  console.log(
23287
- ` 1. Run ${pc35.cyan("wraps sms register")} to start registration`
24906
+ ` 1. Run ${pc36.cyan("wraps sms register")} to start registration`
23288
24907
  );
23289
24908
  console.log(" 2. Submit your business use case information");
23290
24909
  console.log(" 3. Wait for carrier verification (1-5 business days)");
23291
24910
  console.log(
23292
- pc35.dim("\nUntil verified, sending is limited to low volume.\n")
24911
+ pc36.dim("\nUntil verified, sending is limited to low volume.\n")
23293
24912
  );
23294
24913
  const confirmTollFree = await clack33.confirm({
23295
24914
  message: "Continue with toll-free number request?",
@@ -23303,10 +24922,10 @@ ${pc35.yellow("\u26A0")} ${pc35.bold("Toll-free Registration Required")}
23303
24922
  if (selectedType === "10dlc") {
23304
24923
  console.log(
23305
24924
  `
23306
- ${pc35.yellow("\u26A0")} ${pc35.bold("10DLC Campaign Registration Required")}
24925
+ ${pc36.yellow("\u26A0")} ${pc36.bold("10DLC Campaign Registration Required")}
23307
24926
  `
23308
24927
  );
23309
- console.log(pc35.dim("10DLC requires brand and campaign registration:"));
24928
+ console.log(pc36.dim("10DLC requires brand and campaign registration:"));
23310
24929
  console.log(" \u2022 Brand registration: one-time $4 fee");
23311
24930
  console.log(" \u2022 Campaign registration: $15/mo per campaign");
23312
24931
  console.log(" \u2022 Verification takes 1-7 business days");
@@ -23539,12 +25158,12 @@ ${pc35.yellow("\u26A0")} ${pc35.bold("10DLC Campaign Registration Required")}
23539
25158
  const enableLinkTracking = !config2.tracking?.linkTracking;
23540
25159
  if (enableLinkTracking) {
23541
25160
  clack33.log.info(
23542
- pc35.dim(
25161
+ pc36.dim(
23543
25162
  "Link tracking will track clicks on URLs in your SMS messages."
23544
25163
  )
23545
25164
  );
23546
25165
  clack33.log.info(
23547
- pc35.dim("URLs will be rewritten to go through a tracking endpoint.")
25166
+ pc36.dim("URLs will be rewritten to go through a tracking endpoint.")
23548
25167
  );
23549
25168
  }
23550
25169
  const confirmed = await clack33.confirm({
@@ -23757,18 +25376,18 @@ ${pc35.yellow("\u26A0")} ${pc35.bold("10DLC Campaign Registration Required")}
23757
25376
  const newCostData = calculateSMSCosts(updatedConfig, 1e4);
23758
25377
  const costDiff = newCostData.total.monthly - currentCostData.total.monthly;
23759
25378
  console.log(`
23760
- ${pc35.bold("Cost Impact:")}`);
25379
+ ${pc36.bold("Cost Impact:")}`);
23761
25380
  console.log(
23762
- ` Current: ${pc35.cyan(`${formatCost3(currentCostData.total.monthly)}/mo`)}`
25381
+ ` Current: ${pc36.cyan(`${formatCost3(currentCostData.total.monthly)}/mo`)}`
23763
25382
  );
23764
25383
  console.log(
23765
- ` New: ${pc35.cyan(`${formatCost3(newCostData.total.monthly)}/mo`)}`
25384
+ ` New: ${pc36.cyan(`${formatCost3(newCostData.total.monthly)}/mo`)}`
23766
25385
  );
23767
25386
  if (costDiff > 0) {
23768
- console.log(` Change: ${pc35.yellow(`+${formatCost3(costDiff)}/mo`)}`);
25387
+ console.log(` Change: ${pc36.yellow(`+${formatCost3(costDiff)}/mo`)}`);
23769
25388
  } else if (costDiff < 0) {
23770
25389
  console.log(
23771
- ` Change: ${pc35.green(`-${formatCost3(Math.abs(costDiff))}/mo`)}`
25390
+ ` Change: ${pc36.green(`-${formatCost3(Math.abs(costDiff))}/mo`)}`
23772
25391
  );
23773
25392
  }
23774
25393
  console.log("");
@@ -23800,7 +25419,7 @@ ${pc35.bold("Cost Impact:")}`);
23800
25419
  "Updating SMS infrastructure (this may take 2-3 minutes)",
23801
25420
  async () => {
23802
25421
  await ensurePulumiWorkDir();
23803
- const stack = await pulumi26.automation.LocalWorkspace.createOrSelectStack(
25422
+ const stack = await pulumi27.automation.LocalWorkspace.createOrSelectStack(
23804
25423
  {
23805
25424
  stackName: metadata.services.sms?.pulumiStackName || `wraps-sms-${identity.accountId}-${region}`,
23806
25425
  projectName: "wraps-sms",
@@ -23901,43 +25520,43 @@ ${pc35.bold("Cost Impact:")}`);
23901
25520
  await saveConnectionMetadata(metadata);
23902
25521
  progress.info("Connection metadata updated");
23903
25522
  console.log("\n");
23904
- clack33.log.success(pc35.green(pc35.bold("SMS infrastructure upgraded!")));
25523
+ clack33.log.success(pc36.green(pc36.bold("SMS infrastructure upgraded!")));
23905
25524
  console.log("\n");
23906
25525
  clack33.note(
23907
25526
  [
23908
- `${pc35.bold("Phone Number:")} ${pc35.cyan(outputs.phoneNumber || "Provisioning...")}`,
23909
- `${pc35.bold("Phone Type:")} ${pc35.cyan(updatedConfig.phoneNumberType || "simulator")}`,
23910
- `${pc35.bold("Config Set:")} ${pc35.cyan(outputs.configSetName || "wraps-sms-config")}`,
23911
- `${pc35.bold("Region:")} ${pc35.cyan(outputs.region)}`,
23912
- outputs.tableName ? `${pc35.bold("History Table:")} ${pc35.cyan(outputs.tableName)}` : "",
25527
+ `${pc36.bold("Phone Number:")} ${pc36.cyan(outputs.phoneNumber || "Provisioning...")}`,
25528
+ `${pc36.bold("Phone Type:")} ${pc36.cyan(updatedConfig.phoneNumberType || "simulator")}`,
25529
+ `${pc36.bold("Config Set:")} ${pc36.cyan(outputs.configSetName || "wraps-sms-config")}`,
25530
+ `${pc36.bold("Region:")} ${pc36.cyan(outputs.region)}`,
25531
+ outputs.tableName ? `${pc36.bold("History Table:")} ${pc36.cyan(outputs.tableName)}` : "",
23913
25532
  "",
23914
- pc35.dim("IAM Role:"),
23915
- pc35.dim(` ${outputs.roleArn}`)
25533
+ pc36.dim("IAM Role:"),
25534
+ pc36.dim(` ${outputs.roleArn}`)
23916
25535
  ].filter(Boolean).join("\n"),
23917
25536
  "SMS Infrastructure"
23918
25537
  );
23919
25538
  console.log(`
23920
- ${pc35.green("\u2713")} ${pc35.bold("Upgrade complete!")}
25539
+ ${pc36.green("\u2713")} ${pc36.bold("Upgrade complete!")}
23921
25540
  `);
23922
25541
  if (upgradeAction === "phone-number") {
23923
25542
  console.log(
23924
- `Upgraded to ${pc35.cyan(updatedConfig.phoneNumberType)} number (${pc35.green(`${formatCost3(newCostData.total.monthly)}/mo`)})
25543
+ `Upgraded to ${pc36.cyan(updatedConfig.phoneNumberType)} number (${pc36.green(`${formatCost3(newCostData.total.monthly)}/mo`)})
23925
25544
  `
23926
25545
  );
23927
25546
  if (updatedConfig.phoneNumberType === "toll-free") {
23928
- console.log(`${pc35.bold("Next Steps:")}`);
25547
+ console.log(`${pc36.bold("Next Steps:")}`);
23929
25548
  console.log(
23930
- ` 1. Run ${pc35.cyan("wraps sms register")} to start toll-free registration`
25549
+ ` 1. Run ${pc36.cyan("wraps sms register")} to start toll-free registration`
23931
25550
  );
23932
25551
  console.log(" 2. Submit your business information and use case");
23933
25552
  console.log(" 3. Wait for carrier verification (1-5 business days)");
23934
25553
  console.log("");
23935
25554
  console.log(
23936
- pc35.dim("Until verified, your number can only send limited messages.")
25555
+ pc36.dim("Until verified, your number can only send limited messages.")
23937
25556
  );
23938
25557
  console.log("");
23939
25558
  } else if (updatedConfig.phoneNumberType === "10dlc") {
23940
- console.log(`${pc35.bold("Next Steps:")}`);
25559
+ console.log(`${pc36.bold("Next Steps:")}`);
23941
25560
  console.log(" 1. Register your brand in the AWS Console");
23942
25561
  console.log(" 2. Create a 10DLC campaign for your use case");
23943
25562
  console.log(" 3. Wait for campaign approval (1-7 business days)");
@@ -23945,16 +25564,16 @@ ${pc35.green("\u2713")} ${pc35.bold("Upgrade complete!")}
23945
25564
  }
23946
25565
  } else if (upgradeAction === "preset" && newPreset) {
23947
25566
  console.log(
23948
- `Upgraded to ${pc35.cyan(newPreset)} preset (${pc35.green(`${formatCost3(newCostData.total.monthly)}/mo`)})
25567
+ `Upgraded to ${pc36.cyan(newPreset)} preset (${pc36.green(`${formatCost3(newCostData.total.monthly)}/mo`)})
23949
25568
  `
23950
25569
  );
23951
25570
  } else {
23952
25571
  console.log(
23953
- `Updated configuration (${pc35.green(`${formatCost3(newCostData.total.monthly)}/mo`)})
25572
+ `Updated configuration (${pc36.green(`${formatCost3(newCostData.total.monthly)}/mo`)})
23954
25573
  `
23955
25574
  );
23956
25575
  }
23957
- console.log(pc35.dim(getSMSCostSummary(updatedConfig, 1e4)));
25576
+ console.log(pc36.dim(getSMSCostSummary(updatedConfig, 1e4)));
23958
25577
  const enabledFeatures = [];
23959
25578
  if (updatedConfig.tracking?.enabled) {
23960
25579
  enabledFeatures.push("tracking");
@@ -23978,7 +25597,7 @@ ${pc35.green("\u2713")} ${pc35.bold("Upgrade complete!")}
23978
25597
  action: typeof upgradeAction === "string" ? upgradeAction : void 0,
23979
25598
  duration_ms: Date.now() - startTime
23980
25599
  });
23981
- clack33.outro(pc35.green("Upgrade complete!"));
25600
+ clack33.outro(pc36.green("Upgrade complete!"));
23982
25601
  }
23983
25602
 
23984
25603
  // src/commands/sms/verify-number.ts
@@ -23995,14 +25614,14 @@ import {
23995
25614
  VerifyDestinationNumberCommand
23996
25615
  } from "@aws-sdk/client-pinpoint-sms-voice-v2";
23997
25616
  import * as clack34 from "@clack/prompts";
23998
- import pc36 from "picocolors";
25617
+ import pc37 from "picocolors";
23999
25618
  function isValidPhoneNumber2(phone) {
24000
25619
  return /^\+[1-9]\d{1,14}$/.test(phone);
24001
25620
  }
24002
25621
  async function smsVerifyNumber(options) {
24003
25622
  const startTime = Date.now();
24004
25623
  const progress = new DeploymentProgress();
24005
- clack34.intro(pc36.bold("Wraps SMS - Verify Destination Number"));
25624
+ clack34.intro(pc37.bold("Wraps SMS - Verify Destination Number"));
24006
25625
  const identity = await progress.execute(
24007
25626
  "Validating AWS credentials",
24008
25627
  async () => validateAWSCredentials()
@@ -24014,7 +25633,7 @@ async function smsVerifyNumber(options) {
24014
25633
  clack34.log.error("No SMS infrastructure found");
24015
25634
  console.log(
24016
25635
  `
24017
- Run ${pc36.cyan("wraps sms init")} to deploy SMS infrastructure.
25636
+ Run ${pc37.cyan("wraps sms init")} to deploy SMS infrastructure.
24018
25637
  `
24019
25638
  );
24020
25639
  process.exit(1);
@@ -24031,16 +25650,16 @@ Run ${pc36.cyan("wraps sms init")} to deploy SMS infrastructure.
24031
25650
  clack34.log.info("No verified destination numbers found");
24032
25651
  console.log(
24033
25652
  `
24034
- Run ${pc36.cyan("wraps sms verify-number")} to verify a number.
25653
+ Run ${pc37.cyan("wraps sms verify-number")} to verify a number.
24035
25654
  `
24036
25655
  );
24037
25656
  } else {
24038
25657
  console.log("\n");
24039
- clack34.log.info(pc36.bold("Verified Destination Numbers:"));
25658
+ clack34.log.info(pc37.bold("Verified Destination Numbers:"));
24040
25659
  console.log("");
24041
25660
  for (const num of response.VerifiedDestinationNumbers) {
24042
- const status2 = num.Status === "VERIFIED" ? pc36.green("\u2713 Verified") : pc36.yellow("\u29D6 Pending");
24043
- console.log(` ${pc36.cyan(num.DestinationPhoneNumber)} - ${status2}`);
25661
+ const status2 = num.Status === "VERIFIED" ? pc37.green("\u2713 Verified") : pc37.yellow("\u29D6 Pending");
25662
+ console.log(` ${pc37.cyan(num.DestinationPhoneNumber)} - ${status2}`);
24044
25663
  }
24045
25664
  console.log("");
24046
25665
  }
@@ -24049,7 +25668,7 @@ Run ${pc36.cyan("wraps sms verify-number")} to verify a number.
24049
25668
  count: response.VerifiedDestinationNumbers?.length || 0,
24050
25669
  duration_ms: Date.now() - startTime
24051
25670
  });
24052
- clack34.outro(pc36.green("Done!"));
25671
+ clack34.outro(pc37.green("Done!"));
24053
25672
  return;
24054
25673
  } catch (error) {
24055
25674
  progress.stop();
@@ -24068,7 +25687,7 @@ Run ${pc36.cyan("wraps sms verify-number")} to verify a number.
24068
25687
  clack34.log.error("Phone number is required for deletion");
24069
25688
  console.log(
24070
25689
  `
24071
- Usage: ${pc36.cyan("wraps sms verify-number --delete --phone-number +14155551234")}
25690
+ Usage: ${pc37.cyan("wraps sms verify-number --delete --phone-number +14155551234")}
24072
25691
  `
24073
25692
  );
24074
25693
  process.exit(1);
@@ -24093,12 +25712,12 @@ Usage: ${pc36.cyan("wraps sms verify-number --delete --phone-number +14155551234
24093
25712
  );
24094
25713
  });
24095
25714
  progress.stop();
24096
- clack34.log.success(`Removed ${pc36.cyan(phoneNumber2)} from verified list`);
25715
+ clack34.log.success(`Removed ${pc37.cyan(phoneNumber2)} from verified list`);
24097
25716
  trackCommand("sms:verify-number:delete", {
24098
25717
  success: true,
24099
25718
  duration_ms: Date.now() - startTime
24100
25719
  });
24101
- clack34.outro(pc36.green("Done!"));
25720
+ clack34.outro(pc37.green("Done!"));
24102
25721
  return;
24103
25722
  } catch (error) {
24104
25723
  progress.stop();
@@ -24163,17 +25782,17 @@ Usage: ${pc36.cyan("wraps sms verify-number --delete --phone-number +14155551234
24163
25782
  progress.stop();
24164
25783
  console.log("\n");
24165
25784
  clack34.log.success(
24166
- pc36.green(`Phone number ${pc36.cyan(phoneNumber)} verified!`)
25785
+ pc37.green(`Phone number ${pc37.cyan(phoneNumber)} verified!`)
24167
25786
  );
24168
25787
  console.log("");
24169
25788
  console.log(
24170
- `You can now send test messages to this number with ${pc36.cyan("wraps sms test")}`
25789
+ `You can now send test messages to this number with ${pc37.cyan("wraps sms test")}`
24171
25790
  );
24172
25791
  trackCommand("sms:verify-number:confirm", {
24173
25792
  success: true,
24174
25793
  duration_ms: Date.now() - startTime
24175
25794
  });
24176
- clack34.outro(pc36.green("Verification complete!"));
25795
+ clack34.outro(pc37.green("Verification complete!"));
24177
25796
  return;
24178
25797
  } catch (error) {
24179
25798
  progress.stop();
@@ -24182,7 +25801,7 @@ Usage: ${pc36.cyan("wraps sms verify-number --delete --phone-number +14155551234
24182
25801
  clack34.log.error("Invalid verification code. Please try again.");
24183
25802
  console.log(
24184
25803
  `
24185
- Run ${pc36.cyan(`wraps sms verify-number --phone-number ${phoneNumber} --resend`)} to get a new code.
25804
+ Run ${pc37.cyan(`wraps sms verify-number --phone-number ${phoneNumber} --resend`)} to get a new code.
24186
25805
  `
24187
25806
  );
24188
25807
  } else {
@@ -24218,17 +25837,17 @@ Run ${pc36.cyan(`wraps sms verify-number --phone-number ${phoneNumber} --resend`
24218
25837
  );
24219
25838
  });
24220
25839
  progress.stop();
24221
- clack34.log.success(`Verification code resent to ${pc36.cyan(phoneNumber)}`);
25840
+ clack34.log.success(`Verification code resent to ${pc37.cyan(phoneNumber)}`);
24222
25841
  console.log("");
24223
25842
  console.log(
24224
25843
  `Once you receive the code, run:
24225
- ${pc36.cyan(`wraps sms verify-number --phone-number ${phoneNumber} --code YOUR_CODE`)}`
25844
+ ${pc37.cyan(`wraps sms verify-number --phone-number ${phoneNumber} --code YOUR_CODE`)}`
24226
25845
  );
24227
25846
  trackCommand("sms:verify-number:resend", {
24228
25847
  success: true,
24229
25848
  duration_ms: Date.now() - startTime
24230
25849
  });
24231
- clack34.outro(pc36.green("Code sent!"));
25850
+ clack34.outro(pc37.green("Code sent!"));
24232
25851
  return;
24233
25852
  } catch (error) {
24234
25853
  progress.stop();
@@ -24250,9 +25869,9 @@ Run ${pc36.cyan(`wraps sms verify-number --phone-number ${phoneNumber} --resend`
24250
25869
  if (existingNumber?.Status === "VERIFIED") {
24251
25870
  progress.stop();
24252
25871
  clack34.log.info(
24253
- `Number ${pc36.cyan(phoneNumber)} is already verified and ready to use!`
25872
+ `Number ${pc37.cyan(phoneNumber)} is already verified and ready to use!`
24254
25873
  );
24255
- clack34.outro(pc36.green("Done!"));
25874
+ clack34.outro(pc37.green("Done!"));
24256
25875
  return;
24257
25876
  }
24258
25877
  if (existingNumber?.Status === "PENDING") {
@@ -24269,14 +25888,14 @@ Run ${pc36.cyan(`wraps sms verify-number --phone-number ${phoneNumber} --resend`
24269
25888
  );
24270
25889
  progress.stop();
24271
25890
  clack34.log.info(
24272
- `Verification already in progress. New code sent to ${pc36.cyan(phoneNumber)}`
25891
+ `Verification already in progress. New code sent to ${pc37.cyan(phoneNumber)}`
24273
25892
  );
24274
25893
  console.log("");
24275
25894
  console.log(
24276
25895
  `Once you receive the code, run:
24277
- ${pc36.cyan(`wraps sms verify-number --phone-number ${phoneNumber} --code YOUR_CODE`)}`
25896
+ ${pc37.cyan(`wraps sms verify-number --phone-number ${phoneNumber} --code YOUR_CODE`)}`
24278
25897
  );
24279
- clack34.outro(pc36.green("Code sent!"));
25898
+ clack34.outro(pc37.green("Code sent!"));
24280
25899
  return;
24281
25900
  }
24282
25901
  const createResponse = await progress.execute(
@@ -24298,7 +25917,7 @@ Run ${pc36.cyan(`wraps sms verify-number --phone-number ${phoneNumber} --resend`
24298
25917
  progress.stop();
24299
25918
  console.log("\n");
24300
25919
  clack34.log.success(
24301
- `Verification code sent to ${pc36.cyan(phoneNumber)} via SMS`
25920
+ `Verification code sent to ${pc37.cyan(phoneNumber)} via SMS`
24302
25921
  );
24303
25922
  console.log("");
24304
25923
  clack34.note(
@@ -24306,9 +25925,9 @@ Run ${pc36.cyan(`wraps sms verify-number --phone-number ${phoneNumber} --resend`
24306
25925
  "1. Check your phone for the verification code",
24307
25926
  "",
24308
25927
  "2. Complete verification with:",
24309
- ` ${pc36.cyan(`wraps sms verify-number --phone-number ${phoneNumber} --code YOUR_CODE`)}`,
25928
+ ` ${pc37.cyan(`wraps sms verify-number --phone-number ${phoneNumber} --code YOUR_CODE`)}`,
24310
25929
  "",
24311
- pc36.dim("The code expires in 24 hours")
25930
+ pc37.dim("The code expires in 24 hours")
24312
25931
  ].join("\n"),
24313
25932
  "Next Steps"
24314
25933
  );
@@ -24316,7 +25935,7 @@ Run ${pc36.cyan(`wraps sms verify-number --phone-number ${phoneNumber} --resend`
24316
25935
  success: true,
24317
25936
  duration_ms: Date.now() - startTime
24318
25937
  });
24319
- clack34.outro(pc36.green("Verification started!"));
25938
+ clack34.outro(pc37.green("Verification started!"));
24320
25939
  } catch (error) {
24321
25940
  progress.stop();
24322
25941
  const errorMessage = error instanceof Error ? error.message : String(error);
@@ -24324,7 +25943,7 @@ Run ${pc36.cyan(`wraps sms verify-number --phone-number ${phoneNumber} --resend`
24324
25943
  clack34.log.error("This number is already being verified");
24325
25944
  console.log(
24326
25945
  `
24327
- Run ${pc36.cyan(`wraps sms verify-number --phone-number ${phoneNumber} --resend`)} to get a new code.
25946
+ Run ${pc37.cyan(`wraps sms verify-number --phone-number ${phoneNumber} --resend`)} to get a new code.
24328
25947
  `
24329
25948
  );
24330
25949
  } else {
@@ -24340,17 +25959,17 @@ Run ${pc36.cyan(`wraps sms verify-number --phone-number ${phoneNumber} --resend`
24340
25959
  // src/commands/support.ts
24341
25960
  init_esm_shims();
24342
25961
  import * as clack35 from "@clack/prompts";
24343
- import pc37 from "picocolors";
25962
+ import pc38 from "picocolors";
24344
25963
  async function support() {
24345
- clack35.intro(pc37.bold("Get Help with Wraps"));
25964
+ clack35.intro(pc38.bold("Get Help with Wraps"));
24346
25965
  console.log();
24347
- console.log(` ${pc37.bold("Email:")} ${pc37.cyan("hey@wraps.sh")}`);
25966
+ console.log(` ${pc38.bold("Email:")} ${pc38.cyan("hey@wraps.sh")}`);
24348
25967
  console.log(
24349
- ` ${pc37.bold("GitHub:")} ${pc37.cyan("https://github.com/wraps-dev/wraps/issues")}`
25968
+ ` ${pc38.bold("GitHub:")} ${pc38.cyan("https://github.com/wraps-dev/wraps/issues")}`
24350
25969
  );
24351
- console.log(` ${pc37.bold("Docs:")} ${pc37.cyan("https://wraps.dev/docs")}`);
25970
+ console.log(` ${pc38.bold("Docs:")} ${pc38.cyan("https://wraps.dev/docs")}`);
24352
25971
  console.log();
24353
- console.log(pc37.dim(" Response time: Usually within 24 hours"));
25972
+ console.log(pc38.dim(" Response time: Usually within 24 hours"));
24354
25973
  console.log();
24355
25974
  }
24356
25975
 
@@ -24358,55 +25977,55 @@ async function support() {
24358
25977
  init_esm_shims();
24359
25978
  init_client();
24360
25979
  import * as clack36 from "@clack/prompts";
24361
- import pc38 from "picocolors";
25980
+ import pc39 from "picocolors";
24362
25981
  async function telemetryEnable() {
24363
25982
  const client = getTelemetryClient();
24364
25983
  client.enable();
24365
- clack36.log.success(pc38.green("Telemetry enabled"));
24366
- console.log(` Config: ${pc38.dim(client.getConfigPath())}`);
25984
+ clack36.log.success(pc39.green("Telemetry enabled"));
25985
+ console.log(` Config: ${pc39.dim(client.getConfigPath())}`);
24367
25986
  console.log(`
24368
- ${pc38.dim("Thank you for helping improve Wraps!")}
25987
+ ${pc39.dim("Thank you for helping improve Wraps!")}
24369
25988
  `);
24370
25989
  }
24371
25990
  async function telemetryDisable() {
24372
25991
  const client = getTelemetryClient();
24373
25992
  client.disable();
24374
- clack36.log.success(pc38.green("Telemetry disabled"));
24375
- console.log(` Config: ${pc38.dim(client.getConfigPath())}`);
25993
+ clack36.log.success(pc39.green("Telemetry disabled"));
25994
+ console.log(` Config: ${pc39.dim(client.getConfigPath())}`);
24376
25995
  console.log(
24377
25996
  `
24378
- ${pc38.dim("You can re-enable with:")} wraps telemetry enable
25997
+ ${pc39.dim("You can re-enable with:")} wraps telemetry enable
24379
25998
  `
24380
25999
  );
24381
26000
  }
24382
26001
  async function telemetryStatus() {
24383
26002
  const client = getTelemetryClient();
24384
- clack36.intro(pc38.bold("Telemetry Status"));
24385
- const status2 = client.isEnabled() ? pc38.green("Enabled") : pc38.red("Disabled");
26003
+ clack36.intro(pc39.bold("Telemetry Status"));
26004
+ const status2 = client.isEnabled() ? pc39.green("Enabled") : pc39.red("Disabled");
24386
26005
  console.log();
24387
- console.log(` ${pc38.bold("Status:")} ${status2}`);
24388
- console.log(` ${pc38.bold("Config file:")} ${pc38.dim(client.getConfigPath())}`);
26006
+ console.log(` ${pc39.bold("Status:")} ${status2}`);
26007
+ console.log(` ${pc39.bold("Config file:")} ${pc39.dim(client.getConfigPath())}`);
24389
26008
  if (client.isEnabled()) {
24390
26009
  console.log();
24391
- console.log(pc38.bold(" How to opt-out:"));
24392
- console.log(` ${pc38.cyan("wraps telemetry disable")}`);
26010
+ console.log(pc39.bold(" How to opt-out:"));
26011
+ console.log(` ${pc39.cyan("wraps telemetry disable")}`);
24393
26012
  console.log(
24394
- ` ${pc38.dim("Or set:")} ${pc38.cyan("WRAPS_TELEMETRY_DISABLED=1")}`
26013
+ ` ${pc39.dim("Or set:")} ${pc39.cyan("WRAPS_TELEMETRY_DISABLED=1")}`
24395
26014
  );
24396
- console.log(` ${pc38.dim("Or set:")} ${pc38.cyan("DO_NOT_TRACK=1")}`);
26015
+ console.log(` ${pc39.dim("Or set:")} ${pc39.cyan("DO_NOT_TRACK=1")}`);
24397
26016
  } else {
24398
26017
  console.log();
24399
- console.log(pc38.bold(" How to opt-in:"));
24400
- console.log(` ${pc38.cyan("wraps telemetry enable")}`);
26018
+ console.log(pc39.bold(" How to opt-in:"));
26019
+ console.log(` ${pc39.cyan("wraps telemetry enable")}`);
24401
26020
  }
24402
26021
  console.log();
24403
- console.log(pc38.bold(" Debug mode:"));
26022
+ console.log(pc39.bold(" Debug mode:"));
24404
26023
  console.log(
24405
- ` ${pc38.dim("See what would be sent:")} ${pc38.cyan("WRAPS_TELEMETRY_DEBUG=1 wraps <command>")}`
26024
+ ` ${pc39.dim("See what would be sent:")} ${pc39.cyan("WRAPS_TELEMETRY_DEBUG=1 wraps <command>")}`
24406
26025
  );
24407
26026
  console.log();
24408
26027
  console.log(
24409
- ` ${pc38.dim("Learn more:")} ${pc38.cyan("https://wraps.dev/docs/telemetry")}`
26028
+ ` ${pc39.dim("Learn more:")} ${pc39.cyan("https://wraps.dev/docs/telemetry")}`
24410
26029
  );
24411
26030
  console.log();
24412
26031
  }
@@ -24491,135 +26110,138 @@ function showVersion() {
24491
26110
  process.exit(0);
24492
26111
  }
24493
26112
  function showHelp() {
24494
- clack37.intro(pc39.bold(`WRAPS CLI v${VERSION}`));
26113
+ clack37.intro(pc40.bold(`WRAPS CLI v${VERSION}`));
24495
26114
  console.log("Deploy AWS infrastructure to your account\n");
24496
26115
  console.log("Usage: wraps [service] <command> [options]\n");
24497
26116
  console.log("Services:");
24498
- console.log(` ${pc39.cyan("email")} Email infrastructure (AWS SES)`);
26117
+ console.log(` ${pc40.cyan("email")} Email infrastructure (AWS SES)`);
24499
26118
  console.log(
24500
- ` ${pc39.cyan("sms")} SMS infrastructure (AWS End User Messaging)`
26119
+ ` ${pc40.cyan("sms")} SMS infrastructure (AWS End User Messaging)`
24501
26120
  );
24502
26121
  console.log(
24503
- ` ${pc39.cyan("cdn")} CDN infrastructure (AWS S3 + CloudFront)
26122
+ ` ${pc40.cyan("cdn")} CDN infrastructure (AWS S3 + CloudFront)
24504
26123
  `
24505
26124
  );
24506
26125
  console.log("Email Commands:");
24507
26126
  console.log(
24508
- ` ${pc39.cyan("email init")} Deploy new email infrastructure`
26127
+ ` ${pc40.cyan("email init")} Deploy new email infrastructure`
24509
26128
  );
24510
26129
  console.log(
24511
- ` ${pc39.cyan("email check")} Check email deliverability for a domain`
26130
+ ` ${pc40.cyan("email check")} Check email deliverability for a domain`
24512
26131
  );
24513
26132
  console.log(
24514
- ` ${pc39.cyan("email connect")} Connect to existing AWS SES`
26133
+ ` ${pc40.cyan("email connect")} Connect to existing AWS SES`
24515
26134
  );
24516
26135
  console.log(
24517
- ` ${pc39.cyan("email status")} Show email infrastructure details`
26136
+ ` ${pc40.cyan("email status")} Show email infrastructure details`
24518
26137
  );
24519
- console.log(` ${pc39.cyan("email verify")} Verify domain DNS records`);
26138
+ console.log(` ${pc40.cyan("email verify")} Verify domain DNS records`);
24520
26139
  console.log(
24521
- ` ${pc39.cyan("email sync")} Apply CLI updates to infrastructure`
26140
+ ` ${pc40.cyan("email sync")} Apply CLI updates to infrastructure`
24522
26141
  );
24523
- console.log(` ${pc39.cyan("email upgrade")} Add features`);
26142
+ console.log(` ${pc40.cyan("email upgrade")} Add features`);
24524
26143
  console.log(
24525
- ` ${pc39.cyan("email restore")} Restore original configuration`
26144
+ ` ${pc40.cyan("email restore")} Restore original configuration`
24526
26145
  );
24527
26146
  console.log(
24528
- ` ${pc39.cyan("email destroy")} Remove email infrastructure`
26147
+ ` ${pc40.cyan("email destroy")} Remove email infrastructure`
24529
26148
  );
24530
- console.log(` ${pc39.cyan("email domains add")} Add a domain to SES`);
24531
- console.log(` ${pc39.cyan("email domains list")} List all domains`);
24532
- console.log(` ${pc39.cyan("email domains remove")} Remove a domain
26149
+ console.log(` ${pc40.cyan("email domains add")} Add a domain to SES`);
26150
+ console.log(` ${pc40.cyan("email domains list")} List all domains`);
26151
+ console.log(` ${pc40.cyan("email domains remove")} Remove a domain
24533
26152
  `);
24534
26153
  console.log("SMS Commands:");
24535
- console.log(` ${pc39.cyan("sms init")} Deploy SMS infrastructure`);
26154
+ console.log(` ${pc40.cyan("sms init")} Deploy SMS infrastructure`);
24536
26155
  console.log(
24537
- ` ${pc39.cyan("sms status")} Show SMS infrastructure details`
26156
+ ` ${pc40.cyan("sms status")} Show SMS infrastructure details`
24538
26157
  );
24539
- console.log(` ${pc39.cyan("sms test")} Send a test SMS message`);
26158
+ console.log(` ${pc40.cyan("sms test")} Send a test SMS message`);
24540
26159
  console.log(
24541
- ` ${pc39.cyan("sms verify-number")} Verify a destination phone number`
26160
+ ` ${pc40.cyan("sms verify-number")} Verify a destination phone number`
24542
26161
  );
24543
26162
  console.log(
24544
- ` ${pc39.cyan("sms sync")} Sync infrastructure (update Lambda, etc.)`
26163
+ ` ${pc40.cyan("sms sync")} Sync infrastructure (update Lambda, etc.)`
24545
26164
  );
24546
- console.log(` ${pc39.cyan("sms upgrade")} Upgrade SMS features`);
24547
- console.log(` ${pc39.cyan("sms register")} Register toll-free number`);
26165
+ console.log(` ${pc40.cyan("sms upgrade")} Upgrade SMS features`);
26166
+ console.log(` ${pc40.cyan("sms register")} Register toll-free number`);
24548
26167
  console.log(
24549
- ` ${pc39.cyan("sms destroy")} Remove SMS infrastructure
26168
+ ` ${pc40.cyan("sms destroy")} Remove SMS infrastructure
24550
26169
  `
24551
26170
  );
24552
26171
  console.log("CDN Commands:");
24553
26172
  console.log(
24554
- ` ${pc39.cyan("cdn init")} Deploy CDN infrastructure (S3 + CloudFront)`
26173
+ ` ${pc40.cyan("cdn init")} Deploy CDN infrastructure (S3 + CloudFront)`
24555
26174
  );
24556
26175
  console.log(
24557
- ` ${pc39.cyan("cdn status")} Show CDN infrastructure details`
26176
+ ` ${pc40.cyan("cdn status")} Show CDN infrastructure details`
24558
26177
  );
24559
26178
  console.log(
24560
- ` ${pc39.cyan("cdn verify")} Check DNS and certificate status`
26179
+ ` ${pc40.cyan("cdn verify")} Check DNS and certificate status`
24561
26180
  );
24562
26181
  console.log(
24563
- ` ${pc39.cyan("cdn upgrade")} Add custom domain after cert validation`
26182
+ ` ${pc40.cyan("cdn upgrade")} Add custom domain after cert validation`
24564
26183
  );
24565
26184
  console.log(
24566
- ` ${pc39.cyan("cdn sync")} Sync infrastructure with current config`
26185
+ ` ${pc40.cyan("cdn sync")} Sync infrastructure with current config`
24567
26186
  );
24568
26187
  console.log(
24569
- ` ${pc39.cyan("cdn destroy")} Remove CDN infrastructure
26188
+ ` ${pc40.cyan("cdn destroy")} Remove CDN infrastructure
24570
26189
  `
24571
26190
  );
24572
26191
  console.log("Local Development:");
24573
26192
  console.log(
24574
- ` ${pc39.cyan("console")} Start local web console
26193
+ ` ${pc40.cyan("console")} Start local web console
24575
26194
  `
24576
26195
  );
24577
26196
  console.log("Platform:");
24578
26197
  console.log(
24579
- ` ${pc39.cyan("platform")} Show platform info and pricing`
26198
+ ` ${pc40.cyan("platform")} Show platform info and pricing`
26199
+ );
26200
+ console.log(
26201
+ ` ${pc40.cyan("platform connect")} Connect to Wraps Platform (events + IAM)`
24580
26202
  );
24581
26203
  console.log(
24582
- ` ${pc39.cyan("platform update-role")} Update platform IAM permissions
26204
+ ` ${pc40.cyan("platform update-role")} Update platform IAM permissions
24583
26205
  `
24584
26206
  );
24585
26207
  console.log("AWS Setup:");
24586
26208
  console.log(
24587
- ` ${pc39.cyan("aws setup")} Interactive AWS setup wizard`
26209
+ ` ${pc40.cyan("aws setup")} Interactive AWS setup wizard`
24588
26210
  );
24589
26211
  console.log(
24590
- ` ${pc39.cyan("aws doctor")} Diagnose AWS configuration issues
26212
+ ` ${pc40.cyan("aws doctor")} Diagnose AWS configuration issues
24591
26213
  `
24592
26214
  );
24593
26215
  console.log("Global Commands:");
24594
- console.log(` ${pc39.cyan("status")} Show overview of all services`);
24595
- console.log(` ${pc39.cyan("destroy")} Remove deployed infrastructure`);
24596
- console.log(` ${pc39.cyan("permissions")} Show required AWS IAM permissions`);
24597
- console.log(` ${pc39.cyan("completion")} Generate shell completion script`);
26216
+ console.log(` ${pc40.cyan("status")} Show overview of all services`);
26217
+ console.log(` ${pc40.cyan("destroy")} Remove deployed infrastructure`);
26218
+ console.log(` ${pc40.cyan("permissions")} Show required AWS IAM permissions`);
26219
+ console.log(` ${pc40.cyan("completion")} Generate shell completion script`);
24598
26220
  console.log(
24599
- ` ${pc39.cyan("telemetry")} Manage anonymous telemetry settings`
26221
+ ` ${pc40.cyan("telemetry")} Manage anonymous telemetry settings`
24600
26222
  );
24601
- console.log(` ${pc39.cyan("news")} Show recent Wraps updates`);
26223
+ console.log(` ${pc40.cyan("news")} Show recent Wraps updates`);
24602
26224
  console.log(
24603
- ` ${pc39.cyan("support")} Get help and support contact info
26225
+ ` ${pc40.cyan("support")} Get help and support contact info
24604
26226
  `
24605
26227
  );
24606
26228
  console.log("Options:");
24607
26229
  console.log(
24608
- ` ${pc39.dim("-p, --provider")} Hosting provider (vercel, aws, railway, other)`
26230
+ ` ${pc40.dim("-p, --provider")} Hosting provider (vercel, aws, railway, other)`
24609
26231
  );
24610
- console.log(` ${pc39.dim("-r, --region")} AWS region`);
24611
- console.log(` ${pc39.dim("-d, --domain")} Domain name`);
24612
- console.log(` ${pc39.dim("--account")} AWS account ID or alias`);
24613
- console.log(` ${pc39.dim("--preset")} Configuration preset`);
24614
- console.log(` ${pc39.dim("-y, --yes")} Skip confirmation prompts`);
24615
- console.log(` ${pc39.dim("-f, --force")} Force destructive operations`);
26232
+ console.log(` ${pc40.dim("-r, --region")} AWS region`);
26233
+ console.log(` ${pc40.dim("-d, --domain")} Domain name`);
26234
+ console.log(` ${pc40.dim("--account")} AWS account ID or alias`);
26235
+ console.log(` ${pc40.dim("--preset")} Configuration preset`);
26236
+ console.log(` ${pc40.dim("-y, --yes")} Skip confirmation prompts`);
26237
+ console.log(` ${pc40.dim("-f, --force")} Force destructive operations`);
24616
26238
  console.log(
24617
- ` ${pc39.dim("--preview")} Preview changes without deploying`
26239
+ ` ${pc40.dim("--preview")} Preview changes without deploying`
24618
26240
  );
24619
- console.log(` ${pc39.dim("-v, --version")} Show version number
26241
+ console.log(` ${pc40.dim("-v, --version")} Show version number
24620
26242
  `);
24621
26243
  console.log(
24622
- `Run ${pc39.cyan("wraps <service> <command> --help")} for more information.
26244
+ `Run ${pc40.cyan("wraps <service> <command> --help")} for more information.
24623
26245
  `
24624
26246
  );
24625
26247
  process.exit(0);
@@ -24765,7 +26387,7 @@ var flags = args.parse(process.argv);
24765
26387
  var [primaryCommand, subCommand] = args.sub;
24766
26388
  if (!primaryCommand) {
24767
26389
  async function interactiveMenu() {
24768
- clack37.intro(pc39.bold(`WRAPS CLI v${VERSION}`));
26390
+ clack37.intro(pc40.bold(`WRAPS CLI v${VERSION}`));
24769
26391
  console.log(" Deploy AWS infrastructure to your account.\n");
24770
26392
  const action = await clack37.select({
24771
26393
  message: "What would you like to do?",
@@ -24879,20 +26501,20 @@ async function run() {
24879
26501
  const telemetry = getTelemetryClient();
24880
26502
  if (telemetry.shouldShowNotification()) {
24881
26503
  console.log();
24882
- clack37.log.info(pc39.bold("Anonymous Telemetry"));
26504
+ clack37.log.info(pc40.bold("Anonymous Telemetry"));
24883
26505
  console.log(
24884
- ` Wraps collects ${pc39.cyan("anonymous usage data")} to improve the CLI.`
26506
+ ` Wraps collects ${pc40.cyan("anonymous usage data")} to improve the CLI.`
24885
26507
  );
24886
26508
  console.log(
24887
- ` We ${pc39.bold("never")} collect: domains, AWS credentials, email content, or PII.`
26509
+ ` We ${pc40.bold("never")} collect: domains, AWS credentials, email content, or PII.`
24888
26510
  );
24889
26511
  console.log(
24890
- ` We ${pc39.bold("only")} collect: command names, success/failure, CLI version, OS.`
26512
+ ` We ${pc40.bold("only")} collect: command names, success/failure, CLI version, OS.`
24891
26513
  );
24892
26514
  console.log();
24893
- console.log(` Opt-out anytime: ${pc39.cyan("wraps telemetry disable")}`);
24894
- console.log(` Or set: ${pc39.cyan("WRAPS_TELEMETRY_DISABLED=1")}`);
24895
- console.log(` Learn more: ${pc39.cyan("https://wraps.dev/docs")}`);
26515
+ console.log(` Opt-out anytime: ${pc40.cyan("wraps telemetry disable")}`);
26516
+ console.log(` Or set: ${pc40.cyan("WRAPS_TELEMETRY_DISABLED=1")}`);
26517
+ console.log(` Learn more: ${pc40.cyan("https://wraps.dev/docs")}`);
24896
26518
  console.log();
24897
26519
  telemetry.markNotificationShown();
24898
26520
  }
@@ -24962,7 +26584,7 @@ async function run() {
24962
26584
  clack37.log.error("--domain flag is required");
24963
26585
  console.log(
24964
26586
  `
24965
- Usage: ${pc39.cyan("wraps email verify --domain yourapp.com")}
26587
+ Usage: ${pc40.cyan("wraps email verify --domain yourapp.com")}
24966
26588
  `
24967
26589
  );
24968
26590
  process.exit(1);
@@ -24978,7 +26600,7 @@ Usage: ${pc39.cyan("wraps email verify --domain yourapp.com")}
24978
26600
  clack37.log.error("--domain flag is required");
24979
26601
  console.log(
24980
26602
  `
24981
- Usage: ${pc39.cyan("wraps email domains add --domain yourapp.com")}
26603
+ Usage: ${pc40.cyan("wraps email domains add --domain yourapp.com")}
24982
26604
  `
24983
26605
  );
24984
26606
  process.exit(1);
@@ -24994,7 +26616,7 @@ Usage: ${pc39.cyan("wraps email domains add --domain yourapp.com")}
24994
26616
  clack37.log.error("--domain flag is required");
24995
26617
  console.log(
24996
26618
  `
24997
- Usage: ${pc39.cyan("wraps email domains verify --domain yourapp.com")}
26619
+ Usage: ${pc40.cyan("wraps email domains verify --domain yourapp.com")}
24998
26620
  `
24999
26621
  );
25000
26622
  process.exit(1);
@@ -25007,7 +26629,7 @@ Usage: ${pc39.cyan("wraps email domains verify --domain yourapp.com")}
25007
26629
  clack37.log.error("--domain flag is required");
25008
26630
  console.log(
25009
26631
  `
25010
- Usage: ${pc39.cyan("wraps email domains get-dkim --domain yourapp.com")}
26632
+ Usage: ${pc40.cyan("wraps email domains get-dkim --domain yourapp.com")}
25011
26633
  `
25012
26634
  );
25013
26635
  process.exit(1);
@@ -25020,7 +26642,7 @@ Usage: ${pc39.cyan("wraps email domains get-dkim --domain yourapp.com")}
25020
26642
  clack37.log.error("--domain flag is required");
25021
26643
  console.log(
25022
26644
  `
25023
- Usage: ${pc39.cyan("wraps email domains remove --domain yourapp.com --force")}
26645
+ Usage: ${pc40.cyan("wraps email domains remove --domain yourapp.com --force")}
25024
26646
  `
25025
26647
  );
25026
26648
  process.exit(1);
@@ -25037,7 +26659,7 @@ Usage: ${pc39.cyan("wraps email domains remove --domain yourapp.com --force")}
25037
26659
  );
25038
26660
  console.log(
25039
26661
  `
25040
- Available commands: ${pc39.cyan("add")}, ${pc39.cyan("list")}, ${pc39.cyan("verify")}, ${pc39.cyan("get-dkim")}, ${pc39.cyan("remove")}
26662
+ Available commands: ${pc40.cyan("add")}, ${pc40.cyan("list")}, ${pc40.cyan("verify")}, ${pc40.cyan("get-dkim")}, ${pc40.cyan("remove")}
25041
26663
  `
25042
26664
  );
25043
26665
  process.exit(1);
@@ -25055,7 +26677,7 @@ Available commands: ${pc39.cyan("add")}, ${pc39.cyan("list")}, ${pc39.cyan("veri
25055
26677
  clack37.log.error(`Unknown email command: ${subCommand}`);
25056
26678
  console.log(
25057
26679
  `
25058
- Run ${pc39.cyan("wraps --help")} for available commands.
26680
+ Run ${pc40.cyan("wraps --help")} for available commands.
25059
26681
  `
25060
26682
  );
25061
26683
  process.exit(1);
@@ -25126,7 +26748,7 @@ Run ${pc39.cyan("wraps --help")} for available commands.
25126
26748
  clack37.log.error(`Unknown sms command: ${subCommand}`);
25127
26749
  console.log(
25128
26750
  `
25129
- Run ${pc39.cyan("wraps --help")} for available commands.
26751
+ Run ${pc40.cyan("wraps --help")} for available commands.
25130
26752
  `
25131
26753
  );
25132
26754
  process.exit(1);
@@ -25185,7 +26807,7 @@ Run ${pc39.cyan("wraps --help")} for available commands.
25185
26807
  clack37.log.error(`Unknown cdn command: ${subCommand}`);
25186
26808
  console.log(
25187
26809
  `
25188
- Run ${pc39.cyan("wraps --help")} for available commands.
26810
+ Run ${pc40.cyan("wraps --help")} for available commands.
25189
26811
  `
25190
26812
  );
25191
26813
  process.exit(1);
@@ -25210,6 +26832,13 @@ Run ${pc39.cyan("wraps --help")} for available commands.
25210
26832
  return;
25211
26833
  }
25212
26834
  switch (subCommand) {
26835
+ case "connect":
26836
+ await connect3({
26837
+ region: flags.region,
26838
+ force: flags.force,
26839
+ yes: flags.yes
26840
+ });
26841
+ break;
25213
26842
  case "update-role":
25214
26843
  await updateRole({
25215
26844
  region: flags.region,
@@ -25218,11 +26847,13 @@ Run ${pc39.cyan("wraps --help")} for available commands.
25218
26847
  break;
25219
26848
  default:
25220
26849
  clack37.log.error(`Unknown platform command: ${subCommand}`);
25221
- console.log(`
25222
- Available commands: ${pc39.cyan("update-role")}
25223
- `);
25224
26850
  console.log(
25225
- `Run ${pc39.cyan("wraps platform")} for more information.
26851
+ `
26852
+ Available commands: ${pc40.cyan("connect")}, ${pc40.cyan("update-role")}
26853
+ `
26854
+ );
26855
+ console.log(
26856
+ `Run ${pc40.cyan("wraps platform")} for more information.
25226
26857
  `
25227
26858
  );
25228
26859
  process.exit(1);
@@ -25249,10 +26880,10 @@ Available commands: ${pc39.cyan("update-role")}
25249
26880
  clack37.log.error(`Unknown aws command: ${subCommand}`);
25250
26881
  console.log(
25251
26882
  `
25252
- Available commands: ${pc39.cyan("setup")}, ${pc39.cyan("doctor")}
26883
+ Available commands: ${pc40.cyan("setup")}, ${pc40.cyan("doctor")}
25253
26884
  `
25254
26885
  );
25255
- console.log(`Run ${pc39.cyan("wraps --help")} for more information.
26886
+ console.log(`Run ${pc40.cyan("wraps --help")} for more information.
25256
26887
  `);
25257
26888
  process.exit(1);
25258
26889
  }
@@ -25315,7 +26946,7 @@ Available commands: ${pc39.cyan("setup")}, ${pc39.cyan("doctor")}
25315
26946
  clack37.log.error(`Unknown telemetry command: ${subCommand}`);
25316
26947
  console.log(
25317
26948
  `
25318
- Available commands: ${pc39.cyan("enable")}, ${pc39.cyan("disable")}, ${pc39.cyan("status")}
26949
+ Available commands: ${pc40.cyan("enable")}, ${pc40.cyan("disable")}, ${pc40.cyan("status")}
25319
26950
  `
25320
26951
  );
25321
26952
  process.exit(1);
@@ -25338,7 +26969,7 @@ Please specify a command for ${primaryCommand} service.
25338
26969
  clack37.log.error(`Unknown command: ${primaryCommand}`);
25339
26970
  console.log(
25340
26971
  `
25341
- Run ${pc39.cyan("wraps --help")} for available commands.
26972
+ Run ${pc40.cyan("wraps --help")} for available commands.
25342
26973
  `
25343
26974
  );
25344
26975
  process.exit(1);