@wraps.dev/cli 2.11.3 → 2.11.4

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
@@ -18,6 +18,54 @@ var init_esm_shims = __esm({
18
18
  }
19
19
  });
20
20
 
21
+ // src/utils/shared/ci-detection.ts
22
+ function isCI() {
23
+ if (process.env.CI === "true" || process.env.CI === "1") {
24
+ return true;
25
+ }
26
+ const ciEnvVars = [
27
+ "GITHUB_ACTIONS",
28
+ // GitHub Actions
29
+ "GITLAB_CI",
30
+ // GitLab CI
31
+ "CIRCLECI",
32
+ // CircleCI
33
+ "TRAVIS",
34
+ // Travis CI
35
+ "JENKINS_URL",
36
+ // Jenkins
37
+ "BUILDKITE",
38
+ // Buildkite
39
+ "DRONE",
40
+ // Drone
41
+ "SEMAPHORE",
42
+ // Semaphore
43
+ "TEAMCITY_VERSION",
44
+ // TeamCity
45
+ "TF_BUILD",
46
+ // Azure Pipelines
47
+ "CODEBUILD_BUILD_ID",
48
+ // AWS CodeBuild
49
+ "NETLIFY",
50
+ // Netlify
51
+ "VERCEL",
52
+ // Vercel
53
+ "HEROKU_TEST_RUN_ID",
54
+ // Heroku CI
55
+ "BUDDY",
56
+ // Buddy
57
+ "BITBUCKET_BUILD_NUMBER"
58
+ // Bitbucket Pipelines
59
+ ];
60
+ return ciEnvVars.some((envVar) => process.env[envVar] !== void 0);
61
+ }
62
+ var init_ci_detection = __esm({
63
+ "src/utils/shared/ci-detection.ts"() {
64
+ "use strict";
65
+ init_esm_shims();
66
+ }
67
+ });
68
+
21
69
  // src/utils/shared/s3-state.ts
22
70
  var s3_state_exports = {};
