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.
Files changed (3) hide show
  1. package/README.md +113 -4
  2. package/dist/index.js +992 -103
  3. 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, import_fs8, import_path8, import_moment2, PDFExporter, pdf_exporter_default;
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
- import_fs8 = require("fs");
12085
- import_path8 = require("path");
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, import_path8.join)(outputPath, filename);
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, import_fs8.existsSync)(outputPath)) {
12926
- (0, import_fs8.mkdirSync)(outputPath, { recursive: true });
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.0",
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: "git+https://github.com/codecollab-co/infra-cost.git"
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 import_chalk9 = __toESM(require("chalk"));
30562
- var import_path9 = require("path");
30561
+ var import_chalk10 = __toESM(require("chalk"));
30562
+ var import_path10 = require("path");
30563
30563
 
30564
- // src/config/app-config.ts
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, import_fs6.existsSync)(path2)) {
31425
+ if (path2 && (0, import_fs7.existsSync)(path2)) {
30577
31426
  try {
30578
- const configContent = (0, import_fs6.readFileSync)(path2, "utf8");
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, import_fs6.existsSync)(process.env.INFRA_COST_CONFIG)) {
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, import_fs6.existsSync)(path2)) {
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, import_path6.join)((0, import_os2.homedir)(), this.CONFIG_DIR, "config.json");
30608
- const dir = (0, import_path6.dirname)(path2);
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, import_fs6.existsSync)(dir)) {
30611
- (0, import_fs6.mkdirSync)(dir, { recursive: true });
31459
+ if (!(0, import_fs7.existsSync)(dir)) {
31460
+ (0, import_fs7.mkdirSync)(dir, { recursive: true });
30612
31461
  }
30613
- (0, import_fs6.writeFileSync)(path2, JSON.stringify(config, null, 2));
30614
- (0, import_fs6.chmodSync)(path2, 384);
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, import_path6.join)(process.cwd(), this.CONFIG_FILENAME);
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, import_path6.join)(process.cwd(), this.CONFIG_FILENAME);
30698
- if ((0, import_fs6.existsSync)(path2) && !force) {
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, import_path6.join)(process.cwd(), "infra-cost.config.json"),
31768
+ (0, import_path7.join)(process.cwd(), "infra-cost.config.json"),
30920
31769
  // Project-specific
30921
- (0, import_path6.join)(process.cwd(), ".infra-cost.config.json"),
31770
+ (0, import_path7.join)(process.cwd(), ".infra-cost.config.json"),
30922
31771
  // Project-specific (hidden)
30923
- (0, import_path6.join)((0, import_os2.homedir)(), ".infra-cost", "config.json"),
31772
+ (0, import_path7.join)((0, import_os3.homedir)(), ".infra-cost", "config.json"),
30924
31773
  // User global
30925
- (0, import_path6.join)((0, import_os2.homedir)(), ".config", "infra-cost", "config.json")
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 import_fs7 = require("fs");
30998
- var import_path7 = require("path");
30999
- var import_os3 = require("os");
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 import_chalk8 = __toESM(require("chalk"));
31850
+ var import_chalk9 = __toESM(require("chalk"));
31002
31851
  function getAwsConfigPaths() {
31003
31852
  return {
31004
- configPath: process.env.AWS_CONFIG_FILE || (0, import_path7.join)((0, import_os3.homedir)(), ".aws", "config"),
31005
- credentialsPath: process.env.AWS_SHARED_CREDENTIALS_FILE || (0, import_path7.join)((0, import_os3.homedir)(), ".aws", "credentials")
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, import_fs7.existsSync)(configPath)) {
31859
+ if (!(0, import_fs8.existsSync)(configPath)) {
31011
31860
  return {};
31012
31861
  }
31013
31862
  try {
31014
- const content = (0, import_fs7.readFileSync)(configPath, "utf8");
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, import_path7.join)((0, import_os3.homedir)(), ".aws", "sso", "cache");
31107
- if (!(0, import_fs7.existsSync)(cacheDir)) {
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, import_fs7.readdirSync)(cacheDir);
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, import_fs7.readFileSync)((0, import_path7.join)(cacheDir, file), "utf8");
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
- import_chalk8.default.bold("AWS SSO Login Required"),
31140
- import_chalk8.default.gray("\u2501".repeat(50)),
31988
+ import_chalk9.default.bold("AWS SSO Login Required"),
31989
+ import_chalk9.default.gray("\u2501".repeat(50)),
31141
31990
  "",
