@wraps.dev/cli 2.11.2 → 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/
|
|
373
|
-
import
|
|
374
|
-
import {
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
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
|
-
|
|
487
|
-
|
|
488
|
-
|
|
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
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
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
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
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
|
|
815
|
-
import { dirname, join as
|
|
494
|
+
import { readFileSync } from "fs";
|
|
495
|
+
import { dirname, join as join4 } from "path";
|
|
816
496
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
817
|
-
import
|
|
497
|
+
import pc from "picocolors";
|
|
818
498
|
function getTelemetryClient() {
|
|
819
499
|
if (!telemetryInstance) {
|
|
820
500
|
telemetryInstance = new TelemetryClient();
|
|
@@ -1041,91 +721,411 @@ var init_client = __esm({
|
|
|
1041
721
|
if (this.hasShownFooter) {
|
|
1042
722
|
return false;
|
|
1043
723
|
}
|
|
1044
|
-
this.hasShownFooter = true;
|
|
1045
|
-
console.log();
|
|
1046
|
-
console.log(
|
|
1047
|
-
console.log("\u{1F4CA} Wraps Platform \u2014 analytics, templates, automations");
|
|
1048
|
-
console.log(` From $10/mo \u2192 ${
|
|
1049
|
-
console.log();
|
|
1050
|
-
console.log(`\u{1F4AC} ${
|
|
1051
|
-
console.log(
|
|
1052
|
-
return true;
|
|
724
|
+
this.hasShownFooter = true;
|
|
725
|
+
console.log();
|
|
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"));
|
|
727
|
+
console.log("\u{1F4CA} Wraps Platform \u2014 analytics, templates, automations");
|
|
728
|
+
console.log(` From $10/mo \u2192 ${pc.cyan("https://wraps.dev/platform")}`);
|
|
729
|
+
console.log();
|
|
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"));
|
|
732
|
+
return true;
|
|
733
|
+
}
|
|
734
|
+
/**
|
|
735
|
+
* Get CLI version from package.json
|
|
736
|
+
*/
|
|
737
|
+
getCLIVersion() {
|
|
738
|
+
try {
|
|
739
|
+
const __filename3 = fileURLToPath2(import.meta.url);
|
|
740
|
+
const __dirname4 = dirname(__filename3);
|
|
741
|
+
const pkg = JSON.parse(
|
|
742
|
+
readFileSync(join4(__dirname4, "../package.json"), "utf-8")
|
|
743
|
+
);
|
|
744
|
+
return pkg.version;
|
|
745
|
+
} catch {
|
|
746
|
+
return "unknown";
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
};
|
|
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
|
+
}
|
|
1053
925
|
}
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
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;
|
|
1067
955
|
}
|
|
1068
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
|
|
1069
1010
|
};
|
|
1070
|
-
telemetryInstance = null;
|
|
1071
1011
|
}
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
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
|
|
1084
|
-
const
|
|
1085
|
-
|
|
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
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
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
|
|
1099
|
-
|
|
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
|
|
1107
|
-
const
|
|
1108
|
-
|
|
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
|
|
1111
|
-
const
|
|
1112
|
-
|
|
1113
|
-
service,
|
|
1114
|
-
...metadata
|
|
1115
|
-
});
|
|
1095
|
+
function hasCredentialsFile() {
|
|
1096
|
+
const credentialsPath = join5(homedir2(), ".aws", "credentials");
|
|
1097
|
+
return existsSync4(credentialsPath);
|
|
1116
1098
|
}
|
|
1117
|
-
function
|
|
1118
|
-
const
|
|
1119
|
-
|
|
1120
|
-
service,
|
|
1121
|
-
...metadata
|
|
1122
|
-
});
|
|
1099
|
+
function hasConfigFile() {
|
|
1100
|
+
const configPath = join5(homedir2(), ".aws", "config");
|
|
1101
|
+
return existsSync4(configPath);
|
|
1123
1102
|
}
|
|
1124
|
-
|
|
1125
|
-
|
|
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
|
|
|
@@ -4880,9 +4880,36 @@ var init_lambda = __esm({
|
|
|
4880
4880
|
// src/infrastructure/resources/acm.ts
|
|
4881
4881
|
var acm_exports = {};
|
|
4882
4882
|
__export(acm_exports, {
|
|
4883
|
+
checkCertificateValidation: () => checkCertificateValidation,
|
|
4883
4884
|
createACMCertificate: () => createACMCertificate
|
|
4884
4885
|
});
|
|
4886
|
+
import { ACMClient as ACMClient2, DescribeCertificateCommand as DescribeCertificateCommand2 } from "@aws-sdk/client-acm";
|
|
4885
4887
|
import * as aws12 from "@pulumi/aws";
|
|
4888
|
+
async function checkCertificateValidation(domain) {
|
|
4889
|
+
try {
|
|
4890
|
+
const acm3 = new ACMClient2({ region: "us-east-1" });
|
|
4891
|
+
const { ListCertificatesCommand } = await import("@aws-sdk/client-acm");
|
|
4892
|
+
const listResponse = await acm3.send(
|
|
4893
|
+
new ListCertificatesCommand({
|
|
4894
|
+
CertificateStatuses: ["ISSUED"]
|
|
4895
|
+
})
|
|
4896
|
+
);
|
|
4897
|
+
const cert = listResponse.CertificateSummaryList?.find(
|
|
4898
|
+
(c) => c.DomainName === domain
|
|
4899
|
+
);
|
|
4900
|
+
if (cert?.CertificateArn) {
|
|
4901
|
+
const describeResponse = await acm3.send(
|
|
4902
|
+
new DescribeCertificateCommand2({
|
|
4903
|
+
CertificateArn: cert.CertificateArn
|
|
4904
|
+
})
|
|
4905
|
+
);
|
|
4906
|
+
return describeResponse.Certificate?.Status === "ISSUED";
|
|
4907
|
+
}
|
|
4908
|
+
return false;
|
|
4909
|
+
} catch (error) {
|
|
4910
|
+
return false;
|
|
4911
|
+
}
|
|
4912
|
+
}
|
|
4886
4913
|
async function createACMCertificate(config2) {
|
|
4887
4914
|
const usEast1Provider = new aws12.Provider("acm-us-east-1", {
|
|
4888
4915
|
region: "us-east-1"
|
|
@@ -5618,6 +5645,10 @@ var init_eventbridge_inbound = __esm({
|
|
|
5618
5645
|
});
|
|
5619
5646
|
|
|
5620
5647
|
// src/utils/dns/cloudflare.ts
|
|
5648
|
+
var cloudflare_exports = {};
|
|
5649
|
+
__export(cloudflare_exports, {
|
|
5650
|
+
CloudflareDNSClient: () => CloudflareDNSClient
|
|
5651
|
+
});
|
|
5621
5652
|
var CLOUDFLARE_API_BASE, CloudflareDNSClient;
|
|
5622
5653
|
var init_cloudflare = __esm({
|
|
5623
5654
|
"src/utils/dns/cloudflare.ts"() {
|
|
@@ -5844,6 +5875,92 @@ var init_cloudflare = __esm({
|
|
|
5844
5875
|
};
|
|
5845
5876
|
}
|
|
5846
5877
|
}
|
|
5878
|
+
/**
|
|
5879
|
+
* Get the zone name (domain) for this zone ID
|
|
5880
|
+
*/
|
|
5881
|
+
async getZoneName() {
|
|
5882
|
+
try {
|
|
5883
|
+
const response = await fetch(
|
|
5884
|
+
`${CLOUDFLARE_API_BASE}/zones/${this.zoneId}`,
|
|
5885
|
+
{
|
|
5886
|
+
headers: {
|
|
5887
|
+
Authorization: `Bearer ${this.apiToken}`,
|
|
5888
|
+
"Content-Type": "application/json"
|
|
5889
|
+
}
|
|
5890
|
+
}
|
|
5891
|
+
);
|
|
5892
|
+
const data = await response.json();
|
|
5893
|
+
return data.success ? data.result.name : null;
|
|
5894
|
+
} catch {
|
|
5895
|
+
return null;
|
|
5896
|
+
}
|
|
5897
|
+
}
|
|
5898
|
+
/**
|
|
5899
|
+
* Get all CAA records for the zone
|
|
5900
|
+
*/
|
|
5901
|
+
async getCAARecords() {
|
|
5902
|
+
const zoneName = await this.getZoneName();
|
|
5903
|
+
if (!zoneName) return [];
|
|
5904
|
+
const result = await this.request(
|
|
5905
|
+
"/dns_records?type=CAA"
|
|
5906
|
+
);
|
|
5907
|
+
if (!(result.success && result.result)) {
|
|
5908
|
+
return [];
|
|
5909
|
+
}
|
|
5910
|
+
return result.result.filter((r) => r.type === "CAA").map((r) => {
|
|
5911
|
+
const match = r.content.match(/^(\d+)\s+(\w+)\s+"?([^"]+)"?$/);
|
|
5912
|
+
if (match) {
|
|
5913
|
+
return {
|
|
5914
|
+
flags: Number.parseInt(match[1], 10),
|
|
5915
|
+
tag: match[2],
|
|
5916
|
+
value: match[3]
|
|
5917
|
+
};
|
|
5918
|
+
}
|
|
5919
|
+
return null;
|
|
5920
|
+
}).filter(
|
|
5921
|
+
(r) => r !== null
|
|
5922
|
+
);
|
|
5923
|
+
}
|
|
5924
|
+
/**
|
|
5925
|
+
* Check if Amazon is allowed to issue certificates based on CAA records
|
|
5926
|
+
*/
|
|
5927
|
+
async isAmazonCAAAllowed() {
|
|
5928
|
+
const caaRecords = await this.getCAARecords();
|
|
5929
|
+
const issueRecords = caaRecords.filter(
|
|
5930
|
+
(r) => r.tag === "issue" || r.tag === "issuewild"
|
|
5931
|
+
);
|
|
5932
|
+
if (issueRecords.length === 0) {
|
|
5933
|
+
return { allowed: true, hasCAA: false, existingCAs: [] };
|
|
5934
|
+
}
|
|
5935
|
+
const existingCAs = issueRecords.map((r) => r.value);
|
|
5936
|
+
const amazonAllowed = existingCAs.some(
|
|
5937
|
+
(ca) => ca.includes("amazon.com") || ca.includes("amazontrust.com")
|
|
5938
|
+
);
|
|
5939
|
+
return { allowed: amazonAllowed, hasCAA: true, existingCAs };
|
|
5940
|
+
}
|
|
5941
|
+
/**
|
|
5942
|
+
* Add a CAA record to allow Amazon to issue certificates
|
|
5943
|
+
*/
|
|
5944
|
+
async addAmazonCAARecord() {
|
|
5945
|
+
const zoneName = await this.getZoneName();
|
|
5946
|
+
if (!zoneName) return false;
|
|
5947
|
+
const body = {
|
|
5948
|
+
name: zoneName,
|
|
5949
|
+
type: "CAA",
|
|
5950
|
+
data: {
|
|
5951
|
+
flags: 0,
|
|
5952
|
+
tag: "issue",
|
|
5953
|
+
value: "amazon.com"
|
|
5954
|
+
},
|
|
5955
|
+
ttl: 1800
|
|
5956
|
+
};
|
|
5957
|
+
const result = await this.request(
|
|
5958
|
+
"/dns_records",
|
|
5959
|
+
"POST",
|
|
5960
|
+
body
|
|
5961
|
+
);
|
|
5962
|
+
return result.success;
|
|
5963
|
+
}
|
|
5847
5964
|
async verifyRecords(data) {
|
|
5848
5965
|
const { domain, dkimTokens, mailFromDomain, region } = data;
|
|
5849
5966
|
const missing = [];
|
|
@@ -5891,6 +6008,10 @@ var init_cloudflare = __esm({
|
|
|
5891
6008
|
});
|
|
5892
6009
|
|
|
5893
6010
|
// src/utils/dns/vercel.ts
|
|
6011
|
+
var vercel_exports = {};
|
|
6012
|
+
__export(vercel_exports, {
|
|
6013
|
+
VercelDNSClient: () => VercelDNSClient
|
|
6014
|
+
});
|
|
5894
6015
|
var VERCEL_API_BASE, VercelDNSClient;
|
|
5895
6016
|
var init_vercel = __esm({
|
|
5896
6017
|
"src/utils/dns/vercel.ts"() {
|
|
@@ -6122,6 +6243,68 @@ var init_vercel = __esm({
|
|
|
6122
6243
|
};
|
|
6123
6244
|
}
|
|
6124
6245
|
}
|
|
6246
|
+
/**
|
|
6247
|
+
* Get all CAA records for the domain
|
|
6248
|
+
*/
|
|
6249
|
+
async getCAARecords() {
|
|
6250
|
+
const result = await this.request(
|
|
6251
|
+
`/v4/domains/${this.domain}/records`
|
|
6252
|
+
);
|
|
6253
|
+
if (result.error || !result.records) {
|
|
6254
|
+
return [];
|
|
6255
|
+
}
|
|
6256
|
+
return result.records.filter((r) => r.type === "CAA").map((r) => {
|
|
6257
|
+
const match = r.value.match(/^(\d+)\s+(\w+)\s+"?([^"]+)"?$/);
|
|
6258
|
+
if (match) {
|
|
6259
|
+
return {
|
|
6260
|
+
flags: Number.parseInt(match[1], 10),
|
|
6261
|
+
tag: match[2],
|
|
6262
|
+
value: match[3]
|
|
6263
|
+
};
|
|
6264
|
+
}
|
|
6265
|
+
return null;
|
|
6266
|
+
}).filter(
|
|
6267
|
+
(r) => r !== null
|
|
6268
|
+
);
|
|
6269
|
+
}
|
|
6270
|
+
/**
|
|
6271
|
+
* Check if Amazon is allowed to issue certificates based on CAA records
|
|
6272
|
+
* Returns true if:
|
|
6273
|
+
* - No CAA records exist (any CA can issue)
|
|
6274
|
+
* - CAA records exist and include amazon.com or amazontrust.com
|
|
6275
|
+
*/
|
|
6276
|
+
async isAmazonCAAAllowed() {
|
|
6277
|
+
const caaRecords = await this.getCAARecords();
|
|
6278
|
+
const issueRecords = caaRecords.filter(
|
|
6279
|
+
(r) => r.tag === "issue" || r.tag === "issuewild"
|
|
6280
|
+
);
|
|
6281
|
+
if (issueRecords.length === 0) {
|
|
6282
|
+
return { allowed: true, hasCAA: false, existingCAs: [] };
|
|
6283
|
+
}
|
|
6284
|
+
const existingCAs = issueRecords.map((r) => r.value);
|
|
6285
|
+
const amazonAllowed = existingCAs.some(
|
|
6286
|
+
(ca) => ca.includes("amazon.com") || ca.includes("amazontrust.com")
|
|
6287
|
+
);
|
|
6288
|
+
return { allowed: amazonAllowed, hasCAA: true, existingCAs };
|
|
6289
|
+
}
|
|
6290
|
+
/**
|
|
6291
|
+
* Add a CAA record to allow Amazon to issue certificates
|
|
6292
|
+
*/
|
|
6293
|
+
async addAmazonCAARecord() {
|
|
6294
|
+
const body = {
|
|
6295
|
+
name: "@",
|
|
6296
|
+
// Root domain
|
|
6297
|
+
type: "CAA",
|
|
6298
|
+
value: '0 issue "amazon.com"',
|
|
6299
|
+
ttl: 1800
|
|
6300
|
+
};
|
|
6301
|
+
const result = await this.request(
|
|
6302
|
+
`/v2/domains/${this.domain}/records`,
|
|
6303
|
+
"POST",
|
|
6304
|
+
body
|
|
6305
|
+
);
|
|
6306
|
+
return !result.error;
|
|
6307
|
+
}
|
|
6125
6308
|
async verifyRecords(data) {
|
|
6126
6309
|
const { domain, dkimTokens, mailFromDomain, region } = data;
|
|
6127
6310
|
const missing = [];
|
|
@@ -6687,6 +6870,76 @@ var init_dns = __esm({
|
|
|
6687
6870
|
}
|
|
6688
6871
|
});
|
|
6689
6872
|
|
|
6873
|
+
// src/utils/dns/caa.ts
|
|
6874
|
+
var caa_exports = {};
|
|
6875
|
+
__export(caa_exports, {
|
|
6876
|
+
ensureAmazonCAAAllowed: () => ensureAmazonCAAAllowed
|
|
6877
|
+
});
|
|
6878
|
+
async function ensureAmazonCAAAllowed(credentials, domain) {
|
|
6879
|
+
if (credentials.provider === "manual" || credentials.provider === "route53") {
|
|
6880
|
+
return { success: true, wasAlreadyAllowed: true, recordCreated: false };
|
|
6881
|
+
}
|
|
6882
|
+
try {
|
|
6883
|
+
if (credentials.provider === "vercel") {
|
|
6884
|
+
const { VercelDNSClient: VercelDNSClient2 } = await Promise.resolve().then(() => (init_vercel(), vercel_exports));
|
|
6885
|
+
const client = new VercelDNSClient2(
|
|
6886
|
+
domain,
|
|
6887
|
+
credentials.token,
|
|
6888
|
+
credentials.teamId
|
|
6889
|
+
);
|
|
6890
|
+
const caaStatus = await client.isAmazonCAAAllowed();
|
|
6891
|
+
if (caaStatus.hasCAA && caaStatus.allowed) {
|
|
6892
|
+
return { success: true, wasAlreadyAllowed: true, recordCreated: false };
|
|
6893
|
+
}
|
|
6894
|
+
const added = await client.addAmazonCAARecord();
|
|
6895
|
+
if (added) {
|
|
6896
|
+
return { success: true, wasAlreadyAllowed: false, recordCreated: true };
|
|
6897
|
+
}
|
|
6898
|
+
return {
|
|
6899
|
+
success: false,
|
|
6900
|
+
wasAlreadyAllowed: false,
|
|
6901
|
+
recordCreated: false,
|
|
6902
|
+
error: "Failed to create CAA record in Vercel DNS"
|
|
6903
|
+
};
|
|
6904
|
+
}
|
|
6905
|
+
if (credentials.provider === "cloudflare") {
|
|
6906
|
+
const { CloudflareDNSClient: CloudflareDNSClient2 } = await Promise.resolve().then(() => (init_cloudflare(), cloudflare_exports));
|
|
6907
|
+
const client = new CloudflareDNSClient2(
|
|
6908
|
+
credentials.zoneId,
|
|
6909
|
+
credentials.token
|
|
6910
|
+
);
|
|
6911
|
+
const caaStatus = await client.isAmazonCAAAllowed();
|
|
6912
|
+
if (caaStatus.allowed) {
|
|
6913
|
+
return { success: true, wasAlreadyAllowed: true, recordCreated: false };
|
|
6914
|
+
}
|
|
6915
|
+
const added = await client.addAmazonCAARecord();
|
|
6916
|
+
if (added) {
|
|
6917
|
+
return { success: true, wasAlreadyAllowed: false, recordCreated: true };
|
|
6918
|
+
}
|
|
6919
|
+
return {
|
|
6920
|
+
success: false,
|
|
6921
|
+
wasAlreadyAllowed: false,
|
|
6922
|
+
recordCreated: false,
|
|
6923
|
+
error: "Failed to create CAA record in Cloudflare DNS"
|
|
6924
|
+
};
|
|
6925
|
+
}
|
|
6926
|
+
return { success: true, wasAlreadyAllowed: true, recordCreated: false };
|
|
6927
|
+
} catch (error) {
|
|
6928
|
+
return {
|
|
6929
|
+
success: false,
|
|
6930
|
+
wasAlreadyAllowed: false,
|
|
6931
|
+
recordCreated: false,
|
|
6932
|
+
error: error instanceof Error ? error.message : "Unknown error"
|
|
6933
|
+
};
|
|
6934
|
+
}
|
|
6935
|
+
}
|
|
6936
|
+
var init_caa = __esm({
|
|
6937
|
+
"src/utils/dns/caa.ts"() {
|
|
6938
|
+
"use strict";
|
|
6939
|
+
init_esm_shims();
|
|
6940
|
+
}
|
|
6941
|
+
});
|
|
6942
|
+
|
|
6690
6943
|
// src/utils/shared/assume-role.ts
|
|
6691
6944
|
var assume_role_exports = {};
|
|
6692
6945
|
__export(assume_role_exports, {
|
|
@@ -7016,6 +7269,7 @@ import pc49 from "picocolors";
|
|
|
7016
7269
|
|
|
7017
7270
|
// src/commands/auth/login.ts
|
|
7018
7271
|
init_esm_shims();
|
|
7272
|
+
init_events();
|
|
7019
7273
|
init_config();
|
|
7020
7274
|
import * as clack from "@clack/prompts";
|
|
7021
7275
|
import { createAuthClient } from "better-auth/client";
|
|
@@ -7024,7 +7278,7 @@ import {
|
|
|
7024
7278
|
organizationClient
|
|
7025
7279
|
} from "better-auth/client/plugins";
|
|
7026
7280
|
import open from "open";
|
|
7027
|
-
import
|
|
7281
|
+
import pc2 from "picocolors";
|
|
7028
7282
|
function createCliAuthClient(baseURL) {
|
|
7029
7283
|
return createAuthClient({
|
|
7030
7284
|
baseURL,
|
|
@@ -7052,6 +7306,7 @@ async function fetchOrganizations(baseURL, token) {
|
|
|
7052
7306
|
}
|
|
7053
7307
|
}
|
|
7054
7308
|
async function login(options) {
|
|
7309
|
+
const startTime = Date.now();
|
|
7055
7310
|
if (options.token) {
|
|
7056
7311
|
await saveAuthConfig({
|
|
7057
7312
|
auth: {
|
|
@@ -7059,6 +7314,11 @@ async function login(options) {
|
|
|
7059
7314
|
tokenType: "api-key"
|
|
7060
7315
|
}
|
|
7061
7316
|
});
|
|
7317
|
+
trackCommand("auth:login", {
|
|
7318
|
+
success: true,
|
|
7319
|
+
duration_ms: Date.now() - startTime,
|
|
7320
|
+
method: "api-key"
|
|
7321
|
+
});
|
|
7062
7322
|
if (options.json) {
|
|
7063
7323
|
console.log(JSON.stringify({ success: true, tokenType: "api-key" }));
|
|
7064
7324
|
} else {
|
|
@@ -7066,7 +7326,7 @@ async function login(options) {
|
|
|
7066
7326
|
}
|
|
7067
7327
|
return;
|
|
7068
7328
|
}
|
|
7069
|
-
clack.intro(
|
|
7329
|
+
clack.intro(pc2.bold("Wraps \u203A Sign In"));
|
|
7070
7330
|
const baseURL = process.env.WRAPS_API_URL || "https://app.wraps.dev";
|
|
7071
7331
|
const authClient = createCliAuthClient(baseURL);
|
|
7072
7332
|
const spinner8 = clack.spinner();
|
|
@@ -7074,6 +7334,8 @@ async function login(options) {
|
|
|
7074
7334
|
client_id: "wraps-cli"
|
|
7075
7335
|
});
|
|
7076
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" });
|
|
7077
7339
|
clack.log.error("Failed to start device authorization.");
|
|
7078
7340
|
process.exit(1);
|
|
7079
7341
|
}
|
|
@@ -7085,8 +7347,8 @@ async function login(options) {
|
|
|
7085
7347
|
expires_in
|
|
7086
7348
|
} = codeData;
|
|
7087
7349
|
const formatted = `${user_code.slice(0, 4)}-${user_code.slice(4)}`;
|
|
7088
|
-
clack.log.info(`Your code: ${
|
|
7089
|
-
clack.log.info(`Visit: ${
|
|
7350
|
+
clack.log.info(`Your code: ${pc2.bold(pc2.cyan(formatted))}`);
|
|
7351
|
+
clack.log.info(`Visit: ${pc2.underline(`${baseURL}/device`)}`);
|
|
7090
7352
|
try {
|
|
7091
7353
|
await open(`${baseURL}/device?user_code=${user_code}`);
|
|
7092
7354
|
clack.log.info("Opening browser...");
|
|
@@ -7116,9 +7378,14 @@ async function login(options) {
|
|
|
7116
7378
|
organizations: organizations.length > 0 ? organizations : void 0
|
|
7117
7379
|
}
|
|
7118
7380
|
});
|
|
7381
|
+
trackCommand("auth:login", {
|
|
7382
|
+
success: true,
|
|
7383
|
+
duration_ms: Date.now() - startTime,
|
|
7384
|
+
method: "device"
|
|
7385
|
+
});
|
|
7119
7386
|
clack.log.success("Signed in successfully.");
|
|
7120
7387
|
if (organizations.length === 1) {
|
|
7121
|
-
clack.log.info(`Organization: ${
|
|
7388
|
+
clack.log.info(`Organization: ${pc2.cyan(organizations[0].name)}`);
|
|
7122
7389
|
} else if (organizations.length > 1) {
|
|
7123
7390
|
clack.log.info(`${organizations.length} organizations available`);
|
|
7124
7391
|
}
|
|
@@ -7143,6 +7410,8 @@ async function login(options) {
|
|
|
7143
7410
|
continue;
|
|
7144
7411
|
}
|
|
7145
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" });
|
|
7146
7415
|
spinner8.stop("Denied.");
|
|
7147
7416
|
clack.log.error("Authorization was denied.");
|
|
7148
7417
|
process.exit(1);
|
|
@@ -7152,6 +7421,8 @@ async function login(options) {
|
|
|
7152
7421
|
}
|
|
7153
7422
|
}
|
|
7154
7423
|
}
|
|
7424
|
+
trackCommand("auth:login", { success: false, duration_ms: Date.now() - startTime, method: "device" });
|
|
7425
|
+
trackError("DEVICE_CODE_EXPIRED", "auth:login", { step: "poll_token" });
|
|
7155
7426
|
spinner8.stop("Expired.");
|
|
7156
7427
|
clack.log.error("Device code expired. Run `wraps auth login` to try again.");
|
|
7157
7428
|
process.exit(1);
|
|
@@ -7159,32 +7430,37 @@ async function login(options) {
|
|
|
7159
7430
|
|
|
7160
7431
|
// src/commands/auth/logout.ts
|
|
7161
7432
|
init_esm_shims();
|
|
7433
|
+
init_events();
|
|
7162
7434
|
init_config();
|
|
7163
7435
|
import * as clack2 from "@clack/prompts";
|
|
7164
|
-
import
|
|
7436
|
+
import pc3 from "picocolors";
|
|
7165
7437
|
async function logout() {
|
|
7166
|
-
clack2.intro(
|
|
7438
|
+
clack2.intro(pc3.bold("Wraps \u203A Sign Out"));
|
|
7167
7439
|
const config2 = await readAuthConfig();
|
|
7168
7440
|
if (!config2?.auth?.token) {
|
|
7441
|
+
trackCommand("auth:logout", { success: true, already_logged_out: true });
|
|
7169
7442
|
clack2.log.info("Not signed in.");
|
|
7170
7443
|
return;
|
|
7171
7444
|
}
|
|
7172
7445
|
await clearAuthConfig();
|
|
7446
|
+
trackCommand("auth:logout", { success: true });
|
|
7173
7447
|
clack2.log.success("Signed out. Token removed from ~/.wraps/config.json");
|
|
7174
7448
|
}
|
|
7175
7449
|
|
|
7176
7450
|
// src/commands/auth/status.ts
|
|
7177
7451
|
init_esm_shims();
|
|
7452
|
+
init_events();
|
|
7178
7453
|
init_config();
|
|
7179
7454
|
import * as clack3 from "@clack/prompts";
|
|
7180
|
-
import
|
|
7455
|
+
import pc4 from "picocolors";
|
|
7181
7456
|
async function authStatus(options) {
|
|
7182
7457
|
const config2 = await readAuthConfig();
|
|
7183
7458
|
if (!config2?.auth?.token) {
|
|
7459
|
+
trackCommand("auth:status", { success: true, authenticated: false });
|
|
7184
7460
|
if (options.json) {
|
|
7185
7461
|
console.log(JSON.stringify({ authenticated: false }));
|
|
7186
7462
|
} else {
|
|
7187
|
-
clack3.intro(
|
|
7463
|
+
clack3.intro(pc4.bold("Wraps \u203A Auth Status"));
|
|
7188
7464
|
clack3.log.info("Not signed in. Run `wraps auth login` to authenticate.");
|
|
7189
7465
|
}
|
|
7190
7466
|
return;
|
|
@@ -7201,16 +7477,18 @@ async function authStatus(options) {
|
|
|
7201
7477
|
})
|
|
7202
7478
|
);
|
|
7203
7479
|
} else {
|
|
7204
|
-
clack3.intro(
|
|
7480
|
+
clack3.intro(pc4.bold("Wraps \u203A Auth Status"));
|
|
7205
7481
|
clack3.log.info(`Token: ${masked} (${tokenType})`);
|
|
7206
7482
|
if (expiresAt) {
|
|
7207
7483
|
clack3.log.info(`Expires: ${new Date(expiresAt).toLocaleDateString()}`);
|
|
7208
7484
|
}
|
|
7209
7485
|
}
|
|
7486
|
+
trackCommand("auth:status", { success: true, authenticated: true });
|
|
7210
7487
|
}
|
|
7211
7488
|
|
|
7212
7489
|
// src/commands/aws/doctor.ts
|
|
7213
7490
|
init_esm_shims();
|
|
7491
|
+
init_events();
|
|
7214
7492
|
init_aws();
|
|
7215
7493
|
init_aws_detection();
|
|
7216
7494
|
import * as clack5 from "@clack/prompts";
|
|
@@ -7501,6 +7779,7 @@ function generateSuggestions(results, state) {
|
|
|
7501
7779
|
return suggestions;
|
|
7502
7780
|
}
|
|
7503
7781
|
async function doctor() {
|
|
7782
|
+
const startTime = Date.now();
|
|
7504
7783
|
clack5.intro(pc6.bold("AWS Setup Diagnostics"));
|
|
7505
7784
|
const spinner8 = clack5.spinner();
|
|
7506
7785
|
spinner8.start("Running diagnostics...");
|
|
@@ -7528,6 +7807,13 @@ async function doctor() {
|
|
|
7528
7807
|
console.log(` ${pc6.dim("-")} ${suggestion}`);
|
|
7529
7808
|
}
|
|
7530
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
|
+
});
|
|
7531
7817
|
console.log();
|
|
7532
7818
|
clack5.outro(
|
|
7533
7819
|
failCount > 0 ? pc6.dim("Run `wraps aws setup` to fix issues") : pc6.dim("Ready to deploy: wraps email init")
|
|
@@ -7536,6 +7822,7 @@ async function doctor() {
|
|
|
7536
7822
|
|
|
7537
7823
|
// src/commands/aws/setup.ts
|
|
7538
7824
|
init_esm_shims();
|
|
7825
|
+
init_events();
|
|
7539
7826
|
init_aws_detection();
|
|
7540
7827
|
init_prompts();
|
|
7541
7828
|
import * as clack7 from "@clack/prompts";
|
|
@@ -8087,6 +8374,7 @@ function showNextSteps(_state) {
|
|
|
8087
8374
|
console.log();
|
|
8088
8375
|
}
|
|
8089
8376
|
async function setup(_options = {}) {
|
|
8377
|
+
const startTime = Date.now();
|
|
8090
8378
|
clack7.intro(pc8.bold("AWS Setup Wizard"));
|
|
8091
8379
|
const spinner8 = clack7.spinner();
|
|
8092
8380
|
spinner8.start("Checking your AWS setup...");
|
|
@@ -8106,6 +8394,10 @@ async function setup(_options = {}) {
|
|
|
8106
8394
|
await runCredentialSetup();
|
|
8107
8395
|
}
|
|
8108
8396
|
}
|
|
8397
|
+
trackCommand("aws:setup", {
|
|
8398
|
+
success: true,
|
|
8399
|
+
duration_ms: Date.now() - startTime
|
|
8400
|
+
});
|
|
8109
8401
|
clack7.outro(pc8.dim("Run `wraps aws doctor` to verify your setup"));
|
|
8110
8402
|
}
|
|
8111
8403
|
|
|
@@ -10753,10 +11045,10 @@ async function cdnSync(options) {
|
|
|
10753
11045
|
});
|
|
10754
11046
|
const stackOutputs = await checkStack.outputs();
|
|
10755
11047
|
if (stackOutputs.acmCertificateArn?.value) {
|
|
10756
|
-
const { ACMClient:
|
|
10757
|
-
const acmClient = new
|
|
11048
|
+
const { ACMClient: ACMClient3, DescribeCertificateCommand: DescribeCertificateCommand3 } = await import("@aws-sdk/client-acm");
|
|
11049
|
+
const acmClient = new ACMClient3({ region: "us-east-1" });
|
|
10758
11050
|
const certResponse = await acmClient.send(
|
|
10759
|
-
new
|
|
11051
|
+
new DescribeCertificateCommand3({
|
|
10760
11052
|
CertificateArn: stackOutputs.acmCertificateArn.value
|
|
10761
11053
|
})
|
|
10762
11054
|
);
|
|
@@ -10941,12 +11233,12 @@ Current configuration:
|
|
|
10941
11233
|
process.exit(0);
|
|
10942
11234
|
}
|
|
10943
11235
|
progress.start("Checking certificate validation status");
|
|
10944
|
-
const { ACMClient:
|
|
10945
|
-
const acmClient = new
|
|
11236
|
+
const { ACMClient: ACMClient3, DescribeCertificateCommand: DescribeCertificateCommand3 } = await import("@aws-sdk/client-acm");
|
|
11237
|
+
const acmClient = new ACMClient3({ region: "us-east-1" });
|
|
10946
11238
|
let certStatus;
|
|
10947
11239
|
try {
|
|
10948
11240
|
const certResponse = await acmClient.send(
|
|
10949
|
-
new
|
|
11241
|
+
new DescribeCertificateCommand3({
|
|
10950
11242
|
CertificateArn: stackOutputs.acmCertificateArn.value
|
|
10951
11243
|
})
|
|
10952
11244
|
);
|
|
@@ -11142,10 +11434,10 @@ async function checkDNSRecord(hostname, expectedValue) {
|
|
|
11142
11434
|
}
|
|
11143
11435
|
async function checkCertificateStatus(certificateArn) {
|
|
11144
11436
|
try {
|
|
11145
|
-
const { ACMClient:
|
|
11146
|
-
const acm3 = new
|
|
11437
|
+
const { ACMClient: ACMClient3, DescribeCertificateCommand: DescribeCertificateCommand3 } = await import("@aws-sdk/client-acm");
|
|
11438
|
+
const acm3 = new ACMClient3({ region: "us-east-1" });
|
|
11147
11439
|
const result = await acm3.send(
|
|
11148
|
-
new
|
|
11440
|
+
new DescribeCertificateCommand3({ CertificateArn: certificateArn })
|
|
11149
11441
|
);
|
|
11150
11442
|
const cert = result.Certificate;
|
|
11151
11443
|
const validationStatus = cert?.DomainValidationOptions?.[0]?.ValidationStatus;
|
|
@@ -15296,6 +15588,7 @@ async function deployEmailStack(config2) {
|
|
|
15296
15588
|
});
|
|
15297
15589
|
let cloudFrontResources;
|
|
15298
15590
|
let acmResources;
|
|
15591
|
+
let skipCloudFront = false;
|
|
15299
15592
|
if (emailConfig.tracking?.enabled && emailConfig.tracking.customRedirectDomain && emailConfig.tracking.httpsEnabled) {
|
|
15300
15593
|
const { findHostedZone: findHostedZone2 } = await Promise.resolve().then(() => (init_route53(), route53_exports));
|
|
15301
15594
|
const hostedZone = await findHostedZone2(
|
|
@@ -15307,16 +15600,27 @@ async function deployEmailStack(config2) {
|
|
|
15307
15600
|
domain: emailConfig.tracking.customRedirectDomain,
|
|
15308
15601
|
hostedZoneId: hostedZone?.id
|
|
15309
15602
|
});
|
|
15310
|
-
|
|
15311
|
-
|
|
15312
|
-
|
|
15313
|
-
|
|
15314
|
-
|
|
15315
|
-
|
|
15316
|
-
|
|
15317
|
-
|
|
15318
|
-
|
|
15319
|
-
|
|
15603
|
+
if (!hostedZone) {
|
|
15604
|
+
const { checkCertificateValidation: checkCertificateValidation2 } = await Promise.resolve().then(() => (init_acm(), acm_exports));
|
|
15605
|
+
const isValidated = await checkCertificateValidation2(
|
|
15606
|
+
emailConfig.tracking.customRedirectDomain
|
|
15607
|
+
);
|
|
15608
|
+
if (!isValidated) {
|
|
15609
|
+
skipCloudFront = true;
|
|
15610
|
+
}
|
|
15611
|
+
}
|
|
15612
|
+
if (!skipCloudFront) {
|
|
15613
|
+
const { createCloudFrontTracking: createCloudFrontTracking2 } = await Promise.resolve().then(() => (init_cloudfront(), cloudfront_exports));
|
|
15614
|
+
const certificateArn = acmResources.certificateValidation ? acmResources.certificateValidation.certificateArn : acmResources.certificate.arn;
|
|
15615
|
+
cloudFrontResources = await createCloudFrontTracking2({
|
|
15616
|
+
customTrackingDomain: emailConfig.tracking.customRedirectDomain,
|
|
15617
|
+
region: config2.region,
|
|
15618
|
+
certificateArn,
|
|
15619
|
+
hostedZoneId: hostedZone?.id,
|
|
15620
|
+
// Pass hosted zone ID for automatic DNS record creation
|
|
15621
|
+
wafEnabled: emailConfig.tracking.wafEnabled
|
|
15622
|
+
});
|
|
15623
|
+
}
|
|
15320
15624
|
}
|
|
15321
15625
|
let sesResources;
|
|
15322
15626
|
if (emailConfig.tracking?.enabled || emailConfig.eventTracking?.enabled) {
|
|
@@ -15329,11 +15633,19 @@ async function deployEmailStack(config2) {
|
|
|
15329
15633
|
if (!mailFromDomain && emailConfig.mailFromSubdomain && emailConfig.domain) {
|
|
15330
15634
|
mailFromDomain = `${emailConfig.mailFromSubdomain}.${emailConfig.domain}`;
|
|
15331
15635
|
}
|
|
15636
|
+
const effectiveTrackingConfig = skipCloudFront && emailConfig.tracking ? {
|
|
15637
|
+
enabled: emailConfig.tracking.enabled,
|
|
15638
|
+
opens: emailConfig.tracking.opens,
|
|
15639
|
+
clicks: emailConfig.tracking.clicks,
|
|
15640
|
+
customRedirectDomain: emailConfig.tracking.customRedirectDomain,
|
|
15641
|
+
httpsEnabled: false
|
|
15642
|
+
// Use OPTIONAL until CloudFront is ready
|
|
15643
|
+
} : emailConfig.tracking;
|
|
15332
15644
|
sesResources = await createSESResources({
|
|
15333
15645
|
domain: emailConfig.domain,
|
|
15334
15646
|
mailFromDomain,
|
|
15335
15647
|
region: config2.region,
|
|
15336
|
-
trackingConfig:
|
|
15648
|
+
trackingConfig: effectiveTrackingConfig,
|
|
15337
15649
|
eventTypes: emailConfig.eventTracking?.events,
|
|
15338
15650
|
eventTrackingEnabled: emailConfig.eventTracking?.enabled,
|
|
15339
15651
|
// Pass flag to create EventBridge destination
|
|
@@ -15461,6 +15773,8 @@ async function deployEmailStack(config2) {
|
|
|
15461
15773
|
dlqUrl: sqsResources?.dlq.url,
|
|
15462
15774
|
customTrackingDomain: sesResources?.customTrackingDomain,
|
|
15463
15775
|
httpsTrackingEnabled: emailConfig.tracking?.httpsEnabled,
|
|
15776
|
+
httpsTrackingPending: skipCloudFront,
|
|
15777
|
+
// True if HTTPS requested but cert not validated yet
|
|
15464
15778
|
cloudFrontDomain: cloudFrontResources?.domainName,
|
|
15465
15779
|
acmCertificateValidationRecords: acmResources?.validationRecords,
|
|
15466
15780
|
mailFromDomain: sesResources?.mailFromDomain,
|
|
@@ -18987,6 +19301,7 @@ Run ${pc24.cyan("wraps email init")} to deploy email infrastructure.
|
|
|
18987
19301
|
|
|
18988
19302
|
// src/commands/email/templates/init.ts
|
|
18989
19303
|
init_esm_shims();
|
|
19304
|
+
init_events();
|
|
18990
19305
|
init_config();
|
|
18991
19306
|
init_errors();
|
|
18992
19307
|
import { existsSync as existsSync7 } from "fs";
|
|
@@ -18995,6 +19310,7 @@ import { join as join8 } from "path";
|
|
|
18995
19310
|
import * as clack24 from "@clack/prompts";
|
|
18996
19311
|
import pc25 from "picocolors";
|
|
18997
19312
|
async function templatesInit(options) {
|
|
19313
|
+
const startTime = Date.now();
|
|
18998
19314
|
const cwd = process.cwd();
|
|
18999
19315
|
const wrapsDir = join8(cwd, "wraps");
|
|
19000
19316
|
if (!options.json) {
|
|
@@ -19130,6 +19446,10 @@ wraps/.wraps/
|
|
|
19130
19446
|
);
|
|
19131
19447
|
return;
|
|
19132
19448
|
}
|
|
19449
|
+
trackCommand("email:templates:init", {
|
|
19450
|
+
success: true,
|
|
19451
|
+
duration_ms: Date.now() - startTime
|
|
19452
|
+
});
|
|
19133
19453
|
console.log();
|
|
19134
19454
|
clack24.log.success(pc25.green("Templates as Code initialized!"));
|
|
19135
19455
|
console.log();
|
|
@@ -19336,6 +19656,7 @@ const unsubscribeLink = {
|
|
|
19336
19656
|
|
|
19337
19657
|
// src/commands/email/templates/preview.ts
|
|
19338
19658
|
init_esm_shims();
|
|
19659
|
+
init_events();
|
|
19339
19660
|
import { existsSync as existsSync9, watch } from "fs";
|
|
19340
19661
|
import { join as join10 } from "path";
|
|
19341
19662
|
import * as clack25 from "@clack/prompts";
|
|
@@ -19582,6 +19903,10 @@ async function templatesPreview(options) {
|
|
|
19582
19903
|
const port = options.port || await getPort2({ port: [3333, 3334, 3335, 3336, 3337] });
|
|
19583
19904
|
const server = app.listen(port, () => {
|
|
19584
19905
|
const url = `http://localhost:${port}`;
|
|
19906
|
+
trackCommand("email:templates:preview", {
|
|
19907
|
+
success: true,
|
|
19908
|
+
template_count: templateFiles.length
|
|
19909
|
+
});
|
|
19585
19910
|
clack25.log.success(`Preview server running at ${pc26.cyan(url)}`);
|
|
19586
19911
|
if (options.template) {
|
|
19587
19912
|
clack25.log.info(`Previewing: ${pc26.cyan(options.template)}`);
|
|
@@ -19781,6 +20106,7 @@ function renderErrorPage(err) {
|
|
|
19781
20106
|
|
|
19782
20107
|
// src/commands/email/templates/push.ts
|
|
19783
20108
|
init_esm_shims();
|
|
20109
|
+
init_events();
|
|
19784
20110
|
import { createHash } from "crypto";
|
|
19785
20111
|
import { existsSync as existsSync10 } from "fs";
|
|
19786
20112
|
import { mkdir as mkdir4, readFile as readFile4, writeFile as writeFile6 } from "fs/promises";
|
|
@@ -19790,6 +20116,7 @@ import pc27 from "picocolors";
|
|
|
19790
20116
|
init_config();
|
|
19791
20117
|
init_errors();
|
|
19792
20118
|
async function templatesPush(options) {
|
|
20119
|
+
const startTime = Date.now();
|
|
19793
20120
|
const cwd = process.cwd();
|
|
19794
20121
|
const wrapsDir = join11(cwd, "wraps");
|
|
19795
20122
|
const configPath = join11(wrapsDir, "wraps.config.ts");
|
|
@@ -19955,6 +20282,13 @@ async function templatesPush(options) {
|
|
|
19955
20282
|
}
|
|
19956
20283
|
console.log();
|
|
19957
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
|
+
});
|
|
19958
20292
|
}
|
|
19959
20293
|
async function compileTemplate(filePath, slug, source, sourceHash, wrapsDir) {
|
|
19960
20294
|
const { build: build2 } = await import("esbuild");
|
|
@@ -21408,6 +21742,56 @@ ${pc28.bold("Cost Impact:")}`);
|
|
|
21408
21742
|
const stackConfig = buildEmailStackConfig(metadata, region, {
|
|
21409
21743
|
emailConfig: updatedConfig
|
|
21410
21744
|
});
|
|
21745
|
+
if (updatedConfig.tracking?.httpsEnabled && updatedConfig.tracking.customRedirectDomain) {
|
|
21746
|
+
const trackingDomainParts = updatedConfig.tracking.customRedirectDomain.split(".");
|
|
21747
|
+
const parentDomain = trackingDomainParts.length > 2 ? trackingDomainParts.slice(-2).join(".") : updatedConfig.tracking.customRedirectDomain;
|
|
21748
|
+
let dnsProvider = metadata.services.email?.dnsProvider;
|
|
21749
|
+
if (!dnsProvider) {
|
|
21750
|
+
const availableProviders = await progress.execute(
|
|
21751
|
+
"Detecting DNS provider for CAA check",
|
|
21752
|
+
async () => await detectAvailableDNSProviders(parentDomain, region)
|
|
21753
|
+
);
|
|
21754
|
+
const detectedProvider = availableProviders.find(
|
|
21755
|
+
(p) => p.detected && p.provider !== "manual"
|
|
21756
|
+
);
|
|
21757
|
+
if (detectedProvider) {
|
|
21758
|
+
dnsProvider = detectedProvider.provider;
|
|
21759
|
+
if (metadata.services.email) {
|
|
21760
|
+
metadata.services.email.dnsProvider = dnsProvider;
|
|
21761
|
+
}
|
|
21762
|
+
}
|
|
21763
|
+
}
|
|
21764
|
+
if (dnsProvider && dnsProvider !== "manual" && dnsProvider !== "route53") {
|
|
21765
|
+
const credResult = await getDNSCredentials(
|
|
21766
|
+
dnsProvider,
|
|
21767
|
+
parentDomain,
|
|
21768
|
+
region
|
|
21769
|
+
);
|
|
21770
|
+
if (credResult.valid && credResult.credentials) {
|
|
21771
|
+
const { ensureAmazonCAAAllowed: ensureAmazonCAAAllowed2 } = await Promise.resolve().then(() => (init_caa(), caa_exports));
|
|
21772
|
+
const caaResult = await progress.execute(
|
|
21773
|
+
"Checking CAA records for certificate issuance",
|
|
21774
|
+
async () => await ensureAmazonCAAAllowed2(credResult.credentials, parentDomain)
|
|
21775
|
+
);
|
|
21776
|
+
if (caaResult.recordCreated) {
|
|
21777
|
+
progress.info(
|
|
21778
|
+
`Added CAA record to allow Amazon certificate issuance for ${pc28.cyan(parentDomain)}`
|
|
21779
|
+
);
|
|
21780
|
+
await new Promise((resolve) => setTimeout(resolve, 2e3));
|
|
21781
|
+
} else if (!caaResult.success) {
|
|
21782
|
+
clack27.log.warn(
|
|
21783
|
+
`Could not verify CAA records: ${caaResult.error || "Unknown error"}`
|
|
21784
|
+
);
|
|
21785
|
+
clack27.log.info(
|
|
21786
|
+
pc28.dim(
|
|
21787
|
+
"If certificate issuance fails, you may need to add a CAA record manually:"
|
|
21788
|
+
)
|
|
21789
|
+
);
|
|
21790
|
+
clack27.log.info(pc28.dim(` ${parentDomain} CAA 0 issue "amazon.com"`));
|
|
21791
|
+
}
|
|
21792
|
+
}
|
|
21793
|
+
}
|
|
21794
|
+
}
|
|
21411
21795
|
if (options.preview) {
|
|
21412
21796
|
try {
|
|
21413
21797
|
const previewResult = await progress.execute(
|
|
@@ -21508,6 +21892,7 @@ ${pc28.bold("Cost Impact:")}`);
|
|
|
21508
21892
|
dkimTokens: result.dkimTokens,
|
|
21509
21893
|
customTrackingDomain: result.customTrackingDomain,
|
|
21510
21894
|
httpsTrackingEnabled: result.httpsTrackingEnabled,
|
|
21895
|
+
httpsTrackingPending: result.httpsTrackingPending,
|
|
21511
21896
|
cloudFrontDomain: result.cloudFrontDomain,
|
|
21512
21897
|
acmCertificateValidationRecords: result.acmCertificateValidationRecords,
|
|
21513
21898
|
archiveArn: result.archiveArn,
|
|
@@ -21549,6 +21934,7 @@ ${pc28.bold("Cost Impact:")}`);
|
|
|
21549
21934
|
dkimTokens: pulumiOutputs.dkimTokens?.value,
|
|
21550
21935
|
customTrackingDomain: pulumiOutputs.customTrackingDomain?.value,
|
|
21551
21936
|
httpsTrackingEnabled: pulumiOutputs.httpsTrackingEnabled?.value,
|
|
21937
|
+
httpsTrackingPending: pulumiOutputs.httpsTrackingPending?.value,
|
|
21552
21938
|
cloudFrontDomain: pulumiOutputs.cloudFrontDomain?.value,
|
|
21553
21939
|
acmCertificateValidationRecords: pulumiOutputs.acmCertificateValidationRecords?.value,
|
|
21554
21940
|
archiveArn: pulumiOutputs.archiveArn?.value,
|
|
@@ -21703,14 +22089,92 @@ ${pc28.bold("Add these DNS records to your DNS provider:")}
|
|
|
21703
22089
|
if (outputs.httpsTrackingEnabled && outputs.acmCertificateValidationRecords) {
|
|
21704
22090
|
acmValidationRecords.push(...outputs.acmCertificateValidationRecords);
|
|
21705
22091
|
}
|
|
21706
|
-
|
|
22092
|
+
let acmDnsAutoCreated = false;
|
|
22093
|
+
if (outputs.httpsTrackingPending && acmValidationRecords.length > 0 && outputs.customTrackingDomain) {
|
|
22094
|
+
const trackingDnsProvider = metadata.services.email?.dnsProvider;
|
|
22095
|
+
if (trackingDnsProvider && trackingDnsProvider !== "manual") {
|
|
22096
|
+
const trackingDomainParts = outputs.customTrackingDomain.split(".");
|
|
22097
|
+
const parentDomain = trackingDomainParts.length > 2 ? trackingDomainParts.slice(-2).join(".") : outputs.customTrackingDomain;
|
|
22098
|
+
const credResult = await progress.execute(
|
|
22099
|
+
`Validating ${getDNSProviderDisplayName(trackingDnsProvider)} credentials for ACM validation`,
|
|
22100
|
+
async () => await getDNSCredentials(trackingDnsProvider, parentDomain, region)
|
|
22101
|
+
);
|
|
22102
|
+
if (credResult.valid && credResult.credentials) {
|
|
22103
|
+
try {
|
|
22104
|
+
progress.start(
|
|
22105
|
+
`Creating ACM validation DNS record in ${getDNSProviderDisplayName(trackingDnsProvider)}`
|
|
22106
|
+
);
|
|
22107
|
+
if (credResult.credentials.provider === "vercel") {
|
|
22108
|
+
const { VercelDNSClient: VercelDNSClient2 } = await Promise.resolve().then(() => (init_vercel(), vercel_exports));
|
|
22109
|
+
const client = new VercelDNSClient2(
|
|
22110
|
+
parentDomain,
|
|
22111
|
+
credResult.credentials.token,
|
|
22112
|
+
credResult.credentials.teamId
|
|
22113
|
+
);
|
|
22114
|
+
const result = await client.createRecords(
|
|
22115
|
+
acmValidationRecords.map((r) => ({
|
|
22116
|
+
name: r.name,
|
|
22117
|
+
type: r.type,
|
|
22118
|
+
value: r.value
|
|
22119
|
+
}))
|
|
22120
|
+
);
|
|
22121
|
+
if (result.success) {
|
|
22122
|
+
progress.succeed(
|
|
22123
|
+
`Created ACM validation DNS record in ${getDNSProviderDisplayName(trackingDnsProvider)}`
|
|
22124
|
+
);
|
|
22125
|
+
acmDnsAutoCreated = true;
|
|
22126
|
+
progress.info(
|
|
22127
|
+
"Certificate validation usually takes 5-30 minutes. Run this command again after validation completes."
|
|
22128
|
+
);
|
|
22129
|
+
} else {
|
|
22130
|
+
progress.fail(
|
|
22131
|
+
`Failed to create ACM validation record: ${result.errors?.join(", ")}`
|
|
22132
|
+
);
|
|
22133
|
+
}
|
|
22134
|
+
} else if (credResult.credentials.provider === "cloudflare") {
|
|
22135
|
+
const { CloudflareDNSClient: CloudflareDNSClient2 } = await Promise.resolve().then(() => (init_cloudflare(), cloudflare_exports));
|
|
22136
|
+
const client = new CloudflareDNSClient2(
|
|
22137
|
+
credResult.credentials.zoneId,
|
|
22138
|
+
credResult.credentials.token
|
|
22139
|
+
);
|
|
22140
|
+
const result = await client.createRecords(
|
|
22141
|
+
acmValidationRecords.map((r) => ({
|
|
22142
|
+
name: r.name,
|
|
22143
|
+
type: r.type,
|
|
22144
|
+
value: r.value
|
|
22145
|
+
}))
|
|
22146
|
+
);
|
|
22147
|
+
if (result.success) {
|
|
22148
|
+
progress.succeed(
|
|
22149
|
+
`Created ACM validation DNS record in ${getDNSProviderDisplayName(trackingDnsProvider)}`
|
|
22150
|
+
);
|
|
22151
|
+
acmDnsAutoCreated = true;
|
|
22152
|
+
progress.info(
|
|
22153
|
+
"Certificate validation usually takes 5-30 minutes. Run this command again after validation completes."
|
|
22154
|
+
);
|
|
22155
|
+
} else {
|
|
22156
|
+
progress.fail(
|
|
22157
|
+
`Failed to create ACM validation record: ${result.errors?.join(", ")}`
|
|
22158
|
+
);
|
|
22159
|
+
}
|
|
22160
|
+
}
|
|
22161
|
+
} catch (error) {
|
|
22162
|
+
progress.fail(
|
|
22163
|
+
`Failed to create ACM validation record: ${error.message}`
|
|
22164
|
+
);
|
|
22165
|
+
}
|
|
22166
|
+
}
|
|
22167
|
+
}
|
|
22168
|
+
}
|
|
22169
|
+
const needsCertificateValidation = outputs.httpsTrackingPending || outputs.httpsTrackingEnabled && acmValidationRecords.length > 0 && !outputs.cloudFrontDomain;
|
|
21707
22170
|
displaySuccess({
|
|
21708
22171
|
roleArn: outputs.roleArn,
|
|
21709
22172
|
configSetName: outputs.configSetName,
|
|
21710
22173
|
region: outputs.region,
|
|
21711
22174
|
tableName: outputs.tableName,
|
|
21712
22175
|
trackingDomainDnsRecords: trackingDomainDnsRecords.length > 0 ? trackingDomainDnsRecords : void 0,
|
|
21713
|
-
|
|
22176
|
+
// Only show ACM validation records if they weren't auto-created
|
|
22177
|
+
acmValidationRecords: acmValidationRecords.length > 0 && !acmDnsAutoCreated ? acmValidationRecords : void 0,
|
|
21714
22178
|
customTrackingDomain: outputs.customTrackingDomain,
|
|
21715
22179
|
httpsTrackingEnabled: outputs.httpsTrackingEnabled
|
|
21716
22180
|
});
|
|
@@ -21730,16 +22194,27 @@ ${pc28.green("\u2713")} ${pc28.bold("Upgrade complete!")}
|
|
|
21730
22194
|
}
|
|
21731
22195
|
if (needsCertificateValidation) {
|
|
21732
22196
|
console.log(pc28.bold("\u26A0\uFE0F HTTPS Tracking - Next Steps:\n"));
|
|
21733
|
-
|
|
21734
|
-
|
|
21735
|
-
|
|
21736
|
-
|
|
21737
|
-
" 2. Wait for
|
|
21738
|
-
|
|
21739
|
-
|
|
21740
|
-
` 3. Run ${pc28.cyan("wraps email upgrade")} again to complete CloudFront setup
|
|
22197
|
+
if (acmDnsAutoCreated) {
|
|
22198
|
+
console.log(
|
|
22199
|
+
` 1. ${pc28.green("\u2713")} ACM validation DNS record created automatically`
|
|
22200
|
+
);
|
|
22201
|
+
console.log(" 2. Wait for certificate validation (5-30 minutes)");
|
|
22202
|
+
console.log(
|
|
22203
|
+
` 3. Run ${pc28.cyan("wraps email upgrade")} again to complete CloudFront setup
|
|
21741
22204
|
`
|
|
21742
|
-
|
|
22205
|
+
);
|
|
22206
|
+
} else {
|
|
22207
|
+
console.log(
|
|
22208
|
+
" 1. Add the SSL certificate validation DNS record shown above to your DNS provider"
|
|
22209
|
+
);
|
|
22210
|
+
console.log(
|
|
22211
|
+
" 2. Wait for DNS propagation and certificate validation (5-30 minutes)"
|
|
22212
|
+
);
|
|
22213
|
+
console.log(
|
|
22214
|
+
` 3. Run ${pc28.cyan("wraps email upgrade")} again to complete CloudFront setup
|
|
22215
|
+
`
|
|
22216
|
+
);
|
|
22217
|
+
}
|
|
21743
22218
|
console.log(
|
|
21744
22219
|
pc28.dim(
|
|
21745
22220
|
" Note: CloudFront distribution will be created once the certificate is validated.\n"
|
|
@@ -21835,6 +22310,7 @@ ${pc28.green("\u2713")} ${pc28.bold("Upgrade complete!")}
|
|
|
21835
22310
|
|
|
21836
22311
|
// src/commands/email/workflows/push.ts
|
|
21837
22312
|
init_esm_shims();
|
|
22313
|
+
init_events();
|
|
21838
22314
|
import { existsSync as existsSync12 } from "fs";
|
|
21839
22315
|
import { mkdir as mkdir6, readFile as readFile6, writeFile as writeFile8 } from "fs/promises";
|
|
21840
22316
|
import { join as join13 } from "path";
|
|
@@ -22564,6 +23040,7 @@ function validateTemplateReferences(steps, localTemplateSlugs) {
|
|
|
22564
23040
|
init_config();
|
|
22565
23041
|
init_errors();
|
|
22566
23042
|
async function workflowsPush(options) {
|
|
23043
|
+
const startTime = Date.now();
|
|
22567
23044
|
const cwd = process.cwd();
|
|
22568
23045
|
const wrapsDir = join13(cwd, "wraps");
|
|
22569
23046
|
const configPath = join13(wrapsDir, "wraps.config.ts");
|
|
@@ -22808,6 +23285,13 @@ async function workflowsPush(options) {
|
|
|
22808
23285
|
}
|
|
22809
23286
|
console.log();
|
|
22810
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
|
+
});
|
|
22811
23295
|
}
|
|
22812
23296
|
async function pushToAPI2(workflows, token, _org, progress, force) {
|
|
22813
23297
|
if (!token) {
|
|
@@ -22956,12 +23440,14 @@ async function saveLockfile2(path3, lockfile) {
|
|
|
22956
23440
|
|
|
22957
23441
|
// src/commands/email/workflows/validate.ts
|
|
22958
23442
|
init_esm_shims();
|
|
23443
|
+
init_events();
|
|
22959
23444
|
import { existsSync as existsSync13 } from "fs";
|
|
22960
23445
|
import { join as join14 } from "path";
|
|
22961
23446
|
import * as clack29 from "@clack/prompts";
|
|
22962
23447
|
import pc30 from "picocolors";
|
|
22963
23448
|
init_errors();
|
|
22964
23449
|
async function workflowsValidate(options) {
|
|
23450
|
+
const startTime = Date.now();
|
|
22965
23451
|
const cwd = process.cwd();
|
|
22966
23452
|
const wrapsDir = join14(cwd, "wraps");
|
|
22967
23453
|
const configPath = join14(wrapsDir, "wraps.config.ts");
|
|
@@ -23110,13 +23596,22 @@ async function workflowsValidate(options) {
|
|
|
23110
23596
|
}
|
|
23111
23597
|
console.log();
|
|
23112
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
|
+
});
|
|
23113
23606
|
}
|
|
23114
23607
|
|
|
23115
23608
|
// src/commands/news.ts
|
|
23116
23609
|
init_esm_shims();
|
|
23610
|
+
init_events();
|
|
23117
23611
|
import * as clack30 from "@clack/prompts";
|
|
23118
23612
|
import pc31 from "picocolors";
|
|
23119
23613
|
async function news() {
|
|
23614
|
+
trackCommand("news", { success: true });
|
|
23120
23615
|
clack30.intro(pc31.bold("What's New in Wraps"));
|
|
23121
23616
|
console.log();
|
|
23122
23617
|
console.log(" See the latest updates, features, and improvements:");
|
|
@@ -24496,6 +24991,7 @@ async function platform() {
|
|
|
24496
24991
|
|
|
24497
24992
|
// src/commands/platform/update-role.ts
|
|
24498
24993
|
init_esm_shims();
|
|
24994
|
+
init_events();
|
|
24499
24995
|
init_aws();
|
|
24500
24996
|
init_metadata();
|
|
24501
24997
|
import {
|
|
@@ -24506,6 +25002,7 @@ import {
|
|
|
24506
25002
|
import { confirm as confirm13, intro as intro32, isCancel as isCancel19, log as log31, outro as outro19 } from "@clack/prompts";
|
|
24507
25003
|
import pc35 from "picocolors";
|
|
24508
25004
|
async function updateRole(options) {
|
|
25005
|
+
const startTime = Date.now();
|
|
24509
25006
|
intro32(pc35.bold("Update Platform Access Role"));
|
|
24510
25007
|
const progress = new DeploymentProgress();
|
|
24511
25008
|
const identity = await progress.execute(
|
|
@@ -24633,6 +25130,11 @@ Run ${pc35.cyan("wraps email init")} to deploy infrastructure first.
|
|
|
24633
25130
|
}
|
|
24634
25131
|
progress.stop();
|
|
24635
25132
|
const actionVerb = roleExists4 ? "updated" : "created";
|
|
25133
|
+
trackCommand("platform:update-role", {
|
|
25134
|
+
success: true,
|
|
25135
|
+
duration_ms: Date.now() - startTime,
|
|
25136
|
+
action: actionVerb
|
|
25137
|
+
});
|
|
24636
25138
|
outro19(pc35.green(`\u2713 Platform access role ${actionVerb} successfully`));
|
|
24637
25139
|
console.log(`
|
|
24638
25140
|
${pc35.bold("Permissions:")}`);
|
|
@@ -27637,11 +28139,13 @@ async function dashboard(options) {
|
|
|
27637
28139
|
|
|
27638
28140
|
// src/commands/shared/destroy.ts
|
|
27639
28141
|
init_esm_shims();
|
|
28142
|
+
init_events();
|
|
27640
28143
|
init_aws();
|
|
27641
28144
|
init_metadata();
|
|
27642
28145
|
import * as clack34 from "@clack/prompts";
|
|
27643
28146
|
import pc37 from "picocolors";
|
|
27644
28147
|
async function destroy(options) {
|
|
28148
|
+
trackCommand("destroy", { success: true });
|
|
27645
28149
|
clack34.intro(pc37.bold("Wraps Infrastructure Teardown"));
|
|
27646
28150
|
const spinner8 = clack34.spinner();
|
|
27647
28151
|
spinner8.start("Validating AWS credentials");
|
|
@@ -29712,6 +30216,7 @@ ${pc40.yellow(pc40.bold("Important Notes:"))}`);
|
|
|
29712
30216
|
|
|
29713
30217
|
// src/commands/sms/register.ts
|
|
29714
30218
|
init_esm_shims();
|
|
30219
|
+
init_events();
|
|
29715
30220
|
init_aws();
|
|
29716
30221
|
init_metadata();
|
|
29717
30222
|
import * as clack38 from "@clack/prompts";
|
|
@@ -29753,6 +30258,7 @@ async function getRegistrationStatus(region, registrationId) {
|
|
|
29753
30258
|
}
|
|
29754
30259
|
}
|
|
29755
30260
|
async function smsRegister(options) {
|
|
30261
|
+
const startTime = Date.now();
|
|
29756
30262
|
clack38.intro(pc41.bold("Wraps SMS - Toll-Free Registration"));
|
|
29757
30263
|
const progress = new DeploymentProgress();
|
|
29758
30264
|
const identity = await progress.execute(
|
|
@@ -29871,6 +30377,10 @@ async function smsRegister(options) {
|
|
|
29871
30377
|
console.log("When you're ready, go to:");
|
|
29872
30378
|
console.log(` ${pc41.cyan(consoleUrl)}`);
|
|
29873
30379
|
}
|
|
30380
|
+
trackCommand("sms:register", {
|
|
30381
|
+
success: true,
|
|
30382
|
+
duration_ms: Date.now() - startTime
|
|
30383
|
+
});
|
|
29874
30384
|
clack38.outro(pc41.dim("Good luck with your registration!"));
|
|
29875
30385
|
}
|
|
29876
30386
|
|
|
@@ -31646,9 +32156,11 @@ Run ${pc46.cyan(`wraps sms verify-number --phone-number ${phoneNumber} --resend`
|
|
|
31646
32156
|
|
|
31647
32157
|
// src/commands/support.ts
|
|
31648
32158
|
init_esm_shims();
|
|
32159
|
+
init_events();
|
|
31649
32160
|
import * as clack44 from "@clack/prompts";
|
|
31650
32161
|
import pc47 from "picocolors";
|
|
31651
32162
|
async function support() {
|
|
32163
|
+
trackCommand("support", { success: true });
|
|
31652
32164
|
clack44.intro(pc47.bold("Get Help with Wraps"));
|
|
31653
32165
|
console.log();
|
|
31654
32166
|
console.log(` ${pc47.bold("Email:")} ${pc47.cyan("hey@wraps.sh")}`);
|