23
71
  __export(s3_state_exports, {
@@ -369,414 +417,46 @@ var init_config = __esm({
369
417
  }
370
418
  });
371
419
 
372
- // src/utils/shared/aws-detection.ts
373
- import { execSync } from "child_process";
374
- import { existsSync as existsSync4, readdirSync, readFileSync } from "fs";
375
- import { homedir as homedir2 } from "os";
376
- import { join as join4 } from "path";
377
- import { GetCallerIdentityCommand, STSClient } from "@aws-sdk/client-sts";
378
- async function isAWSCLIInstalled() {
379
- try {
380
- execSync("aws --version", { stdio: "pipe" });
381
- return true;
382
- } catch {
383
- return false;
384
- }
385
- }
386
- async function getAWSCLIVersion() {
387
- try {
388
- const output4 = execSync("aws --version", { encoding: "utf-8" });
389
- const match = output4.match(/aws-cli\/(\d+\.\d+\.\d+)/);
390
- return match ? match[1] : null;
391
- } catch {
392
- return null;
393
- }
394
- }
395
- function detectCredentialSource() {
396
- if (process.env.AWS_ACCESS_KEY_ID && process.env.AWS_SECRET_ACCESS_KEY) {
397
- return "environment";
398
- }
399
- if (process.env.AWS_SSO_ACCOUNT_ID || process.env.AWS_SSO_SESSION) {
400
- return "sso";
401
- }
402
- if (process.env.AWS_PROFILE) {
403
- return "profile";
404
- }
405
- const credentialsPath = join4(homedir2(), ".aws", "credentials");
406
- if (existsSync4(credentialsPath)) {
407
- const content = readFileSync(credentialsPath, "utf-8");
408
- if (content.includes("[default]")) {
409
- return "profile";
410
- }
411
- }
412
- const ssoCachePath = join4(homedir2(), ".aws", "sso", "cache");
413
- if (existsSync4(ssoCachePath)) {
414
- return "sso";
415
- }
416
- if (process.env.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI || process.env.AWS_EXECUTION_ENV) {
417
- return "instance";
418
- }
419
- return null;
420
- }
421
- function detectHostingProvider() {
422
- if (process.env.VERCEL || process.env.VERCEL_ENV) {
423
- return "vercel";
424
- }
425
- if (process.env.RAILWAY_ENVIRONMENT || process.env.RAILWAY_PROJECT_ID) {
426
- return "railway";
427
- }
428
- if (process.env.NETLIFY || process.env.NETLIFY_DEV) {
429
- return "netlify";
430
- }
431
- if (process.env.AWS_LAMBDA_FUNCTION_NAME || process.env.AWS_EXECUTION_ENV || process.env.ECS_CONTAINER_METADATA_URI || process.env.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI) {
432
- return "aws";
433
- }
434
- return null;
435
- }
436
- async function validateCredentials() {
437
- try {
438
- const sts = new STSClient({ region: "us-east-1" });
439
- const identity = await sts.send(new GetCallerIdentityCommand({}));
440
- return identity.Account || null;
441
- } catch {
442
- return null;
443
- }
444
- }
445
- function getCurrentProfile() {
446
- return process.env.AWS_PROFILE || "default";
447
- }
448
- function getCurrentRegion() {
449
- if (process.env.AWS_REGION) {
450
- return process.env.AWS_REGION;
451
- }
452
- if (process.env.AWS_DEFAULT_REGION) {
453
- return process.env.AWS_DEFAULT_REGION;
454
- }
455
- try {
456
- const region = execSync("aws configure get region", {
457
- encoding: "utf-8",
458
- stdio: ["pipe", "pipe", "pipe"]
459
- }).trim();
460
- return region || null;
461
- } catch {
462
- return null;
463
- }
464
- }
465
- function parseSSOProfiles() {
466
- const configPath = join4(homedir2(), ".aws", "config");
467
- if (!existsSync4(configPath)) {
468
- return [];
469
- }
470
- const content = readFileSync(configPath, "utf-8");
471
- const profiles = [];
472
- const sessionMap = /* @__PURE__ */ new Map();
473
- const sessionSections = content.split(/^\[/m).filter(Boolean);
474
- for (const section of sessionSections) {
475
- const lines = section.split("\n");
476
- const header = lines[0]?.replace("]", "").trim();
477
- if (header?.startsWith("sso-session ")) {
478
- const sessionName = header.replace("sso-session ", "");
479
- const config2 = {};
480
- for (const line of lines.slice(1)) {
481
- const match = line.match(/^\s*([^=\s]+)\s*=\s*(.+?)\s*$/);
482
- if (match) {
483
- config2[match[1]] = match[2];
484
- }
420
+ // src/telemetry/config.ts
421
+ import Conf from "conf";
422
+ import { v4 as uuidv4 } from "uuid";
423
+ var CONFIG_DEFAULTS, TelemetryConfigManager;
424
+ var init_config2 = __esm({
425
+ "src/telemetry/config.ts"() {
426
+ "use strict";
427
+ init_esm_shims();
428
+ CONFIG_DEFAULTS = {
429
+ enabled: true,
430
+ anonymousId: uuidv4(),
431
+ notificationShown: false
432
+ };
433
+ TelemetryConfigManager = class {
434
+ config;
435
+ constructor(options) {
436
+ this.config = new Conf({
437
+ projectName: "wraps",
438
+ configName: "telemetry",
439
+ defaults: CONFIG_DEFAULTS,
440
+ cwd: options?.cwd
441
+ });
485
442
  }
486
- sessionMap.set(sessionName, {
487
- startUrl: config2.sso_start_url || "",
488
- region: config2.sso_region || ""
489
- });
490
- }
491
- }
492
- const sections = content.split(/^\[/m).filter(Boolean);
493
- for (const section of sections) {
494
- const lines = section.split("\n");
495
- const header = lines[0]?.replace("]", "").trim();
496
- if (!header?.startsWith("profile ") && header !== "default") {
497
- continue;
498
- }
499
- const profileName = header === "default" ? "default" : header.replace("profile ", "");
500
- const config2 = {};
501
- for (const line of lines.slice(1)) {
502
- const match = line.match(/^\s*([^=\s]+)\s*=\s*(.+?)\s*$/);
503
- if (match) {
504
- config2[match[1]] = match[2];
443
+ /**
444
+ * Check if telemetry is enabled
445
+ */
446
+ isEnabled() {
447
+ return this.config.get("enabled");
505
448
  }
506
- }
507
- if (config2.sso_start_url || config2.sso_session) {
508
- let ssoStartUrl = config2.sso_start_url || "";
509
- let ssoRegion = config2.sso_region || "";
510
- if (config2.sso_session) {
511
- const session = sessionMap.get(config2.sso_session);
512
- if (session) {
513
- ssoStartUrl = ssoStartUrl || session.startUrl;
514
- ssoRegion = ssoRegion || session.region;
515
- }
449
+ /**
450
+ * Enable or disable telemetry
451
+ */
452
+ setEnabled(enabled) {
453
+ this.config.set("enabled", enabled);
516
454
  }
517
- profiles.push({
518
- name: profileName,
519
- ssoStartUrl,
520
- ssoRegion,
521
- ssoAccountId: config2.sso_account_id || "",
522
- ssoRoleName: config2.sso_role_name || "",
523
- region: config2.region,
524
- ssoSession: config2.sso_session
525
- });
526
- }
527
- }
528
- return profiles;
529
- }
530
- function parseSSOSessions() {
531
- const configPath = join4(homedir2(), ".aws", "config");
532
- if (!existsSync4(configPath)) {
533
- return [];
534
- }
535
- const content = readFileSync(configPath, "utf-8");
536
- const sessions = [];
537
- const sections = content.split(/^\[/m).filter(Boolean);
538
- for (const section of sections) {
539
- const lines = section.split("\n");
540
- const header = lines[0]?.replace("]", "").trim();
541
- if (!header?.startsWith("sso-session ")) {
542
- continue;
543
- }
544
- const sessionName = header.replace("sso-session ", "");
545
- const config2 = {};
546
- for (const line of lines.slice(1)) {
547
- const match = line.match(/^\s*([^=\s]+)\s*=\s*(.+?)\s*$/);
548
- if (match) {
549
- config2[match[1]] = match[2];
550
- }
551
- }
552
- sessions.push({
553
- name: sessionName,
554
- ssoStartUrl: config2.sso_start_url || "",
555
- ssoRegion: config2.sso_region || "",
556
- ssoRegistrationScopes: config2.sso_registration_scopes?.split(",").map((s) => s.trim())
557
- });
558
- }
559
- return sessions;
560
- }
561
- function checkSSOTokenStatus(startUrl) {
562
- const ssoCachePath = join4(homedir2(), ".aws", "sso", "cache");
563
- if (!existsSync4(ssoCachePath)) {
564
- return {
565
- valid: false,
566
- expiresAt: null,
567
- expired: true,
568
- minutesRemaining: null,
569
- startUrl: null
570
- };
571
- }
572
- try {
573
- const cacheFiles = readdirSync(ssoCachePath).filter(
574
- (f) => f.endsWith(".json")
575
- );
576
- for (const file of cacheFiles) {
577
- const content = readFileSync(join4(ssoCachePath, file), "utf-8");
578
- const token = JSON.parse(content);
579
- if (!(token.accessToken && token.expiresAt)) {
580
- continue;
581
- }
582
- if (startUrl && token.startUrl !== startUrl) {
583
- continue;
584
- }
585
- const expiresAt = new Date(token.expiresAt);
586
- const now = /* @__PURE__ */ new Date();
587
- const expired = expiresAt <= now;
588
- const minutesRemaining = Math.floor(
589
- (expiresAt.getTime() - now.getTime()) / 6e4
590
- );
591
- return {
592
- valid: !expired,
593
- expiresAt,
594
- expired,
595
- minutesRemaining,
596
- startUrl: token.startUrl || null
597
- };
598
- }
599
- } catch {
600
- }
601
- return {
602
- valid: false,
603
- expiresAt: null,
604
- expired: true,
605
- minutesRemaining: null,
606
- startUrl: null
607
- };
608
- }
609
- function getActiveSSOProfile(profiles) {
610
- const currentProfile = process.env.AWS_PROFILE || "default";
611
- return profiles.find((p) => p.name === currentProfile) || null;
612
- }
613
- function getSSOLoginCommand(profile) {
614
- if (profile && profile !== "default") {
615
- return `aws sso login --profile ${profile}`;
616
- }
617
- return "aws sso login";
618
- }
619
- function formatSSOProfile(profile) {
620
- return `${profile.name} (${profile.ssoAccountId} / ${profile.ssoRoleName})`;
621
- }
622
- async function detectAWSState() {
623
- const [cliInstalled, cliVersion, accountId] = await Promise.all([
624
- isAWSCLIInstalled(),
625
- getAWSCLIVersion(),
626
- validateCredentials()
627
- ]);
628
- const credentialSource = detectCredentialSource();
629
- const detectedProvider = detectHostingProvider();
630
- const region = getCurrentRegion();
631
- const profileName = getCurrentProfile();
632
- const ssoProfiles = parseSSOProfiles();
633
- const ssoSessions = parseSSOSessions();
634
- const activeProfile = getActiveSSOProfile(ssoProfiles);
635
- const tokenStatus = ssoProfiles.length > 0 ? checkSSOTokenStatus(activeProfile?.ssoStartUrl) : null;
636
- const isUsingSSO = credentialSource === "sso" || activeProfile !== null && accountId !== null;
637
- return {
638
- cliInstalled,
639
- cliVersion,
640
- credentialsConfigured: accountId !== null,
641
- credentialSource: isUsingSSO ? "sso" : accountId !== null ? credentialSource : null,
642
- profileName,
643
- accountId,
644
- detectedProvider,
645
- region,
646
- sso: {
647
- configured: ssoProfiles.length > 0,
648
- profiles: ssoProfiles,
649
- sessions: ssoSessions,
650
- tokenStatus,
651
- activeProfile: isUsingSSO ? activeProfile : null
652
- }
653
- };
654
- }
655
- function hasCredentialsFile() {
656
- const credentialsPath = join4(homedir2(), ".aws", "credentials");
657
- return existsSync4(credentialsPath);
658
- }
659
- function hasConfigFile() {
660
- const configPath = join4(homedir2(), ".aws", "config");
661
- return existsSync4(configPath);
662
- }
663
- function getConfiguredProfiles() {
664
- const profiles = [];
665
- const credentialsPath = join4(homedir2(), ".aws", "credentials");
666
- if (existsSync4(credentialsPath)) {
667
- const content = readFileSync(credentialsPath, "utf-8");
668
- const matches = content.matchAll(/\[([^\]]+)\]/g);
669
- for (const match of matches) {
670
- profiles.push(match[1]);
671
- }
672
- }
673
- const configPath = join4(homedir2(), ".aws", "config");
674
- if (existsSync4(configPath)) {
675
- const content = readFileSync(configPath, "utf-8");
676
- const matches = content.matchAll(/\[profile ([^\]]+)\]/g);
677
- for (const match of matches) {
678
- if (!profiles.includes(match[1])) {
679
- profiles.push(match[1]);
680
- }
681
- }
682
- }
683
- return profiles;
684
- }
685
- var init_aws_detection = __esm({
686
- "src/utils/shared/aws-detection.ts"() {
687
- "use strict";
688
- init_esm_shims();
689
- }
690
- });
691
-
692
- // src/utils/shared/ci-detection.ts
693
- function isCI() {
694
- if (process.env.CI === "true" || process.env.CI === "1") {
695
- return true;
696
- }
697
- const ciEnvVars = [
698
- "GITHUB_ACTIONS",
699
- // GitHub Actions
700
- "GITLAB_CI",
701
- // GitLab CI
702
- "CIRCLECI",
703
- // CircleCI
704
- "TRAVIS",
705
- // Travis CI
706
- "JENKINS_URL",
707
- // Jenkins
708
- "BUILDKITE",
709
- // Buildkite
710
- "DRONE",
711
- // Drone
712
- "SEMAPHORE",
713
- // Semaphore
714
- "TEAMCITY_VERSION",
715
- // TeamCity
716
- "TF_BUILD",
717
- // Azure Pipelines
718
- "CODEBUILD_BUILD_ID",
719
- // AWS CodeBuild
720
- "NETLIFY",
721
- // Netlify
722
- "VERCEL",
723
- // Vercel
724
- "HEROKU_TEST_RUN_ID",
725
- // Heroku CI
726
- "BUDDY",
727
- // Buddy
728
- "BITBUCKET_BUILD_NUMBER"
729
- // Bitbucket Pipelines
730
- ];
731
- return ciEnvVars.some((envVar) => process.env[envVar] !== void 0);
732
- }
733
- var init_ci_detection = __esm({
734
- "src/utils/shared/ci-detection.ts"() {
735
- "use strict";
736
- init_esm_shims();
737
- }
738
- });
739
-
740
- // src/telemetry/config.ts
741
- import Conf from "conf";
742
- import { v4 as uuidv4 } from "uuid";
743
- var CONFIG_DEFAULTS, TelemetryConfigManager;
744
- var init_config2 = __esm({
745
- "src/telemetry/config.ts"() {
746
- "use strict";
747
- init_esm_shims();
748
- CONFIG_DEFAULTS = {
749
- enabled: true,
750
- anonymousId: uuidv4(),
751
- notificationShown: false
752
- };
753
- TelemetryConfigManager = class {
754
- config;
755
- constructor(options) {
756
- this.config = new Conf({
757
- projectName: "wraps",
758
- configName: "telemetry",
759
- defaults: CONFIG_DEFAULTS,
760
- cwd: options?.cwd
761
- });
762
- }
763
- /**
764
- * Check if telemetry is enabled
765
- */
766
- isEnabled() {
767
- return this.config.get("enabled");
768
- }
769
- /**
770
- * Enable or disable telemetry
771
- */
772
- setEnabled(enabled) {
773
- this.config.set("enabled", enabled);
774
- }
775
- /**
776
- * Get the anonymous user ID
777
- */
778
- getAnonymousId() {
779
- return this.config.get("anonymousId");
455
+ /**
456
+ * Get the anonymous user ID
457
+ */
458
+ getAnonymousId() {
459
+ return this.config.get("anonymousId");
780
460
  }
781
461
  /**
782
462
  * Check if the first-run notification has been shown
@@ -811,10 +491,10 @@ var init_config2 = __esm({
811
491
  });
812
492
 
813
493
  // src/telemetry/client.ts
814
- import { readFileSync as readFileSync2 } from "fs";
815
- import { dirname, join as join5 } from "path";
494
+ import { readFileSync } from "fs";
495
+ import { dirname, join as join4 } from "path";
816
496
  import { fileURLToPath as fileURLToPath2 } from "url";
817
- import pc4 from "picocolors";
497
+ import pc from "picocolors";
818
498
  function getTelemetryClient() {
819
499
  if (!telemetryInstance) {
820
500
  telemetryInstance = new TelemetryClient();
@@ -1043,12 +723,12 @@ var init_client = __esm({
1043
723
  }
1044
724
  this.hasShownFooter = true;
1045
725
  console.log();
1046
- console.log(pc4.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"));
726
+ console.log(pc.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"));
1047
727
  console.log("\u{1F4CA} Wraps Platform \u2014 analytics, templates, automations");
1048
- console.log(` From $10/mo \u2192 ${pc4.cyan("https://wraps.dev/platform")}`);
728
+ console.log(` From $10/mo \u2192 ${pc.cyan("https://wraps.dev/platform")}`);
1049
729
  console.log();
1050
- console.log(`\u{1F4AC} ${pc4.cyan("hey@wraps.sh")}`);
1051
- console.log(pc4.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"));
730
+ console.log(`\u{1F4AC} ${pc.cyan("hey@wraps.sh")}`);
731
+ console.log(pc.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"));
1052
732
  return true;
1053
733
  }
1054
734
  /**
@@ -1059,7 +739,7 @@ var init_client = __esm({
1059
739
  const __filename3 = fileURLToPath2(import.meta.url);
1060
740
  const __dirname4 = dirname(__filename3);
1061
741
  const pkg = JSON.parse(
1062
- readFileSync2(join5(__dirname4, "../package.json"), "utf-8")
742
+ readFileSync(join4(__dirname4, "../package.json"), "utf-8")
1063
743
  );
1064
744
  return pkg.version;
1065
745
  } catch {
@@ -1067,65 +747,385 @@ var init_client = __esm({
1067
747
  }
1068
748
  }
1069
749
  };
1070
- telemetryInstance = null;
750
+ telemetryInstance = null;
751
+ }
752
+ });
753
+
754
+ // src/telemetry/events.ts
755
+ function trackCommand(command, metadata) {
756
+ const client = getTelemetryClient();
757
+ const sanitized = metadata ? { ...metadata } : {};
758
+ sanitized.domain = void 0;
759
+ sanitized.accountId = void 0;
760
+ sanitized.email = void 0;
761
+ client.track(`command:${command}`, sanitized);
762
+ }
763
+ function trackServiceInit(service, success, metadata) {
764
+ const client = getTelemetryClient();
765
+ client.track("service:init", {
766
+ service,
767
+ success,
768
+ ...metadata
769
+ });
770
+ }
771
+ function trackServiceDeployed(service, metadata) {
772
+ const client = getTelemetryClient();
773
+ client.track("service:deployed", {
774
+ service,
775
+ ...metadata
776
+ });
777
+ }
778
+ function trackError(errorCode, command, metadata) {
779
+ const client = getTelemetryClient();
780
+ client.track("error:occurred", {
781
+ error_code: errorCode,
782
+ command,
783
+ ...metadata
784
+ });
785
+ }
786
+ function trackFeature(feature, metadata) {
787
+ const client = getTelemetryClient();
788
+ client.track(`feature:${feature}`, metadata || {});
789
+ }
790
+ function trackServiceUpgrade(service, metadata) {
791
+ const client = getTelemetryClient();
792
+ client.track("service:upgraded", {
793
+ service,
794
+ ...metadata
795
+ });
796
+ }
797
+ function trackServiceRemoved(service, metadata) {
798
+ const client = getTelemetryClient();
799
+ client.track("service:removed", {
800
+ service,
801
+ ...metadata
802
+ });
803
+ }
804
+ var init_events = __esm({
805
+ "src/telemetry/events.ts"() {
806
+ "use strict";
807
+ init_esm_shims();
808
+ init_client();
809
+ }
810
+ });
811
+
812
+ // src/utils/shared/aws-detection.ts
813
+ import { execSync } from "child_process";
814
+ import { existsSync as existsSync4, readdirSync, readFileSync as readFileSync2 } from "fs";
815
+ import { homedir as homedir2 } from "os";
816
+ import { join as join5 } from "path";
817
+ import { GetCallerIdentityCommand, STSClient } from "@aws-sdk/client-sts";
818
+ async function isAWSCLIInstalled() {
819
+ try {
820
+ execSync("aws --version", { stdio: "pipe" });
821
+ return true;
822
+ } catch {
823
+ return false;
824
+ }
825
+ }
826
+ async function getAWSCLIVersion() {
827
+ try {
828
+ const output4 = execSync("aws --version", { encoding: "utf-8" });
829
+ const match = output4.match(/aws-cli\/(\d+\.\d+\.\d+)/);
830
+ return match ? match[1] : null;
831
+ } catch {
832
+ return null;
833
+ }
834
+ }
835
+ function detectCredentialSource() {
836
+ if (process.env.AWS_ACCESS_KEY_ID && process.env.AWS_SECRET_ACCESS_KEY) {
837
+ return "environment";
838
+ }
839
+ if (process.env.AWS_SSO_ACCOUNT_ID || process.env.AWS_SSO_SESSION) {
840
+ return "sso";
841
+ }
842
+ if (process.env.AWS_PROFILE) {
843
+ return "profile";
844
+ }
845
+ const credentialsPath = join5(homedir2(), ".aws", "credentials");
846
+ if (existsSync4(credentialsPath)) {
847
+ const content = readFileSync2(credentialsPath, "utf-8");
848
+ if (content.includes("[default]")) {
849
+ return "profile";
850
+ }
851
+ }
852
+ const ssoCachePath = join5(homedir2(), ".aws", "sso", "cache");
853
+ if (existsSync4(ssoCachePath)) {
854
+ return "sso";
855
+ }
856
+ if (process.env.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI || process.env.AWS_EXECUTION_ENV) {
857
+ return "instance";
858
+ }
859
+ return null;
860
+ }
861
+ function detectHostingProvider() {
862
+ if (process.env.VERCEL || process.env.VERCEL_ENV) {
863
+ return "vercel";
864
+ }
865
+ if (process.env.RAILWAY_ENVIRONMENT || process.env.RAILWAY_PROJECT_ID) {
866
+ return "railway";
867
+ }
868
+ if (process.env.NETLIFY || process.env.NETLIFY_DEV) {
869
+ return "netlify";
870
+ }
871
+ if (process.env.AWS_LAMBDA_FUNCTION_NAME || process.env.AWS_EXECUTION_ENV || process.env.ECS_CONTAINER_METADATA_URI || process.env.AWS_CONTAINER_CREDENTIALS_RELATIVE_URI) {
872
+ return "aws";
873
+ }
874
+ return null;
875
+ }
876
+ async function validateCredentials() {
877
+ try {
878
+ const sts = new STSClient({ region: "us-east-1" });
879
+ const identity = await sts.send(new GetCallerIdentityCommand({}));
880
+ return identity.Account || null;
881
+ } catch {
882
+ return null;
883
+ }
884
+ }
885
+ function getCurrentProfile() {
886
+ return process.env.AWS_PROFILE || "default";
887
+ }
888
+ function getCurrentRegion() {
889
+ if (process.env.AWS_REGION) {
890
+ return process.env.AWS_REGION;
891
+ }
892
+ if (process.env.AWS_DEFAULT_REGION) {
893
+ return process.env.AWS_DEFAULT_REGION;
894
+ }
895
+ try {
896
+ const region = execSync("aws configure get region", {
897
+ encoding: "utf-8",
898
+ stdio: ["pipe", "pipe", "pipe"]
899
+ }).trim();
900
+ return region || null;
901
+ } catch {
902
+ return null;
903
+ }
904
+ }
905
+ function parseSSOProfiles() {
906
+ const configPath = join5(homedir2(), ".aws", "config");
907
+ if (!existsSync4(configPath)) {
908
+ return [];
909
+ }
910
+ const content = readFileSync2(configPath, "utf-8");
911
+ const profiles = [];
912
+ const sessionMap = /* @__PURE__ */ new Map();
913
+ const sessionSections = content.split(/^\[/m).filter(Boolean);
914
+ for (const section of sessionSections) {
915
+ const lines = section.split("\n");
916
+ const header = lines[0]?.replace("]", "").trim();
917
+ if (header?.startsWith("sso-session ")) {
918
+ const sessionName = header.replace("sso-session ", "");
919
+ const config2 = {};
920
+ for (const line of lines.slice(1)) {
921
+ const match = line.match(/^\s*([^=\s]+)\s*=\s*(.+?)\s*$/);
922
+ if (match) {
923
+ config2[match[1]] = match[2];
924
+ }
925
+ }
926
+ sessionMap.set(sessionName, {
927
+ startUrl: config2.sso_start_url || "",
928
+ region: config2.sso_region || ""
929
+ });
930
+ }
931
+ }
932
+ const sections = content.split(/^\[/m).filter(Boolean);
933
+ for (const section of sections) {
934
+ const lines = section.split("\n");
935
+ const header = lines[0]?.replace("]", "").trim();
936
+ if (!header?.startsWith("profile ") && header !== "default") {
937
+ continue;
938
+ }
939
+ const profileName = header === "default" ? "default" : header.replace("profile ", "");
940
+ const config2 = {};
941
+ for (const line of lines.slice(1)) {
942
+ const match = line.match(/^\s*([^=\s]+)\s*=\s*(.+?)\s*$/);
943
+ if (match) {
944
+ config2[match[1]] = match[2];
945
+ }
946
+ }
947
+ if (config2.sso_start_url || config2.sso_session) {
948
+ let ssoStartUrl = config2.sso_start_url || "";
949
+ let ssoRegion = config2.sso_region || "";
950
+ if (config2.sso_session) {
951
+ const session = sessionMap.get(config2.sso_session);
952
+ if (session) {
953
+ ssoStartUrl = ssoStartUrl || session.startUrl;
954
+ ssoRegion = ssoRegion || session.region;
955
+ }
956
+ }
957
+ profiles.push({
958
+ name: profileName,
959
+ ssoStartUrl,
960
+ ssoRegion,
961
+ ssoAccountId: config2.sso_account_id || "",
962
+ ssoRoleName: config2.sso_role_name || "",
963
+ region: config2.region,
964
+ ssoSession: config2.sso_session
965
+ });
966
+ }
967
+ }
968
+ return profiles;
969
+ }
970
+ function parseSSOSessions() {
971
+ const configPath = join5(homedir2(), ".aws", "config");
972
+ if (!existsSync4(configPath)) {
973
+ return [];
974
+ }
975
+ const content = readFileSync2(configPath, "utf-8");
976
+ const sessions = [];
977
+ const sections = content.split(/^\[/m).filter(Boolean);
978
+ for (const section of sections) {
979
+ const lines = section.split("\n");
980
+ const header = lines[0]?.replace("]", "").trim();
981
+ if (!header?.startsWith("sso-session ")) {
982
+ continue;
983
+ }
984
+ const sessionName = header.replace("sso-session ", "");
985
+ const config2 = {};
986
+ for (const line of lines.slice(1)) {
987
+ const match = line.match(/^\s*([^=\s]+)\s*=\s*(.+?)\s*$/);
988
+ if (match) {
989
+ config2[match[1]] = match[2];
990
+ }
991
+ }
992
+ sessions.push({
993
+ name: sessionName,
994
+ ssoStartUrl: config2.sso_start_url || "",
995
+ ssoRegion: config2.sso_region || "",
996
+ ssoRegistrationScopes: config2.sso_registration_scopes?.split(",").map((s) => s.trim())
997
+ });
998
+ }
999
+ return sessions;
1000
+ }
1001
+ function checkSSOTokenStatus(startUrl) {
1002
+ const ssoCachePath = join5(homedir2(), ".aws", "sso", "cache");
1003
+ if (!existsSync4(ssoCachePath)) {
1004
+ return {
1005
+ valid: false,
1006
+ expiresAt: null,
1007
+ expired: true,
1008
+ minutesRemaining: null,
1009
+ startUrl: null
1010
+ };
1071
1011
  }
1072
- });
1073
-
1074
- // src/telemetry/events.ts
1075
- function trackCommand(command, metadata) {
1076
- const client = getTelemetryClient();
1077
- const sanitized = metadata ? { ...metadata } : {};
1078
- sanitized.domain = void 0;
1079
- sanitized.accountId = void 0;
1080
- sanitized.email = void 0;
1081
- client.track(`command:${command}`, sanitized);
1012
+ try {
1013
+ const cacheFiles = readdirSync(ssoCachePath).filter(
1014
+ (f) => f.endsWith(".json")
1015
+ );
1016
+ for (const file of cacheFiles) {
1017
+ const content = readFileSync2(join5(ssoCachePath, file), "utf-8");
1018
+ const token = JSON.parse(content);
1019
+ if (!(token.accessToken && token.expiresAt)) {
1020
+ continue;
1021
+ }
1022
+ if (startUrl && token.startUrl !== startUrl) {
1023
+ continue;
1024
+ }
1025
+ const expiresAt = new Date(token.expiresAt);
1026
+ const now = /* @__PURE__ */ new Date();
1027
+ const expired = expiresAt <= now;
1028
+ const minutesRemaining = Math.floor(
1029
+ (expiresAt.getTime() - now.getTime()) / 6e4
1030
+ );
1031
+ return {
1032
+ valid: !expired,
1033
+ expiresAt,
1034
+ expired,
1035
+ minutesRemaining,
1036
+ startUrl: token.startUrl || null
1037
+ };
1038
+ }
1039
+ } catch {
1040
+ }
1041
+ return {
1042
+ valid: false,
1043
+ expiresAt: null,
1044
+ expired: true,
1045
+ minutesRemaining: null,
1046
+ startUrl: null
1047
+ };
1082
1048
  }
1083
- function trackServiceInit(service, success, metadata) {
1084
- const client = getTelemetryClient();
1085
- client.track("service:init", {
1086
- service,
1087
- success,
1088
- ...metadata
1089
- });
1049
+ function getActiveSSOProfile(profiles) {
1050
+ const currentProfile = process.env.AWS_PROFILE || "default";
1051
+ return profiles.find((p) => p.name === currentProfile) || null;
1090
1052
  }
1091
- function trackServiceDeployed(service, metadata) {
1092
- const client = getTelemetryClient();
1093
- client.track("service:deployed", {
1094
- service,
1095
- ...metadata
1096
- });
1053
+ function getSSOLoginCommand(profile) {
1054
+ if (profile && profile !== "default") {
1055
+ return `aws sso login --profile ${profile}`;
1056
+ }
1057
+ return "aws sso login";
1097
1058
  }
1098
- function trackError(errorCode, command, metadata) {
1099
- const client = getTelemetryClient();
1100
- client.track("error:occurred", {
1101
- error_code: errorCode,
1102
- command,
1103
- ...metadata
1104
- });
1059
+ function formatSSOProfile(profile) {
1060
+ return `${profile.name} (${profile.ssoAccountId} / ${profile.ssoRoleName})`;
1105
1061
  }
1106
- function trackFeature(feature, metadata) {
1107
- const client = getTelemetryClient();
1108
- client.track(`feature:${feature}`, metadata || {});
1062
+ async function detectAWSState() {
1063
+ const [cliInstalled, cliVersion, accountId] = await Promise.all([
1064
+ isAWSCLIInstalled(),
1065
+ getAWSCLIVersion(),
1066
+ validateCredentials()
1067
+ ]);
1068
+ const credentialSource = detectCredentialSource();
1069
+ const detectedProvider = detectHostingProvider();
1070
+ const region = getCurrentRegion();
1071
+ const profileName = getCurrentProfile();
1072
+ const ssoProfiles = parseSSOProfiles();
1073
+ const ssoSessions = parseSSOSessions();
1074
+ const activeProfile = getActiveSSOProfile(ssoProfiles);
1075
+ const tokenStatus = ssoProfiles.length > 0 ? checkSSOTokenStatus(activeProfile?.ssoStartUrl) : null;
1076
+ const isUsingSSO = credentialSource === "sso" || activeProfile !== null && accountId !== null;
1077
+ return {
1078
+ cliInstalled,
1079
+ cliVersion,
1080
+ credentialsConfigured: accountId !== null,
1081
+ credentialSource: isUsingSSO ? "sso" : accountId !== null ? credentialSource : null,
1082
+ profileName,
1083
+ accountId,
1084
+ detectedProvider,
1085
+ region,
1086
+ sso: {
1087
+ configured: ssoProfiles.length > 0,
1088
+ profiles: ssoProfiles,
1089
+ sessions: ssoSessions,
1090
+ tokenStatus,
1091
+ activeProfile: isUsingSSO ? activeProfile : null
1092
+ }
1093
+ };
1109
1094
  }
1110
- function trackServiceUpgrade(service, metadata) {
1111
- const client = getTelemetryClient();
1112
- client.track("service:upgraded", {
1113
- service,
1114
- ...metadata
1115
- });
1095
+ function hasCredentialsFile() {
1096
+ const credentialsPath = join5(homedir2(), ".aws", "credentials");
1097
+ return existsSync4(credentialsPath);
1116
1098
  }
1117
- function trackServiceRemoved(service, metadata) {
1118
- const client = getTelemetryClient();
1119
- client.track("service:removed", {
1120
- service,
1121
- ...metadata
1122
- });
1099
+ function hasConfigFile() {
1100
+ const configPath = join5(homedir2(), ".aws", "config");
1101
+ return existsSync4(configPath);
1123
1102
  }
1124
- var init_events = __esm({
1125
- "src/telemetry/events.ts"() {
1103
+ function getConfiguredProfiles() {
1104
+ const profiles = [];
1105
+ const credentialsPath = join5(homedir2(), ".aws", "credentials");
1106
+ if (existsSync4(credentialsPath)) {
1107
+ const content = readFileSync2(credentialsPath, "utf-8");
1108
+ const matches = content.matchAll(/\[([^\]]+)\]/g);
1109
+ for (const match of matches) {
1110
+ profiles.push(match[1]);
1111
+ }
1112
+ }
1113
+ const configPath = join5(homedir2(), ".aws", "config");
1114
+ if (existsSync4(configPath)) {
1115
+ const content = readFileSync2(configPath, "utf-8");
1116
+ const matches = content.matchAll(/\[profile ([^\]]+)\]/g);
1117
+ for (const match of matches) {
1118
+ if (!profiles.includes(match[1])) {
1119
+ profiles.push(match[1]);
1120
+ }
1121
+ }
1122
+ }
1123
+ return profiles;
1124
+ }
1125
+ var init_aws_detection = __esm({
1126
+ "src/utils/shared/aws-detection.ts"() {
1126
1127
  "use strict";
1127
1128
  init_esm_shims();
1128
- init_client();
1129
1129
  }
1130
1130
  });
1131
1131
 
@@ -7269,6 +7269,7 @@ import pc49 from "picocolors";
7269
7269
 
7270
7270
  // src/commands/auth/login.ts
7271
7271
  init_esm_shims();
7272
+ init_events();
7272
7273
  init_config();
7273
7274
  import * as clack from "@clack/prompts";
7274
7275
  import { createAuthClient } from "better-auth/client";
@@ -7277,7 +7278,7 @@ import {
7277
7278
  organizationClient
7278
7279
  } from "better-auth/client/plugins";
7279
7280
  import open from "open";
7280
- import pc from "picocolors";
7281
+ import pc2 from "picocolors";
7281
7282
  function createCliAuthClient(baseURL) {
7282
7283
  return createAuthClient({
7283
7284
  baseURL,
@@ -7305,6 +7306,7 @@ async function fetchOrganizations(baseURL, token) {
7305
7306
  }
7306
7307
  }
7307
7308
  async function login(options) {
7309
+ const startTime = Date.now();
7308
7310
  if (options.token) {
7309
7311
  await saveAuthConfig({
7310
7312
  auth: {
@@ -7312,6 +7314,11 @@ async function login(options) {
7312
7314
  tokenType: "api-key"
7313
7315
  }
7314
7316
  });
7317
+ trackCommand("auth:login", {
7318
+ success: true,
7319
+ duration_ms: Date.now() - startTime,
7320
+ method: "api-key"
7321
+ });
7315
7322
  if (options.json) {
7316
7323
  console.log(JSON.stringify({ success: true, tokenType: "api-key" }));
7317
7324
  } else {
@@ -7319,7 +7326,7 @@ async function login(options) {
7319
7326
  }
7320
7327
  return;
7321
7328
  }
7322
- clack.intro(pc.bold("Wraps \u203A Sign In"));
7329
+ clack.intro(pc2.bold("Wraps \u203A Sign In"));
7323
7330
  const baseURL = process.env.WRAPS_API_URL || "https://app.wraps.dev";
7324
7331
  const authClient = createCliAuthClient(baseURL);
7325
7332
  const spinner8 = clack.spinner();
@@ -7327,6 +7334,8 @@ async function login(options) {
7327
7334
  client_id: "wraps-cli"
7328
7335
  });
7329
7336
  if (codeError || !codeData) {
7337
+ trackCommand("auth:login", { success: false, duration_ms: Date.now() - startTime, method: "device" });
7338
+ trackError("DEVICE_AUTH_FAILED", "auth:login", { step: "request_code" });
7330
7339
  clack.log.error("Failed to start device authorization.");
7331
7340
  process.exit(1);
7332
7341
  }
@@ -7338,8 +7347,8 @@ async function login(options) {
7338
7347
  expires_in
7339
7348
  } = codeData;
7340
7349
  const formatted = `${user_code.slice(0, 4)}-${user_code.slice(4)}`;
7341
- clack.log.info(`Your code: ${pc.bold(pc.cyan(formatted))}`);
7342
- clack.log.info(`Visit: ${pc.underline(`${baseURL}/device`)}`);
7350
+ clack.log.info(`Your code: ${pc2.bold(pc2.cyan(formatted))}`);
7351
+ clack.log.info(`Visit: ${pc2.underline(`${baseURL}/device`)}`);
7343
7352
  try {
7344
7353
  await open(`${baseURL}/device?user_code=${user_code}`);
7345
7354
  clack.log.info("Opening browser...");
@@ -7369,9 +7378,14 @@ async function login(options) {
7369
7378
  organizations: organizations.length > 0 ? organizations : void 0
7370
7379
  }
7371
7380
  });
7381
+ trackCommand("auth:login", {
7382
+ success: true,
7383
+ duration_ms: Date.now() - startTime,
7384
+ method: "device"
7385
+ });
7372
7386
  clack.log.success("Signed in successfully.");
7373
7387
  if (organizations.length === 1) {
7374
- clack.log.info(`Organization: ${pc.cyan(organizations[0].name)}`);
7388
+ clack.log.info(`Organization: ${pc2.cyan(organizations[0].name)}`);
7375
7389
  } else if (organizations.length > 1) {
7376
7390
  clack.log.info(`${organizations.length} organizations available`);
7377
7391
  }
@@ -7396,6 +7410,8 @@ async function login(options) {
7396
7410
  continue;
7397
7411
  }
7398
7412
  if (errorCode === "access_denied") {
7413
+ trackCommand("auth:login", { success: false, duration_ms: Date.now() - startTime, method: "device" });
7414
+ trackError("ACCESS_DENIED", "auth:login", { step: "poll_token" });
7399
7415
  spinner8.stop("Denied.");
7400
7416
  clack.log.error("Authorization was denied.");
7401
7417
  process.exit(1);
@@ -7405,6 +7421,8 @@ async function login(options) {
7405
7421
  }
7406
7422
  }
7407
7423
  }
7424
+ trackCommand("auth:login", { success: false, duration_ms: Date.now() - startTime, method: "device" });
7425
+ trackError("DEVICE_CODE_EXPIRED", "auth:login", { step: "poll_token" });
7408
7426
  spinner8.stop("Expired.");
7409
7427
  clack.log.error("Device code expired. Run `wraps auth login` to try again.");
7410
7428
  process.exit(1);
@@ -7412,32 +7430,37 @@ async function login(options) {
7412
7430
 
7413
7431
  // src/commands/auth/logout.ts
7414
7432
  init_esm_shims();
7433
+ init_events();
7415
7434
  init_config();
7416
7435
  import * as clack2 from "@clack/prompts";
7417
- import pc2 from "picocolors";
7436
+ import pc3 from "picocolors";
7418
7437
  async function logout() {
7419
- clack2.intro(pc2.bold("Wraps \u203A Sign Out"));
7438
+ clack2.intro(pc3.bold("Wraps \u203A Sign Out"));
7420
7439
  const config2 = await readAuthConfig();
7421
7440
  if (!config2?.auth?.token) {
7441
+ trackCommand("auth:logout", { success: true, already_logged_out: true });
7422
7442
  clack2.log.info("Not signed in.");
7423
7443
  return;
7424
7444
  }
7425
7445
  await clearAuthConfig();
7446
+ trackCommand("auth:logout", { success: true });
7426
7447
  clack2.log.success("Signed out. Token removed from ~/.wraps/config.json");
7427
7448
  }
7428
7449
 
7429
7450
  // src/commands/auth/status.ts
7430
7451
  init_esm_shims();
7452
+ init_events();
7431
7453
  init_config();
7432
7454
  import * as clack3 from "@clack/prompts";
7433
- import pc3 from "picocolors";
7455
+ import pc4 from "picocolors";
7434
7456
  async function authStatus(options) {
7435
7457
  const config2 = await readAuthConfig();
7436
7458
  if (!config2?.auth?.token) {
7459
+ trackCommand("auth:status", { success: true, authenticated: false });
7437
7460
  if (options.json) {
7438
7461
  console.log(JSON.stringify({ authenticated: false }));
7439
7462
  } else {
7440
- clack3.intro(pc3.bold("Wraps \u203A Auth Status"));
7463
+ clack3.intro(pc4.bold("Wraps \u203A Auth Status"));
7441
7464
  clack3.log.info("Not signed in. Run `wraps auth login` to authenticate.");
7442
7465
  }
7443
7466
  return;
@@ -7454,16 +7477,18 @@ async function authStatus(options) {
7454
7477
  })
7455
7478
  );
7456
7479
  } else {
7457
- clack3.intro(pc3.bold("Wraps \u203A Auth Status"));
7480
+ clack3.intro(pc4.bold("Wraps \u203A Auth Status"));
7458
7481
  clack3.log.info(`Token: ${masked} (${tokenType})`);
7459
7482
  if (expiresAt) {
7460
7483
  clack3.log.info(`Expires: ${new Date(expiresAt).toLocaleDateString()}`);
7461
7484
  }
7462
7485
  }
7486
+ trackCommand("auth:status", { success: true, authenticated: true });
7463
7487
  }
7464
7488
 
7465
7489
  // src/commands/aws/doctor.ts
7466
7490
  init_esm_shims();
7491
+ init_events();
7467
7492
  init_aws();
7468
7493
  init_aws_detection();
7469
7494
  import * as clack5 from "@clack/prompts";
@@ -7754,6 +7779,7 @@ function generateSuggestions(results, state) {
7754
7779
  return suggestions;
7755
7780
  }
7756
7781
  async function doctor() {
7782
+ const startTime = Date.now();
7757
7783
  clack5.intro(pc6.bold("AWS Setup Diagnostics"));
7758
7784
  const spinner8 = clack5.spinner();
7759
7785
  spinner8.start("Running diagnostics...");
@@ -7781,6 +7807,13 @@ async function doctor() {
7781
7807
  console.log(` ${pc6.dim("-")} ${suggestion}`);
7782
7808
  }
7783
7809
  }
7810
+ trackCommand("aws:doctor", {
7811
+ success: true,
7812
+ duration_ms: Date.now() - startTime,
7813
+ pass_count: passCount,
7814
+ fail_count: failCount,
7815
+ warn_count: warnCount
7816
+ });
7784
7817
  console.log();
7785
7818
  clack5.outro(
7786
7819
  failCount > 0 ? pc6.dim("Run `wraps aws setup` to fix issues") : pc6.dim("Ready to deploy: wraps email init")
@@ -7789,6 +7822,7 @@ async function doctor() {
7789
7822
 
7790
7823
  // src/commands/aws/setup.ts
7791
7824
  init_esm_shims();
7825
+ init_events();
7792
7826
  init_aws_detection();
7793
7827
  init_prompts();
7794
7828
  import * as clack7 from "@clack/prompts";
@@ -8340,6 +8374,7 @@ function showNextSteps(_state) {
8340
8374
  console.log();
8341
8375
  }
8342
8376
  async function setup(_options = {}) {
8377
+ const startTime = Date.now();
8343
8378
  clack7.intro(pc8.bold("AWS Setup Wizard"));
8344
8379
  const spinner8 = clack7.spinner();
8345
8380
  spinner8.start("Checking your AWS setup...");
@@ -8359,6 +8394,10 @@ async function setup(_options = {}) {
8359
8394
  await runCredentialSetup();
8360
8395
  }
8361
8396
  }
8397
+ trackCommand("aws:setup", {
8398
+ success: true,
8399
+ duration_ms: Date.now() - startTime
8400
+ });
8362
8401
  clack7.outro(pc8.dim("Run `wraps aws doctor` to verify your setup"));
8363
8402
  }
8364
8403
 
@@ -19262,6 +19301,7 @@ Run ${pc24.cyan("wraps email init")} to deploy email infrastructure.
19262
19301
 
19263
19302
  // src/commands/email/templates/init.ts
19264
19303
  init_esm_shims();
19304
+ init_events();
19265
19305
  init_config();
19266
19306
  init_errors();
19267
19307
  import { existsSync as existsSync7 } from "fs";
@@ -19270,6 +19310,7 @@ import { join as join8 } from "path";
19270
19310
  import * as clack24 from "@clack/prompts";
19271
19311
  import pc25 from "picocolors";
19272
19312
  async function templatesInit(options) {
19313
+ const startTime = Date.now();
19273
19314
  const cwd = process.cwd();
19274
19315
  const wrapsDir = join8(cwd, "wraps");
19275
19316
  if (!options.json) {
@@ -19405,6 +19446,10 @@ wraps/.wraps/
19405
19446
  );
19406
19447
  return;
19407
19448
  }
19449
+ trackCommand("email:templates:init", {
19450
+ success: true,
19451
+ duration_ms: Date.now() - startTime
19452
+ });
19408
19453
  console.log();
19409
19454
  clack24.log.success(pc25.green("Templates as Code initialized!"));
19410
19455
  console.log();
@@ -19611,6 +19656,7 @@ const unsubscribeLink = {
19611
19656
 
19612
19657
  // src/commands/email/templates/preview.ts
19613
19658
  init_esm_shims();
19659
+ init_events();
19614
19660
  import { existsSync as existsSync9, watch } from "fs";
19615
19661
  import { join as join10 } from "path";
19616
19662
  import * as clack25 from "@clack/prompts";
@@ -19857,6 +19903,10 @@ async function templatesPreview(options) {
19857
19903
  const port = options.port || await getPort2({ port: [3333, 3334, 3335, 3336, 3337] });
19858
19904
  const server = app.listen(port, () => {
19859
19905
  const url = `http://localhost:${port}`;
19906
+ trackCommand("email:templates:preview", {
19907
+ success: true,
19908
+ template_count: templateFiles.length
19909
+ });
19860
19910
  clack25.log.success(`Preview server running at ${pc26.cyan(url)}`);
19861
19911
  if (options.template) {
19862
19912
  clack25.log.info(`Previewing: ${pc26.cyan(options.template)}`);
@@ -20056,6 +20106,7 @@ function renderErrorPage(err) {
20056
20106
 
20057
20107
  // src/commands/email/templates/push.ts
20058
20108
  init_esm_shims();
20109
+ init_events();
20059
20110
  import { createHash } from "crypto";
20060
20111
  import { existsSync as existsSync10 } from "fs";
20061
20112
  import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile6 } from "fs/promises";
@@ -20065,6 +20116,7 @@ import pc27 from "picocolors";
20065
20116
  init_config();
20066
20117
  init_errors();
20067
20118
  async function templatesPush(options) {
20119
+ const startTime = Date.now();
20068
20120
  const cwd = process.cwd();
20069
20121
  const wrapsDir = join11(cwd, "wraps");
20070
20122
  const configPath = join11(wrapsDir, "wraps.config.ts");
@@ -20230,6 +20282,13 @@ async function templatesPush(options) {
20230
20282
  }
20231
20283
  console.log();
20232
20284
  }
20285
+ trackCommand("email:templates:push", {
20286
+ success: compileErrors.length === 0,
20287
+ duration_ms: Date.now() - startTime,
20288
+ pushed_count: compiled.length,
20289
+ unchanged_count: unchanged.length,
20290
+ error_count: compileErrors.length
20291
+ });
20233
20292
  }
20234
20293
  async function compileTemplate(filePath, slug, source, sourceHash, wrapsDir) {
20235
20294
  const { build: build2 } = await import("esbuild");
@@ -22251,6 +22310,7 @@ ${pc28.green("\u2713")} ${pc28.bold("Upgrade complete!")}
22251
22310
 
22252
22311
  // src/commands/email/workflows/push.ts
22253
22312
  init_esm_shims();
22313
+ init_events();
22254
22314
  import { existsSync as existsSync12 } from "fs";
22255
22315
  import { mkdir as mkdir6, readFile as readFile6, writeFile as writeFile8 } from "fs/promises";
22256
22316
  import { join as join13 } from "path";
@@ -22980,6 +23040,7 @@ function validateTemplateReferences(steps, localTemplateSlugs) {
22980
23040
  init_config();
22981
23041
  init_errors();
22982
23042
  async function workflowsPush(options) {
23043
+ const startTime = Date.now();
22983
23044
  const cwd = process.cwd();
22984
23045
  const wrapsDir = join13(cwd, "wraps");
22985
23046
  const configPath = join13(wrapsDir, "wraps.config.ts");
@@ -23224,6 +23285,13 @@ async function workflowsPush(options) {
23224
23285
  }
23225
23286
  console.log();
23226
23287
  }
23288
+ trackCommand("email:workflows:push", {
23289
+ success: conflicts.length === 0 && pushed.length > 0,
23290
+ duration_ms: Date.now() - startTime,
23291
+ pushed_count: pushed.length,
23292
+ unchanged_count: unchanged.length,
23293
+ conflict_count: conflicts.length
23294
+ });
23227
23295
  }
23228
23296
  async function pushToAPI2(workflows, token, _org, progress, force) {
23229
23297
  if (!token) {
@@ -23372,12 +23440,14 @@ async function saveLockfile2(path3, lockfile) {
23372
23440
 
23373
23441
  // src/commands/email/workflows/validate.ts
23374
23442
  init_esm_shims();
23443
+ init_events();
23375
23444
  import { existsSync as existsSync13 } from "fs";
23376
23445
  import { join as join14 } from "path";
23377
23446
  import * as clack29 from "@clack/prompts";
23378
23447
  import pc30 from "picocolors";
23379
23448
  init_errors();
23380
23449
  async function workflowsValidate(options) {
23450
+ const startTime = Date.now();
23381
23451
  const cwd = process.cwd();
23382
23452
  const wrapsDir = join14(cwd, "wraps");
23383
23453
  const configPath = join14(wrapsDir, "wraps.config.ts");
@@ -23526,13 +23596,22 @@ async function workflowsValidate(options) {
23526
23596
  }
23527
23597
  console.log();
23528
23598
  }
23599
+ trackCommand("email:workflows:validate", {
23600
+ success: parseErrors.length === 0 && validationResults.every((r) => r.valid),
23601
+ duration_ms: Date.now() - startTime,
23602
+ valid_count: validationResults.filter((r) => r.valid).length,
23603
+ invalid_count: validationResults.filter((r) => !r.valid).length,
23604
+ parse_error_count: parseErrors.length
23605
+ });
23529
23606
  }
23530
23607
 
23531
23608
  // src/commands/news.ts
23532
23609
  init_esm_shims();
23610
+ init_events();
23533
23611
  import * as clack30 from "@clack/prompts";
23534
23612
  import pc31 from "picocolors";
23535
23613
  async function news() {
23614
+ trackCommand("news", { success: true });
23536
23615
  clack30.intro(pc31.bold("What's New in Wraps"));
23537
23616
  console.log();
23538
23617
  console.log(" See the latest updates, features, and improvements:");
@@ -28060,11 +28139,13 @@ async function dashboard(options) {
28060
28139
 
28061
28140
  // src/commands/shared/destroy.ts
28062
28141
  init_esm_shims();
28142
+ init_events();
28063
28143
  init_aws();
28064
28144
  init_metadata();
28065
28145
  import * as clack34 from "@clack/prompts";
28066
28146
  import pc37 from "picocolors";
28067
28147
  async function destroy(options) {
28148
+ trackCommand("destroy", { success: true });
28068
28149
  clack34.intro(pc37.bold("Wraps Infrastructure Teardown"));
28069
28150
  const spinner8 = clack34.spinner();
28070
28151
  spinner8.start("Validating AWS credentials");
@@ -30135,6 +30216,7 @@ ${pc40.yellow(pc40.bold("Important Notes:"))}`);
30135
30216
 
30136
30217
  // src/commands/sms/register.ts
30137
30218
  init_esm_shims();
30219
+ init_events();
30138
30220
  init_aws();
30139
30221
  init_metadata();
30140
30222
  import * as clack38 from "@clack/prompts";
@@ -30176,6 +30258,7 @@ async function getRegistrationStatus(region, registrationId) {
30176
30258
  }
30177
30259
  }
30178
30260
  async function smsRegister(options) {
30261
+ const startTime = Date.now();
30179
30262
  clack38.intro(pc41.bold("Wraps SMS - Toll-Free Registration"));
30180
30263
  const progress = new DeploymentProgress();
30181
30264
  const identity = await progress.execute(
@@ -30294,6 +30377,10 @@ async function smsRegister(options) {
30294
30377
  console.log("When you're ready, go to:");
30295
30378
  console.log(` ${pc41.cyan(consoleUrl)}`);
30296
30379
  }
30380
+ trackCommand("sms:register", {
30381
+ success: true,
30382
+ duration_ms: Date.now() - startTime
30383
+ });
30297
30384
  clack38.outro(pc41.dim("Good luck with your registration!"));
30298
30385
  }
30299
30386
 
@@ -32069,9 +32156,11 @@ Run ${pc46.cyan(`wraps sms verify-number --phone-number ${phoneNumber} --resend`
32069
32156
 
32070
32157
  // src/commands/support.ts
32071
32158
  init_esm_shims();
32159
+ init_events();
32072
32160
  import * as clack44 from "@clack/prompts";
32073
32161
  import pc47 from "picocolors";
32074
32162
  async function support() {
32163
+ trackCommand("support", { success: true });
32075
32164
  clack44.intro(pc47.bold("Get Help with Wraps"));
32076
32165
  console.log();
32077
32166
  console.log(` ${pc47.bold("Email:")} ${pc47.cyan("hey@wraps.sh")}`);