31142
- `Profile: ${import_chalk8.default.cyan(profileName)}`
31991
+ `Profile: ${import_chalk9.default.cyan(profileName)}`
31143
31992
  ];
31144
31993
  if (profileInfo.ssoStartUrl) {
31145
- lines.push(`SSO Start URL: ${import_chalk8.default.cyan(profileInfo.ssoStartUrl)}`);
31994
+ lines.push(`SSO Start URL: ${import_chalk9.default.cyan(profileInfo.ssoStartUrl)}`);
31146
31995
  }
31147
31996
  if (profileInfo.ssoAccountId) {
31148
- lines.push(`Account ID: ${import_chalk8.default.cyan(profileInfo.ssoAccountId)}`);
31997
+ lines.push(`Account ID: ${import_chalk9.default.cyan(profileInfo.ssoAccountId)}`);
31149
31998
  }
31150
31999
  if (profileInfo.ssoRoleName) {
31151
- lines.push(`Role: ${import_chalk8.default.cyan(profileInfo.ssoRoleName)}`);
32000
+ lines.push(`Role: ${import_chalk9.default.cyan(profileInfo.ssoRoleName)}`);
31152
32001
  }
31153
32002
  lines.push("");
31154
- lines.push(import_chalk8.default.bold("To authenticate, run:"));
32003
+ lines.push(import_chalk9.default.bold("To authenticate, run:"));
31155
32004
  lines.push("");
31156
- lines.push(import_chalk8.default.green(` aws sso login --profile ${profileName}`));
32005
+ lines.push(import_chalk9.default.green(` aws sso login --profile ${profileName}`));
31157
32006
  lines.push("");
31158
- lines.push(import_chalk8.default.gray("This will open a browser window for authentication."));
31159
- lines.push(import_chalk8.default.gray("Once complete, run infra-cost again."));
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(import_chalk8.default.red(`Profile "${profileName}" not found`));
32065
+ console.log(import_chalk9.default.red(`Profile "${profileName}" not found`));
31217
32066
  return;
31218
32067
  }
31219
32068
  if (!profileInfo.isSSO) {
31220
- console.log(import_chalk8.default.yellow(`Profile "${profileName}" is not configured for SSO`));
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(import_chalk8.default.bold("SSO Profile Information"));
31225
- console.log(import_chalk8.default.gray("\u2501".repeat(40)));
31226
- console.log(`Profile Name: ${import_chalk8.default.cyan(profileInfo.profileName)}`);
31227
- console.log(`SSO Start URL: ${profileInfo.ssoStartUrl || import_chalk8.default.gray("N/A")}`);
31228
- console.log(`SSO Region: ${profileInfo.ssoRegion || import_chalk8.default.gray("N/A")}`);
31229
- console.log(`Account ID: ${profileInfo.ssoAccountId || import_chalk8.default.gray("N/A")}`);
31230
- console.log(`Role Name: ${profileInfo.ssoRoleName || import_chalk8.default.gray("N/A")}`);
31231
- console.log(`Default Region: ${profileInfo.region || import_chalk8.default.gray("N/A")}`);
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: ${import_chalk8.default.green("Valid")}`);
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: ${import_chalk8.default.red("Expired or Not Found")}`);
32091
+ console.log(`Token Status: ${import_chalk9.default.red("Expired or Not Found")}`);
31243
32092
  console.log("");
