infra-cost 0.3.0 → 0.3.2
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 +113 -4
- package/dist/index.js +992 -103
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -12077,12 +12077,12 @@ __export(pdf_exporter_exports, {
|
|
|
12077
12077
|
PDFExporter: () => PDFExporter,
|
|
12078
12078
|
default: () => pdf_exporter_default
|
|
12079
12079
|
});
|
|
12080
|
-
var import_puppeteer,
|
|
12080
|
+
var import_puppeteer, import_fs9, import_path9, import_moment2, PDFExporter, pdf_exporter_default;
|
|
12081
12081
|
var init_pdf_exporter = __esm({
|
|
12082
12082
|
"src/exporters/pdf-exporter.ts"() {
|
|
12083
12083
|
import_puppeteer = __toESM(require("puppeteer"));
|
|
12084
|
-
|
|
12085
|
-
|
|
12084
|
+
import_fs9 = require("fs");
|
|
12085
|
+
import_path9 = require("path");
|
|
12086
12086
|
import_moment2 = __toESM(require("moment"));
|
|
12087
12087
|
init_providers();
|
|
12088
12088
|
PDFExporter = class {
|
|
@@ -12109,7 +12109,7 @@ var init_pdf_exporter = __esm({
|
|
|
12109
12109
|
const html = this.generateAuditHTML(data);
|
|
12110
12110
|
const filename = this.options.filename || `infra-cost-audit-${(0, import_moment2.default)(data.generatedAt).format("YYYY-MM-DD-HHmm")}.pdf`;
|
|
12111
12111
|
const outputPath = this.ensureOutputDirectory();
|
|
12112
|
-
const fullPath = (0,
|
|
12112
|
+
const fullPath = (0, import_path9.join)(outputPath, filename);
|
|
12113
12113
|
const page = await this.browser.newPage();
|
|
12114
12114
|
await page.setContent(html, { waitUntil: "networkidle0" });
|
|
12115
12115
|
await page.pdf({
|
|
@@ -12922,8 +12922,8 @@ var init_pdf_exporter = __esm({
|
|
|
12922
12922
|
}
|
|
12923
12923
|
ensureOutputDirectory() {
|
|
12924
12924
|
const outputPath = this.options.outputPath;
|
|
12925
|
-
if (!(0,
|
|
12926
|
-
(0,
|
|
12925
|
+
if (!(0, import_fs9.existsSync)(outputPath)) {
|
|
12926
|
+
(0, import_fs9.mkdirSync)(outputPath, { recursive: true });
|
|
12927
12927
|
}
|
|
12928
12928
|
return outputPath;
|
|
12929
12929
|
}
|
|
@@ -12974,7 +12974,7 @@ var import_commander = require("commander");
|
|
|
12974
12974
|
// package.json
|
|
12975
12975
|
var package_default = {
|
|
12976
12976
|
name: "infra-cost",
|
|
12977
|
-
version: "0.3.
|
|
12977
|
+
version: "0.3.2",
|
|
12978
12978
|
description: "Multi-cloud FinOps CLI tool for comprehensive cost analysis and infrastructure optimization across AWS, GCP, Azure, Alibaba Cloud, and Oracle Cloud",
|
|
12979
12979
|
keywords: [
|
|
12980
12980
|
"aws",
|
|
@@ -13005,8 +13005,8 @@ var package_default = {
|
|
|
13005
13005
|
"bin/**/*"
|
|
13006
13006
|
],
|
|
13007
13007
|
bin: {
|
|
13008
|
-
"infra-cost": "bin/index.js",
|
|
13009
|
-
"aws-cost": "bin/index.js"
|
|
13008
|
+
"infra-cost": "./bin/index.js",
|
|
13009
|
+
"aws-cost": "./bin/index.js"
|
|
13010
13010
|
},
|
|
13011
13011
|
main: "./dist/index.js",
|
|
13012
13012
|
scripts: {
|
|
@@ -13034,7 +13034,7 @@ var package_default = {
|
|
|
13034
13034
|
},
|
|
13035
13035
|
repository: {
|
|
13036
13036
|
type: "git",
|
|
13037
|
-
url: "
|
|
13037
|
+
url: "https://github.com/codecollab-co/infra-cost.git"
|
|
13038
13038
|
},
|
|
13039
13039
|
bugs: {
|
|
13040
13040
|
url: "https://github.com/codecollab-co/infra-cost/issues"
|
|
@@ -30558,13 +30558,862 @@ var MultiCloudDashboard = class {
|
|
|
30558
30558
|
};
|
|
30559
30559
|
|
|
30560
30560
|
// src/index.ts
|
|
30561
|
-
var
|
|
30562
|
-
var
|
|
30561
|
+
var import_chalk10 = __toESM(require("chalk"));
|
|
30562
|
+
var import_path10 = require("path");
|
|
30563
30563
|
|
|
30564
|
-
// src/
|
|
30564
|
+
// src/cache/cost-cache.ts
|
|
30565
30565
|
var import_fs6 = require("fs");
|
|
30566
30566
|
var import_path6 = require("path");
|
|
30567
30567
|
var import_os2 = require("os");
|
|
30568
|
+
var import_crypto6 = require("crypto");
|
|
30569
|
+
var DEFAULT_CONFIG = {
|
|
30570
|
+
type: "file",
|
|
30571
|
+
ttl: 4 * 60 * 60 * 1e3,
|
|
30572
|
+
// 4 hours default
|
|
30573
|
+
maxEntries: 1e3,
|
|
30574
|
+
cacheDir: (0, import_path6.join)((0, import_os2.homedir)(), ".infra-cost", "cache"),
|
|
30575
|
+
prefix: "infra-cost"
|
|
30576
|
+
};
|
|
30577
|
+
var FileCacheStorage = class {
|
|
30578
|
+
constructor(cacheDir) {
|
|
30579
|
+
this.hitCount = 0;
|
|
30580
|
+
this.missCount = 0;
|
|
30581
|
+
this.cacheDir = cacheDir;
|
|
30582
|
+
this.ensureCacheDir();
|
|
30583
|
+
}
|
|
30584
|
+
ensureCacheDir() {
|
|
30585
|
+
if (!(0, import_fs6.existsSync)(this.cacheDir)) {
|
|
30586
|
+
(0, import_fs6.mkdirSync)(this.cacheDir, { recursive: true, mode: 448 });
|
|
30587
|
+
} else {
|
|
30588
|
+
(0, import_fs6.chmodSync)(this.cacheDir, 448);
|
|
30589
|
+
}
|
|
30590
|
+
}
|
|
30591
|
+
getFilePath(key) {
|
|
30592
|
+
const hash = (0, import_crypto6.createHash)("sha256").update(key).digest("hex").substring(0, 16);
|
|
30593
|
+
return (0, import_path6.join)(this.cacheDir, `${hash}.json`);
|
|
30594
|
+
}
|
|
30595
|
+
async get(key) {
|
|
30596
|
+
const filePath = this.getFilePath(key);
|
|
30597
|
+
if (!(0, import_fs6.existsSync)(filePath)) {
|
|
30598
|
+
this.missCount++;
|
|
30599
|
+
return null;
|
|
30600
|
+
}
|
|
30601
|
+
try {
|
|
30602
|
+
const content = (0, import_fs6.readFileSync)(filePath, "utf8");
|
|
30603
|
+
const entry = JSON.parse(content);
|
|
30604
|
+
if (entry.key !== key) {
|
|
30605
|
+
this.missCount++;
|
|
30606
|
+
return null;
|
|
30607
|
+
}
|
|
30608
|
+
if (Date.now() > entry.timestamp + entry.ttl) {
|
|
30609
|
+
await this.delete(key);
|
|
30610
|
+
this.missCount++;
|
|
30611
|
+
return null;
|
|
30612
|
+
}
|
|
30613
|
+
this.hitCount++;
|
|
30614
|
+
return entry;
|
|
30615
|
+
} catch (error) {
|
|
30616
|
+
this.missCount++;
|
|
30617
|
+
return null;
|
|
30618
|
+
}
|
|
30619
|
+
}
|
|
30620
|
+
async set(key, entry) {
|
|
30621
|
+
const filePath = this.getFilePath(key);
|
|
30622
|
+
try {
|
|
30623
|
+
(0, import_fs6.writeFileSync)(filePath, JSON.stringify(entry, null, 2), { mode: 384 });
|
|
30624
|
+
(0, import_fs6.chmodSync)(filePath, 384);
|
|
30625
|
+
} catch (error) {
|
|
30626
|
+
console.warn(`Failed to write cache entry: ${error.message}`);
|
|
30627
|
+
}
|
|
30628
|
+
}
|
|
30629
|
+
async delete(key) {
|
|
30630
|
+
const filePath = this.getFilePath(key);
|
|
30631
|
+
if ((0, import_fs6.existsSync)(filePath)) {
|
|
30632
|
+
try {
|
|
30633
|
+
(0, import_fs6.unlinkSync)(filePath);
|
|
30634
|
+
return true;
|
|
30635
|
+
} catch {
|
|
30636
|
+
return false;
|
|
30637
|
+
}
|
|
30638
|
+
}
|
|
30639
|
+
return false;
|
|
30640
|
+
}
|
|
30641
|
+
async clear() {
|
|
30642
|
+
if (!(0, import_fs6.existsSync)(this.cacheDir))
|
|
30643
|
+
return;
|
|
30644
|
+
const files = (0, import_fs6.readdirSync)(this.cacheDir);
|
|
30645
|
+
for (const file of files) {
|
|
30646
|
+
if (file.endsWith(".json")) {
|
|
30647
|
+
try {
|
|
30648
|
+
(0, import_fs6.unlinkSync)((0, import_path6.join)(this.cacheDir, file));
|
|
30649
|
+
} catch {
|
|
30650
|
+
}
|
|
30651
|
+
}
|
|
30652
|
+
}
|
|
30653
|
+
this.hitCount = 0;
|
|
30654
|
+
this.missCount = 0;
|
|
30655
|
+
}
|
|
30656
|
+
async keys(pattern) {
|
|
30657
|
+
if (!(0, import_fs6.existsSync)(this.cacheDir))
|
|
30658
|
+
return [];
|
|
30659
|
+
const keys = [];
|
|
30660
|
+
const files = (0, import_fs6.readdirSync)(this.cacheDir);
|
|
30661
|
+
for (const file of files) {
|
|
30662
|
+
if (!file.endsWith(".json"))
|
|
30663
|
+
continue;
|
|
30664
|
+
try {
|
|
30665
|
+
const content = (0, import_fs6.readFileSync)((0, import_path6.join)(this.cacheDir, file), "utf8");
|
|
30666
|
+
const entry = JSON.parse(content);
|
|
30667
|
+
if (!pattern || entry.key.includes(pattern)) {
|
|
30668
|
+
keys.push(entry.key);
|
|
30669
|
+
}
|
|
30670
|
+
} catch {
|
|
30671
|
+
}
|
|
30672
|
+
}
|
|
30673
|
+
return keys;
|
|
30674
|
+
}
|
|
30675
|
+
async stats() {
|
|
30676
|
+
const stats = {
|
|
30677
|
+
totalEntries: 0,
|
|
30678
|
+
totalSize: 0,
|
|
30679
|
+
hitCount: this.hitCount,
|
|
30680
|
+
missCount: this.missCount,
|
|
30681
|
+
hitRate: this.hitCount + this.missCount > 0 ? this.hitCount / (this.hitCount + this.missCount) : 0,
|
|
30682
|
+
oldestEntry: null,
|
|
30683
|
+
newestEntry: null
|
|
30684
|
+
};
|
|
30685
|
+
if (!(0, import_fs6.existsSync)(this.cacheDir))
|
|
30686
|
+
return stats;
|
|
30687
|
+
const files = (0, import_fs6.readdirSync)(this.cacheDir);
|
|
30688
|
+
for (const file of files) {
|
|
30689
|
+
if (!file.endsWith(".json"))
|
|
30690
|
+
continue;
|
|
30691
|
+
try {
|
|
30692
|
+
const filePath = (0, import_path6.join)(this.cacheDir, file);
|
|
30693
|
+
const fileStat = (0, import_fs6.statSync)(filePath);
|
|
30694
|
+
const content = (0, import_fs6.readFileSync)(filePath, "utf8");
|
|
30695
|
+
const entry = JSON.parse(content);
|
|
30696
|
+
stats.totalEntries++;
|
|
30697
|
+
stats.totalSize += fileStat.size;
|
|
30698
|
+
if (stats.oldestEntry === null || entry.timestamp < stats.oldestEntry) {
|
|
30699
|
+
stats.oldestEntry = entry.timestamp;
|
|
30700
|
+
}
|
|
30701
|
+
if (stats.newestEntry === null || entry.timestamp > stats.newestEntry) {
|
|
30702
|
+
stats.newestEntry = entry.timestamp;
|
|
30703
|
+
}
|
|
30704
|
+
} catch {
|
|
30705
|
+
}
|
|
30706
|
+
}
|
|
30707
|
+
return stats;
|
|
30708
|
+
}
|
|
30709
|
+
};
|
|
30710
|
+
var MemoryCacheStorage = class {
|
|
30711
|
+
constructor(maxEntries = 1e3) {
|
|
30712
|
+
this.cache = /* @__PURE__ */ new Map();
|
|
30713
|
+
this.hitCount = 0;
|
|
30714
|
+
this.missCount = 0;
|
|
30715
|
+
this.maxEntries = maxEntries;
|
|
30716
|
+
}
|
|
30717
|
+
async get(key) {
|
|
30718
|
+
const entry = this.cache.get(key);
|
|
30719
|
+
if (!entry) {
|
|
30720
|
+
this.missCount++;
|
|
30721
|
+
return null;
|
|
30722
|
+
}
|
|
30723
|
+
if (Date.now() > entry.timestamp + entry.ttl) {
|
|
30724
|
+
this.cache.delete(key);
|
|
30725
|
+
this.missCount++;
|
|
30726
|
+
return null;
|
|
30727
|
+
}
|
|
30728
|
+
this.hitCount++;
|
|
30729
|
+
return entry;
|
|
30730
|
+
}
|
|
30731
|
+
async set(key, entry) {
|
|
30732
|
+
if (this.cache.size >= this.maxEntries) {
|
|
30733
|
+
const oldestKey = this.findOldestEntry();
|
|
30734
|
+
if (oldestKey) {
|
|
30735
|
+
this.cache.delete(oldestKey);
|
|
30736
|
+
}
|
|
30737
|
+
}
|
|
30738
|
+
this.cache.set(key, entry);
|
|
30739
|
+
}
|
|
30740
|
+
async delete(key) {
|
|
30741
|
+
return this.cache.delete(key);
|
|
30742
|
+
}
|
|
30743
|
+
async clear() {
|
|
30744
|
+
this.cache.clear();
|
|
30745
|
+
this.hitCount = 0;
|
|
30746
|
+
this.missCount = 0;
|
|
30747
|
+
}
|
|
30748
|
+
async keys(pattern) {
|
|
30749
|
+
const keys = Array.from(this.cache.keys());
|
|
30750
|
+
if (!pattern)
|
|
30751
|
+
return keys;
|
|
30752
|
+
return keys.filter((key) => key.includes(pattern));
|
|
30753
|
+
}
|
|
30754
|
+
async stats() {
|
|
30755
|
+
let oldestEntry = null;
|
|
30756
|
+
let newestEntry = null;
|
|
30757
|
+
let totalSize = 0;
|
|
30758
|
+
for (const entry of this.cache.values()) {
|
|
30759
|
+
const entrySize = JSON.stringify(entry).length;
|
|
30760
|
+
totalSize += entrySize;
|
|
30761
|
+
if (oldestEntry === null || entry.timestamp < oldestEntry) {
|
|
30762
|
+
oldestEntry = entry.timestamp;
|
|
30763
|
+
}
|
|
30764
|
+
if (newestEntry === null || entry.timestamp > newestEntry) {
|
|
30765
|
+
newestEntry = entry.timestamp;
|
|
30766
|
+
}
|
|
30767
|
+
}
|
|
30768
|
+
return {
|
|
30769
|
+
totalEntries: this.cache.size,
|
|
30770
|
+
totalSize,
|
|
30771
|
+
hitCount: this.hitCount,
|
|
30772
|
+
missCount: this.missCount,
|
|
30773
|
+
hitRate: this.hitCount + this.missCount > 0 ? this.hitCount / (this.hitCount + this.missCount) : 0,
|
|
30774
|
+
oldestEntry,
|
|
30775
|
+
newestEntry
|
|
30776
|
+
};
|
|
30777
|
+
}
|
|
30778
|
+
findOldestEntry() {
|
|
30779
|
+
let oldestKey = null;
|
|
30780
|
+
let oldestTime = Infinity;
|
|
30781
|
+
for (const [key, entry] of this.cache.entries()) {
|
|
30782
|
+
if (entry.timestamp < oldestTime) {
|
|
30783
|
+
oldestTime = entry.timestamp;
|
|
30784
|
+
oldestKey = key;
|
|
30785
|
+
}
|
|
30786
|
+
}
|
|
30787
|
+
return oldestKey;
|
|
30788
|
+
}
|
|
30789
|
+
};
|
|
30790
|
+
var CostCacheManager = class {
|
|
30791
|
+
constructor(config = {}) {
|
|
30792
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
30793
|
+
switch (this.config.type) {
|
|
30794
|
+
case "memory":
|
|
30795
|
+
this.storage = new MemoryCacheStorage(this.config.maxEntries);
|
|
30796
|
+
break;
|
|
30797
|
+
case "redis":
|
|
30798
|
+
console.warn("Redis cache not yet implemented, using file-based cache");
|
|
30799
|
+
this.storage = new FileCacheStorage(this.config.cacheDir);
|
|
30800
|
+
break;
|
|
30801
|
+
case "file":
|
|
30802
|
+
default:
|
|
30803
|
+
this.storage = new FileCacheStorage(this.config.cacheDir);
|
|
30804
|
+
break;
|
|
30805
|
+
}
|
|
30806
|
+
}
|
|
30807
|
+
/**
|
|
30808
|
+
* Generate a cache key from parameters
|
|
30809
|
+
*/
|
|
30810
|
+
generateKey(params) {
|
|
30811
|
+
const parts = [
|
|
30812
|
+
this.config.prefix,
|
|
30813
|
+
params.provider || "aws",
|
|
30814
|
+
// Default to 'aws' for backward compatibility
|
|
30815
|
+
params.account,
|
|
30816
|
+
params.profile,
|
|
30817
|
+
params.region,
|
|
30818
|
+
params.dataType
|
|
30819
|
+
];
|
|
30820
|
+
if (params.dateRange) {
|
|
30821
|
+
parts.push(params.dateRange.start, params.dateRange.end);
|
|
30822
|
+
}
|
|
30823
|
+
return parts.join(":");
|
|
30824
|
+
}
|
|
30825
|
+
/**
|
|
30826
|
+
* Get cached data
|
|
30827
|
+
*/
|
|
30828
|
+
async get(key) {
|
|
30829
|
+
const entry = await this.storage.get(key);
|
|
30830
|
+
return entry ? entry.data : null;
|
|
30831
|
+
}
|
|
30832
|
+
/**
|
|
30833
|
+
* Get cached data with metadata
|
|
30834
|
+
*/
|
|
30835
|
+
async getEntry(key) {
|
|
30836
|
+
return this.storage.get(key);
|
|
30837
|
+
}
|
|
30838
|
+
/**
|
|
30839
|
+
* Set cached data
|
|
30840
|
+
*/
|
|
30841
|
+
async set(key, data, options) {
|
|
30842
|
+
const entry = {
|
|
30843
|
+
key,
|
|
30844
|
+
data,
|
|
30845
|
+
timestamp: Date.now(),
|
|
30846
|
+
ttl: options.ttl || this.config.ttl,
|
|
30847
|
+
account: options.account,
|
|
30848
|
+
profile: options.profile,
|
|
30849
|
+
region: options.region,
|
|
30850
|
+
metadata: options.metadata
|
|
30851
|
+
};
|
|
30852
|
+
await this.storage.set(key, entry);
|
|
30853
|
+
}
|
|
30854
|
+
/**
|
|
30855
|
+
* Check if data is cached and not expired
|
|
30856
|
+
*/
|
|
30857
|
+
async has(key) {
|
|
30858
|
+
const entry = await this.storage.get(key);
|
|
30859
|
+
return entry !== null;
|
|
30860
|
+
}
|
|
30861
|
+
/**
|
|
30862
|
+
* Invalidate a specific cache entry
|
|
30863
|
+
*/
|
|
30864
|
+
async invalidate(key) {
|
|
30865
|
+
return this.storage.delete(key);
|
|
30866
|
+
}
|
|
30867
|
+
/**
|
|
30868
|
+
* Invalidate cache entries matching a pattern
|
|
30869
|
+
*/
|
|
30870
|
+
async invalidatePattern(pattern) {
|
|
30871
|
+
const keys = await this.storage.keys(pattern);
|
|
30872
|
+
let count = 0;
|
|
30873
|
+
for (const key of keys) {
|
|
30874
|
+
if (await this.storage.delete(key)) {
|
|
30875
|
+
count++;
|
|
30876
|
+
}
|
|
30877
|
+
}
|
|
30878
|
+
return count;
|
|
30879
|
+
}
|
|
30880
|
+
/**
|
|
30881
|
+
* Invalidate all cache entries for an account
|
|
30882
|
+
*/
|
|
30883
|
+
async invalidateAccount(account) {
|
|
30884
|
+
return this.invalidatePattern(`:${account}:`);
|
|
30885
|
+
}
|
|
30886
|
+
/**
|
|
30887
|
+
* Invalidate all cache entries for a profile
|
|
30888
|
+
*/
|
|
30889
|
+
async invalidateProfile(profile) {
|
|
30890
|
+
return this.invalidatePattern(`:${profile}:`);
|
|
30891
|
+
}
|
|
30892
|
+
/**
|
|
30893
|
+
* Clear all cache entries
|
|
30894
|
+
*/
|
|
30895
|
+
async clear() {
|
|
30896
|
+
await this.storage.clear();
|
|
30897
|
+
}
|
|
30898
|
+
/**
|
|
30899
|
+
* Get cache statistics
|
|
30900
|
+
*/
|
|
30901
|
+
async getStats() {
|
|
30902
|
+
return this.storage.stats();
|
|
30903
|
+
}
|
|
30904
|
+
/**
|
|
30905
|
+
* Get all cache keys
|
|
30906
|
+
*/
|
|
30907
|
+
async getKeys(pattern) {
|
|
30908
|
+
return this.storage.keys(pattern);
|
|
30909
|
+
}
|
|
30910
|
+
/**
|
|
30911
|
+
* Get cache configuration
|
|
30912
|
+
*/
|
|
30913
|
+
getConfig() {
|
|
30914
|
+
return { ...this.config };
|
|
30915
|
+
}
|
|
30916
|
+
/**
|
|
30917
|
+
* Check if cache entry is stale (expired but still present)
|
|
30918
|
+
*/
|
|
30919
|
+
async isStale(key) {
|
|
30920
|
+
const entry = await this.storage.get(key);
|
|
30921
|
+
if (!entry)
|
|
30922
|
+
return true;
|
|
30923
|
+
return Date.now() > entry.timestamp + entry.ttl;
|
|
30924
|
+
}
|
|
30925
|
+
/**
|
|
30926
|
+
* Get time until cache entry expires (in milliseconds)
|
|
30927
|
+
*/
|
|
30928
|
+
async getTimeToLive(key) {
|
|
30929
|
+
const entry = await this.storage.get(key);
|
|
30930
|
+
if (!entry)
|
|
30931
|
+
return null;
|
|
30932
|
+
const expiresAt = entry.timestamp + entry.ttl;
|
|
30933
|
+
const ttl = expiresAt - Date.now();
|
|
30934
|
+
return ttl > 0 ? ttl : 0;
|
|
30935
|
+
}
|
|
30936
|
+
/**
|
|
30937
|
+
* Prune expired entries
|
|
30938
|
+
*/
|
|
30939
|
+
async prune() {
|
|
30940
|
+
const keys = await this.storage.keys();
|
|
30941
|
+
let pruned = 0;
|
|
30942
|
+
for (const key of keys) {
|
|
30943
|
+
if (await this.isStale(key)) {
|
|
30944
|
+
if (await this.storage.delete(key)) {
|
|
30945
|
+
pruned++;
|
|
30946
|
+
}
|
|
30947
|
+
}
|
|
30948
|
+
}
|
|
30949
|
+
return pruned;
|
|
30950
|
+
}
|
|
30951
|
+
};
|
|
30952
|
+
function parseTTL(ttl) {
|
|
30953
|
+
const match = ttl.match(/^(\d+)([smhd])$/);
|
|
30954
|
+
if (!match) {
|
|
30955
|
+
return 4 * 60 * 60 * 1e3;
|
|
30956
|
+
}
|
|
30957
|
+
const value = parseInt(match[1], 10);
|
|
30958
|
+
const unit = match[2];
|
|
30959
|
+
switch (unit) {
|
|
30960
|
+
case "s":
|
|
30961
|
+
return value * 1e3;
|
|
30962
|
+
case "m":
|
|
30963
|
+
return value * 60 * 1e3;
|
|
30964
|
+
case "h":
|
|
30965
|
+
return value * 60 * 60 * 1e3;
|
|
30966
|
+
case "d":
|
|
30967
|
+
return value * 24 * 60 * 60 * 1e3;
|
|
30968
|
+
default:
|
|
30969
|
+
return 4 * 60 * 60 * 1e3;
|
|
30970
|
+
}
|
|
30971
|
+
}
|
|
30972
|
+
function formatTTL(ms) {
|
|
30973
|
+
if (ms < 60 * 1e3) {
|
|
30974
|
+
return `${Math.round(ms / 1e3)}s`;
|
|
30975
|
+
} else if (ms < 60 * 60 * 1e3) {
|
|
30976
|
+
return `${Math.round(ms / (60 * 1e3))}m`;
|
|
30977
|
+
} else if (ms < 24 * 60 * 60 * 1e3) {
|
|
30978
|
+
return `${Math.round(ms / (60 * 60 * 1e3))}h`;
|
|
30979
|
+
} else {
|
|
30980
|
+
return `${Math.round(ms / (24 * 60 * 60 * 1e3))}d`;
|
|
30981
|
+
}
|
|
30982
|
+
}
|
|
30983
|
+
function formatCacheStats(stats) {
|
|
30984
|
+
const lines = [];
|
|
30985
|
+
lines.push("Cache Statistics");
|
|
30986
|
+
lines.push("\u2500".repeat(40));
|
|
30987
|
+
lines.push(`Total Entries: ${stats.totalEntries}`);
|
|
30988
|
+
lines.push(`Total Size: ${formatBytes(stats.totalSize)}`);
|
|
30989
|
+
lines.push(`Hit Count: ${stats.hitCount}`);
|
|
30990
|
+
lines.push(`Miss Count: ${stats.missCount}`);
|
|
30991
|
+
lines.push(`Hit Rate: ${(stats.hitRate * 100).toFixed(1)}%`);
|
|
30992
|
+
if (stats.oldestEntry) {
|
|
30993
|
+
const oldestAge = Date.now() - stats.oldestEntry;
|
|
30994
|
+
lines.push(`Oldest Entry: ${formatTTL(oldestAge)} ago`);
|
|
30995
|
+
}
|
|
30996
|
+
if (stats.newestEntry) {
|
|
30997
|
+
const newestAge = Date.now() - stats.newestEntry;
|
|
30998
|
+
lines.push(`Newest Entry: ${formatTTL(newestAge)} ago`);
|
|
30999
|
+
}
|
|
31000
|
+
return lines.join("\n");
|
|
31001
|
+
}
|
|
31002
|
+
function formatBytes(bytes) {
|
|
31003
|
+
if (bytes < 1024)
|
|
31004
|
+
return `${bytes} B`;
|
|
31005
|
+
if (bytes < 1024 * 1024)
|
|
31006
|
+
return `${(bytes / 1024).toFixed(1)} KB`;
|
|
31007
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
31008
|
+
}
|
|
31009
|
+
var globalCacheInstance = null;
|
|
31010
|
+
function getGlobalCache(config) {
|
|
31011
|
+
if (!globalCacheInstance || config) {
|
|
31012
|
+
globalCacheInstance = new CostCacheManager(config);
|
|
31013
|
+
}
|
|
31014
|
+
return globalCacheInstance;
|
|
31015
|
+
}
|
|
31016
|
+
|
|
31017
|
+
// src/cache/cached-provider.ts
|
|
31018
|
+
init_providers();
|
|
31019
|
+
var import_chalk8 = __toESM(require("chalk"));
|
|
31020
|
+
var CachedProviderWrapper = class extends CloudProviderAdapter {
|
|
31021
|
+
constructor(provider, options) {
|
|
31022
|
+
super(provider["config"]);
|
|
31023
|
+
this.accountId = "";
|
|
31024
|
+
this.provider = provider;
|
|
31025
|
+
this.profile = options.profile;
|
|
31026
|
+
this.region = options.region;
|
|
31027
|
+
this.providerName = options.providerName || "aws";
|
|
31028
|
+
this.useCache = options.useCache ?? true;
|
|
31029
|
+
this.writeCache = options.writeCache ?? true;
|
|
31030
|
+
this.verbose = options.verbose ?? false;
|
|
31031
|
+
this.cache = getGlobalCache(options.cacheConfig);
|
|
31032
|
+
}
|
|
31033
|
+
log(message) {
|
|
31034
|
+
if (this.verbose) {
|
|
31035
|
+
console.log(import_chalk8.default.gray(`[Cache] ${message}`));
|
|
31036
|
+
}
|
|
31037
|
+
}
|
|
31038
|
+
/**
|
|
31039
|
+
* Stable JSON stringify to ensure consistent cache keys for objects
|
|
31040
|
+
*/
|
|
31041
|
+
stableStringify(value) {
|
|
31042
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
31043
|
+
const obj = value;
|
|
31044
|
+
return JSON.stringify(
|
|
31045
|
+
Object.keys(obj).sort().reduce((acc, key) => {
|
|
31046
|
+
acc[key] = obj[key];
|
|
31047
|
+
return acc;
|
|
31048
|
+
}, {})
|
|
31049
|
+
);
|
|
31050
|
+
}
|
|
31051
|
+
return JSON.stringify(value);
|
|
31052
|
+
}
|
|
31053
|
+
async getAccountInfo() {
|
|
31054
|
+
const cacheKey = this.cache.generateKey({
|
|
31055
|
+
account: `profile-${this.profile}`,
|
|
31056
|
+
profile: this.profile,
|
|
31057
|
+
region: this.region,
|
|
31058
|
+
provider: this.providerName,
|
|
31059
|
+
dataType: "account-info"
|
|
31060
|
+
});
|
|
31061
|
+
if (this.useCache) {
|
|
31062
|
+
const cached = await this.cache.get(cacheKey);
|
|
31063
|
+
if (cached) {
|
|
31064
|
+
this.log("Account info retrieved from cache");
|
|
31065
|
+
this.accountId = cached.id;
|
|
31066
|
+
return cached;
|
|
31067
|
+
}
|
|
31068
|
+
}
|
|
31069
|
+
this.log("Fetching account info from provider...");
|
|
31070
|
+
const accountInfo = await this.provider.getAccountInfo();
|
|
31071
|
+
this.accountId = accountInfo.id;
|
|
31072
|
+
if (this.writeCache) {
|
|
31073
|
+
await this.cache.set(cacheKey, accountInfo, {
|
|
31074
|
+
account: accountInfo.id,
|
|
31075
|
+
profile: this.profile,
|
|
31076
|
+
region: this.region,
|
|
31077
|
+
ttl: 24 * 60 * 60 * 1e3,
|
|
31078
|
+
// 24 hours
|
|
31079
|
+
metadata: { dataType: "account-info" }
|
|
31080
|
+
});
|
|
31081
|
+
}
|
|
31082
|
+
return accountInfo;
|
|
31083
|
+
}
|
|
31084
|
+
async getRawCostData() {
|
|
31085
|
+
if (!this.accountId) {
|
|
31086
|
+
await this.getAccountInfo();
|
|
31087
|
+
}
|
|
31088
|
+
const cacheKey = this.cache.generateKey({
|
|
31089
|
+
account: this.accountId,
|
|
31090
|
+
profile: this.profile,
|
|
31091
|
+
region: this.region,
|
|
31092
|
+
provider: this.providerName,
|
|
31093
|
+
dataType: "raw-cost-data"
|
|
31094
|
+
});
|
|
31095
|
+
if (this.useCache) {
|
|
31096
|
+
const cached = await this.cache.get(cacheKey);
|
|
31097
|
+
if (cached) {
|
|
31098
|
+
this.log("Raw cost data retrieved from cache");
|
|
31099
|
+
return cached;
|
|
31100
|
+
}
|
|
31101
|
+
}
|
|
31102
|
+
this.log("Fetching raw cost data from provider...");
|
|
31103
|
+
const rawCostData = await this.provider.getRawCostData();
|
|
31104
|
+
if (this.writeCache) {
|
|
31105
|
+
await this.cache.set(cacheKey, rawCostData, {
|
|
31106
|
+
account: this.accountId,
|
|
31107
|
+
profile: this.profile,
|
|
31108
|
+
region: this.region,
|
|
31109
|
+
metadata: { dataType: "raw-cost-data" }
|
|
31110
|
+
});
|
|
31111
|
+
}
|
|
31112
|
+
return rawCostData;
|
|
31113
|
+
}
|
|
31114
|
+
async getCostBreakdown() {
|
|
31115
|
+
if (!this.accountId) {
|
|
31116
|
+
await this.getAccountInfo();
|
|
31117
|
+
}
|
|
31118
|
+
const cacheKey = this.cache.generateKey({
|
|
31119
|
+
account: this.accountId,
|
|
31120
|
+
profile: this.profile,
|
|
31121
|
+
region: this.region,
|
|
31122
|
+
provider: this.providerName,
|
|
31123
|
+
dataType: "cost-breakdown"
|
|
31124
|
+
});
|
|
31125
|
+
if (this.useCache) {
|
|
31126
|
+
const cached = await this.cache.get(cacheKey);
|
|
31127
|
+
if (cached) {
|
|
31128
|
+
this.log("Cost breakdown retrieved from cache");
|
|
31129
|
+
return cached;
|
|
31130
|
+
}
|
|
31131
|
+
}
|
|
31132
|
+
this.log("Fetching cost breakdown from provider...");
|
|
31133
|
+
const costBreakdown = await this.provider.getCostBreakdown();
|
|
31134
|
+
if (this.writeCache) {
|
|
31135
|
+
await this.cache.set(cacheKey, costBreakdown, {
|
|
31136
|
+
account: this.accountId,
|
|
31137
|
+
profile: this.profile,
|
|
31138
|
+
region: this.region,
|
|
31139
|
+
metadata: { dataType: "cost-breakdown" }
|
|
31140
|
+
});
|
|
31141
|
+
}
|
|
31142
|
+
return costBreakdown;
|
|
31143
|
+
}
|
|
31144
|
+
async validateCredentials() {
|
|
31145
|
+
return this.provider.validateCredentials();
|
|
31146
|
+
}
|
|
31147
|
+
async getResourceInventory(filters) {
|
|
31148
|
+
if (!this.accountId) {
|
|
31149
|
+
await this.getAccountInfo();
|
|
31150
|
+
}
|
|
31151
|
+
const filterKey = filters ? this.stableStringify(filters) : "all";
|
|
31152
|
+
const cacheKey = this.cache.generateKey({
|
|
31153
|
+
account: this.accountId,
|
|
31154
|
+
profile: this.profile,
|
|
31155
|
+
region: this.region,
|
|
31156
|
+
provider: this.providerName,
|
|
31157
|
+
dataType: `inventory-${filterKey}`
|
|
31158
|
+
});
|
|
31159
|
+
if (this.useCache) {
|
|
31160
|
+
const cached = await this.cache.get(cacheKey);
|
|
31161
|
+
if (cached) {
|
|
31162
|
+
this.log("Resource inventory retrieved from cache");
|
|
31163
|
+
return cached;
|
|
31164
|
+
}
|
|
31165
|
+
}
|
|
31166
|
+
this.log("Fetching resource inventory from provider...");
|
|
31167
|
+
const inventory = await this.provider.getResourceInventory(filters);
|
|
31168
|
+
if (this.writeCache) {
|
|
31169
|
+
await this.cache.set(cacheKey, inventory, {
|
|
31170
|
+
account: this.accountId,
|
|
31171
|
+
profile: this.profile,
|
|
31172
|
+
region: this.region,
|
|
31173
|
+
ttl: 60 * 60 * 1e3,
|
|
31174
|
+
// 1 hour
|
|
31175
|
+
metadata: { dataType: "inventory" }
|
|
31176
|
+
});
|
|
31177
|
+
}
|
|
31178
|
+
return inventory;
|
|
31179
|
+
}
|
|
31180
|
+
async getResourceCosts(resourceId) {
|
|
31181
|
+
return this.provider.getResourceCosts(resourceId);
|
|
31182
|
+
}
|
|
31183
|
+
async getOptimizationRecommendations() {
|
|
31184
|
+
if (!this.accountId) {
|
|
31185
|
+
await this.getAccountInfo();
|
|
31186
|
+
}
|
|
31187
|
+
const cacheKey = this.cache.generateKey({
|
|
31188
|
+
account: this.accountId,
|
|
31189
|
+
profile: this.profile,
|
|
31190
|
+
region: this.region,
|
|
31191
|
+
provider: this.providerName,
|
|
31192
|
+
dataType: "optimization-recommendations"
|
|
31193
|
+
});
|
|
31194
|
+
if (this.useCache) {
|
|
31195
|
+
const cached = await this.cache.get(cacheKey);
|
|
31196
|
+
if (cached) {
|
|
31197
|
+
this.log("Optimization recommendations retrieved from cache");
|
|
31198
|
+
return cached;
|
|
31199
|
+
}
|
|
31200
|
+
}
|
|
31201
|
+
this.log("Fetching optimization recommendations from provider...");
|
|
31202
|
+
const recommendations = await this.provider.getOptimizationRecommendations();
|
|
31203
|
+
if (this.writeCache) {
|
|
31204
|
+
await this.cache.set(cacheKey, recommendations, {
|
|
31205
|
+
account: this.accountId,
|
|
31206
|
+
profile: this.profile,
|
|
31207
|
+
region: this.region,
|
|
31208
|
+
ttl: 6 * 60 * 60 * 1e3,
|
|
31209
|
+
// 6 hours
|
|
31210
|
+
metadata: { dataType: "optimization-recommendations" }
|
|
31211
|
+
});
|
|
31212
|
+
}
|
|
31213
|
+
return recommendations;
|
|
31214
|
+
}
|
|
31215
|
+
async getBudgets() {
|
|
31216
|
+
if (!this.accountId) {
|
|
31217
|
+
await this.getAccountInfo();
|
|
31218
|
+
}
|
|
31219
|
+
const cacheKey = this.cache.generateKey({
|
|
31220
|
+
account: this.accountId,
|
|
31221
|
+
profile: this.profile,
|
|
31222
|
+
region: this.region,
|
|
31223
|
+
provider: this.providerName,
|
|
31224
|
+
dataType: "budgets"
|
|
31225
|
+
});
|
|
31226
|
+
if (this.useCache) {
|
|
31227
|
+
const cached = await this.cache.get(cacheKey);
|
|
31228
|
+
if (cached) {
|
|
31229
|
+
this.log("Budgets retrieved from cache");
|
|
31230
|
+
return cached;
|
|
31231
|
+
}
|
|
31232
|
+
}
|
|
31233
|
+
this.log("Fetching budgets from provider...");
|
|
31234
|
+
const budgets = await this.provider.getBudgets();
|
|
31235
|
+
if (this.writeCache) {
|
|
31236
|
+
await this.cache.set(cacheKey, budgets, {
|
|
31237
|
+
account: this.accountId,
|
|
31238
|
+
profile: this.profile,
|
|
31239
|
+
region: this.region,
|
|
31240
|
+
ttl: 2 * 60 * 60 * 1e3,
|
|
31241
|
+
// 2 hours
|
|
31242
|
+
metadata: { dataType: "budgets" }
|
|
31243
|
+
});
|
|
31244
|
+
}
|
|
31245
|
+
return budgets;
|
|
31246
|
+
}
|
|
31247
|
+
async getBudgetAlerts() {
|
|
31248
|
+
if (!this.accountId) {
|
|
31249
|
+
await this.getAccountInfo();
|
|
31250
|
+
}
|
|
31251
|
+
const cacheKey = this.cache.generateKey({
|
|
31252
|
+
account: this.accountId,
|
|
31253
|
+
profile: this.profile,
|
|
31254
|
+
region: this.region,
|
|
31255
|
+
provider: this.providerName,
|
|
31256
|
+
dataType: "budget-alerts"
|
|
31257
|
+
});
|
|
31258
|
+
if (this.useCache) {
|
|
31259
|
+
const cached = await this.cache.get(cacheKey);
|
|
31260
|
+
if (cached) {
|
|
31261
|
+
this.log("Budget alerts retrieved from cache");
|
|
31262
|
+
return cached;
|
|
31263
|
+
}
|
|
31264
|
+
}
|
|
31265
|
+
this.log("Fetching budget alerts from provider...");
|
|
31266
|
+
const alerts = await this.provider.getBudgetAlerts();
|
|
31267
|
+
if (this.writeCache) {
|
|
31268
|
+
await this.cache.set(cacheKey, alerts, {
|
|
31269
|
+
account: this.accountId,
|
|
31270
|
+
profile: this.profile,
|
|
31271
|
+
region: this.region,
|
|
31272
|
+
ttl: 30 * 60 * 1e3,
|
|
31273
|
+
// 30 minutes
|
|
31274
|
+
metadata: { dataType: "budget-alerts" }
|
|
31275
|
+
});
|
|
31276
|
+
}
|
|
31277
|
+
return alerts;
|
|
31278
|
+
}
|
|
31279
|
+
async getCostTrendAnalysis(months) {
|
|
31280
|
+
if (!this.accountId) {
|
|
31281
|
+
await this.getAccountInfo();
|
|
31282
|
+
}
|
|
31283
|
+
const cacheKey = this.cache.generateKey({
|
|
31284
|
+
account: this.accountId,
|
|
31285
|
+
profile: this.profile,
|
|
31286
|
+
region: this.region,
|
|
31287
|
+
provider: this.providerName,
|
|
31288
|
+
dataType: `trend-analysis-${months || 6}`
|
|
31289
|
+
});
|
|
31290
|
+
if (this.useCache) {
|
|
31291
|
+
const cached = await this.cache.get(cacheKey);
|
|
31292
|
+
if (cached) {
|
|
31293
|
+
this.log("Cost trend analysis retrieved from cache");
|
|
31294
|
+
return cached;
|
|
31295
|
+
}
|
|
31296
|
+
}
|
|
31297
|
+
this.log("Fetching cost trend analysis from provider...");
|
|
31298
|
+
const analysis = await this.provider.getCostTrendAnalysis(months);
|
|
31299
|
+
if (this.writeCache) {
|
|
31300
|
+
await this.cache.set(cacheKey, analysis, {
|
|
31301
|
+
account: this.accountId,
|
|
31302
|
+
profile: this.profile,
|
|
31303
|
+
region: this.region,
|
|
31304
|
+
metadata: { dataType: "trend-analysis" }
|
|
31305
|
+
});
|
|
31306
|
+
}
|
|
31307
|
+
return analysis;
|
|
31308
|
+
}
|
|
31309
|
+
async getFinOpsRecommendations() {
|
|
31310
|
+
if (!this.accountId) {
|
|
31311
|
+
await this.getAccountInfo();
|
|
31312
|
+
}
|
|
31313
|
+
const cacheKey = this.cache.generateKey({
|
|
31314
|
+
account: this.accountId,
|
|
31315
|
+
profile: this.profile,
|
|
31316
|
+
region: this.region,
|
|
31317
|
+
provider: this.providerName,
|
|
31318
|
+
dataType: "finops-recommendations"
|
|
31319
|
+
});
|
|
31320
|
+
if (this.useCache) {
|
|
31321
|
+
const cached = await this.cache.get(cacheKey);
|
|
31322
|
+
if (cached) {
|
|
31323
|
+
this.log("FinOps recommendations retrieved from cache");
|
|
31324
|
+
return cached;
|
|
31325
|
+
}
|
|
31326
|
+
}
|
|
31327
|
+
this.log("Fetching FinOps recommendations from provider...");
|
|
31328
|
+
const recommendations = await this.provider.getFinOpsRecommendations();
|
|
31329
|
+
if (this.writeCache) {
|
|
31330
|
+
await this.cache.set(cacheKey, recommendations, {
|
|
31331
|
+
account: this.accountId,
|
|
31332
|
+
profile: this.profile,
|
|
31333
|
+
region: this.region,
|
|
31334
|
+
ttl: 6 * 60 * 60 * 1e3,
|
|
31335
|
+
// 6 hours
|
|
31336
|
+
metadata: { dataType: "finops-recommendations" }
|
|
31337
|
+
});
|
|
31338
|
+
}
|
|
31339
|
+
return recommendations;
|
|
31340
|
+
}
|
|
31341
|
+
/**
|
|
31342
|
+
* Force refresh all cached data for this account
|
|
31343
|
+
*/
|
|
31344
|
+
async refreshCache() {
|
|
31345
|
+
const profileAccountKey = this.cache.generateKey({
|
|
31346
|
+
account: `profile-${this.profile}`,
|
|
31347
|
+
profile: this.profile,
|
|
31348
|
+
region: this.region,
|
|
31349
|
+
provider: this.providerName,
|
|
31350
|
+
dataType: "account-info"
|
|
31351
|
+
});
|
|
31352
|
+
await this.cache.invalidate(profileAccountKey);
|
|
31353
|
+
if (!this.accountId) {
|
|
31354
|
+
await this.getAccountInfo();
|
|
31355
|
+
}
|
|
31356
|
+
this.log("Refreshing cache for account...");
|
|
31357
|
+
const invalidated = await this.cache.invalidateAccount(this.accountId);
|
|
31358
|
+
this.log(`Invalidated ${invalidated} cache entries`);
|
|
31359
|
+
await this.getCostBreakdown();
|
|
31360
|
+
this.log("Cache refreshed with fresh cost data");
|
|
31361
|
+
}
|
|
31362
|
+
/**
|
|
31363
|
+
* Get cache statistics
|
|
31364
|
+
*/
|
|
31365
|
+
async getCacheStats() {
|
|
31366
|
+
const stats = await this.cache.getStats();
|
|
31367
|
+
return formatCacheStats(stats);
|
|
31368
|
+
}
|
|
31369
|
+
/**
|
|
31370
|
+
* Clear all cache
|
|
31371
|
+
*/
|
|
31372
|
+
async clearCache() {
|
|
31373
|
+
await this.cache.clear();
|
|
31374
|
+
this.log("Cache cleared");
|
|
31375
|
+
}
|
|
31376
|
+
/**
|
|
31377
|
+
* Get underlying provider
|
|
31378
|
+
*/
|
|
31379
|
+
getProvider() {
|
|
31380
|
+
return this.provider;
|
|
31381
|
+
}
|
|
31382
|
+
/**
|
|
31383
|
+
* Enable/disable caching
|
|
31384
|
+
*/
|
|
31385
|
+
setCacheEnabled(enabled) {
|
|
31386
|
+
this.useCache = enabled;
|
|
31387
|
+
}
|
|
31388
|
+
/**
|
|
31389
|
+
* Check if caching is enabled
|
|
31390
|
+
*/
|
|
31391
|
+
isCacheEnabled() {
|
|
31392
|
+
return this.useCache;
|
|
31393
|
+
}
|
|
31394
|
+
};
|
|
31395
|
+
function wrapWithCache(provider, options) {
|
|
31396
|
+
const cacheConfig = {
|
|
31397
|
+
type: options.cacheType || "file"
|
|
31398
|
+
};
|
|
31399
|
+
if (options.cacheTtl) {
|
|
31400
|
+
cacheConfig.ttl = parseTTL(options.cacheTtl);
|
|
31401
|
+
}
|
|
31402
|
+
return new CachedProviderWrapper(provider, {
|
|
31403
|
+
profile: options.profile,
|
|
31404
|
+
region: options.region,
|
|
31405
|
+
providerName: options.providerName,
|
|
31406
|
+
useCache: options.useCache,
|
|
31407
|
+
writeCache: options.writeCache,
|
|
31408
|
+
cacheConfig,
|
|
31409
|
+
verbose: options.verbose
|
|
31410
|
+
});
|
|
31411
|
+
}
|
|
31412
|
+
|
|
31413
|
+
// src/config/app-config.ts
|
|
31414
|
+
var import_fs7 = require("fs");
|
|
31415
|
+
var import_path7 = require("path");
|
|
31416
|
+
var import_os3 = require("os");
|
|
30568
31417
|
init_providers();
|
|
30569
31418
|
var AppConfigManager = class {
|
|
30570
31419
|
/**
|
|
@@ -30573,9 +31422,9 @@ var AppConfigManager = class {
|
|
|
30573
31422
|
*/
|
|
30574
31423
|
static loadConfiguration(configPath) {
|
|
30575
31424
|
const path2 = configPath || this.findConfigFile();
|
|
30576
|
-
if (path2 && (0,
|
|
31425
|
+
if (path2 && (0, import_fs7.existsSync)(path2)) {
|
|
30577
31426
|
try {
|
|
30578
|
-
const configContent = (0,
|
|
31427
|
+
const configContent = (0, import_fs7.readFileSync)(path2, "utf8");
|
|
30579
31428
|
const config = JSON.parse(configContent);
|
|
30580
31429
|
return this.mergeWithDefaults(config);
|
|
30581
31430
|
} catch (error) {
|
|
@@ -30590,11 +31439,11 @@ var AppConfigManager = class {
|
|
|
30590
31439
|
* Find the first existing configuration file from search paths
|
|
30591
31440
|
*/
|
|
30592
31441
|
static findConfigFile() {
|
|
30593
|
-
if (process.env.INFRA_COST_CONFIG && (0,
|
|
31442
|
+
if (process.env.INFRA_COST_CONFIG && (0, import_fs7.existsSync)(process.env.INFRA_COST_CONFIG)) {
|
|
30594
31443
|
return process.env.INFRA_COST_CONFIG;
|
|
30595
31444
|
}
|
|
30596
31445
|
for (const path2 of this.CONFIG_PATHS) {
|
|
30597
|
-
if ((0,
|
|
31446
|
+
if ((0, import_fs7.existsSync)(path2)) {
|
|
30598
31447
|
return path2;
|
|
30599
31448
|
}
|
|
30600
31449
|
}
|
|
@@ -30604,14 +31453,14 @@ var AppConfigManager = class {
|
|
|
30604
31453
|
* Save configuration to file
|
|
30605
31454
|
*/
|
|
30606
31455
|
static saveConfiguration(config, configPath) {
|
|
30607
|
-
const path2 = configPath || (0,
|
|
30608
|
-
const dir = (0,
|
|
31456
|
+
const path2 = configPath || (0, import_path7.join)((0, import_os3.homedir)(), this.CONFIG_DIR, "config.json");
|
|
31457
|
+
const dir = (0, import_path7.dirname)(path2);
|
|
30609
31458
|
try {
|
|
30610
|
-
if (!(0,
|
|
30611
|
-
(0,
|
|
31459
|
+
if (!(0, import_fs7.existsSync)(dir)) {
|
|
31460
|
+
(0, import_fs7.mkdirSync)(dir, { recursive: true });
|
|
30612
31461
|
}
|
|
30613
|
-
(0,
|
|
30614
|
-
(0,
|
|
31462
|
+
(0, import_fs7.writeFileSync)(path2, JSON.stringify(config, null, 2));
|
|
31463
|
+
(0, import_fs7.chmodSync)(path2, 384);
|
|
30615
31464
|
console.log(`Configuration saved to ${path2}`);
|
|
30616
31465
|
} catch (error) {
|
|
30617
31466
|
throw new Error(`Failed to save configuration: ${error.message}`);
|
|
@@ -30621,7 +31470,7 @@ var AppConfigManager = class {
|
|
|
30621
31470
|
* Create a sample configuration file
|
|
30622
31471
|
*/
|
|
30623
31472
|
static createSampleConfig(configPath) {
|
|
30624
|
-
const path2 = configPath || (0,
|
|
31473
|
+
const path2 = configPath || (0, import_path7.join)(process.cwd(), this.CONFIG_FILENAME);
|
|
30625
31474
|
const sampleConfig = {
|
|
30626
31475
|
$schema: "https://infra-cost.io/config.schema.json",
|
|
30627
31476
|
version: "1.0",
|
|
@@ -30694,8 +31543,8 @@ var AppConfigManager = class {
|
|
|
30694
31543
|
* Initialize configuration file interactively
|
|
30695
31544
|
*/
|
|
30696
31545
|
static initializeConfig(configPath, force = false) {
|
|
30697
|
-
const path2 = configPath || (0,
|
|
30698
|
-
if ((0,
|
|
31546
|
+
const path2 = configPath || (0, import_path7.join)(process.cwd(), this.CONFIG_FILENAME);
|
|
31547
|
+
if ((0, import_fs7.existsSync)(path2) && !force) {
|
|
30699
31548
|
console.log(`Configuration file already exists at ${path2}`);
|
|
30700
31549
|
console.log("Use --force to overwrite or specify a different path");
|
|
30701
31550
|
return;
|
|
@@ -30916,13 +31765,13 @@ AppConfigManager.CONFIG_FILENAME = "infra-cost.config.json";
|
|
|
30916
31765
|
AppConfigManager.CONFIG_DIR = ".infra-cost";
|
|
30917
31766
|
// Configuration file search paths in priority order
|
|
30918
31767
|
AppConfigManager.CONFIG_PATHS = [
|
|
30919
|
-
(0,
|
|
31768
|
+
(0, import_path7.join)(process.cwd(), "infra-cost.config.json"),
|
|
30920
31769
|
// Project-specific
|
|
30921
|
-
(0,
|
|
31770
|
+
(0, import_path7.join)(process.cwd(), ".infra-cost.config.json"),
|
|
30922
31771
|
// Project-specific (hidden)
|
|
30923
|
-
(0,
|
|
31772
|
+
(0, import_path7.join)((0, import_os3.homedir)(), ".infra-cost", "config.json"),
|
|
30924
31773
|
// User global
|
|
30925
|
-
(0,
|
|
31774
|
+
(0, import_path7.join)((0, import_os3.homedir)(), ".config", "infra-cost", "config.json")
|
|
30926
31775
|
// XDG standard
|
|
30927
31776
|
];
|
|
30928
31777
|
AppConfigManager.DEFAULT_CONFIG = {
|
|
@@ -30994,24 +31843,24 @@ function configShow(configPath) {
|
|
|
30994
31843
|
// src/auth/sso-provider.ts
|
|
30995
31844
|
var import_credential_providers2 = require("@aws-sdk/credential-providers");
|
|
30996
31845
|
var import_credential_providers3 = require("@aws-sdk/credential-providers");
|
|
30997
|
-
var
|
|
30998
|
-
var
|
|
30999
|
-
var
|
|
31846
|
+
var import_fs8 = require("fs");
|
|
31847
|
+
var import_path8 = require("path");
|
|
31848
|
+
var import_os4 = require("os");
|
|
31000
31849
|
var import_ini = require("ini");
|
|
31001
|
-
var
|
|
31850
|
+
var import_chalk9 = __toESM(require("chalk"));
|
|
31002
31851
|
function getAwsConfigPaths() {
|
|
31003
31852
|
return {
|
|
31004
|
-
configPath: process.env.AWS_CONFIG_FILE || (0,
|
|
31005
|
-
credentialsPath: process.env.AWS_SHARED_CREDENTIALS_FILE || (0,
|
|
31853
|
+
configPath: process.env.AWS_CONFIG_FILE || (0, import_path8.join)((0, import_os4.homedir)(), ".aws", "config"),
|
|
31854
|
+
credentialsPath: process.env.AWS_SHARED_CREDENTIALS_FILE || (0, import_path8.join)((0, import_os4.homedir)(), ".aws", "credentials")
|
|
31006
31855
|
};
|
|
31007
31856
|
}
|
|
31008
31857
|
function parseAwsConfig() {
|
|
31009
31858
|
const { configPath } = getAwsConfigPaths();
|
|
31010
|
-
if (!(0,
|
|
31859
|
+
if (!(0, import_fs8.existsSync)(configPath)) {
|
|
31011
31860
|
return {};
|
|
31012
31861
|
}
|
|
31013
31862
|
try {
|
|
31014
|
-
const content = (0,
|
|
31863
|
+
const content = (0, import_fs8.readFileSync)(configPath, "utf8");
|
|
31015
31864
|
return (0, import_ini.parse)(content);
|
|
31016
31865
|
} catch (error) {
|
|
31017
31866
|
console.warn(`Failed to parse AWS config file: ${error.message}`);
|
|
@@ -31103,16 +31952,16 @@ function checkSSOTokenCache(profileName) {
|
|
|
31103
31952
|
if (!profileInfo || !profileInfo.isSSO) {
|
|
31104
31953
|
return { valid: false };
|
|
31105
31954
|
}
|
|
31106
|
-
const cacheDir = (0,
|
|
31107
|
-
if (!(0,
|
|
31955
|
+
const cacheDir = (0, import_path8.join)((0, import_os4.homedir)(), ".aws", "sso", "cache");
|
|
31956
|
+
if (!(0, import_fs8.existsSync)(cacheDir)) {
|
|
31108
31957
|
return { valid: false };
|
|
31109
31958
|
}
|
|
31110
31959
|
try {
|
|
31111
|
-
const files = (0,
|
|
31960
|
+
const files = (0, import_fs8.readdirSync)(cacheDir);
|
|
31112
31961
|
for (const file of files) {
|
|
31113
31962
|
if (file.endsWith(".json")) {
|
|
31114
31963
|
try {
|
|
31115
|
-
const content = (0,
|
|
31964
|
+
const content = (0, import_fs8.readFileSync)((0, import_path8.join)(cacheDir, file), "utf8");
|
|
31116
31965
|
const tokenData = JSON.parse(content);
|
|
31117
31966
|
if (tokenData.startUrl === profileInfo.ssoStartUrl && tokenData.expiresAt) {
|
|
31118
31967
|
const expiresAt = new Date(tokenData.expiresAt);
|
|
@@ -31136,27 +31985,27 @@ function getSSOLoginInstructions(profileName) {
|
|
|
31136
31985
|
}
|
|
31137
31986
|
const lines = [
|
|
31138
31987
|
"",
|
|
31139
|
-
|
|
31140
|
-
|
|
31988
|
+
import_chalk9.default.bold("AWS SSO Login Required"),
|
|
31989
|
+
import_chalk9.default.gray("\u2501".repeat(50)),
|
|
31141
31990
|
"",
|
|
31142
|
-
`Profile: ${
|
|
31991
|
+
`Profile: ${import_chalk9.default.cyan(profileName)}`
|
|
31143
31992
|
];
|
|
31144
31993
|
if (profileInfo.ssoStartUrl) {
|
|
31145
|
-
lines.push(`SSO Start URL: ${
|
|
31994
|
+
lines.push(`SSO Start URL: ${import_chalk9.default.cyan(profileInfo.ssoStartUrl)}`);
|
|
31146
31995
|
}
|
|
31147
31996
|
if (profileInfo.ssoAccountId) {
|
|
31148
|
-
lines.push(`Account ID: ${
|
|
31997
|
+
lines.push(`Account ID: ${import_chalk9.default.cyan(profileInfo.ssoAccountId)}`);
|
|
31149
31998
|
}
|
|
31150
31999
|
if (profileInfo.ssoRoleName) {
|
|
31151
|
-
lines.push(`Role: ${
|
|
32000
|
+
lines.push(`Role: ${import_chalk9.default.cyan(profileInfo.ssoRoleName)}`);
|
|
31152
32001
|
}
|
|
31153
32002
|
lines.push("");
|
|
31154
|
-
lines.push(
|
|
32003
|
+
lines.push(import_chalk9.default.bold("To authenticate, run:"));
|
|
31155
32004
|
lines.push("");
|
|
31156
|
-
lines.push(
|
|
32005
|
+
lines.push(import_chalk9.default.green(` aws sso login --profile ${profileName}`));
|
|
31157
32006
|
lines.push("");
|
|
31158
|
-
lines.push(
|
|
31159
|
-
lines.push(
|
|
32007
|
+
lines.push(import_chalk9.default.gray("This will open a browser window for authentication."));
|
|
32008
|
+
lines.push(import_chalk9.default.gray("Once complete, run infra-cost again."));
|
|
31160
32009
|
lines.push("");
|
|
31161
32010
|
return lines.join("\n");
|
|
31162
32011
|
}
|
|
@@ -31213,73 +32062,73 @@ async function validateSSOCredentials(profileName) {
|
|
|
31213
32062
|
function printSSOProfileInfo(profileName) {
|
|
31214
32063
|
const profileInfo = getSSOProfileInfo(profileName);
|
|
31215
32064
|
if (!profileInfo) {
|
|
31216
|
-
console.log(
|
|
32065
|
+
console.log(import_chalk9.default.red(`Profile "${profileName}" not found`));
|
|
31217
32066
|
return;
|
|
31218
32067
|
}
|
|
31219
32068
|
if (!profileInfo.isSSO) {
|
|
31220
|
-
console.log(
|
|
32069
|
+
console.log(import_chalk9.default.yellow(`Profile "${profileName}" is not configured for SSO`));
|
|
31221
32070
|
return;
|
|
31222
32071
|
}
|
|
31223
32072
|
console.log("");
|
|
31224
|
-
console.log(
|
|
31225
|
-
console.log(
|
|
31226
|
-
console.log(`Profile Name: ${
|
|
31227
|
-
console.log(`SSO Start URL: ${profileInfo.ssoStartUrl ||
|
|
31228
|
-
console.log(`SSO Region: ${profileInfo.ssoRegion ||
|
|
31229
|
-
console.log(`Account ID: ${profileInfo.ssoAccountId ||
|
|
31230
|
-
console.log(`Role Name: ${profileInfo.ssoRoleName ||
|
|
31231
|
-
console.log(`Default Region: ${profileInfo.region ||
|
|
32073
|
+
console.log(import_chalk9.default.bold("SSO Profile Information"));
|
|
32074
|
+
console.log(import_chalk9.default.gray("\u2501".repeat(40)));
|
|
32075
|
+
console.log(`Profile Name: ${import_chalk9.default.cyan(profileInfo.profileName)}`);
|
|
32076
|
+
console.log(`SSO Start URL: ${profileInfo.ssoStartUrl || import_chalk9.default.gray("N/A")}`);
|
|
32077
|
+
console.log(`SSO Region: ${profileInfo.ssoRegion || import_chalk9.default.gray("N/A")}`);
|
|
32078
|
+
console.log(`Account ID: ${profileInfo.ssoAccountId || import_chalk9.default.gray("N/A")}`);
|
|
32079
|
+
console.log(`Role Name: ${profileInfo.ssoRoleName || import_chalk9.default.gray("N/A")}`);
|
|
32080
|
+
console.log(`Default Region: ${profileInfo.region || import_chalk9.default.gray("N/A")}`);
|
|
31232
32081
|
if (profileInfo.ssoSession) {
|
|
31233
32082
|
console.log(`SSO Session: ${profileInfo.ssoSession}`);
|
|
31234
32083
|
}
|
|
31235
32084
|
const tokenStatus = checkSSOTokenCache(profileName);
|
|
31236
32085
|
if (tokenStatus.valid) {
|
|
31237
|
-
console.log(`Token Status: ${
|
|
32086
|
+
console.log(`Token Status: ${import_chalk9.default.green("Valid")}`);
|
|
31238
32087
|
if (tokenStatus.expiresAt) {
|
|
31239
32088
|
console.log(`Expires At: ${tokenStatus.expiresAt.toLocaleString()}`);
|
|
31240
32089
|
}
|
|
31241
32090
|
} else {
|
|
31242
|
-
console.log(`Token Status: ${
|
|
32091
|
+
console.log(`Token Status: ${import_chalk9.default.red("Expired or Not Found")}`);
|
|
31243
32092
|
console.log("");
|
|
31244
|
-
console.log(
|
|
32093
|
+
console.log(import_chalk9.default.yellow("Run `aws sso login --profile " + profileName + "` to authenticate"));
|
|
31245
32094
|
}
|
|
31246
32095
|
console.log("");
|
|
31247
32096
|
}
|
|
31248
32097
|
function listSSOProfiles() {
|
|
31249
32098
|
const profiles = discoverSSOProfiles();
|
|
31250
32099
|
if (profiles.length === 0) {
|
|
31251
|
-
console.log(
|
|
32100
|
+
console.log(import_chalk9.default.yellow("No SSO profiles found in AWS config"));
|
|
31252
32101
|
console.log("");
|
|
31253
32102
|
console.log("To configure SSO, add a profile to ~/.aws/config:");
|
|
31254
32103
|
console.log("");
|
|
31255
|
-
console.log(
|
|
31256
|
-
console.log(
|
|
31257
|
-
console.log(
|
|
31258
|
-
console.log(
|
|
31259
|
-
console.log(
|
|
31260
|
-
console.log(
|
|
32104
|
+
console.log(import_chalk9.default.gray(" [profile my-sso-profile]"));
|
|
32105
|
+
console.log(import_chalk9.default.gray(" sso_start_url = https://my-sso-portal.awsapps.com/start"));
|
|
32106
|
+
console.log(import_chalk9.default.gray(" sso_region = us-east-1"));
|
|
32107
|
+
console.log(import_chalk9.default.gray(" sso_account_id = 123456789012"));
|
|
32108
|
+
console.log(import_chalk9.default.gray(" sso_role_name = MyRole"));
|
|
32109
|
+
console.log(import_chalk9.default.gray(" region = us-east-1"));
|
|
31261
32110
|
console.log("");
|
|
31262
32111
|
return;
|
|
31263
32112
|
}
|
|
31264
32113
|
console.log("");
|
|
31265
|
-
console.log(
|
|
31266
|
-
console.log(
|
|
32114
|
+
console.log(import_chalk9.default.bold("Available SSO Profiles"));
|
|
32115
|
+
console.log(import_chalk9.default.gray("\u2501".repeat(60)));
|
|
31267
32116
|
for (const profile of profiles) {
|
|
31268
32117
|
const tokenStatus = checkSSOTokenCache(profile.profileName);
|
|
31269
|
-
const statusIcon = tokenStatus.valid ?
|
|
31270
|
-
const statusText = tokenStatus.valid ?
|
|
31271
|
-
console.log(`${statusIcon} ${
|
|
32118
|
+
const statusIcon = tokenStatus.valid ? import_chalk9.default.green("\u25CF") : import_chalk9.default.red("\u25CB");
|
|
32119
|
+
const statusText = tokenStatus.valid ? import_chalk9.default.green("Active") : import_chalk9.default.gray("Inactive");
|
|
32120
|
+
console.log(`${statusIcon} ${import_chalk9.default.cyan(profile.profileName.padEnd(20))} ${statusText}`);
|
|
31272
32121
|
console.log(` Account: ${profile.ssoAccountId || "N/A"} | Role: ${profile.ssoRoleName || "N/A"}`);
|
|
31273
32122
|
}
|
|
31274
32123
|
console.log("");
|
|
31275
|
-
console.log(
|
|
31276
|
-
console.log(
|
|
32124
|
+
console.log(import_chalk9.default.gray("Use --profile <name> to select a profile"));
|
|
32125
|
+
console.log(import_chalk9.default.gray("Use --sso-info <name> to see detailed profile information"));
|
|
31277
32126
|
console.log("");
|
|
31278
32127
|
}
|
|
31279
32128
|
|
|
31280
32129
|
// src/index.ts
|
|
31281
32130
|
var program = new import_commander.Command();
|
|
31282
|
-
program.version(package_default.version).name("infra-cost").description(package_default.description).option("--provider [provider]", "Cloud provider to use (aws, gcp, azure, alicloud, oracle)", "aws").option("-p, --profile [profile]", "Cloud provider profile to use", "default").option("-k, --access-key [key]", "Access key (AWS Access Key, GCP Service Account, etc.)").option("-s, --secret-key [key]", "Secret key (AWS Secret Key, etc.)").option("-T, --session-token [key]", "Session token (AWS Session Token, etc.)").option("-r, --region [region]", "Cloud provider region", "us-east-1").option("--config-file [path]", "Path to configuration file").option("--config-profile [name]", "Use named profile from configuration file").option("--app-config-init [path]", "Initialize application configuration file").option("--app-config-validate [path]", "Validate application configuration file").option("--app-config-list-profiles", "List available configuration profiles").option("--app-config-show", "Show current configuration").option("--sso", "Use AWS SSO for authentication").option("--sso-list", "List available SSO profiles").option("--sso-info [profile]", "Show SSO profile information").option("--sso-validate [profile]", "Validate SSO credentials").option("--delta", "Show cost delta/difference compared to previous period").option("--delta-threshold [percent]", "Alert threshold for cost changes (default: 10%)", "10").option("--project-id [id]", "GCP Project ID").option("--key-file [path]", "Path to service account key file (GCP) or private key (Oracle)").option("--subscription-id [id]", "Azure Subscription ID").option("--tenant-id [id]", "Azure Tenant ID").option("--client-id [id]", "Azure Client ID").option("--client-secret [secret]", "Azure Client Secret").option("--user-id [id]", "Oracle User OCID").option("--tenancy-id [id]", "Oracle Tenancy OCID").option("--fingerprint [fingerprint]", "Oracle Public Key Fingerprint").option("-j, --json", "Get the output as JSON").option("-u, --summary", "Get only the summary without service breakdown").option("-t, --text", "Get the output as plain text (no colors / tables)").option("-S, --slack-token [token]", "Token for the slack integration").option("-C, --slack-channel [channel]", "Channel to which the slack integration should post").option("--inventory", "Show complete resource inventory").option("--inventory-type [type]", "Filter by resource type (compute, storage, database, network, security, serverless, container, analytics)").option("--inventory-regions [regions]", "Comma-separated list of regions for inventory scanning").option("--resource-costs", "Include cost-per-resource analysis").option("--optimization-tips", "Show resource optimization recommendations").option("--inventory-export [format]", "Export inventory (json, csv, xlsx)").option("--include-metadata", "Include detailed metadata in inventory export").option("--group-by [field]", "Group inventory results by provider, region, or type").option("--budgets", "Show budget information and alerts").option("--trends [months]", "Show cost trend analysis (default: 6 months)", "6").option("--finops", "Show comprehensive FinOps recommendations with potential savings").option("--alerts", "Show budget alerts and cost anomalies").option("--compare-clouds [providers]", "Compare costs across multiple cloud providers (comma-separated)").option("--optimization-report", "Generate cross-cloud optimization recommendations").option("--multi-cloud-dashboard", "Display comprehensive multi-cloud infrastructure dashboard").option("--all-clouds-inventory", "Show inventory across all configured cloud providers").option("--dependency-mapping", "Analyze resource dependencies and relationships").option("--tagging-compliance", "Analyze tagging compliance against standards").option("--resource-graph", "Generate resource dependency graph visualization").option("--monitor", "Start real-time cost monitoring").option("--monitor-setup [config]", "Setup monitoring configuration (json file path or inline json)").option("--monitor-start [name]", "Start a named monitoring session").option("--monitor-stop [name]", "Stop a named monitoring session").option("--monitor-status", "Show status of all monitoring sessions").option("--alert-threshold [value]", "Set alert threshold (e.g., 1000 for $1000, 20% for percentage)").option("--alert-type [type]", "Alert type: ABSOLUTE, PERCENTAGE, ANOMALY, TREND, BUDGET_FORECAST").option("--alert-channel [channel]", "Notification channel: slack, email, webhook, teams, sms, discord").option("--config-init [path]", "Initialize monitoring configuration file").option("--config-validate [path]", "Validate monitoring configuration file").option("--config-sample [path]", "Create sample configuration file").option("--forecast [days]", "Generate cost forecast (default: 30 days)", "30").option("--forecast-model [model]", "Forecasting model: LINEAR, EXPONENTIAL, SEASONAL, AUTO", "AUTO").option("--forecast-confidence [level]", "Confidence level for predictions (80, 90, 95, 99)", "95").option("--forecast-services", "Generate individual service forecasts").option("--forecast-export [format]", "Export forecast results (json, csv, xlsx)").option("--optimize", "Run automated cost optimization").option("--optimize-plan [file]", "Execute optimization plan from file").option("--optimize-dry-run", "Run optimization in dry-run mode (no changes)").option("--optimize-rules [rules]", "Comma-separated list of rule IDs to execute").option("--optimize-budget [amount]", "Maximum budget for optimization actions").option("--optimize-stats", "Show optimization statistics and history").option("--optimize-stop", "Stop all active optimizations").option("--optimize-create-plan [file]", "Create a new optimization plan template").option("--audit-query [filters]", "Query audit logs with JSON filters").option("--audit-export [format]", "Export audit logs (json, csv, xml, syslog)").option("--audit-report [framework]", "Generate compliance report (soc2, gdpr, hipaa, pci_dss)").option("--audit-period [days]", "Audit period in days (default: 30)", "30").option("--audit-stats", "Show audit statistics and compliance overview").option("--compliance-check [framework]", "Run compliance check against framework").option("--compliance-frameworks", "List available compliance frameworks").option("--rightsize", "Generate ML-based rightsizing recommendations").option("--rightsize-conservative", "Use conservative rightsizing approach (lower risk)").option("--rightsize-aggressive", "Use aggressive rightsizing approach (higher savings)").option("--rightsize-min-savings [amount]", "Minimum monthly savings threshold (default: $10)", "10").option("--rightsize-export [format]", "Export rightsizing recommendations (json, csv, xlsx)").option("--rightsize-simulate", "Show potential savings without generating recommendations").option("--rightsize-filter [types]", "Filter by resource types (comma-separated)").option("--sustainability", "Analyze carbon footprint and sustainability metrics").option("--carbon-footprint", "Calculate detailed carbon emissions for resources").option("--sustainability-export [format]", "Export sustainability analysis (json, csv, xlsx, pdf)").option("--green-recommendations", "Generate green optimization recommendations").option("--sustainability-targets", "Set sustainability targets (JSON format)").option("--carbon-pricing [model]", "Carbon pricing model: SOCIAL_COST, CARBON_TAX, MARKET_PRICE").option("--sustainability-deep", "Include supply chain and indirect emissions analysis").option("--renewable-regions", "Show regions with highest renewable energy adoption").option("--sustainability-score", "Calculate overall sustainability score").option("--security-analysis", "Analyze security costs and risk posture").option("--security-vulnerabilities", "Detailed vulnerability assessment and costs").option("--security-compliance [framework]", "Analyze compliance against framework (soc2, iso27001, pci_dss, hipaa)").option("--security-recommendations", "Generate security cost optimization recommendations").option("--security-export [format]", "Export security analysis (json, csv, xlsx)").option("--security-risk-tolerance [level]", "Risk tolerance level: LOW, MEDIUM, HIGH").option("--security-industry [vertical]", "Industry vertical: FINANCE, HEALTHCARE, RETAIL, TECHNOLOGY, GOVERNMENT").option("--security-deep", "Include detailed vulnerability and incident cost analysis").option("--security-trends", "Show security cost and risk trends").option("--integrations", "List all available tool integrations").option("--integrations-status", "Show status of all configured integrations").option("--integrations-configure [integration]", "Configure a specific integration").option("--integrations-enable [integration]", "Enable a specific integration").option("--integrations-disable [integration]", "Disable a specific integration").option("--integrations-sync [integration]", "Manually sync a specific integration").option("--integrations-test [integration]", "Test connection to a specific integration").option("--integrations-export [format]", "Export integration report (json, csv)").option("--integrations-category [category]", "Filter integrations by category (ci_cd, monitoring, collaboration)").option("--analytics", "Generate comprehensive cost intelligence report").option("--analytics-executive", "Generate executive summary report").option("--analytics-insights", "Show key business insights and recommendations").option("--analytics-trends", "Analyze cost trends and patterns").option("--analytics-drivers", "Identify and analyze primary cost drivers").option("--analytics-efficiency", "Calculate efficiency metrics and benchmarks").option("--analytics-forecast", "Generate predictive analytics and forecasts").option("--analytics-alerts", "Show intelligent cost alerts and anomalies").option("--analytics-export [format]", "Export analytics report (json, csv, xlsx, pdf)").option("--analytics-timeframe [days]", "Analysis timeframe in days (default: 30)", "30").option("--analytics-dashboard [name]", "Create custom dashboard with specified name").option("--cohort-analysis [criteria]", "Perform cohort analysis with specified criteria").option("--unit-economics [unit]", "Calculate unit economics for specified business unit").option("--enterprise", "Show enterprise and multi-tenant overview").option("--tenants", "List all tenants and their status").option("--tenant-create [name]", "Create a new tenant").option("--tenant-info [id]", "Show detailed tenant information").option("--tenant-suspend [id]", "Suspend a tenant").option("--tenant-activate [id]", "Activate a suspended tenant").option("--users [tenant]", "List users (optionally filtered by tenant)").option("--user-create [email]", "Create a new user").option("--user-role [userId:role]", "Update user role").option("--api-key-generate [userId:name]", "Generate API key for user").option("--quotas [tenantId]", "Check quota usage for tenant").option("--platform-metrics", "Show platform-wide metrics and health").option("--enterprise-export [format]", "Export enterprise report (json, csv)").option("--api-server", "Start the REST API server").option("--api-port [port]", "API server port (default: 3000)", "3000").option("--api-host [host]", "API server host (default: 0.0.0.0)", "0.0.0.0").option("--api-key-create [name:permissions]", "Create API key with optional permissions").option("--api-key-list", "List all API keys for current user").option("--api-key-revoke [id]", "Revoke an API key").option("--api-status", "Show API server status and statistics").option("--webhook-create [url:events]", "Create webhook subscription").option("--webhook-list", "List webhook subscriptions").option("--webhook-delete [id]", "Delete webhook subscription").option("--webhook-test [id]", "Test webhook delivery").option("--webhook-history [id]", "Show webhook delivery history").option("--webhook-stats", "Show webhook statistics").option("--anomaly-detect", "Run AI-powered cost anomaly detection").option("--anomaly-report [days]", "Generate anomaly detection report (default: 30 days)", "30").option("--anomaly-config [config]", "Set anomaly detection configuration (JSON)").option("--anomaly-sensitivity [level]", "Set detection sensitivity: low, medium, high", "medium").option("--anomaly-models [models]", "Enable specific AI models: spike,pattern,seasonal,ensemble,deep", "spike,pattern,seasonal").option("--anomaly-realtime", "Enable real-time anomaly monitoring").option("--anomaly-list [filter]", "List detected anomalies with optional filter").option("--anomaly-status [id]", "Update anomaly status (id:status format)").option("--anomaly-export [format]", "Export anomaly report (json, csv, xlsx)").option("--anomaly-insights", "Show AI-generated insights about cost patterns").option("--dashboard-create [name]", "Create a new dashboard with specified name").option("--dashboard-template [id]", "Create dashboard from template (cost-overview, resource-optimization)").option("--dashboard-list", "List all available dashboards").option("--dashboard-view [id]", "View dashboard by ID").option("--dashboard-export [id:format]", "Export dashboard (id:html|pdf|json format)").option("--chart-create [type:title]", "Create a new chart (line:title, bar:title, pie:title, etc.)").option("--chart-list", "List all available charts").option("--chart-export [id:format]", "Export chart (id:html|svg|csv|json format)").option("--visualization-theme [theme]", "Set visualization theme (default, dark, corporate)").option("--visualization-templates", "Show available dashboard templates").option("--visualization-demo", "Generate demo dashboard with sample data").option("--trend", "Generate 6-month cost trend analysis with visualization").option("--audit", "Generate comprehensive audit report with recommendations").option("--executive-summary", "Generate executive-level cost summary report").option("--pdf-report [filename]", "Generate PDF report with specified filename").option("--combine-profiles", "Combine cost data from multiple profiles of same account").option("--all-profiles", "Use all available cloud provider profiles").option("--interactive", "Start interactive guided cost analysis mode").option("--discover-profiles", "Auto-discover available cloud provider profiles").option("--auto-profile", "Automatically select best available profile").option("--smart-alerts", "Enable intelligent cost alerting with visual indicators").option("--compact", "Use compact display mode for large datasets").option("-h, --help", "Get the help of the CLI").parse(process.argv);
|
|
32131
|
+
program.version(package_default.version).name("infra-cost").description(package_default.description).option("--provider [provider]", "Cloud provider to use (aws, gcp, azure, alicloud, oracle)", "aws").option("-p, --profile [profile]", "Cloud provider profile to use", "default").option("-k, --access-key [key]", "Access key (AWS Access Key, GCP Service Account, etc.)").option("-s, --secret-key [key]", "Secret key (AWS Secret Key, etc.)").option("-T, --session-token [key]", "Session token (AWS Session Token, etc.)").option("-r, --region [region]", "Cloud provider region", "us-east-1").option("--config-file [path]", "Path to configuration file").option("--config-profile [name]", "Use named profile from configuration file").option("--app-config-init [path]", "Initialize application configuration file").option("--app-config-validate [path]", "Validate application configuration file").option("--app-config-list-profiles", "List available configuration profiles").option("--app-config-show", "Show current configuration").option("--sso", "Use AWS SSO for authentication").option("--sso-list", "List available SSO profiles").option("--sso-info [profile]", "Show SSO profile information").option("--sso-validate [profile]", "Validate SSO credentials").option("--delta", "Show cost delta/difference compared to previous period").option("--delta-threshold [percent]", "Alert threshold for cost changes (default: 10%)", "10").option("--project-id [id]", "GCP Project ID").option("--key-file [path]", "Path to service account key file (GCP) or private key (Oracle)").option("--subscription-id [id]", "Azure Subscription ID").option("--tenant-id [id]", "Azure Tenant ID").option("--client-id [id]", "Azure Client ID").option("--client-secret [secret]", "Azure Client Secret").option("--user-id [id]", "Oracle User OCID").option("--tenancy-id [id]", "Oracle Tenancy OCID").option("--fingerprint [fingerprint]", "Oracle Public Key Fingerprint").option("-j, --json", "Get the output as JSON").option("-u, --summary", "Get only the summary without service breakdown").option("-t, --text", "Get the output as plain text (no colors / tables)").option("-S, --slack-token [token]", "Token for the slack integration").option("-C, --slack-channel [channel]", "Channel to which the slack integration should post").option("--inventory", "Show complete resource inventory").option("--inventory-type [type]", "Filter by resource type (compute, storage, database, network, security, serverless, container, analytics)").option("--inventory-regions [regions]", "Comma-separated list of regions for inventory scanning").option("--resource-costs", "Include cost-per-resource analysis").option("--optimization-tips", "Show resource optimization recommendations").option("--inventory-export [format]", "Export inventory (json, csv, xlsx)").option("--include-metadata", "Include detailed metadata in inventory export").option("--group-by [field]", "Group inventory results by provider, region, or type").option("--budgets", "Show budget information and alerts").option("--trends [months]", "Show cost trend analysis (default: 6 months)", "6").option("--finops", "Show comprehensive FinOps recommendations with potential savings").option("--alerts", "Show budget alerts and cost anomalies").option("--compare-clouds [providers]", "Compare costs across multiple cloud providers (comma-separated)").option("--optimization-report", "Generate cross-cloud optimization recommendations").option("--multi-cloud-dashboard", "Display comprehensive multi-cloud infrastructure dashboard").option("--all-clouds-inventory", "Show inventory across all configured cloud providers").option("--dependency-mapping", "Analyze resource dependencies and relationships").option("--tagging-compliance", "Analyze tagging compliance against standards").option("--resource-graph", "Generate resource dependency graph visualization").option("--monitor", "Start real-time cost monitoring").option("--monitor-setup [config]", "Setup monitoring configuration (json file path or inline json)").option("--monitor-start [name]", "Start a named monitoring session").option("--monitor-stop [name]", "Stop a named monitoring session").option("--monitor-status", "Show status of all monitoring sessions").option("--alert-threshold [value]", "Set alert threshold (e.g., 1000 for $1000, 20% for percentage)").option("--alert-type [type]", "Alert type: ABSOLUTE, PERCENTAGE, ANOMALY, TREND, BUDGET_FORECAST").option("--alert-channel [channel]", "Notification channel: slack, email, webhook, teams, sms, discord").option("--config-init [path]", "Initialize monitoring configuration file").option("--config-validate [path]", "Validate monitoring configuration file").option("--config-sample [path]", "Create sample configuration file").option("--forecast [days]", "Generate cost forecast (default: 30 days)", "30").option("--forecast-model [model]", "Forecasting model: LINEAR, EXPONENTIAL, SEASONAL, AUTO", "AUTO").option("--forecast-confidence [level]", "Confidence level for predictions (80, 90, 95, 99)", "95").option("--forecast-services", "Generate individual service forecasts").option("--forecast-export [format]", "Export forecast results (json, csv, xlsx)").option("--optimize", "Run automated cost optimization").option("--optimize-plan [file]", "Execute optimization plan from file").option("--optimize-dry-run", "Run optimization in dry-run mode (no changes)").option("--optimize-rules [rules]", "Comma-separated list of rule IDs to execute").option("--optimize-budget [amount]", "Maximum budget for optimization actions").option("--optimize-stats", "Show optimization statistics and history").option("--optimize-stop", "Stop all active optimizations").option("--optimize-create-plan [file]", "Create a new optimization plan template").option("--audit-query [filters]", "Query audit logs with JSON filters").option("--audit-export [format]", "Export audit logs (json, csv, xml, syslog)").option("--audit-report [framework]", "Generate compliance report (soc2, gdpr, hipaa, pci_dss)").option("--audit-period [days]", "Audit period in days (default: 30)", "30").option("--audit-stats", "Show audit statistics and compliance overview").option("--compliance-check [framework]", "Run compliance check against framework").option("--compliance-frameworks", "List available compliance frameworks").option("--rightsize", "Generate ML-based rightsizing recommendations").option("--rightsize-conservative", "Use conservative rightsizing approach (lower risk)").option("--rightsize-aggressive", "Use aggressive rightsizing approach (higher savings)").option("--rightsize-min-savings [amount]", "Minimum monthly savings threshold (default: $10)", "10").option("--rightsize-export [format]", "Export rightsizing recommendations (json, csv, xlsx)").option("--rightsize-simulate", "Show potential savings without generating recommendations").option("--rightsize-filter [types]", "Filter by resource types (comma-separated)").option("--sustainability", "Analyze carbon footprint and sustainability metrics").option("--carbon-footprint", "Calculate detailed carbon emissions for resources").option("--sustainability-export [format]", "Export sustainability analysis (json, csv, xlsx, pdf)").option("--green-recommendations", "Generate green optimization recommendations").option("--sustainability-targets", "Set sustainability targets (JSON format)").option("--carbon-pricing [model]", "Carbon pricing model: SOCIAL_COST, CARBON_TAX, MARKET_PRICE").option("--sustainability-deep", "Include supply chain and indirect emissions analysis").option("--renewable-regions", "Show regions with highest renewable energy adoption").option("--sustainability-score", "Calculate overall sustainability score").option("--security-analysis", "Analyze security costs and risk posture").option("--security-vulnerabilities", "Detailed vulnerability assessment and costs").option("--security-compliance [framework]", "Analyze compliance against framework (soc2, iso27001, pci_dss, hipaa)").option("--security-recommendations", "Generate security cost optimization recommendations").option("--security-export [format]", "Export security analysis (json, csv, xlsx)").option("--security-risk-tolerance [level]", "Risk tolerance level: LOW, MEDIUM, HIGH").option("--security-industry [vertical]", "Industry vertical: FINANCE, HEALTHCARE, RETAIL, TECHNOLOGY, GOVERNMENT").option("--security-deep", "Include detailed vulnerability and incident cost analysis").option("--security-trends", "Show security cost and risk trends").option("--integrations", "List all available tool integrations").option("--integrations-status", "Show status of all configured integrations").option("--integrations-configure [integration]", "Configure a specific integration").option("--integrations-enable [integration]", "Enable a specific integration").option("--integrations-disable [integration]", "Disable a specific integration").option("--integrations-sync [integration]", "Manually sync a specific integration").option("--integrations-test [integration]", "Test connection to a specific integration").option("--integrations-export [format]", "Export integration report (json, csv)").option("--integrations-category [category]", "Filter integrations by category (ci_cd, monitoring, collaboration)").option("--analytics", "Generate comprehensive cost intelligence report").option("--analytics-executive", "Generate executive summary report").option("--analytics-insights", "Show key business insights and recommendations").option("--analytics-trends", "Analyze cost trends and patterns").option("--analytics-drivers", "Identify and analyze primary cost drivers").option("--analytics-efficiency", "Calculate efficiency metrics and benchmarks").option("--analytics-forecast", "Generate predictive analytics and forecasts").option("--analytics-alerts", "Show intelligent cost alerts and anomalies").option("--analytics-export [format]", "Export analytics report (json, csv, xlsx, pdf)").option("--analytics-timeframe [days]", "Analysis timeframe in days (default: 30)", "30").option("--analytics-dashboard [name]", "Create custom dashboard with specified name").option("--cohort-analysis [criteria]", "Perform cohort analysis with specified criteria").option("--unit-economics [unit]", "Calculate unit economics for specified business unit").option("--enterprise", "Show enterprise and multi-tenant overview").option("--tenants", "List all tenants and their status").option("--tenant-create [name]", "Create a new tenant").option("--tenant-info [id]", "Show detailed tenant information").option("--tenant-suspend [id]", "Suspend a tenant").option("--tenant-activate [id]", "Activate a suspended tenant").option("--users [tenant]", "List users (optionally filtered by tenant)").option("--user-create [email]", "Create a new user").option("--user-role [userId:role]", "Update user role").option("--api-key-generate [userId:name]", "Generate API key for user").option("--quotas [tenantId]", "Check quota usage for tenant").option("--platform-metrics", "Show platform-wide metrics and health").option("--enterprise-export [format]", "Export enterprise report (json, csv)").option("--api-server", "Start the REST API server").option("--api-port [port]", "API server port (default: 3000)", "3000").option("--api-host [host]", "API server host (default: 0.0.0.0)", "0.0.0.0").option("--api-key-create [name:permissions]", "Create API key with optional permissions").option("--api-key-list", "List all API keys for current user").option("--api-key-revoke [id]", "Revoke an API key").option("--api-status", "Show API server status and statistics").option("--webhook-create [url:events]", "Create webhook subscription").option("--webhook-list", "List webhook subscriptions").option("--webhook-delete [id]", "Delete webhook subscription").option("--webhook-test [id]", "Test webhook delivery").option("--webhook-history [id]", "Show webhook delivery history").option("--webhook-stats", "Show webhook statistics").option("--anomaly-detect", "Run AI-powered cost anomaly detection").option("--anomaly-report [days]", "Generate anomaly detection report (default: 30 days)", "30").option("--anomaly-config [config]", "Set anomaly detection configuration (JSON)").option("--anomaly-sensitivity [level]", "Set detection sensitivity: low, medium, high", "medium").option("--anomaly-models [models]", "Enable specific AI models: spike,pattern,seasonal,ensemble,deep", "spike,pattern,seasonal").option("--anomaly-realtime", "Enable real-time anomaly monitoring").option("--anomaly-list [filter]", "List detected anomalies with optional filter").option("--anomaly-status [id]", "Update anomaly status (id:status format)").option("--anomaly-export [format]", "Export anomaly report (json, csv, xlsx)").option("--anomaly-insights", "Show AI-generated insights about cost patterns").option("--dashboard-create [name]", "Create a new dashboard with specified name").option("--dashboard-template [id]", "Create dashboard from template (cost-overview, resource-optimization)").option("--dashboard-list", "List all available dashboards").option("--dashboard-view [id]", "View dashboard by ID").option("--dashboard-export [id:format]", "Export dashboard (id:html|pdf|json format)").option("--chart-create [type:title]", "Create a new chart (line:title, bar:title, pie:title, etc.)").option("--chart-list", "List all available charts").option("--chart-export [id:format]", "Export chart (id:html|svg|csv|json format)").option("--visualization-theme [theme]", "Set visualization theme (default, dark, corporate)").option("--visualization-templates", "Show available dashboard templates").option("--visualization-demo", "Generate demo dashboard with sample data").option("--trend", "Generate 6-month cost trend analysis with visualization").option("--audit", "Generate comprehensive audit report with recommendations").option("--executive-summary", "Generate executive-level cost summary report").option("--pdf-report [filename]", "Generate PDF report with specified filename").option("--combine-profiles", "Combine cost data from multiple profiles of same account").option("--all-profiles", "Use all available cloud provider profiles").option("--interactive", "Start interactive guided cost analysis mode").option("--discover-profiles", "Auto-discover available cloud provider profiles").option("--auto-profile", "Automatically select best available profile").option("--smart-alerts", "Enable intelligent cost alerting with visual indicators").option("--compact", "Use compact display mode for large datasets").option("--cache", "Use cached data if available (default: enabled)").option("--no-cache", "Disable caching, always fetch fresh data").option("--refresh-cache", "Force refresh cache with fresh data").option("--clear-cache", "Clear all cached data").option("--cache-stats", "Show cache statistics").option("--cache-ttl [duration]", "Cache TTL (e.g., 30m, 2h, 1d)", "4h").option("--cache-type [type]", "Cache type: file, memory", "file").option("-h, --help", "Get the help of the CLI").parse(process.argv);
|
|
31283
32132
|
async function main() {
|
|
31284
32133
|
const options = program.opts();
|
|
31285
32134
|
function getCategoryIcon(category) {
|
|
@@ -31369,6 +32218,27 @@ async function main() {
|
|
|
31369
32218
|
program.help();
|
|
31370
32219
|
process.exit(0);
|
|
31371
32220
|
}
|
|
32221
|
+
if (options.clearCache || options.cacheStats) {
|
|
32222
|
+
const cacheConfig = {
|
|
32223
|
+
type: options.cacheType || "file",
|
|
32224
|
+
ttl: parseTTL(options.cacheTtl || "4h")
|
|
32225
|
+
};
|
|
32226
|
+
const cache = getGlobalCache(cacheConfig);
|
|
32227
|
+
if (options.clearCache) {
|
|
32228
|
+
await cache.clear();
|
|
32229
|
+
console.log(import_chalk10.default.green("Cache cleared successfully"));
|
|
32230
|
+
process.exit(0);
|
|
32231
|
+
}
|
|
32232
|
+
if (options.cacheStats) {
|
|
32233
|
+
const stats = await cache.getStats();
|
|
32234
|
+
console.log("");
|
|
32235
|
+
console.log(import_chalk10.default.bold("Cache Statistics"));
|
|
32236
|
+
console.log(import_chalk10.default.gray("\u2501".repeat(40)));
|
|
32237
|
+
console.log(formatCacheStats(stats));
|
|
32238
|
+
console.log("");
|
|
32239
|
+
process.exit(0);
|
|
32240
|
+
}
|
|
32241
|
+
}
|
|
31372
32242
|
if (options.appConfigInit || options.appConfigValidate || options.appConfigListProfiles || options.appConfigShow) {
|
|
31373
32243
|
if (options.appConfigInit) {
|
|
31374
32244
|
const configPath = typeof options.appConfigInit === "string" && options.appConfigInit.length > 0 ? options.appConfigInit : void 0;
|
|
@@ -31403,14 +32273,14 @@ async function main() {
|
|
|
31403
32273
|
const profileName = typeof options.ssoValidate === "string" && options.ssoValidate.length > 0 ? options.ssoValidate : options.profile;
|
|
31404
32274
|
const result = await validateSSOCredentials(profileName);
|
|
31405
32275
|
if (result.success) {
|
|
31406
|
-
console.log(
|
|
32276
|
+
console.log(import_chalk10.default.green("SSO credentials are valid"));
|
|
31407
32277
|
console.log(` Account: ${result.accountId}`);
|
|
31408
32278
|
console.log(` Role: ${result.roleName}`);
|
|
31409
32279
|
if (result.expiresAt) {
|
|
31410
32280
|
console.log(` Expires: ${result.expiresAt.toLocaleString()}`);
|
|
31411
32281
|
}
|
|
31412
32282
|
} else {
|
|
31413
|
-
console.log(
|
|
32283
|
+
console.log(import_chalk10.default.red("SSO validation failed"));
|
|
31414
32284
|
console.log(` Error: ${result.error}`);
|
|
31415
32285
|
console.log(getSSOLoginInstructions(profileName));
|
|
31416
32286
|
}
|
|
@@ -31525,13 +32395,13 @@ async function main() {
|
|
|
31525
32395
|
if (useSSO) {
|
|
31526
32396
|
const profileInfo = getSSOProfileInfo(options.profile);
|
|
31527
32397
|
if (!profileInfo || !profileInfo.isSSO) {
|
|
31528
|
-
console.error(
|
|
32398
|
+
console.error(import_chalk10.default.red(`Profile "${options.profile}" is not configured for SSO.`));
|
|
31529
32399
|
console.log(getSSOLoginInstructions(options.profile));
|
|
31530
32400
|
process.exit(1);
|
|
31531
32401
|
}
|
|
31532
32402
|
const ssoResult = await validateSSOCredentials(options.profile);
|
|
31533
32403
|
if (!ssoResult.success) {
|
|
31534
|
-
console.error(
|
|
32404
|
+
console.error(import_chalk10.default.red("SSO authentication required."));
|
|
31535
32405
|
console.log(getSSOLoginInstructions(options.profile));
|
|
31536
32406
|
process.exit(1);
|
|
31537
32407
|
}
|
|
@@ -31542,9 +32412,9 @@ async function main() {
|
|
|
31542
32412
|
credentials: ssoCredentials,
|
|
31543
32413
|
region
|
|
31544
32414
|
};
|
|
31545
|
-
console.log(
|
|
32415
|
+
console.log(import_chalk10.default.green(`Using SSO profile: ${options.profile}`));
|
|
31546
32416
|
if (ssoResult.expiresAt) {
|
|
31547
|
-
console.log(
|
|
32417
|
+
console.log(import_chalk10.default.gray(`Token expires: ${ssoResult.expiresAt.toLocaleString()}`));
|
|
31548
32418
|
}
|
|
31549
32419
|
} else {
|
|
31550
32420
|
const awsConfig = await getAwsConfigFromOptionsOrFile({
|
|
@@ -31603,7 +32473,23 @@ async function main() {
|
|
|
31603
32473
|
};
|
|
31604
32474
|
}
|
|
31605
32475
|
const providerFactory = new CloudProviderFactory();
|
|
31606
|
-
const
|
|
32476
|
+
const baseProvider = providerFactory.createProvider(providerConfig);
|
|
32477
|
+
const useCache = options.cache !== false && !options.refreshCache;
|
|
32478
|
+
const provider = wrapWithCache(baseProvider, {
|
|
32479
|
+
profile: options.profile,
|
|
32480
|
+
region: options.region,
|
|
32481
|
+
providerName: providerType,
|
|
32482
|
+
useCache,
|
|
32483
|
+
writeCache: useCache,
|
|
32484
|
+
// Don't write to cache if caching is disabled
|
|
32485
|
+
cacheTtl: options.cacheTtl || "4h",
|
|
32486
|
+
cacheType: options.cacheType || "file",
|
|
32487
|
+
verbose: false
|
|
32488
|
+
});
|
|
32489
|
+
if (options.refreshCache) {
|
|
32490
|
+
console.log(import_chalk10.default.yellow("Refreshing cache..."));
|
|
32491
|
+
await provider.refreshCache();
|
|
32492
|
+
}
|
|
31607
32493
|
const credentialsValid = await provider.validateCredentials();
|
|
31608
32494
|
if (!credentialsValid) {
|
|
31609
32495
|
console.error(`Invalid credentials for ${providerType.toUpperCase()}`);
|
|
@@ -31611,6 +32497,9 @@ async function main() {
|
|
|
31611
32497
|
}
|
|
31612
32498
|
const accountInfo = await provider.getAccountInfo();
|
|
31613
32499
|
const costBreakdown = await provider.getCostBreakdown();
|
|
32500
|
+
if (options.refreshCache) {
|
|
32501
|
+
console.log(import_chalk10.default.green("Cache refreshed with fresh data"));
|
|
32502
|
+
}
|
|
31614
32503
|
if (options.budgets || options.trends || options.finops || options.alerts) {
|
|
31615
32504
|
try {
|
|
31616
32505
|
let hasOutput = false;
|
|
@@ -32135,7 +33024,7 @@ async function main() {
|
|
|
32135
33024
|
if (options.auditQuery || options.auditExport || options.auditReport || options.auditStats || options.complianceCheck || options.complianceFrameworks) {
|
|
32136
33025
|
try {
|
|
32137
33026
|
const auditLogger = new AuditLogger({
|
|
32138
|
-
logDirectory: (0,
|
|
33027
|
+
logDirectory: (0, import_path10.join)(process.cwd(), "audit-logs"),
|
|
32139
33028
|
encryptionEnabled: process.env.AUDIT_ENCRYPTION === "true",
|
|
32140
33029
|
retentionDays: parseInt(process.env.AUDIT_RETENTION_DAYS || "2555")
|
|
32141
33030
|
// 7 years default
|
|
@@ -33941,8 +34830,8 @@ ${categoryIcon} ${category.replace("_", " ")} (${categoryIntegrations.length})`)
|
|
|
33941
34830
|
delta: deltaAnalysis
|
|
33942
34831
|
};
|
|
33943
34832
|
if (deltaAnalysis.insights.anomalyDetected) {
|
|
33944
|
-
console.log(
|
|
33945
|
-
console.log(
|
|
34833
|
+
console.log(import_chalk10.default.yellow("\n\u26A0\uFE0F Cost Anomaly Detected"));
|
|
34834
|
+
console.log(import_chalk10.default.gray("\u2501".repeat(50)));
|
|
33946
34835
|
console.log(generateDeltaSummary(deltaAnalysis));
|
|
33947
34836
|
console.log("");
|
|
33948
34837
|
}
|
|
@@ -34066,7 +34955,7 @@ ${categoryIcon} ${category.replace("_", " ")} (${categoryIntegrations.length})`)
|
|
|
34066
34955
|
try {
|
|
34067
34956
|
const multiCloudDashboard = new MultiCloudDashboard();
|
|
34068
34957
|
if (options.multiCloudDashboard) {
|
|
34069
|
-
console.log(
|
|
34958
|
+
console.log(import_chalk10.default.yellow("\u{1F310} Generating Multi-Cloud Infrastructure Dashboard..."));
|
|
34070
34959
|
let targetProviders;
|
|
34071
34960
|
if (options.compareClouds) {
|
|
34072
34961
|
const requestedProviders = options.compareClouds.split(",").map((p) => p.trim().toLowerCase());
|
|
@@ -34076,7 +34965,7 @@ ${categoryIcon} ${category.replace("_", " ")} (${categoryIntegrations.length})`)
|
|
|
34076
34965
|
const dashboardOutput = await multiCloudDashboard.generateMultiCloudInventoryDashboard(targetProviders);
|
|
34077
34966
|
console.log(dashboardOutput);
|
|
34078
34967
|
} else if (options.allCloudsInventory) {
|
|
34079
|
-
console.log(
|
|
34968
|
+
console.log(import_chalk10.default.yellow("\u2601\uFE0F Collecting inventory from all configured cloud providers..."));
|
|
34080
34969
|
const dashboardOutput = await multiCloudDashboard.generateMultiCloudInventoryDashboard();
|
|
34081
34970
|
console.log(dashboardOutput);
|
|
34082
34971
|
}
|
|
@@ -34087,7 +34976,7 @@ ${categoryIcon} ${category.replace("_", " ")} (${categoryIntegrations.length})`)
|
|
|
34087
34976
|
if (options.enterprise || options.tenants || options.tenantCreate || options.tenantInfo || options.tenantSuspend || options.tenantActivate || options.users || options.userCreate || options.userRole || options.apiKeyGenerate || options.quotas || options.platformMetrics || options.enterpriseExport) {
|
|
34088
34977
|
try {
|
|
34089
34978
|
const multiTenantManager = new MultiTenantManager({
|
|
34090
|
-
dataStorePath: (0,
|
|
34979
|
+
dataStorePath: (0, import_path10.join)(process.cwd(), "enterprise-data"),
|
|
34091
34980
|
encryptionEnabled: process.env.ENTERPRISE_ENCRYPTION === "true",
|
|
34092
34981
|
auditingEnabled: process.env.ENTERPRISE_AUDITING !== "false",
|
|
34093
34982
|
maxTenantsPerInstance: parseInt(process.env.MAX_TENANTS_PER_INSTANCE || "100"),
|
|
@@ -34438,7 +35327,7 @@ ${categoryIcon} ${category.replace("_", " ")} (${categoryIntegrations.length})`)
|
|
|
34438
35327
|
console.log("\u2550".repeat(60));
|
|
34439
35328
|
try {
|
|
34440
35329
|
const multiTenantManager = new MultiTenantManager({
|
|
34441
|
-
dataStorePath: (0,
|
|
35330
|
+
dataStorePath: (0, import_path10.join)(process.cwd(), "enterprise-data"),
|
|
34442
35331
|
encryptionEnabled: process.env.ENTERPRISE_ENCRYPTION === "true"
|
|
34443
35332
|
});
|
|
34444
35333
|
apiServer.setMultiTenantManager(multiTenantManager);
|
|
@@ -35338,7 +36227,7 @@ ${categoryIcon} ${category.replace("_", " ")} (${categoryIntegrations.length})`)
|
|
|
35338
36227
|
anomalies = [];
|
|
35339
36228
|
}
|
|
35340
36229
|
} catch (error) {
|
|
35341
|
-
console.log(
|
|
36230
|
+
console.log(import_chalk10.default.yellow("\u26A0\uFE0F Anomaly detection not available"));
|
|
35342
36231
|
}
|
|
35343
36232
|
ui.updateProgress(100);
|
|
35344
36233
|
ui.stopProgress();
|
|
@@ -35352,14 +36241,14 @@ ${categoryIcon} ${category.replace("_", " ")} (${categoryIntegrations.length})`)
|
|
|
35352
36241
|
console.log(ui.createAnomalyAlert(anomalies));
|
|
35353
36242
|
}
|
|
35354
36243
|
if (recommendations.length > 0) {
|
|
35355
|
-
console.log("\n" +
|
|
36244
|
+
console.log("\n" + import_chalk10.default.bold.cyan("\u{1F4A1} Cost Optimization Recommendations"));
|
|
35356
36245
|
console.log("\u2550".repeat(60));
|
|
35357
36246
|
recommendations.slice(0, 5).forEach((rec, index) => {
|
|
35358
36247
|
console.log(`
|
|
35359
|
-
${index + 1}. ${
|
|
36248
|
+
${index + 1}. ${import_chalk10.default.bold(rec.title)}`);
|
|
35360
36249
|
console.log(` ${rec.description}`);
|
|
35361
|
-
console.log(` \u{1F4B0} Potential savings: ${
|
|
35362
|
-
console.log(` \u{1F3AF} Priority: ${
|
|
36250
|
+
console.log(` \u{1F4B0} Potential savings: ${import_chalk10.default.green("$" + rec.potentialSavings.amount.toFixed(2))} ${rec.potentialSavings.timeframe.toLowerCase()}`);
|
|
36251
|
+
console.log(` \u{1F3AF} Priority: ${import_chalk10.default[rec.priority === "HIGH" ? "red" : rec.priority === "MEDIUM" ? "yellow" : "cyan"](rec.priority)}`);
|
|
35363
36252
|
});
|
|
35364
36253
|
}
|
|
35365
36254
|
if (options.pdfReport) {
|
|
@@ -35399,11 +36288,11 @@ ${index + 1}. ${import_chalk9.default.bold(rec.title)}`);
|
|
|
35399
36288
|
compact: true
|
|
35400
36289
|
}));
|
|
35401
36290
|
const totalSavings = recommendations.reduce((sum, rec) => sum + rec.potentialSavings.amount, 0);
|
|
35402
|
-
console.log("\n" +
|
|
36291
|
+
console.log("\n" + import_chalk10.default.bold.cyan("\u{1F3AF} Key Performance Indicators"));
|
|
35403
36292
|
console.log("\u2550".repeat(50));
|
|
35404
|
-
console.log(`Monthly Spend: ${
|
|
35405
|
-
console.log(`Optimization Potential: ${
|
|
35406
|
-
console.log(`Active Recommendations: ${
|
|
36293
|
+
console.log(`Monthly Spend: ${import_chalk10.default.yellow("$" + costBreakdown2.totals.thisMonth.toFixed(2))}`);
|
|
36294
|
+
console.log(`Optimization Potential: ${import_chalk10.default.green("$" + totalSavings.toFixed(2))}`);
|
|
36295
|
+
console.log(`Active Recommendations: ${import_chalk10.default.cyan(recommendations.length.toString())}`);
|
|
35407
36296
|
if (options.pdfReport) {
|
|
35408
36297
|
console.log("\n\u{1F4C4} Generating executive PDF summary...");
|
|
35409
36298
|
const pdfExporter = new PDFExporter2();
|