infra-cost 0.2.3 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +325 -201
- package/bin/index.js +1 -1
- package/dist/demo/test-enhanced-ui.js +5 -9
- package/dist/demo/test-multi-cloud-dashboard.js +42 -53
- package/dist/index.js +24225 -7855
- package/package.json +93 -101
|
@@ -419,7 +419,6 @@ var DemoDataGenerator = class {
|
|
|
419
419
|
* Generate trend analysis data for the last 6 months
|
|
420
420
|
*/
|
|
421
421
|
static generateTrendAnalysis() {
|
|
422
|
-
var _a;
|
|
423
422
|
const months = 6;
|
|
424
423
|
const trendData = [];
|
|
425
424
|
for (let i = months - 1; i >= 0; i--) {
|
|
@@ -430,7 +429,7 @@ var DemoDataGenerator = class {
|
|
|
430
429
|
const trendFactor = 1 + (months - i - 1) * 0.05;
|
|
431
430
|
const randomVariation = 0.9 + Math.random() * 0.2;
|
|
432
431
|
const actualCost = baselineCost * seasonalFactor * trendFactor * randomVariation;
|
|
433
|
-
const previousCost = i === months - 1 ? actualCost * 0.95 :
|
|
432
|
+
const previousCost = i === months - 1 ? actualCost * 0.95 : trendData[i - 1]?.actualCost || actualCost;
|
|
434
433
|
trendData.push({
|
|
435
434
|
period: date.toISOString().substring(0, 7),
|
|
436
435
|
// YYYY-MM format
|
|
@@ -729,7 +728,6 @@ var PDFExporter = class {
|
|
|
729
728
|
* Generate cost trend report PDF
|
|
730
729
|
*/
|
|
731
730
|
async generateTrendReport(accountInfo, trendAnalysis) {
|
|
732
|
-
var _a, _b;
|
|
733
731
|
const data = {
|
|
734
732
|
accountInfo,
|
|
735
733
|
costBreakdown: {
|
|
@@ -749,8 +747,8 @@ var PDFExporter = class {
|
|
|
749
747
|
trendAnalysis,
|
|
750
748
|
generatedAt: /* @__PURE__ */ new Date(),
|
|
751
749
|
reportPeriod: {
|
|
752
|
-
start: new Date(
|
|
753
|
-
end: new Date(
|
|
750
|
+
start: new Date(trendAnalysis.timeRange?.start || Date.now() - 6 * 30 * 24 * 60 * 60 * 1e3),
|
|
751
|
+
end: new Date(trendAnalysis.timeRange?.end || Date.now())
|
|
754
752
|
}
|
|
755
753
|
};
|
|
756
754
|
return this.generateAuditReport(data);
|
|
@@ -861,7 +859,6 @@ var PDFExporter = class {
|
|
|
861
859
|
<div class="page-break"></div>`;
|
|
862
860
|
}
|
|
863
861
|
generateExecutiveSummary(data) {
|
|
864
|
-
var _a;
|
|
865
862
|
const monthlyChange = this.calculatePercentageChange(
|
|
866
863
|
data.costBreakdown.totals.thisMonth,
|
|
867
864
|
data.costBreakdown.totals.lastMonth
|
|
@@ -909,7 +906,7 @@ var PDFExporter = class {
|
|
|
909
906
|
<div class="metric-label">Active Services</div>
|
|
910
907
|
</div>
|
|
911
908
|
<div class="metric">
|
|
912
|
-
<div class="metric-value">${
|
|
909
|
+
<div class="metric-value">${data.trendAnalysis?.projectedMonthlyCost ? this.formatCurrency(data.trendAnalysis.projectedMonthlyCost) : "N/A"}</div>
|
|
913
910
|
<div class="metric-label">Projected Monthly</div>
|
|
914
911
|
</div>
|
|
915
912
|
</div>
|
|
@@ -995,7 +992,6 @@ var PDFExporter = class {
|
|
|
995
992
|
<div class="page-break"></div>`;
|
|
996
993
|
}
|
|
997
994
|
generateTrendAnalysisSection(trendAnalysis) {
|
|
998
|
-
var _a;
|
|
999
995
|
return `
|
|
1000
996
|
<div class="section">
|
|
1001
997
|
<h1>Trend Analysis</h1>
|
|
@@ -1015,7 +1011,7 @@ var PDFExporter = class {
|
|
|
1015
1011
|
<div class="insight-label">Projected Monthly</div>
|
|
1016
1012
|
</div>
|
|
1017
1013
|
<div class="insight-item">
|
|
1018
|
-
<div class="insight-value">${
|
|
1014
|
+
<div class="insight-value">${trendAnalysis.avgMonthOverMonthGrowth?.toFixed(1) || "N/A"}%</div>
|
|
1019
1015
|
<div class="insight-label">Avg MoM Growth</div>
|
|
1020
1016
|
</div>
|
|
1021
1017
|
</div>
|
|
@@ -435,7 +435,6 @@ var AWSProvider = class extends CloudProviderAdapter {
|
|
|
435
435
|
}
|
|
436
436
|
}
|
|
437
437
|
async getAccountInfo() {
|
|
438
|
-
var _a;
|
|
439
438
|
showSpinner("Getting AWS account information");
|
|
440
439
|
try {
|
|
441
440
|
const iam = new import_client_iam.IAMClient({
|
|
@@ -443,7 +442,7 @@ var AWSProvider = class extends CloudProviderAdapter {
|
|
|
443
442
|
region: this.getRegion()
|
|
444
443
|
});
|
|
445
444
|
const accountAliases = await iam.send(new import_client_iam.ListAccountAliasesCommand({}));
|
|
446
|
-
const foundAlias =
|
|
445
|
+
const foundAlias = accountAliases?.AccountAliases?.[0];
|
|
447
446
|
if (foundAlias) {
|
|
448
447
|
return {
|
|
449
448
|
id: foundAlias,
|
|
@@ -466,7 +465,6 @@ var AWSProvider = class extends CloudProviderAdapter {
|
|
|
466
465
|
}
|
|
467
466
|
}
|
|
468
467
|
async getRawCostData() {
|
|
469
|
-
var _a, _b, _c, _d;
|
|
470
468
|
showSpinner("Getting AWS pricing data");
|
|
471
469
|
try {
|
|
472
470
|
const costExplorer = new import_client_cost_explorer.CostExplorerClient({
|
|
@@ -500,9 +498,9 @@ var AWSProvider = class extends CloudProviderAdapter {
|
|
|
500
498
|
const costByService = {};
|
|
501
499
|
for (const day of pricingData.ResultsByTime || []) {
|
|
502
500
|
for (const group of day.Groups || []) {
|
|
503
|
-
const serviceName =
|
|
504
|
-
const cost =
|
|
505
|
-
const costDate =
|
|
501
|
+
const serviceName = group.Keys?.[0];
|
|
502
|
+
const cost = group.Metrics?.UnblendedCost?.Amount;
|
|
503
|
+
const costDate = day.TimePeriod?.End;
|
|
506
504
|
if (serviceName && cost && costDate) {
|
|
507
505
|
costByService[serviceName] = costByService[serviceName] || {};
|
|
508
506
|
costByService[serviceName][costDate] = parseFloat(cost);
|
|
@@ -520,9 +518,9 @@ var AWSProvider = class extends CloudProviderAdapter {
|
|
|
520
518
|
}
|
|
521
519
|
async getResourceInventory(filters) {
|
|
522
520
|
showSpinner("Discovering AWS resources");
|
|
523
|
-
const regions =
|
|
524
|
-
const resourceTypes =
|
|
525
|
-
const includeCosts =
|
|
521
|
+
const regions = filters?.regions || [this.config.region || "us-east-1"];
|
|
522
|
+
const resourceTypes = filters?.resourceTypes || Object.values(ResourceType);
|
|
523
|
+
const includeCosts = filters?.includeCosts || false;
|
|
526
524
|
const inventory = {
|
|
527
525
|
provider: "aws" /* AWS */,
|
|
528
526
|
region: regions.join(", "),
|
|
@@ -590,7 +588,6 @@ var AWSProvider = class extends CloudProviderAdapter {
|
|
|
590
588
|
return inventory;
|
|
591
589
|
}
|
|
592
590
|
async discoverEC2Instances(region, includeCosts) {
|
|
593
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
594
591
|
try {
|
|
595
592
|
const ec2Client = new import_client_ec2.EC2Client({
|
|
596
593
|
credentials: this.getCredentials(),
|
|
@@ -603,10 +600,10 @@ var AWSProvider = class extends CloudProviderAdapter {
|
|
|
603
600
|
for (const instance of reservation.Instances || []) {
|
|
604
601
|
const ec2Instance = {
|
|
605
602
|
id: instance.InstanceId || "",
|
|
606
|
-
name:
|
|
607
|
-
state:
|
|
603
|
+
name: instance.Tags?.find((tag) => tag.Key === "Name")?.Value || instance.InstanceId || "",
|
|
604
|
+
state: instance.State?.Name || "unknown",
|
|
608
605
|
region,
|
|
609
|
-
tags:
|
|
606
|
+
tags: instance.Tags?.reduce((acc, tag) => {
|
|
610
607
|
if (tag.Key && tag.Value)
|
|
611
608
|
acc[tag.Key] = tag.Value;
|
|
612
609
|
return acc;
|
|
@@ -620,15 +617,15 @@ var AWSProvider = class extends CloudProviderAdapter {
|
|
|
620
617
|
instanceId: instance.InstanceId || "",
|
|
621
618
|
imageId: instance.ImageId || "",
|
|
622
619
|
keyName: instance.KeyName,
|
|
623
|
-
securityGroups:
|
|
620
|
+
securityGroups: instance.SecurityGroups?.map((sg) => sg.GroupId || "") || [],
|
|
624
621
|
subnetId: instance.SubnetId || "",
|
|
625
622
|
vpcId: instance.VpcId || "",
|
|
626
623
|
publicDnsName: instance.PublicDnsName,
|
|
627
624
|
privateDnsName: instance.PrivateDnsName,
|
|
628
|
-
monitoring:
|
|
625
|
+
monitoring: instance.Monitoring?.State === "enabled",
|
|
629
626
|
placement: {
|
|
630
|
-
availabilityZone:
|
|
631
|
-
groupName:
|
|
627
|
+
availabilityZone: instance.Placement?.AvailabilityZone || "",
|
|
628
|
+
groupName: instance.Placement?.GroupName
|
|
632
629
|
},
|
|
633
630
|
publicIp: instance.PublicIpAddress,
|
|
634
631
|
privateIp: instance.PrivateIpAddress
|
|
@@ -681,7 +678,6 @@ var AWSProvider = class extends CloudProviderAdapter {
|
|
|
681
678
|
}
|
|
682
679
|
}
|
|
683
680
|
async discoverEBSVolumes(region, includeCosts) {
|
|
684
|
-
var _a, _b, _c, _d;
|
|
685
681
|
try {
|
|
686
682
|
const ec2Client = new import_client_ec2.EC2Client({
|
|
687
683
|
credentials: this.getCredentials(),
|
|
@@ -695,10 +691,10 @@ var AWSProvider = class extends CloudProviderAdapter {
|
|
|
695
691
|
continue;
|
|
696
692
|
const ebsVolume = {
|
|
697
693
|
id: volume.VolumeId,
|
|
698
|
-
name:
|
|
694
|
+
name: volume.Tags?.find((tag) => tag.Key === "Name")?.Value || volume.VolumeId,
|
|
699
695
|
state: volume.State || "unknown",
|
|
700
696
|
region,
|
|
701
|
-
tags:
|
|
697
|
+
tags: volume.Tags?.reduce((acc, tag) => {
|
|
702
698
|
if (tag.Key && tag.Value)
|
|
703
699
|
acc[tag.Key] = tag.Value;
|
|
704
700
|
return acc;
|
|
@@ -712,7 +708,7 @@ var AWSProvider = class extends CloudProviderAdapter {
|
|
|
712
708
|
volumeType: volume.VolumeType || "gp2",
|
|
713
709
|
iops: volume.Iops,
|
|
714
710
|
throughput: volume.Throughput,
|
|
715
|
-
attachments:
|
|
711
|
+
attachments: volume.Attachments?.map((attachment) => ({
|
|
716
712
|
instanceId: attachment.InstanceId || "",
|
|
717
713
|
device: attachment.Device || ""
|
|
718
714
|
})),
|
|
@@ -730,7 +726,6 @@ var AWSProvider = class extends CloudProviderAdapter {
|
|
|
730
726
|
}
|
|
731
727
|
}
|
|
732
728
|
async discoverRDSInstances(region, includeCosts) {
|
|
733
|
-
var _a, _b;
|
|
734
729
|
try {
|
|
735
730
|
const rdsClient = new import_client_rds.RDSClient({
|
|
736
731
|
credentials: this.getCredentials(),
|
|
@@ -757,8 +752,8 @@ var AWSProvider = class extends CloudProviderAdapter {
|
|
|
757
752
|
dbInstanceIdentifier: dbInstance.DBInstanceIdentifier,
|
|
758
753
|
dbName: dbInstance.DBName,
|
|
759
754
|
masterUsername: dbInstance.MasterUsername || "",
|
|
760
|
-
endpoint:
|
|
761
|
-
port:
|
|
755
|
+
endpoint: dbInstance.Endpoint?.Address,
|
|
756
|
+
port: dbInstance.Endpoint?.Port,
|
|
762
757
|
availabilityZone: dbInstance.AvailabilityZone,
|
|
763
758
|
backupRetentionPeriod: dbInstance.BackupRetentionPeriod,
|
|
764
759
|
storageEncrypted: dbInstance.StorageEncrypted
|
|
@@ -815,7 +810,6 @@ var AWSProvider = class extends CloudProviderAdapter {
|
|
|
815
810
|
}
|
|
816
811
|
}
|
|
817
812
|
async discoverVPCs(region, includeCosts) {
|
|
818
|
-
var _a, _b, _c;
|
|
819
813
|
try {
|
|
820
814
|
const ec2Client = new import_client_ec2.EC2Client({
|
|
821
815
|
credentials: this.getCredentials(),
|
|
@@ -829,10 +823,10 @@ var AWSProvider = class extends CloudProviderAdapter {
|
|
|
829
823
|
continue;
|
|
830
824
|
const awsVpc = {
|
|
831
825
|
id: vpc.VpcId,
|
|
832
|
-
name:
|
|
826
|
+
name: vpc.Tags?.find((tag) => tag.Key === "Name")?.Value || vpc.VpcId,
|
|
833
827
|
state: vpc.State || "unknown",
|
|
834
828
|
region,
|
|
835
|
-
tags:
|
|
829
|
+
tags: vpc.Tags?.reduce((acc, tag) => {
|
|
836
830
|
if (tag.Key && tag.Value)
|
|
837
831
|
acc[tag.Key] = tag.Value;
|
|
838
832
|
return acc;
|
|
@@ -857,7 +851,6 @@ var AWSProvider = class extends CloudProviderAdapter {
|
|
|
857
851
|
}
|
|
858
852
|
}
|
|
859
853
|
async discoverSubnets(region, includeCosts) {
|
|
860
|
-
var _a, _b, _c;
|
|
861
854
|
try {
|
|
862
855
|
const ec2Client = new import_client_ec2.EC2Client({
|
|
863
856
|
credentials: this.getCredentials(),
|
|
@@ -871,10 +864,10 @@ var AWSProvider = class extends CloudProviderAdapter {
|
|
|
871
864
|
continue;
|
|
872
865
|
const awsSubnet = {
|
|
873
866
|
id: subnet.SubnetId,
|
|
874
|
-
name:
|
|
867
|
+
name: subnet.Tags?.find((tag) => tag.Key === "Name")?.Value || subnet.SubnetId,
|
|
875
868
|
state: subnet.State || "unknown",
|
|
876
869
|
region,
|
|
877
|
-
tags:
|
|
870
|
+
tags: subnet.Tags?.reduce((acc, tag) => {
|
|
878
871
|
if (tag.Key && tag.Value)
|
|
879
872
|
acc[tag.Key] = tag.Value;
|
|
880
873
|
return acc;
|
|
@@ -919,7 +912,6 @@ var AWSProvider = class extends CloudProviderAdapter {
|
|
|
919
912
|
return recommendations;
|
|
920
913
|
}
|
|
921
914
|
async getBudgets() {
|
|
922
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l;
|
|
923
915
|
showSpinner("Getting AWS budgets");
|
|
924
916
|
try {
|
|
925
917
|
const budgetsClient = new import_client_budgets.BudgetsClient({
|
|
@@ -945,12 +937,12 @@ var AWSProvider = class extends CloudProviderAdapter {
|
|
|
945
937
|
const budgetInfo = {
|
|
946
938
|
budgetName: budget.BudgetName,
|
|
947
939
|
budgetLimit: parseFloat(budget.BudgetLimit.Amount || "0"),
|
|
948
|
-
actualSpend: parseFloat(
|
|
949
|
-
forecastedSpend: parseFloat(
|
|
940
|
+
actualSpend: parseFloat(budget.CalculatedSpend?.ActualSpend?.Amount || "0"),
|
|
941
|
+
forecastedSpend: parseFloat(budget.CalculatedSpend?.ForecastedSpend?.Amount || "0"),
|
|
950
942
|
timeUnit: budget.TimeUnit || "MONTHLY",
|
|
951
943
|
timePeriod: {
|
|
952
|
-
start: typeof
|
|
953
|
-
end: typeof
|
|
944
|
+
start: typeof budget.TimePeriod?.Start === "string" ? budget.TimePeriod?.Start : budget.TimePeriod?.Start instanceof Date ? budget.TimePeriod?.Start.toISOString() : "",
|
|
945
|
+
end: typeof budget.TimePeriod?.End === "string" ? budget.TimePeriod?.End : budget.TimePeriod?.End instanceof Date ? budget.TimePeriod?.End.toISOString() : ""
|
|
954
946
|
},
|
|
955
947
|
budgetType: budget.BudgetType || "COST",
|
|
956
948
|
status: this.determineBudgetStatus(budget),
|
|
@@ -1010,7 +1002,6 @@ var AWSProvider = class extends CloudProviderAdapter {
|
|
|
1010
1002
|
return alerts;
|
|
1011
1003
|
}
|
|
1012
1004
|
async getCostTrendAnalysis(months = 6) {
|
|
1013
|
-
var _a, _b, _c, _d, _e;
|
|
1014
1005
|
showSpinner("Analyzing cost trends");
|
|
1015
1006
|
try {
|
|
1016
1007
|
const costExplorer = new import_client_cost_explorer.CostExplorerClient({
|
|
@@ -1038,15 +1029,14 @@ var AWSProvider = class extends CloudProviderAdapter {
|
|
|
1038
1029
|
const serviceTrends = {};
|
|
1039
1030
|
let totalCost = 0;
|
|
1040
1031
|
for (const result of monthlyData.ResultsByTime || []) {
|
|
1041
|
-
const period =
|
|
1042
|
-
const monthCost =
|
|
1032
|
+
const period = result.TimePeriod?.Start || "";
|
|
1033
|
+
const monthCost = result.Total?.UnblendedCost?.Amount ? parseFloat(result.Total.UnblendedCost.Amount) : 0;
|
|
1043
1034
|
monthlyCosts.push(monthCost);
|
|
1044
1035
|
totalCost += monthCost;
|
|
1045
1036
|
const services = {};
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
const
|
|
1049
|
-
const cost = parseFloat(((_c2 = (_b2 = group.Metrics) == null ? void 0 : _b2.UnblendedCost) == null ? void 0 : _c2.Amount) || "0");
|
|
1037
|
+
result.Groups?.forEach((group) => {
|
|
1038
|
+
const serviceName = group.Keys?.[0] || "Unknown";
|
|
1039
|
+
const cost = parseFloat(group.Metrics?.UnblendedCost?.Amount || "0");
|
|
1050
1040
|
services[serviceName] = cost;
|
|
1051
1041
|
if (!serviceTrends[serviceName]) {
|
|
1052
1042
|
serviceTrends[serviceName] = [];
|
|
@@ -1073,7 +1063,7 @@ var AWSProvider = class extends CloudProviderAdapter {
|
|
|
1073
1063
|
const costAnomalies = [];
|
|
1074
1064
|
for (let i = 0; i < monthlyCosts.length; i++) {
|
|
1075
1065
|
const monthCost = monthlyCosts[i];
|
|
1076
|
-
const period =
|
|
1066
|
+
const period = monthlyBreakdown[i]?.month || "";
|
|
1077
1067
|
if (i > 0) {
|
|
1078
1068
|
const prevMonthCost = monthlyCosts[i - 1];
|
|
1079
1069
|
const monthOverMonthChange = (monthCost - prevMonthCost) / prevMonthCost * 100;
|
|
@@ -1340,12 +1330,11 @@ var AWSProvider = class extends CloudProviderAdapter {
|
|
|
1340
1330
|
}
|
|
1341
1331
|
// Helper methods for budget functionality
|
|
1342
1332
|
determineBudgetStatus(budget) {
|
|
1343
|
-
var _a, _b, _c;
|
|
1344
1333
|
if (!budget.CalculatedSpend)
|
|
1345
1334
|
return "OK";
|
|
1346
|
-
const actual = parseFloat(
|
|
1347
|
-
const limit = parseFloat(
|
|
1348
|
-
const forecasted = parseFloat(
|
|
1335
|
+
const actual = parseFloat(budget.CalculatedSpend.ActualSpend?.Amount || "0");
|
|
1336
|
+
const limit = parseFloat(budget.BudgetLimit?.Amount || "0");
|
|
1337
|
+
const forecasted = parseFloat(budget.CalculatedSpend.ForecastedSpend?.Amount || "0");
|
|
1349
1338
|
if (actual >= limit)
|
|
1350
1339
|
return "ALARM";
|
|
1351
1340
|
if (forecasted >= limit)
|
|
@@ -1502,9 +1491,9 @@ var GCPProvider = class extends CloudProviderAdapter {
|
|
|
1502
1491
|
}
|
|
1503
1492
|
async getResourceInventory(filters) {
|
|
1504
1493
|
showSpinner("Discovering GCP resources");
|
|
1505
|
-
const regions =
|
|
1506
|
-
const resourceTypes =
|
|
1507
|
-
const includeCosts =
|
|
1494
|
+
const regions = filters?.regions || [this.config.region || "us-central1"];
|
|
1495
|
+
const resourceTypes = filters?.resourceTypes || Object.values(ResourceType);
|
|
1496
|
+
const includeCosts = filters?.includeCosts || false;
|
|
1508
1497
|
const inventory = {
|
|
1509
1498
|
provider: "gcp" /* GOOGLE_CLOUD */,
|
|
1510
1499
|
region: regions.join(", "),
|
|
@@ -1803,9 +1792,9 @@ var AzureProvider = class extends CloudProviderAdapter {
|
|
|
1803
1792
|
}
|
|
1804
1793
|
async getResourceInventory(filters) {
|
|
1805
1794
|
showSpinner("Discovering Azure resources");
|
|
1806
|
-
const regions =
|
|
1807
|
-
const resourceTypes =
|
|
1808
|
-
const includeCosts =
|
|
1795
|
+
const regions = filters?.regions || [this.config.region || "eastus"];
|
|
1796
|
+
const resourceTypes = filters?.resourceTypes || Object.values(ResourceType);
|
|
1797
|
+
const includeCosts = filters?.includeCosts || false;
|
|
1809
1798
|
const inventory = {
|
|
1810
1799
|
provider: "azure" /* AZURE */,
|
|
1811
1800
|
region: regions.join(", "),
|