31244
- console.log(import_chalk8.default.yellow("Run `aws sso login --profile " + profileName + "` to authenticate"));
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(import_chalk8.default.yellow("No SSO profiles found in AWS config"));
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(import_chalk8.default.gray(" [profile my-sso-profile]"));
31256
- console.log(import_chalk8.default.gray(" sso_start_url = https://my-sso-portal.awsapps.com/start"));
31257
- console.log(import_chalk8.default.gray(" sso_region = us-east-1"));
31258
- console.log(import_chalk8.default.gray(" sso_account_id = 123456789012"));
31259
- console.log(import_chalk8.default.gray(" sso_role_name = MyRole"));
31260
- console.log(import_chalk8.default.gray(" region = us-east-1"));
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(import_chalk8.default.bold("Available SSO Profiles"));
31266
- console.log(import_chalk8.default.gray("\u2501".repeat(60)));
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 ? import_chalk8.default.green("\u25CF") : import_chalk8.default.red("\u25CB");
31270
- const statusText = tokenStatus.valid ? import_chalk8.default.green("Active") : import_chalk8.default.gray("Inactive");
31271
- console.log(`${statusIcon} ${import_chalk8.default.cyan(profile.profileName.padEnd(20))} ${statusText}`);
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(import_chalk8.default.gray("Use --profile <name> to select a profile"));
31276
- console.log(import_chalk8.default.gray("Use --sso-info <name> to see detailed profile information"));
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(import_chalk9.default.green("SSO credentials are valid"));
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(import_chalk9.default.red("SSO validation failed"));
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(import_chalk9.default.red(`Profile "${options.profile}" is not configured for SSO.`));
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(import_chalk9.default.red("SSO authentication required."));
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(import_chalk9.default.green(`Using SSO profile: ${options.profile}`));
32415
+ console.log(import_chalk10.default.green(`Using SSO profile: ${options.profile}`));
31546
32416
  if (ssoResult.expiresAt) {
31547
- console.log(import_chalk9.default.gray(`Token expires: ${ssoResult.expiresAt.toLocaleString()}`));
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 provider = providerFactory.createProvider(providerConfig);
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, import_path9.join)(process.cwd(), "audit-logs"),
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(import_chalk9.default.yellow("\n\u26A0\uFE0F Cost Anomaly Detected"));
33945
- console.log(import_chalk9.default.gray("\u2501".repeat(50)));
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(import_chalk9.default.yellow("\u{1F310} Generating Multi-Cloud Infrastructure Dashboard..."));
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(import_chalk9.default.yellow("\u2601\uFE0F Collecting inventory from all configured cloud providers..."));
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, import_path9.join)(process.cwd(), "enterprise-data"),
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, import_path9.join)(process.cwd(), "enterprise-data"),
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(import_chalk9.default.yellow("\u26A0\uFE0F Anomaly detection not available"));
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" + import_chalk9.default.bold.cyan("\u{1F4A1} Cost Optimization Recommendations"));
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}. ${import_chalk9.default.bold(rec.title)}`);
36248
+ ${index + 1}. ${import_chalk10.default.bold(rec.title)}`);
35360
36249
  console.log(` ${rec.description}`);
35361
- console.log(` \u{1F4B0} Potential savings: ${import_chalk9.default.green("$" + rec.potentialSavings.amount.toFixed(2))} ${rec.potentialSavings.timeframe.toLowerCase()}`);
35362
- console.log(` \u{1F3AF} Priority: ${import_chalk9.default[rec.priority === "HIGH" ? "red" : rec.priority === "MEDIUM" ? "yellow" : "cyan"](rec.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" + import_chalk9.default.bold.cyan("\u{1F3AF} Key Performance Indicators"));
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: ${import_chalk9.default.yellow("$" + costBreakdown2.totals.thisMonth.toFixed(2))}`);
35405
- console.log(`Optimization Potential: ${import_chalk9.default.green("$" + totalSavings.toFixed(2))}`);
35406
- console.log(`Active Recommendations: ${import_chalk9.default.cyan(recommendations.length.toString())}`);
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();