infra-cost 1.6.0 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +1235 -356
- package/dist/index.js +1231 -351
- package/package.json +8 -1
package/dist/cli/index.js
CHANGED
|
@@ -8,6 +8,9 @@ var __name = (target, value) => __defProp(target, "name", { value, configurable:
|
|
|
8
8
|
var __esm = (fn, res) => function __init() {
|
|
9
9
|
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
10
10
|
};
|
|
11
|
+
var __commonJS = (cb, mod) => function __require() {
|
|
12
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
13
|
+
};
|
|
11
14
|
var __export = (target, all) => {
|
|
12
15
|
for (var name in all)
|
|
13
16
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -30,6 +33,171 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
30
33
|
));
|
|
31
34
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
32
35
|
|
|
36
|
+
// package.json
|
|
37
|
+
var require_package = __commonJS({
|
|
38
|
+
"package.json"(exports, module2) {
|
|
39
|
+
module2.exports = {
|
|
40
|
+
name: "infra-cost",
|
|
41
|
+
version: "1.7.0",
|
|
42
|
+
description: "Multi-cloud FinOps CLI tool for comprehensive cost analysis and infrastructure optimization across AWS, GCP, Azure, Alibaba Cloud, and Oracle Cloud",
|
|
43
|
+
keywords: [
|
|
44
|
+
"aws",
|
|
45
|
+
"gcp",
|
|
46
|
+
"azure",
|
|
47
|
+
"cloud-cost",
|
|
48
|
+
"finops",
|
|
49
|
+
"cost-optimization",
|
|
50
|
+
"multi-cloud",
|
|
51
|
+
"cost-analysis",
|
|
52
|
+
"infrastructure",
|
|
53
|
+
"cloud-billing",
|
|
54
|
+
"cost-management",
|
|
55
|
+
"devops",
|
|
56
|
+
"cli-tool",
|
|
57
|
+
"cost-monitoring",
|
|
58
|
+
"budget-tracking"
|
|
59
|
+
],
|
|
60
|
+
author: {
|
|
61
|
+
name: "Code Collab",
|
|
62
|
+
email: "codecollab.co@gmail.com",
|
|
63
|
+
url: "https://github.com/codecollab-co/infra-cost"
|
|
64
|
+
},
|
|
65
|
+
files: [
|
|
66
|
+
"!tests/**/*",
|
|
67
|
+
"dist/**/*",
|
|
68
|
+
"!dist/**/*.js.map",
|
|
69
|
+
"bin/**/*"
|
|
70
|
+
],
|
|
71
|
+
bin: {
|
|
72
|
+
"infra-cost": "./bin/index.js",
|
|
73
|
+
"aws-cost": "./bin/index.js"
|
|
74
|
+
},
|
|
75
|
+
main: "./dist/index.js",
|
|
76
|
+
scripts: {
|
|
77
|
+
prebuild: "run-s clean",
|
|
78
|
+
build: "tsup",
|
|
79
|
+
clean: "rm -rf dist",
|
|
80
|
+
typecheck: "tsc --noEmit",
|
|
81
|
+
lint: "eslint src --ext .ts",
|
|
82
|
+
"lint:fix": "eslint src --ext .ts --fix",
|
|
83
|
+
test: "jest",
|
|
84
|
+
"test:watch": "jest --watch",
|
|
85
|
+
"test:coverage": "jest --coverage",
|
|
86
|
+
dev: "tsup --watch",
|
|
87
|
+
"version:check": `echo "Current version: $(npm pkg get version | tr -d '"')"`,
|
|
88
|
+
"version:next": "npm version patch --no-git-tag-version",
|
|
89
|
+
"version:bump:patch": "npm version patch",
|
|
90
|
+
"version:bump:minor": "npm version minor",
|
|
91
|
+
"version:bump:major": "npm version major",
|
|
92
|
+
"publish:dry": "npm publish --dry-run",
|
|
93
|
+
"publish:latest": "npm publish",
|
|
94
|
+
"publish:beta": "npm publish --tag beta",
|
|
95
|
+
"prepare-release": "npm run build && npm run test && npm run version:bump:patch",
|
|
96
|
+
postpublish: 'echo "\u{1F389} Published $(npm pkg get name)@$(npm pkg get version) to npm!"',
|
|
97
|
+
prepublishOnly: "npm run build"
|
|
98
|
+
},
|
|
99
|
+
repository: {
|
|
100
|
+
type: "git",
|
|
101
|
+
url: "https://github.com/codecollab-co/infra-cost.git"
|
|
102
|
+
},
|
|
103
|
+
bugs: {
|
|
104
|
+
url: "https://github.com/codecollab-co/infra-cost/issues"
|
|
105
|
+
},
|
|
106
|
+
homepage: "https://github.com/codecollab-co/infra-cost#readme",
|
|
107
|
+
license: "MIT",
|
|
108
|
+
engines: {
|
|
109
|
+
node: ">=20.0.0",
|
|
110
|
+
npm: ">=10.0.0"
|
|
111
|
+
},
|
|
112
|
+
dependencies: {
|
|
113
|
+
"@alicloud/bssopenapi20171214": "^2.0.1",
|
|
114
|
+
"@alicloud/cs20151215": "^4.0.1",
|
|
115
|
+
"@alicloud/ecs20140526": "^4.0.3",
|
|
116
|
+
"@alicloud/oss20190517": "^1.0.6",
|
|
117
|
+
"@alicloud/rds20140815": "^3.0.2",
|
|
118
|
+
"@alicloud/tea-util": "^1.4.7",
|
|
119
|
+
"@aws-sdk/client-budgets": "^3.975.0",
|
|
120
|
+
"@aws-sdk/client-cost-explorer": "^3.975.0",
|
|
121
|
+
"@aws-sdk/client-ec2": "^3.975.0",
|
|
122
|
+
"@aws-sdk/client-elastic-load-balancing-v2": "^3.975.0",
|
|
123
|
+
"@aws-sdk/client-iam": "^3.975.0",
|
|
124
|
+
"@aws-sdk/client-lambda": "^3.975.0",
|
|
125
|
+
"@aws-sdk/client-rds": "^3.975.0",
|
|
126
|
+
"@aws-sdk/client-s3": "^3.975.0",
|
|
127
|
+
"@aws-sdk/client-sts": "^3.975.0",
|
|
128
|
+
"@aws-sdk/credential-providers": "^3.975.0",
|
|
129
|
+
"@azure/arm-compute": "^23.3.0",
|
|
130
|
+
"@azure/arm-consumption": "^9.2.1",
|
|
131
|
+
"@azure/arm-containerservice": "^24.1.0",
|
|
132
|
+
"@azure/arm-costmanagement": "^1.0.0-beta.2",
|
|
133
|
+
"@azure/arm-network": "^35.0.0",
|
|
134
|
+
"@azure/arm-sql": "^10.0.0",
|
|
135
|
+
"@azure/arm-storage": "^19.1.0",
|
|
136
|
+
"@azure/arm-subscriptions": "^6.0.0",
|
|
137
|
+
"@azure/identity": "^4.13.0",
|
|
138
|
+
"@google-cloud/bigquery": "^8.1.1",
|
|
139
|
+
"@google-cloud/billing": "^5.1.1",
|
|
140
|
+
"@google-cloud/compute": "^6.7.0",
|
|
141
|
+
"@google-cloud/container": "^6.6.0",
|
|
142
|
+
"@google-cloud/monitoring": "^5.3.1",
|
|
143
|
+
"@google-cloud/resource-manager": "^6.2.1",
|
|
144
|
+
"@google-cloud/sql": "^0.24.0",
|
|
145
|
+
"@google-cloud/storage": "^7.18.0",
|
|
146
|
+
"@slack/web-api": "^7.5.0",
|
|
147
|
+
callsites: "^3.1.0",
|
|
148
|
+
chalk: "^4.1.2",
|
|
149
|
+
"cli-progress": "^3.12.0",
|
|
150
|
+
"cli-table3": "^0.6.5",
|
|
151
|
+
commander: "^12.1.0",
|
|
152
|
+
cors: "^2.8.6",
|
|
153
|
+
dayjs: "^1.11.19",
|
|
154
|
+
exceljs: "^4.4.0",
|
|
155
|
+
express: "^5.2.1",
|
|
156
|
+
"express-rate-limit": "^8.2.1",
|
|
157
|
+
"fd-slicer": "^1.1.0",
|
|
158
|
+
"google-auth-library": "^10.5.0",
|
|
159
|
+
googleapis: "^171.0.0",
|
|
160
|
+
helmet: "^8.1.0",
|
|
161
|
+
ini: "^6.0.0",
|
|
162
|
+
ink: "^6.6.0",
|
|
163
|
+
moment: "^2.30.1",
|
|
164
|
+
"node-fetch": "^2.7.0",
|
|
165
|
+
"oci-budget": "^2.88.0",
|
|
166
|
+
"oci-common": "^2.88.0",
|
|
167
|
+
"oci-containerengine": "^2.88.0",
|
|
168
|
+
"oci-core": "^2.88.0",
|
|
169
|
+
"oci-database": "^2.88.0",
|
|
170
|
+
"oci-identity": "^2.88.0",
|
|
171
|
+
"oci-objectstorage": "^2.88.0",
|
|
172
|
+
"oci-usageapi": "^2.88.0",
|
|
173
|
+
ora: "^9.1.0",
|
|
174
|
+
pako: "^2.1.0",
|
|
175
|
+
pend: "^1.2.0",
|
|
176
|
+
react: "^19.2.4",
|
|
177
|
+
"swagger-jsdoc": "^6.2.8",
|
|
178
|
+
"swagger-ui-express": "^5.0.1",
|
|
179
|
+
yauzl: "^3.0.0",
|
|
180
|
+
zod: "^3.23.8"
|
|
181
|
+
},
|
|
182
|
+
devDependencies: {
|
|
183
|
+
"@types/cors": "^2.8.19",
|
|
184
|
+
"@types/express": "^5.0.6",
|
|
185
|
+
"@types/jest": "^29.5.12",
|
|
186
|
+
"@types/node": "^22.5.4",
|
|
187
|
+
"@types/yauzl": "^2.10.3",
|
|
188
|
+
"@typescript-eslint/eslint-plugin": "^8.5.0",
|
|
189
|
+
"@typescript-eslint/parser": "^8.5.0",
|
|
190
|
+
eslint: "^8.57.0",
|
|
191
|
+
jest: "^29.7.0",
|
|
192
|
+
"npm-run-all": "^4.1.5",
|
|
193
|
+
"ts-jest": "^29.2.5",
|
|
194
|
+
tsup: "^6.7.0",
|
|
195
|
+
typescript: "^5.6.2"
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
|
|
33
201
|
// src/core/logging/structured-logger.ts
|
|
34
202
|
function initializeLogger(config) {
|
|
35
203
|
globalLogger = new StructuredLogger(config);
|
|
@@ -565,6 +733,209 @@ var init_schema = __esm({
|
|
|
565
733
|
}
|
|
566
734
|
});
|
|
567
735
|
|
|
736
|
+
// src/core/config/loader.ts
|
|
737
|
+
function discoverConfigFile() {
|
|
738
|
+
for (const configPath of CONFIG_PATHS) {
|
|
739
|
+
if ((0, import_fs.existsSync)(configPath)) {
|
|
740
|
+
return configPath;
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
return null;
|
|
744
|
+
}
|
|
745
|
+
function loadConfigFile(configPath) {
|
|
746
|
+
try {
|
|
747
|
+
const content = (0, import_fs.readFileSync)(configPath, "utf8");
|
|
748
|
+
const config = JSON.parse(content);
|
|
749
|
+
if (config.profiles && config.defaults?.profile) {
|
|
750
|
+
const activeProfile = config.profiles[config.defaults.profile];
|
|
751
|
+
if (activeProfile) {
|
|
752
|
+
return mergeConfigs(config.defaults, activeProfile);
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
return config.defaults || config;
|
|
756
|
+
} catch (error) {
|
|
757
|
+
console.warn(`Warning: Could not load config from ${configPath}: ${error.message}`);
|
|
758
|
+
return {};
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
function resolveEnvVars(config) {
|
|
762
|
+
const resolved = JSON.parse(JSON.stringify(config));
|
|
763
|
+
if (process.env.AWS_ACCESS_KEY_ID) {
|
|
764
|
+
resolved.accessKey = process.env.AWS_ACCESS_KEY_ID;
|
|
765
|
+
}
|
|
766
|
+
if (process.env.AWS_SECRET_ACCESS_KEY) {
|
|
767
|
+
resolved.secretKey = process.env.AWS_SECRET_ACCESS_KEY;
|
|
768
|
+
}
|
|
769
|
+
if (process.env.AWS_SESSION_TOKEN) {
|
|
770
|
+
resolved.sessionToken = process.env.AWS_SESSION_TOKEN;
|
|
771
|
+
}
|
|
772
|
+
if (process.env.AWS_REGION) {
|
|
773
|
+
resolved.region = process.env.AWS_REGION;
|
|
774
|
+
}
|
|
775
|
+
if (process.env.AWS_PROFILE) {
|
|
776
|
+
resolved.profile = process.env.AWS_PROFILE;
|
|
777
|
+
}
|
|
778
|
+
if (process.env.SLACK_TOKEN || process.env.SLACK_CHANNEL) {
|
|
779
|
+
resolved.slack = {
|
|
780
|
+
...resolved.slack,
|
|
781
|
+
token: process.env.SLACK_TOKEN ?? resolved.slack?.token,
|
|
782
|
+
channel: process.env.SLACK_CHANNEL ?? resolved.slack?.channel,
|
|
783
|
+
enabled: true
|
|
784
|
+
};
|
|
785
|
+
}
|
|
786
|
+
return resolved;
|
|
787
|
+
}
|
|
788
|
+
function mergeConfigs(...configs) {
|
|
789
|
+
const result = {};
|
|
790
|
+
for (const config of configs) {
|
|
791
|
+
for (const [key, value] of Object.entries(config)) {
|
|
792
|
+
if (value === void 0 || value === null) {
|
|
793
|
+
continue;
|
|
794
|
+
}
|
|
795
|
+
if (typeof value === "object" && !Array.isArray(value)) {
|
|
796
|
+
result[key] = { ...result[key], ...value };
|
|
797
|
+
} else {
|
|
798
|
+
result[key] = value;
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
return result;
|
|
803
|
+
}
|
|
804
|
+
function autoLoadConfig(cliOptions = {}) {
|
|
805
|
+
let config = { ...DEFAULT_CONFIG2 };
|
|
806
|
+
const configPath = cliOptions.configFile || discoverConfigFile();
|
|
807
|
+
if (configPath) {
|
|
808
|
+
const fileConfig = loadConfigFile(configPath);
|
|
809
|
+
config = mergeConfigs(config, fileConfig);
|
|
810
|
+
}
|
|
811
|
+
config = mergeConfigs(config, resolveEnvVars(config));
|
|
812
|
+
const cliConfig = mapCliOptionsToConfig(cliOptions);
|
|
813
|
+
config = mergeConfigs(config, cliConfig);
|
|
814
|
+
return config;
|
|
815
|
+
}
|
|
816
|
+
function mapCliOptionsToConfig(options) {
|
|
817
|
+
const config = {};
|
|
818
|
+
if (options.provider)
|
|
819
|
+
config.provider = options.provider;
|
|
820
|
+
if (options.profile)
|
|
821
|
+
config.profile = options.profile;
|
|
822
|
+
if (options.region)
|
|
823
|
+
config.region = options.region;
|
|
824
|
+
if (options.accessKey)
|
|
825
|
+
config.accessKey = options.accessKey;
|
|
826
|
+
if (options.secretKey)
|
|
827
|
+
config.secretKey = options.secretKey;
|
|
828
|
+
if (options.sessionToken)
|
|
829
|
+
config.sessionToken = options.sessionToken;
|
|
830
|
+
if (options.json || options.text || options.summary) {
|
|
831
|
+
config.output = config.output || {};
|
|
832
|
+
if (options.json)
|
|
833
|
+
config.output.format = "json";
|
|
834
|
+
if (options.text)
|
|
835
|
+
config.output.format = "text";
|
|
836
|
+
if (options.summary)
|
|
837
|
+
config.output.summary = true;
|
|
838
|
+
}
|
|
839
|
+
if (options.delta !== void 0) {
|
|
840
|
+
config.output = config.output || {};
|
|
841
|
+
config.output.showDelta = options.delta;
|
|
842
|
+
}
|
|
843
|
+
if (options.deltaThreshold) {
|
|
844
|
+
config.output = config.output || {};
|
|
845
|
+
config.output.deltaThreshold = parseFloat(options.deltaThreshold);
|
|
846
|
+
}
|
|
847
|
+
if (options.quickWins !== void 0) {
|
|
848
|
+
config.output = config.output || {};
|
|
849
|
+
config.output.showQuickWins = options.quickWins;
|
|
850
|
+
}
|
|
851
|
+
if (options.quickWinsCount) {
|
|
852
|
+
config.output = config.output || {};
|
|
853
|
+
config.output.quickWinsCount = parseInt(options.quickWinsCount, 10);
|
|
854
|
+
}
|
|
855
|
+
if (options.cache !== void 0 || options.noCache !== void 0) {
|
|
856
|
+
config.cache = config.cache || {};
|
|
857
|
+
config.cache.enabled = options.cache === true || options.noCache !== true;
|
|
858
|
+
}
|
|
859
|
+
if (options.cacheTtl) {
|
|
860
|
+
config.cache = config.cache || {};
|
|
861
|
+
config.cache.ttl = options.cacheTtl;
|
|
862
|
+
}
|
|
863
|
+
if (options.cacheType) {
|
|
864
|
+
config.cache = config.cache || {};
|
|
865
|
+
config.cache.type = options.cacheType;
|
|
866
|
+
}
|
|
867
|
+
if (options.slackToken || options.slackChannel) {
|
|
868
|
+
config.slack = config.slack || {};
|
|
869
|
+
if (options.slackToken)
|
|
870
|
+
config.slack.token = options.slackToken;
|
|
871
|
+
if (options.slackChannel)
|
|
872
|
+
config.slack.channel = options.slackChannel;
|
|
873
|
+
config.slack.enabled = true;
|
|
874
|
+
}
|
|
875
|
+
if (options.logLevel || options.verbose || options.quiet) {
|
|
876
|
+
config.logging = config.logging || {};
|
|
877
|
+
if (options.logLevel)
|
|
878
|
+
config.logging.level = options.logLevel;
|
|
879
|
+
if (options.verbose)
|
|
880
|
+
config.logging.level = "debug";
|
|
881
|
+
if (options.quiet)
|
|
882
|
+
config.logging.level = "error";
|
|
883
|
+
}
|
|
884
|
+
return config;
|
|
885
|
+
}
|
|
886
|
+
var import_fs, import_path, import_os, CONFIG_PATHS, DEFAULT_CONFIG2;
|
|
887
|
+
var init_loader = __esm({
|
|
888
|
+
"src/core/config/loader.ts"() {
|
|
889
|
+
import_fs = require("fs");
|
|
890
|
+
import_path = require("path");
|
|
891
|
+
import_os = require("os");
|
|
892
|
+
CONFIG_PATHS = [
|
|
893
|
+
(0, import_path.join)(process.cwd(), "infra-cost.config.json"),
|
|
894
|
+
// Project-specific
|
|
895
|
+
(0, import_path.join)(process.cwd(), ".infra-cost.config.json"),
|
|
896
|
+
// Project-specific (hidden)
|
|
897
|
+
(0, import_path.join)(process.cwd(), ".infra-cost", "config.json"),
|
|
898
|
+
// Project directory
|
|
899
|
+
(0, import_path.join)((0, import_os.homedir)(), ".infra-cost", "config.json"),
|
|
900
|
+
// User global
|
|
901
|
+
(0, import_path.join)((0, import_os.homedir)(), ".config", "infra-cost", "config.json")
|
|
902
|
+
// XDG standard
|
|
903
|
+
];
|
|
904
|
+
DEFAULT_CONFIG2 = {
|
|
905
|
+
provider: "aws",
|
|
906
|
+
profile: "default",
|
|
907
|
+
region: "us-east-1",
|
|
908
|
+
output: {
|
|
909
|
+
format: "fancy",
|
|
910
|
+
summary: false,
|
|
911
|
+
showDelta: true,
|
|
912
|
+
// NEW: Show delta by default
|
|
913
|
+
showQuickWins: true,
|
|
914
|
+
// NEW: Show quick wins by default
|
|
915
|
+
deltaThreshold: 10,
|
|
916
|
+
quickWinsCount: 3
|
|
917
|
+
},
|
|
918
|
+
cache: {
|
|
919
|
+
enabled: true,
|
|
920
|
+
// NEW: Cache enabled by default
|
|
921
|
+
ttl: "4h",
|
|
922
|
+
type: "file"
|
|
923
|
+
},
|
|
924
|
+
logging: {
|
|
925
|
+
level: "info",
|
|
926
|
+
format: "pretty",
|
|
927
|
+
auditEnabled: false
|
|
928
|
+
}
|
|
929
|
+
};
|
|
930
|
+
__name(discoverConfigFile, "discoverConfigFile");
|
|
931
|
+
__name(loadConfigFile, "loadConfigFile");
|
|
932
|
+
__name(resolveEnvVars, "resolveEnvVars");
|
|
933
|
+
__name(mergeConfigs, "mergeConfigs");
|
|
934
|
+
__name(autoLoadConfig, "autoLoadConfig");
|
|
935
|
+
__name(mapCliOptionsToConfig, "mapCliOptionsToConfig");
|
|
936
|
+
}
|
|
937
|
+
});
|
|
938
|
+
|
|
568
939
|
// src/types/providers.ts
|
|
569
940
|
var CloudProviderAdapter, ResourceType;
|
|
570
941
|
var init_providers = __esm({
|
|
@@ -10208,372 +10579,675 @@ var init_multicloud = __esm({
|
|
|
10208
10579
|
}
|
|
10209
10580
|
});
|
|
10210
10581
|
|
|
10211
|
-
// src/
|
|
10212
|
-
|
|
10213
|
-
|
|
10214
|
-
|
|
10215
|
-
|
|
10582
|
+
// src/api/utils.ts
|
|
10583
|
+
async function getProviderFromConfig() {
|
|
10584
|
+
const config = autoLoadConfig();
|
|
10585
|
+
const factory = new CloudProviderFactory();
|
|
10586
|
+
return factory.createProvider(config);
|
|
10587
|
+
}
|
|
10588
|
+
function getConfig2() {
|
|
10589
|
+
return autoLoadConfig();
|
|
10590
|
+
}
|
|
10591
|
+
var init_utils = __esm({
|
|
10592
|
+
"src/api/utils.ts"() {
|
|
10593
|
+
init_factory();
|
|
10594
|
+
init_loader();
|
|
10595
|
+
__name(getProviderFromConfig, "getProviderFromConfig");
|
|
10596
|
+
__name(getConfig2, "getConfig");
|
|
10597
|
+
}
|
|
10216
10598
|
});
|
|
10217
|
-
module.exports = __toCommonJS(cli_exports);
|
|
10218
|
-
var import_commander = require("commander");
|
|
10219
10599
|
|
|
10220
|
-
//
|
|
10221
|
-
var
|
|
10222
|
-
|
|
10223
|
-
|
|
10224
|
-
|
|
10225
|
-
|
|
10226
|
-
|
|
10227
|
-
|
|
10228
|
-
"
|
|
10229
|
-
|
|
10230
|
-
|
|
10231
|
-
|
|
10232
|
-
"
|
|
10233
|
-
|
|
10234
|
-
|
|
10235
|
-
|
|
10236
|
-
|
|
10237
|
-
|
|
10238
|
-
|
|
10239
|
-
|
|
10240
|
-
|
|
10241
|
-
|
|
10242
|
-
|
|
10243
|
-
|
|
10244
|
-
|
|
10245
|
-
|
|
10246
|
-
|
|
10247
|
-
|
|
10248
|
-
|
|
10249
|
-
|
|
10250
|
-
|
|
10251
|
-
|
|
10252
|
-
|
|
10253
|
-
|
|
10254
|
-
|
|
10255
|
-
|
|
10256
|
-
|
|
10257
|
-
|
|
10258
|
-
|
|
10259
|
-
|
|
10260
|
-
|
|
10261
|
-
|
|
10262
|
-
|
|
10263
|
-
|
|
10264
|
-
|
|
10265
|
-
|
|
10266
|
-
|
|
10267
|
-
|
|
10268
|
-
|
|
10269
|
-
|
|
10270
|
-
|
|
10271
|
-
|
|
10272
|
-
|
|
10273
|
-
|
|
10274
|
-
|
|
10275
|
-
|
|
10276
|
-
|
|
10277
|
-
|
|
10278
|
-
|
|
10279
|
-
|
|
10280
|
-
|
|
10281
|
-
|
|
10282
|
-
|
|
10283
|
-
|
|
10284
|
-
|
|
10285
|
-
|
|
10286
|
-
|
|
10287
|
-
|
|
10288
|
-
|
|
10289
|
-
|
|
10290
|
-
|
|
10291
|
-
|
|
10292
|
-
|
|
10293
|
-
|
|
10294
|
-
|
|
10295
|
-
|
|
10296
|
-
|
|
10297
|
-
|
|
10298
|
-
|
|
10299
|
-
|
|
10300
|
-
|
|
10301
|
-
|
|
10302
|
-
|
|
10303
|
-
|
|
10304
|
-
|
|
10305
|
-
|
|
10306
|
-
"
|
|
10307
|
-
|
|
10308
|
-
|
|
10309
|
-
|
|
10310
|
-
|
|
10311
|
-
|
|
10312
|
-
|
|
10313
|
-
|
|
10314
|
-
|
|
10315
|
-
|
|
10316
|
-
|
|
10317
|
-
|
|
10318
|
-
|
|
10319
|
-
|
|
10320
|
-
|
|
10321
|
-
|
|
10322
|
-
|
|
10323
|
-
|
|
10324
|
-
|
|
10325
|
-
|
|
10326
|
-
|
|
10327
|
-
|
|
10328
|
-
|
|
10329
|
-
|
|
10330
|
-
|
|
10331
|
-
|
|
10332
|
-
|
|
10333
|
-
|
|
10334
|
-
|
|
10335
|
-
|
|
10336
|
-
|
|
10337
|
-
|
|
10338
|
-
|
|
10339
|
-
|
|
10340
|
-
|
|
10341
|
-
|
|
10342
|
-
|
|
10343
|
-
|
|
10344
|
-
|
|
10345
|
-
|
|
10346
|
-
|
|
10347
|
-
|
|
10348
|
-
|
|
10349
|
-
|
|
10350
|
-
|
|
10351
|
-
|
|
10352
|
-
|
|
10353
|
-
|
|
10354
|
-
|
|
10355
|
-
|
|
10356
|
-
|
|
10357
|
-
|
|
10358
|
-
|
|
10359
|
-
|
|
10360
|
-
|
|
10361
|
-
|
|
10362
|
-
|
|
10363
|
-
|
|
10364
|
-
|
|
10365
|
-
|
|
10366
|
-
jest: "^29.7.0",
|
|
10367
|
-
"npm-run-all": "^4.1.5",
|
|
10368
|
-
"ts-jest": "^29.2.5",
|
|
10369
|
-
tsup: "^6.7.0",
|
|
10370
|
-
typescript: "^5.6.2"
|
|
10600
|
+
// src/api/routes/costs.ts
|
|
10601
|
+
var costs_exports2 = {};
|
|
10602
|
+
__export(costs_exports2, {
|
|
10603
|
+
default: () => costs_default
|
|
10604
|
+
});
|
|
10605
|
+
var import_express, router, costs_default;
|
|
10606
|
+
var init_costs2 = __esm({
|
|
10607
|
+
"src/api/routes/costs.ts"() {
|
|
10608
|
+
import_express = require("express");
|
|
10609
|
+
init_utils();
|
|
10610
|
+
init_server();
|
|
10611
|
+
router = (0, import_express.Router)();
|
|
10612
|
+
router.get("/", async (req, res) => {
|
|
10613
|
+
try {
|
|
10614
|
+
const config = getConfig2();
|
|
10615
|
+
const provider = await getProviderFromConfig();
|
|
10616
|
+
const today = /* @__PURE__ */ new Date();
|
|
10617
|
+
const todayStart = new Date(today.setHours(0, 0, 0, 0));
|
|
10618
|
+
const todayEnd = new Date(today.setHours(23, 59, 59, 999));
|
|
10619
|
+
const monthStart = new Date(today.getFullYear(), today.getMonth(), 1);
|
|
10620
|
+
const monthEnd = new Date(today.getFullYear(), today.getMonth() + 1, 0);
|
|
10621
|
+
const [todayCosts, mtdCosts] = await Promise.all([
|
|
10622
|
+
provider.getCostBreakdown(todayStart, todayEnd, "SERVICE"),
|
|
10623
|
+
provider.getCostBreakdown(monthStart, monthEnd, "SERVICE")
|
|
10624
|
+
]);
|
|
10625
|
+
const serviceBreakdown = {};
|
|
10626
|
+
todayCosts.breakdown.forEach((item) => {
|
|
10627
|
+
serviceBreakdown[item.service] = item.cost;
|
|
10628
|
+
});
|
|
10629
|
+
const accountInfo = await provider.getAccountInfo();
|
|
10630
|
+
const response = {
|
|
10631
|
+
account: {
|
|
10632
|
+
id: accountInfo.accountId,
|
|
10633
|
+
name: accountInfo.accountAlias || accountInfo.accountId,
|
|
10634
|
+
provider: config.provider
|
|
10635
|
+
},
|
|
10636
|
+
costs: {
|
|
10637
|
+
today: {
|
|
10638
|
+
total: todayCosts.totalCost,
|
|
10639
|
+
currency: "USD",
|
|
10640
|
+
byService: serviceBreakdown
|
|
10641
|
+
},
|
|
10642
|
+
mtd: {
|
|
10643
|
+
total: mtdCosts.totalCost,
|
|
10644
|
+
projected: mtdCosts.totalCost * (30 / today.getDate())
|
|
10645
|
+
},
|
|
10646
|
+
delta: {
|
|
10647
|
+
vsYesterday: 0,
|
|
10648
|
+
// Would need historical data
|
|
10649
|
+
vsLastWeek: 0
|
|
10650
|
+
// Would need historical data
|
|
10651
|
+
}
|
|
10652
|
+
},
|
|
10653
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
10654
|
+
};
|
|
10655
|
+
res.json(createApiResponse(response));
|
|
10656
|
+
} catch (error) {
|
|
10657
|
+
res.status(500).json(createErrorResponse("FETCH_ERROR", error.message));
|
|
10658
|
+
}
|
|
10659
|
+
});
|
|
10660
|
+
router.get("/services", async (req, res) => {
|
|
10661
|
+
try {
|
|
10662
|
+
const provider = await getProviderFromConfig();
|
|
10663
|
+
const { startDate, endDate } = req.query;
|
|
10664
|
+
const start = startDate ? new Date(startDate) : /* @__PURE__ */ new Date();
|
|
10665
|
+
const end = endDate ? new Date(endDate) : /* @__PURE__ */ new Date();
|
|
10666
|
+
const breakdown = await provider.getCostBreakdown(start, end, "SERVICE");
|
|
10667
|
+
const services = breakdown.breakdown.map((item) => ({
|
|
10668
|
+
service: item.service,
|
|
10669
|
+
cost: item.cost,
|
|
10670
|
+
percentage: item.cost / breakdown.totalCost * 100
|
|
10671
|
+
}));
|
|
10672
|
+
res.json(
|
|
10673
|
+
createApiResponse({
|
|
10674
|
+
total: breakdown.totalCost,
|
|
10675
|
+
services,
|
|
10676
|
+
period: {
|
|
10677
|
+
start: start.toISOString(),
|
|
10678
|
+
end: end.toISOString()
|
|
10679
|
+
}
|
|
10680
|
+
})
|
|
10681
|
+
);
|
|
10682
|
+
} catch (error) {
|
|
10683
|
+
res.status(500).json(createErrorResponse("FETCH_ERROR", error.message));
|
|
10684
|
+
}
|
|
10685
|
+
});
|
|
10686
|
+
router.get("/daily", async (req, res) => {
|
|
10687
|
+
try {
|
|
10688
|
+
const provider = await getProviderFromConfig();
|
|
10689
|
+
const { days = 30 } = req.query;
|
|
10690
|
+
const daysNum = parseInt(days, 10);
|
|
10691
|
+
const end = /* @__PURE__ */ new Date();
|
|
10692
|
+
const start = /* @__PURE__ */ new Date();
|
|
10693
|
+
start.setDate(start.getDate() - daysNum);
|
|
10694
|
+
const breakdown = await provider.getCostBreakdown(start, end, "DAILY");
|
|
10695
|
+
const dailyCosts = breakdown.breakdown.map((item) => ({
|
|
10696
|
+
date: item.date,
|
|
10697
|
+
cost: item.cost
|
|
10698
|
+
}));
|
|
10699
|
+
res.json(
|
|
10700
|
+
createApiResponse({
|
|
10701
|
+
total: breakdown.totalCost,
|
|
10702
|
+
daily: dailyCosts,
|
|
10703
|
+
period: {
|
|
10704
|
+
start: start.toISOString(),
|
|
10705
|
+
end: end.toISOString(),
|
|
10706
|
+
days: daysNum
|
|
10707
|
+
}
|
|
10708
|
+
})
|
|
10709
|
+
);
|
|
10710
|
+
} catch (error) {
|
|
10711
|
+
res.status(500).json(createErrorResponse("FETCH_ERROR", error.message));
|
|
10712
|
+
}
|
|
10713
|
+
});
|
|
10714
|
+
router.get("/trends", async (req, res) => {
|
|
10715
|
+
try {
|
|
10716
|
+
const provider = await getProviderFromConfig();
|
|
10717
|
+
const end = /* @__PURE__ */ new Date();
|
|
10718
|
+
const start = /* @__PURE__ */ new Date();
|
|
10719
|
+
start.setMonth(start.getMonth() - 3);
|
|
10720
|
+
const breakdown = await provider.getCostBreakdown(start, end, "MONTHLY");
|
|
10721
|
+
const trends = breakdown.breakdown.map((item, index, arr) => {
|
|
10722
|
+
const previousMonth = index > 0 ? arr[index - 1].cost : null;
|
|
10723
|
+
const changePercent = previousMonth ? (item.cost - previousMonth) / previousMonth * 100 : 0;
|
|
10724
|
+
return {
|
|
10725
|
+
month: item.date,
|
|
10726
|
+
cost: item.cost,
|
|
10727
|
+
change: item.cost - (previousMonth || 0),
|
|
10728
|
+
changePercent
|
|
10729
|
+
};
|
|
10730
|
+
});
|
|
10731
|
+
res.json(
|
|
10732
|
+
createApiResponse({
|
|
10733
|
+
trends,
|
|
10734
|
+
average: breakdown.totalCost / breakdown.breakdown.length,
|
|
10735
|
+
period: {
|
|
10736
|
+
start: start.toISOString(),
|
|
10737
|
+
end: end.toISOString()
|
|
10738
|
+
}
|
|
10739
|
+
})
|
|
10740
|
+
);
|
|
10741
|
+
} catch (error) {
|
|
10742
|
+
res.status(500).json(createErrorResponse("FETCH_ERROR", error.message));
|
|
10743
|
+
}
|
|
10744
|
+
});
|
|
10745
|
+
costs_default = router;
|
|
10371
10746
|
}
|
|
10372
|
-
};
|
|
10373
|
-
|
|
10374
|
-
// src/cli/index.ts
|
|
10375
|
-
init_logging();
|
|
10376
|
-
|
|
10377
|
-
// src/core/config/index.ts
|
|
10378
|
-
init_schema();
|
|
10747
|
+
});
|
|
10379
10748
|
|
|
10380
|
-
// src/
|
|
10381
|
-
var
|
|
10382
|
-
|
|
10383
|
-
|
|
10384
|
-
|
|
10385
|
-
|
|
10386
|
-
|
|
10387
|
-
|
|
10388
|
-
|
|
10389
|
-
|
|
10390
|
-
|
|
10391
|
-
|
|
10392
|
-
|
|
10393
|
-
|
|
10394
|
-
|
|
10395
|
-
|
|
10396
|
-
|
|
10397
|
-
|
|
10398
|
-
|
|
10399
|
-
|
|
10400
|
-
|
|
10401
|
-
|
|
10402
|
-
|
|
10403
|
-
|
|
10404
|
-
|
|
10405
|
-
|
|
10406
|
-
|
|
10407
|
-
|
|
10408
|
-
|
|
10409
|
-
|
|
10410
|
-
|
|
10411
|
-
|
|
10412
|
-
|
|
10413
|
-
|
|
10414
|
-
|
|
10415
|
-
|
|
10416
|
-
|
|
10417
|
-
|
|
10418
|
-
|
|
10419
|
-
auditEnabled: false
|
|
10749
|
+
// src/api/routes/inventory.ts
|
|
10750
|
+
var inventory_exports2 = {};
|
|
10751
|
+
__export(inventory_exports2, {
|
|
10752
|
+
default: () => inventory_default
|
|
10753
|
+
});
|
|
10754
|
+
var import_express2, router2, inventory_default;
|
|
10755
|
+
var init_inventory6 = __esm({
|
|
10756
|
+
"src/api/routes/inventory.ts"() {
|
|
10757
|
+
import_express2 = require("express");
|
|
10758
|
+
init_utils();
|
|
10759
|
+
init_server();
|
|
10760
|
+
router2 = (0, import_express2.Router)();
|
|
10761
|
+
router2.get("/", async (req, res) => {
|
|
10762
|
+
try {
|
|
10763
|
+
const provider = await getProviderFromConfig();
|
|
10764
|
+
const inventory = await provider.getResourceInventory();
|
|
10765
|
+
const summary = {
|
|
10766
|
+
totalResources: inventory.resources.length,
|
|
10767
|
+
byType: inventory.resources.reduce((acc, resource) => {
|
|
10768
|
+
acc[resource.type] = (acc[resource.type] || 0) + 1;
|
|
10769
|
+
return acc;
|
|
10770
|
+
}, {}),
|
|
10771
|
+
byRegion: inventory.resources.reduce((acc, resource) => {
|
|
10772
|
+
const region = resource.region || "global";
|
|
10773
|
+
acc[region] = (acc[region] || 0) + 1;
|
|
10774
|
+
return acc;
|
|
10775
|
+
}, {})
|
|
10776
|
+
};
|
|
10777
|
+
res.json(
|
|
10778
|
+
createApiResponse({
|
|
10779
|
+
summary,
|
|
10780
|
+
resources: inventory.resources
|
|
10781
|
+
})
|
|
10782
|
+
);
|
|
10783
|
+
} catch (error) {
|
|
10784
|
+
res.status(500).json(createErrorResponse("FETCH_ERROR", error.message));
|
|
10785
|
+
}
|
|
10786
|
+
});
|
|
10787
|
+
inventory_default = router2;
|
|
10420
10788
|
}
|
|
10421
|
-
};
|
|
10422
|
-
|
|
10423
|
-
|
|
10424
|
-
|
|
10425
|
-
|
|
10426
|
-
|
|
10427
|
-
|
|
10428
|
-
|
|
10429
|
-
|
|
10430
|
-
|
|
10431
|
-
|
|
10432
|
-
|
|
10433
|
-
|
|
10434
|
-
|
|
10435
|
-
|
|
10436
|
-
|
|
10437
|
-
|
|
10438
|
-
|
|
10789
|
+
});
|
|
10790
|
+
|
|
10791
|
+
// src/api/routes/optimization.ts
|
|
10792
|
+
var optimization_exports = {};
|
|
10793
|
+
__export(optimization_exports, {
|
|
10794
|
+
default: () => optimization_default
|
|
10795
|
+
});
|
|
10796
|
+
var import_express3, router3, optimization_default;
|
|
10797
|
+
var init_optimization = __esm({
|
|
10798
|
+
"src/api/routes/optimization.ts"() {
|
|
10799
|
+
import_express3 = require("express");
|
|
10800
|
+
init_utils();
|
|
10801
|
+
init_server();
|
|
10802
|
+
router3 = (0, import_express3.Router)();
|
|
10803
|
+
router3.get("/", async (req, res) => {
|
|
10804
|
+
try {
|
|
10805
|
+
const provider = await getProviderFromConfig();
|
|
10806
|
+
const recommendations = await provider.getOptimizationRecommendations();
|
|
10807
|
+
const summary = {
|
|
10808
|
+
totalSavings: recommendations.reduce((sum, rec) => sum + (rec.estimatedMonthlySavings || 0), 0),
|
|
10809
|
+
recommendationCount: recommendations.length,
|
|
10810
|
+
byCategory: recommendations.reduce((acc, rec) => {
|
|
10811
|
+
const category = rec.category || "other";
|
|
10812
|
+
acc[category] = (acc[category] || 0) + 1;
|
|
10813
|
+
return acc;
|
|
10814
|
+
}, {})
|
|
10815
|
+
};
|
|
10816
|
+
res.json(
|
|
10817
|
+
createApiResponse({
|
|
10818
|
+
summary,
|
|
10819
|
+
recommendations
|
|
10820
|
+
})
|
|
10821
|
+
);
|
|
10822
|
+
} catch (error) {
|
|
10823
|
+
res.status(500).json(createErrorResponse("FETCH_ERROR", error.message));
|
|
10439
10824
|
}
|
|
10440
|
-
}
|
|
10441
|
-
|
|
10442
|
-
} catch (error) {
|
|
10443
|
-
console.warn(`Warning: Could not load config from ${configPath}: ${error.message}`);
|
|
10444
|
-
return {};
|
|
10445
|
-
}
|
|
10446
|
-
}
|
|
10447
|
-
__name(loadConfigFile, "loadConfigFile");
|
|
10448
|
-
function resolveEnvVars(config) {
|
|
10449
|
-
const resolved = JSON.parse(JSON.stringify(config));
|
|
10450
|
-
if (process.env.AWS_ACCESS_KEY_ID) {
|
|
10451
|
-
resolved.accessKey = process.env.AWS_ACCESS_KEY_ID;
|
|
10452
|
-
}
|
|
10453
|
-
if (process.env.AWS_SECRET_ACCESS_KEY) {
|
|
10454
|
-
resolved.secretKey = process.env.AWS_SECRET_ACCESS_KEY;
|
|
10455
|
-
}
|
|
10456
|
-
if (process.env.AWS_SESSION_TOKEN) {
|
|
10457
|
-
resolved.sessionToken = process.env.AWS_SESSION_TOKEN;
|
|
10458
|
-
}
|
|
10459
|
-
if (process.env.AWS_REGION) {
|
|
10460
|
-
resolved.region = process.env.AWS_REGION;
|
|
10825
|
+
});
|
|
10826
|
+
optimization_default = router3;
|
|
10461
10827
|
}
|
|
10462
|
-
|
|
10463
|
-
|
|
10828
|
+
});
|
|
10829
|
+
|
|
10830
|
+
// src/api/routes/chargeback.ts
|
|
10831
|
+
var chargeback_exports = {};
|
|
10832
|
+
__export(chargeback_exports, {
|
|
10833
|
+
default: () => chargeback_default
|
|
10834
|
+
});
|
|
10835
|
+
var import_express4, router4, chargeback_default;
|
|
10836
|
+
var init_chargeback = __esm({
|
|
10837
|
+
"src/api/routes/chargeback.ts"() {
|
|
10838
|
+
import_express4 = require("express");
|
|
10839
|
+
init_utils();
|
|
10840
|
+
init_server();
|
|
10841
|
+
router4 = (0, import_express4.Router)();
|
|
10842
|
+
router4.get("/", async (req, res) => {
|
|
10843
|
+
try {
|
|
10844
|
+
const provider = await getProviderFromConfig();
|
|
10845
|
+
const { groupBy = "tag" } = req.query;
|
|
10846
|
+
const now = /* @__PURE__ */ new Date();
|
|
10847
|
+
const monthStart = new Date(now.getFullYear(), now.getMonth(), 1);
|
|
10848
|
+
const breakdown = await provider.getCostBreakdown(
|
|
10849
|
+
monthStart,
|
|
10850
|
+
now,
|
|
10851
|
+
groupBy === "tag" ? "TAG" : "SERVICE"
|
|
10852
|
+
);
|
|
10853
|
+
res.json(
|
|
10854
|
+
createApiResponse({
|
|
10855
|
+
total: breakdown.totalCost,
|
|
10856
|
+
breakdown: breakdown.breakdown,
|
|
10857
|
+
period: {
|
|
10858
|
+
start: monthStart.toISOString(),
|
|
10859
|
+
end: now.toISOString()
|
|
10860
|
+
},
|
|
10861
|
+
groupBy
|
|
10862
|
+
})
|
|
10863
|
+
);
|
|
10864
|
+
} catch (error) {
|
|
10865
|
+
res.status(500).json(createErrorResponse("FETCH_ERROR", error.message));
|
|
10866
|
+
}
|
|
10867
|
+
});
|
|
10868
|
+
chargeback_default = router4;
|
|
10464
10869
|
}
|
|
10465
|
-
|
|
10466
|
-
|
|
10467
|
-
|
|
10468
|
-
|
|
10469
|
-
|
|
10470
|
-
|
|
10471
|
-
|
|
10870
|
+
});
|
|
10871
|
+
|
|
10872
|
+
// src/api/routes/forecast.ts
|
|
10873
|
+
var forecast_exports = {};
|
|
10874
|
+
__export(forecast_exports, {
|
|
10875
|
+
default: () => forecast_default
|
|
10876
|
+
});
|
|
10877
|
+
var import_express5, router5, forecast_default;
|
|
10878
|
+
var init_forecast = __esm({
|
|
10879
|
+
"src/api/routes/forecast.ts"() {
|
|
10880
|
+
import_express5 = require("express");
|
|
10881
|
+
init_server();
|
|
10882
|
+
router5 = (0, import_express5.Router)();
|
|
10883
|
+
router5.get("/", async (req, res) => {
|
|
10884
|
+
try {
|
|
10885
|
+
const { days = 30 } = req.query;
|
|
10886
|
+
const daysNum = parseInt(days, 10);
|
|
10887
|
+
const currentDailyAverage = 150;
|
|
10888
|
+
const projectedDaily = currentDailyAverage * 1.05;
|
|
10889
|
+
const forecast = [];
|
|
10890
|
+
for (let i = 1; i <= daysNum; i++) {
|
|
10891
|
+
const date = /* @__PURE__ */ new Date();
|
|
10892
|
+
date.setDate(date.getDate() + i);
|
|
10893
|
+
forecast.push({
|
|
10894
|
+
date: date.toISOString().split("T")[0],
|
|
10895
|
+
projected: projectedDaily,
|
|
10896
|
+
confidence: Math.max(0.9 - i * 0.01, 0.5)
|
|
10897
|
+
// Decreasing confidence
|
|
10898
|
+
});
|
|
10899
|
+
}
|
|
10900
|
+
const totalProjected = projectedDaily * daysNum;
|
|
10901
|
+
res.json(
|
|
10902
|
+
createApiResponse({
|
|
10903
|
+
forecast,
|
|
10904
|
+
summary: {
|
|
10905
|
+
totalProjected,
|
|
10906
|
+
averageDaily: projectedDaily,
|
|
10907
|
+
period: {
|
|
10908
|
+
days: daysNum,
|
|
10909
|
+
start: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
|
|
10910
|
+
end: forecast[forecast.length - 1].date
|
|
10911
|
+
}
|
|
10912
|
+
},
|
|
10913
|
+
model: "linear-growth",
|
|
10914
|
+
confidence: 0.75
|
|
10915
|
+
})
|
|
10916
|
+
);
|
|
10917
|
+
} catch (error) {
|
|
10918
|
+
res.status(500).json(createErrorResponse("FORECAST_ERROR", error.message));
|
|
10919
|
+
}
|
|
10920
|
+
});
|
|
10921
|
+
forecast_default = router5;
|
|
10472
10922
|
}
|
|
10473
|
-
|
|
10474
|
-
|
|
10475
|
-
|
|
10476
|
-
|
|
10477
|
-
|
|
10478
|
-
|
|
10479
|
-
|
|
10480
|
-
|
|
10481
|
-
|
|
10923
|
+
});
|
|
10924
|
+
|
|
10925
|
+
// src/api/routes/accounts.ts
|
|
10926
|
+
var accounts_exports = {};
|
|
10927
|
+
__export(accounts_exports, {
|
|
10928
|
+
default: () => accounts_default
|
|
10929
|
+
});
|
|
10930
|
+
var import_express6, router6, accounts_default;
|
|
10931
|
+
var init_accounts = __esm({
|
|
10932
|
+
"src/api/routes/accounts.ts"() {
|
|
10933
|
+
import_express6 = require("express");
|
|
10934
|
+
init_utils();
|
|
10935
|
+
init_server();
|
|
10936
|
+
router6 = (0, import_express6.Router)();
|
|
10937
|
+
router6.get("/", async (req, res) => {
|
|
10938
|
+
try {
|
|
10939
|
+
const config = getConfig2();
|
|
10940
|
+
const provider = await getProviderFromConfig();
|
|
10941
|
+
const accountInfo = await provider.getAccountInfo();
|
|
10942
|
+
const accounts = [
|
|
10943
|
+
{
|
|
10944
|
+
id: accountInfo.accountId,
|
|
10945
|
+
name: accountInfo.accountAlias || accountInfo.accountId,
|
|
10946
|
+
provider: config.provider,
|
|
10947
|
+
region: accountInfo.region
|
|
10948
|
+
}
|
|
10949
|
+
];
|
|
10950
|
+
res.json(
|
|
10951
|
+
createApiResponse({
|
|
10952
|
+
accounts,
|
|
10953
|
+
count: accounts.length
|
|
10954
|
+
})
|
|
10955
|
+
);
|
|
10956
|
+
} catch (error) {
|
|
10957
|
+
res.status(500).json(createErrorResponse("FETCH_ERROR", error.message));
|
|
10482
10958
|
}
|
|
10483
|
-
|
|
10484
|
-
|
|
10485
|
-
|
|
10486
|
-
|
|
10959
|
+
});
|
|
10960
|
+
router6.get("/:id/costs", async (req, res) => {
|
|
10961
|
+
try {
|
|
10962
|
+
const config = getConfig2();
|
|
10963
|
+
const provider = await getProviderFromConfig();
|
|
10964
|
+
const { id } = req.params;
|
|
10965
|
+
const accountInfo = await provider.getAccountInfo();
|
|
10966
|
+
if (accountInfo.accountId !== id) {
|
|
10967
|
+
return res.status(404).json(createErrorResponse("NOT_FOUND", `Account ${id} not found`));
|
|
10968
|
+
}
|
|
10969
|
+
const now = /* @__PURE__ */ new Date();
|
|
10970
|
+
const monthStart = new Date(now.getFullYear(), now.getMonth(), 1);
|
|
10971
|
+
const costs = await provider.getCostBreakdown(monthStart, now, "SERVICE");
|
|
10972
|
+
res.json(
|
|
10973
|
+
createApiResponse({
|
|
10974
|
+
accountId: id,
|
|
10975
|
+
costs: {
|
|
10976
|
+
total: costs.totalCost,
|
|
10977
|
+
breakdown: costs.breakdown
|
|
10978
|
+
},
|
|
10979
|
+
period: {
|
|
10980
|
+
start: monthStart.toISOString(),
|
|
10981
|
+
end: now.toISOString()
|
|
10982
|
+
}
|
|
10983
|
+
})
|
|
10984
|
+
);
|
|
10985
|
+
} catch (error) {
|
|
10986
|
+
res.status(500).json(createErrorResponse("FETCH_ERROR", error.message));
|
|
10487
10987
|
}
|
|
10488
|
-
}
|
|
10988
|
+
});
|
|
10989
|
+
accounts_default = router6;
|
|
10489
10990
|
}
|
|
10490
|
-
|
|
10491
|
-
|
|
10492
|
-
|
|
10493
|
-
|
|
10494
|
-
|
|
10495
|
-
|
|
10496
|
-
|
|
10497
|
-
|
|
10498
|
-
|
|
10991
|
+
});
|
|
10992
|
+
|
|
10993
|
+
// src/api/routes/reports.ts
|
|
10994
|
+
var reports_exports2 = {};
|
|
10995
|
+
__export(reports_exports2, {
|
|
10996
|
+
default: () => reports_default
|
|
10997
|
+
});
|
|
10998
|
+
var import_express7, router7, reports_default;
|
|
10999
|
+
var init_reports2 = __esm({
|
|
11000
|
+
"src/api/routes/reports.ts"() {
|
|
11001
|
+
import_express7 = require("express");
|
|
11002
|
+
init_utils();
|
|
11003
|
+
init_server();
|
|
11004
|
+
router7 = (0, import_express7.Router)();
|
|
11005
|
+
router7.post("/generate", async (req, res) => {
|
|
11006
|
+
try {
|
|
11007
|
+
const {
|
|
11008
|
+
reportType = "summary",
|
|
11009
|
+
startDate,
|
|
11010
|
+
endDate,
|
|
11011
|
+
format = "json",
|
|
11012
|
+
groupBy = "service"
|
|
11013
|
+
} = req.body;
|
|
11014
|
+
const config = getConfig2();
|
|
11015
|
+
const provider = await getProviderFromConfig();
|
|
11016
|
+
const start = startDate ? new Date(startDate) : /* @__PURE__ */ new Date();
|
|
11017
|
+
const end = endDate ? new Date(endDate) : /* @__PURE__ */ new Date();
|
|
11018
|
+
let groupByType = "SERVICE";
|
|
11019
|
+
if (groupBy === "tag")
|
|
11020
|
+
groupByType = "TAG";
|
|
11021
|
+
else if (groupBy === "daily")
|
|
11022
|
+
groupByType = "DAILY";
|
|
11023
|
+
else if (groupBy === "monthly")
|
|
11024
|
+
groupByType = "MONTHLY";
|
|
11025
|
+
const breakdown = await provider.getCostBreakdown(start, end, groupByType);
|
|
11026
|
+
const report = {
|
|
11027
|
+
reportType,
|
|
11028
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
11029
|
+
period: {
|
|
11030
|
+
start: start.toISOString(),
|
|
11031
|
+
end: end.toISOString()
|
|
11032
|
+
},
|
|
11033
|
+
data: {
|
|
11034
|
+
total: breakdown.totalCost,
|
|
11035
|
+
breakdown: breakdown.breakdown,
|
|
11036
|
+
groupBy
|
|
11037
|
+
},
|
|
11038
|
+
format
|
|
11039
|
+
};
|
|
11040
|
+
res.json(createApiResponse(report));
|
|
11041
|
+
} catch (error) {
|
|
11042
|
+
res.status(500).json(createErrorResponse("REPORT_ERROR", error.message));
|
|
11043
|
+
}
|
|
11044
|
+
});
|
|
11045
|
+
reports_default = router7;
|
|
10499
11046
|
}
|
|
10500
|
-
|
|
10501
|
-
|
|
10502
|
-
|
|
10503
|
-
|
|
11047
|
+
});
|
|
11048
|
+
|
|
11049
|
+
// src/api/server.ts
|
|
11050
|
+
function createApiResponse(data) {
|
|
11051
|
+
return {
|
|
11052
|
+
status: "success",
|
|
11053
|
+
data,
|
|
11054
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
11055
|
+
};
|
|
10504
11056
|
}
|
|
10505
|
-
|
|
10506
|
-
|
|
10507
|
-
|
|
10508
|
-
|
|
10509
|
-
|
|
10510
|
-
|
|
10511
|
-
config.profile = options.profile;
|
|
10512
|
-
if (options.region)
|
|
10513
|
-
config.region = options.region;
|
|
10514
|
-
if (options.accessKey)
|
|
10515
|
-
config.accessKey = options.accessKey;
|
|
10516
|
-
if (options.secretKey)
|
|
10517
|
-
config.secretKey = options.secretKey;
|
|
10518
|
-
if (options.sessionToken)
|
|
10519
|
-
config.sessionToken = options.sessionToken;
|
|
10520
|
-
if (options.json || options.text || options.summary) {
|
|
10521
|
-
config.output = config.output || {};
|
|
10522
|
-
if (options.json)
|
|
10523
|
-
config.output.format = "json";
|
|
10524
|
-
if (options.text)
|
|
10525
|
-
config.output.format = "text";
|
|
10526
|
-
if (options.summary)
|
|
10527
|
-
config.output.summary = true;
|
|
10528
|
-
}
|
|
10529
|
-
if (options.delta !== void 0) {
|
|
10530
|
-
config.output = config.output || {};
|
|
10531
|
-
config.output.showDelta = options.delta;
|
|
10532
|
-
}
|
|
10533
|
-
if (options.deltaThreshold) {
|
|
10534
|
-
config.output = config.output || {};
|
|
10535
|
-
config.output.deltaThreshold = parseFloat(options.deltaThreshold);
|
|
10536
|
-
}
|
|
10537
|
-
if (options.quickWins !== void 0) {
|
|
10538
|
-
config.output = config.output || {};
|
|
10539
|
-
config.output.showQuickWins = options.quickWins;
|
|
10540
|
-
}
|
|
10541
|
-
if (options.quickWinsCount) {
|
|
10542
|
-
config.output = config.output || {};
|
|
10543
|
-
config.output.quickWinsCount = parseInt(options.quickWinsCount, 10);
|
|
10544
|
-
}
|
|
10545
|
-
if (options.cache !== void 0 || options.noCache !== void 0) {
|
|
10546
|
-
config.cache = config.cache || {};
|
|
10547
|
-
config.cache.enabled = options.cache === true || options.noCache !== true;
|
|
10548
|
-
}
|
|
10549
|
-
if (options.cacheTtl) {
|
|
10550
|
-
config.cache = config.cache || {};
|
|
10551
|
-
config.cache.ttl = options.cacheTtl;
|
|
10552
|
-
}
|
|
10553
|
-
if (options.cacheType) {
|
|
10554
|
-
config.cache = config.cache || {};
|
|
10555
|
-
config.cache.type = options.cacheType;
|
|
10556
|
-
}
|
|
10557
|
-
if (options.slackToken || options.slackChannel) {
|
|
10558
|
-
config.slack = config.slack || {};
|
|
10559
|
-
if (options.slackToken)
|
|
10560
|
-
config.slack.token = options.slackToken;
|
|
10561
|
-
if (options.slackChannel)
|
|
10562
|
-
config.slack.channel = options.slackChannel;
|
|
10563
|
-
config.slack.enabled = true;
|
|
10564
|
-
}
|
|
10565
|
-
if (options.logLevel || options.verbose || options.quiet) {
|
|
10566
|
-
config.logging = config.logging || {};
|
|
10567
|
-
if (options.logLevel)
|
|
10568
|
-
config.logging.level = options.logLevel;
|
|
10569
|
-
if (options.verbose)
|
|
10570
|
-
config.logging.level = "debug";
|
|
10571
|
-
if (options.quiet)
|
|
10572
|
-
config.logging.level = "error";
|
|
10573
|
-
}
|
|
10574
|
-
return config;
|
|
11057
|
+
function createErrorResponse(code, message) {
|
|
11058
|
+
return {
|
|
11059
|
+
status: "error",
|
|
11060
|
+
error: { code, message },
|
|
11061
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
11062
|
+
};
|
|
10575
11063
|
}
|
|
10576
|
-
|
|
11064
|
+
var import_express8, import_cors, import_express_rate_limit, import_helmet, cache, ApiServer;
|
|
11065
|
+
var init_server = __esm({
|
|
11066
|
+
"src/api/server.ts"() {
|
|
11067
|
+
import_express8 = __toESM(require("express"));
|
|
11068
|
+
import_cors = __toESM(require("cors"));
|
|
11069
|
+
import_express_rate_limit = __toESM(require("express-rate-limit"));
|
|
11070
|
+
import_helmet = __toESM(require("helmet"));
|
|
11071
|
+
cache = /* @__PURE__ */ new Map();
|
|
11072
|
+
ApiServer = class {
|
|
11073
|
+
constructor(config) {
|
|
11074
|
+
this.config = config;
|
|
11075
|
+
this.app = (0, import_express8.default)();
|
|
11076
|
+
this.setupMiddleware();
|
|
11077
|
+
this.setupRoutes();
|
|
11078
|
+
}
|
|
11079
|
+
setupMiddleware() {
|
|
11080
|
+
this.app.use((0, import_helmet.default)());
|
|
11081
|
+
this.app.use(import_express8.default.json());
|
|
11082
|
+
if (this.config.cors.enabled) {
|
|
11083
|
+
this.app.use(
|
|
11084
|
+
(0, import_cors.default)({
|
|
11085
|
+
origin: this.config.cors.origins || "*",
|
|
11086
|
+
methods: ["GET", "POST", "PUT", "DELETE"],
|
|
11087
|
+
credentials: true
|
|
11088
|
+
})
|
|
11089
|
+
);
|
|
11090
|
+
}
|
|
11091
|
+
if (this.config.rateLimit.enabled) {
|
|
11092
|
+
const limiter = (0, import_express_rate_limit.default)({
|
|
11093
|
+
windowMs: this.config.rateLimit.windowMs,
|
|
11094
|
+
max: this.config.rateLimit.max,
|
|
11095
|
+
message: {
|
|
11096
|
+
status: "error",
|
|
11097
|
+
error: {
|
|
11098
|
+
code: "RATE_LIMIT_EXCEEDED",
|
|
11099
|
+
message: "Too many requests, please try again later."
|
|
11100
|
+
},
|
|
11101
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
11102
|
+
}
|
|
11103
|
+
});
|
|
11104
|
+
this.app.use("/api/", limiter);
|
|
11105
|
+
}
|
|
11106
|
+
this.app.use("/api/", this.authMiddleware.bind(this));
|
|
11107
|
+
if (this.config.cache.enabled) {
|
|
11108
|
+
this.app.use("/api/", this.cacheMiddleware.bind(this));
|
|
11109
|
+
}
|
|
11110
|
+
}
|
|
11111
|
+
authMiddleware(req, res, next) {
|
|
11112
|
+
if (req.path === "/api/v1/health") {
|
|
11113
|
+
return next();
|
|
11114
|
+
}
|
|
11115
|
+
if (this.config.auth.type === "none") {
|
|
11116
|
+
return next();
|
|
11117
|
+
}
|
|
11118
|
+
if (this.config.auth.type === "api-key") {
|
|
11119
|
+
const apiKey = req.headers["x-api-key"] || req.query.apiKey;
|
|
11120
|
+
if (!apiKey || !this.config.auth.apiKeys?.includes(apiKey)) {
|
|
11121
|
+
return res.status(401).json({
|
|
11122
|
+
status: "error",
|
|
11123
|
+
error: {
|
|
11124
|
+
code: "UNAUTHORIZED",
|
|
11125
|
+
message: "Invalid or missing API key"
|
|
11126
|
+
},
|
|
11127
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
11128
|
+
});
|
|
11129
|
+
}
|
|
11130
|
+
}
|
|
11131
|
+
next();
|
|
11132
|
+
}
|
|
11133
|
+
cacheMiddleware(req, res, next) {
|
|
11134
|
+
if (req.method !== "GET") {
|
|
11135
|
+
return next();
|
|
11136
|
+
}
|
|
11137
|
+
const key = `${req.method}:${req.path}:${JSON.stringify(req.query)}`;
|
|
11138
|
+
const cached = cache.get(key);
|
|
11139
|
+
if (cached && cached.expiry > Date.now()) {
|
|
11140
|
+
return res.json(cached.data);
|
|
11141
|
+
}
|
|
11142
|
+
const originalJson = res.json.bind(res);
|
|
11143
|
+
res.json = (body) => {
|
|
11144
|
+
if (res.statusCode === 200) {
|
|
11145
|
+
cache.set(key, {
|
|
11146
|
+
data: body,
|
|
11147
|
+
expiry: Date.now() + this.config.cache.ttl * 1e3
|
|
11148
|
+
});
|
|
11149
|
+
}
|
|
11150
|
+
return originalJson(body);
|
|
11151
|
+
};
|
|
11152
|
+
next();
|
|
11153
|
+
}
|
|
11154
|
+
setupRoutes() {
|
|
11155
|
+
this.app.get("/api/v1/health", (_req, res) => {
|
|
11156
|
+
res.json({
|
|
11157
|
+
status: "success",
|
|
11158
|
+
data: {
|
|
11159
|
+
healthy: true,
|
|
11160
|
+
version: require_package().version,
|
|
11161
|
+
uptime: process.uptime()
|
|
11162
|
+
},
|
|
11163
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
11164
|
+
});
|
|
11165
|
+
});
|
|
11166
|
+
this.app.use("/api/v1/costs", (init_costs2(), __toCommonJS(costs_exports2)).default);
|
|
11167
|
+
this.app.use("/api/v1/inventory", (init_inventory6(), __toCommonJS(inventory_exports2)).default);
|
|
11168
|
+
this.app.use("/api/v1/optimization", (init_optimization(), __toCommonJS(optimization_exports)).default);
|
|
11169
|
+
this.app.use("/api/v1/chargeback", (init_chargeback(), __toCommonJS(chargeback_exports)).default);
|
|
11170
|
+
this.app.use("/api/v1/forecast", (init_forecast(), __toCommonJS(forecast_exports)).default);
|
|
11171
|
+
this.app.use("/api/v1/accounts", (init_accounts(), __toCommonJS(accounts_exports)).default);
|
|
11172
|
+
this.app.use("/api/v1/reports", (init_reports2(), __toCommonJS(reports_exports2)).default);
|
|
11173
|
+
this.app.use((_req, res) => {
|
|
11174
|
+
res.status(404).json({
|
|
11175
|
+
status: "error",
|
|
11176
|
+
error: {
|
|
11177
|
+
code: "NOT_FOUND",
|
|
11178
|
+
message: "API endpoint not found"
|
|
11179
|
+
},
|
|
11180
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
11181
|
+
});
|
|
11182
|
+
});
|
|
11183
|
+
this.app.use((err, _req, res, _next) => {
|
|
11184
|
+
console.error("API Error:", err);
|
|
11185
|
+
res.status(500).json({
|
|
11186
|
+
status: "error",
|
|
11187
|
+
error: {
|
|
11188
|
+
code: "INTERNAL_ERROR",
|
|
11189
|
+
message: err.message || "Internal server error"
|
|
11190
|
+
},
|
|
11191
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
11192
|
+
});
|
|
11193
|
+
});
|
|
11194
|
+
}
|
|
11195
|
+
async start() {
|
|
11196
|
+
return new Promise((resolve2, reject) => {
|
|
11197
|
+
try {
|
|
11198
|
+
this.server = this.app.listen(this.config.port, this.config.host, () => {
|
|
11199
|
+
console.log(
|
|
11200
|
+
`\u2705 API Server running at http://${this.config.host}:${this.config.port}`
|
|
11201
|
+
);
|
|
11202
|
+
console.log(`\u{1F4D6} API Documentation: http://${this.config.host}:${this.config.port}/api/docs`);
|
|
11203
|
+
resolve2();
|
|
11204
|
+
});
|
|
11205
|
+
this.server.on("error", reject);
|
|
11206
|
+
} catch (error) {
|
|
11207
|
+
reject(error);
|
|
11208
|
+
}
|
|
11209
|
+
});
|
|
11210
|
+
}
|
|
11211
|
+
async stop() {
|
|
11212
|
+
return new Promise((resolve2, reject) => {
|
|
11213
|
+
if (!this.server) {
|
|
11214
|
+
resolve2();
|
|
11215
|
+
return;
|
|
11216
|
+
}
|
|
11217
|
+
this.server.close((err) => {
|
|
11218
|
+
if (err) {
|
|
11219
|
+
reject(err);
|
|
11220
|
+
} else {
|
|
11221
|
+
console.log("API Server stopped");
|
|
11222
|
+
resolve2();
|
|
11223
|
+
}
|
|
11224
|
+
});
|
|
11225
|
+
});
|
|
11226
|
+
}
|
|
11227
|
+
getApp() {
|
|
11228
|
+
return this.app;
|
|
11229
|
+
}
|
|
11230
|
+
};
|
|
11231
|
+
__name(ApiServer, "ApiServer");
|
|
11232
|
+
__name(createApiResponse, "createApiResponse");
|
|
11233
|
+
__name(createErrorResponse, "createErrorResponse");
|
|
11234
|
+
}
|
|
11235
|
+
});
|
|
11236
|
+
|
|
11237
|
+
// src/cli/index.ts
|
|
11238
|
+
var cli_exports = {};
|
|
11239
|
+
__export(cli_exports, {
|
|
11240
|
+
createCLI: () => createCLI,
|
|
11241
|
+
main: () => main
|
|
11242
|
+
});
|
|
11243
|
+
module.exports = __toCommonJS(cli_exports);
|
|
11244
|
+
var import_commander = require("commander");
|
|
11245
|
+
var import_package = __toESM(require_package());
|
|
11246
|
+
init_logging();
|
|
11247
|
+
|
|
11248
|
+
// src/core/config/index.ts
|
|
11249
|
+
init_schema();
|
|
11250
|
+
init_loader();
|
|
10577
11251
|
|
|
10578
11252
|
// src/core/config/discovery.ts
|
|
10579
11253
|
var import_chalk2 = __toESM(require("chalk"));
|
|
@@ -13887,6 +14561,210 @@ function registerPluginCommands(program) {
|
|
|
13887
14561
|
}
|
|
13888
14562
|
__name(registerPluginCommands, "registerPluginCommands");
|
|
13889
14563
|
|
|
14564
|
+
// src/cli/commands/server/index.ts
|
|
14565
|
+
var import_chalk22 = __toESM(require("chalk"));
|
|
14566
|
+
var import_fs8 = require("fs");
|
|
14567
|
+
var import_path7 = require("path");
|
|
14568
|
+
var import_os7 = require("os");
|
|
14569
|
+
init_server();
|
|
14570
|
+
var CONFIG_DIR4 = (0, import_path7.join)((0, import_os7.homedir)(), ".infra-cost");
|
|
14571
|
+
var SERVER_CONFIG_PATH = (0, import_path7.join)(CONFIG_DIR4, "server-config.json");
|
|
14572
|
+
var PID_FILE2 = (0, import_path7.join)(CONFIG_DIR4, "server.pid");
|
|
14573
|
+
var DEFAULT_CONFIG3 = {
|
|
14574
|
+
port: 3e3,
|
|
14575
|
+
host: "127.0.0.1",
|
|
14576
|
+
cors: {
|
|
14577
|
+
enabled: true,
|
|
14578
|
+
origins: ["*"]
|
|
14579
|
+
},
|
|
14580
|
+
auth: {
|
|
14581
|
+
type: "none"
|
|
14582
|
+
},
|
|
14583
|
+
rateLimit: {
|
|
14584
|
+
enabled: true,
|
|
14585
|
+
windowMs: 6e4,
|
|
14586
|
+
max: 100
|
|
14587
|
+
},
|
|
14588
|
+
cache: {
|
|
14589
|
+
enabled: true,
|
|
14590
|
+
ttl: 300
|
|
14591
|
+
// 5 minutes
|
|
14592
|
+
}
|
|
14593
|
+
};
|
|
14594
|
+
function loadServerConfig() {
|
|
14595
|
+
if ((0, import_fs8.existsSync)(SERVER_CONFIG_PATH)) {
|
|
14596
|
+
const config = JSON.parse((0, import_fs8.readFileSync)(SERVER_CONFIG_PATH, "utf-8"));
|
|
14597
|
+
return { ...DEFAULT_CONFIG3, ...config };
|
|
14598
|
+
}
|
|
14599
|
+
return DEFAULT_CONFIG3;
|
|
14600
|
+
}
|
|
14601
|
+
__name(loadServerConfig, "loadServerConfig");
|
|
14602
|
+
function saveServerConfig(config) {
|
|
14603
|
+
(0, import_fs8.writeFileSync)(SERVER_CONFIG_PATH, JSON.stringify(config, null, 2));
|
|
14604
|
+
}
|
|
14605
|
+
__name(saveServerConfig, "saveServerConfig");
|
|
14606
|
+
async function handleStart2(options) {
|
|
14607
|
+
try {
|
|
14608
|
+
if ((0, import_fs8.existsSync)(PID_FILE2)) {
|
|
14609
|
+
const pid = parseInt((0, import_fs8.readFileSync)(PID_FILE2, "utf-8").trim(), 10);
|
|
14610
|
+
try {
|
|
14611
|
+
process.kill(pid, 0);
|
|
14612
|
+
console.log(import_chalk22.default.yellow("\u26A0\uFE0F Server is already running"));
|
|
14613
|
+
console.log(import_chalk22.default.gray(` PID: ${pid}`));
|
|
14614
|
+
return;
|
|
14615
|
+
} catch {
|
|
14616
|
+
require("fs").unlinkSync(PID_FILE2);
|
|
14617
|
+
}
|
|
14618
|
+
}
|
|
14619
|
+
const config = loadServerConfig();
|
|
14620
|
+
if (options.port)
|
|
14621
|
+
config.port = parseInt(options.port, 10);
|
|
14622
|
+
if (options.host)
|
|
14623
|
+
config.host = options.host;
|
|
14624
|
+
if (options.apiKey) {
|
|
14625
|
+
config.auth = {
|
|
14626
|
+
type: "api-key",
|
|
14627
|
+
apiKeys: [options.apiKey]
|
|
14628
|
+
};
|
|
14629
|
+
}
|
|
14630
|
+
if (options.apiKeyRequired && !options.apiKey) {
|
|
14631
|
+
console.log(import_chalk22.default.red("\u274C --api-key is required when --api-key-required is set"));
|
|
14632
|
+
process.exit(1);
|
|
14633
|
+
}
|
|
14634
|
+
const server = new ApiServer(config);
|
|
14635
|
+
if (options.daemon) {
|
|
14636
|
+
console.log(import_chalk22.default.blue("Starting server in daemon mode..."));
|
|
14637
|
+
const { spawn } = require("child_process");
|
|
14638
|
+
const child = spawn(
|
|
14639
|
+
process.argv[0],
|
|
14640
|
+
[process.argv[1], "server", "start", "--port", config.port.toString(), "--host", config.host],
|
|
14641
|
+
{
|
|
14642
|
+
detached: true,
|
|
14643
|
+
stdio: "ignore"
|
|
14644
|
+
}
|
|
14645
|
+
);
|
|
14646
|
+
child.unref();
|
|
14647
|
+
(0, import_fs8.writeFileSync)(PID_FILE2, child.pid.toString());
|
|
14648
|
+
console.log(import_chalk22.default.green("\u2705 Server started in background"));
|
|
14649
|
+
console.log(import_chalk22.default.gray(` PID: ${child.pid}`));
|
|
14650
|
+
console.log(import_chalk22.default.gray(` URL: http://${config.host}:${config.port}`));
|
|
14651
|
+
return;
|
|
14652
|
+
}
|
|
14653
|
+
await server.start();
|
|
14654
|
+
(0, import_fs8.writeFileSync)(PID_FILE2, process.pid.toString());
|
|
14655
|
+
console.log();
|
|
14656
|
+
console.log(import_chalk22.default.bold("Server Configuration:"));
|
|
14657
|
+
console.log(import_chalk22.default.gray(` Port: ${config.port}`));
|
|
14658
|
+
console.log(import_chalk22.default.gray(` Host: ${config.host}`));
|
|
14659
|
+
console.log(import_chalk22.default.gray(` Auth: ${config.auth.type}`));
|
|
14660
|
+
console.log(import_chalk22.default.gray(` Rate Limit: ${config.rateLimit.enabled ? "enabled" : "disabled"}`));
|
|
14661
|
+
console.log(import_chalk22.default.gray(` Cache: ${config.cache.enabled ? `${config.cache.ttl}s` : "disabled"}`));
|
|
14662
|
+
console.log();
|
|
14663
|
+
if (config.auth.type === "api-key") {
|
|
14664
|
+
console.log(import_chalk22.default.yellow("\u26A0\uFE0F API Key Authentication Enabled"));
|
|
14665
|
+
console.log(import_chalk22.default.gray(" Use header: X-API-Key: your-api-key"));
|
|
14666
|
+
console.log();
|
|
14667
|
+
}
|
|
14668
|
+
console.log(import_chalk22.default.green("Press Ctrl+C to stop the server"));
|
|
14669
|
+
const shutdown = /* @__PURE__ */ __name(async () => {
|
|
14670
|
+
console.log(import_chalk22.default.yellow("\n\nShutting down server..."));
|
|
14671
|
+
await server.stop();
|
|
14672
|
+
if ((0, import_fs8.existsSync)(PID_FILE2)) {
|
|
14673
|
+
require("fs").unlinkSync(PID_FILE2);
|
|
14674
|
+
}
|
|
14675
|
+
process.exit(0);
|
|
14676
|
+
}, "shutdown");
|
|
14677
|
+
process.on("SIGINT", shutdown);
|
|
14678
|
+
process.on("SIGTERM", shutdown);
|
|
14679
|
+
} catch (error) {
|
|
14680
|
+
console.error(import_chalk22.default.red("\u274C Failed to start server:"), error.message);
|
|
14681
|
+
process.exit(1);
|
|
14682
|
+
}
|
|
14683
|
+
}
|
|
14684
|
+
__name(handleStart2, "handleStart");
|
|
14685
|
+
async function handleStop2() {
|
|
14686
|
+
try {
|
|
14687
|
+
if (!(0, import_fs8.existsSync)(PID_FILE2)) {
|
|
14688
|
+
console.log(import_chalk22.default.yellow("\u26A0\uFE0F Server is not running"));
|
|
14689
|
+
return;
|
|
14690
|
+
}
|
|
14691
|
+
const pid = parseInt((0, import_fs8.readFileSync)(PID_FILE2, "utf-8").trim(), 10);
|
|
14692
|
+
try {
|
|
14693
|
+
process.kill(pid, "SIGTERM");
|
|
14694
|
+
console.log(import_chalk22.default.green("\u2705 Server stopped"));
|
|
14695
|
+
setTimeout(() => {
|
|
14696
|
+
if ((0, import_fs8.existsSync)(PID_FILE2)) {
|
|
14697
|
+
require("fs").unlinkSync(PID_FILE2);
|
|
14698
|
+
}
|
|
14699
|
+
}, 1e3);
|
|
14700
|
+
} catch {
|
|
14701
|
+
console.log(import_chalk22.default.yellow("\u26A0\uFE0F Server process not found"));
|
|
14702
|
+
if ((0, import_fs8.existsSync)(PID_FILE2)) {
|
|
14703
|
+
require("fs").unlinkSync(PID_FILE2);
|
|
14704
|
+
}
|
|
14705
|
+
}
|
|
14706
|
+
} catch (error) {
|
|
14707
|
+
console.error(import_chalk22.default.red("\u274C Failed to stop server:"), error.message);
|
|
14708
|
+
process.exit(1);
|
|
14709
|
+
}
|
|
14710
|
+
}
|
|
14711
|
+
__name(handleStop2, "handleStop");
|
|
14712
|
+
async function handleStatus3() {
|
|
14713
|
+
try {
|
|
14714
|
+
if (!(0, import_fs8.existsSync)(PID_FILE2)) {
|
|
14715
|
+
console.log(import_chalk22.default.yellow("\u26A0\uFE0F Server is not running"));
|
|
14716
|
+
return;
|
|
14717
|
+
}
|
|
14718
|
+
const pid = parseInt((0, import_fs8.readFileSync)(PID_FILE2, "utf-8").trim(), 10);
|
|
14719
|
+
try {
|
|
14720
|
+
process.kill(pid, 0);
|
|
14721
|
+
const config = loadServerConfig();
|
|
14722
|
+
console.log(import_chalk22.default.green("\u2705 Server is running"));
|
|
14723
|
+
console.log(import_chalk22.default.gray(` PID: ${pid}`));
|
|
14724
|
+
console.log(import_chalk22.default.gray(` URL: http://${config.host}:${config.port}`));
|
|
14725
|
+
console.log(import_chalk22.default.gray(` Docs: http://${config.host}:${config.port}/api/docs`));
|
|
14726
|
+
} catch {
|
|
14727
|
+
console.log(import_chalk22.default.yellow("\u26A0\uFE0F Server is not running (stale PID file)"));
|
|
14728
|
+
require("fs").unlinkSync(PID_FILE2);
|
|
14729
|
+
}
|
|
14730
|
+
} catch (error) {
|
|
14731
|
+
console.error(import_chalk22.default.red("\u274C Failed to check status:"), error.message);
|
|
14732
|
+
process.exit(1);
|
|
14733
|
+
}
|
|
14734
|
+
}
|
|
14735
|
+
__name(handleStatus3, "handleStatus");
|
|
14736
|
+
async function handleConfigure2(options) {
|
|
14737
|
+
try {
|
|
14738
|
+
const config = loadServerConfig();
|
|
14739
|
+
if (options.port)
|
|
14740
|
+
config.port = parseInt(options.port, 10);
|
|
14741
|
+
if (options.host)
|
|
14742
|
+
config.host = options.host;
|
|
14743
|
+
if (options.enableCors !== void 0)
|
|
14744
|
+
config.cors.enabled = options.enableCors === "true";
|
|
14745
|
+
if (options.cacheEnabled !== void 0)
|
|
14746
|
+
config.cache.enabled = options.cacheEnabled === "true";
|
|
14747
|
+
if (options.cacheTtl)
|
|
14748
|
+
config.cache.ttl = parseInt(options.cacheTtl, 10);
|
|
14749
|
+
saveServerConfig(config);
|
|
14750
|
+
console.log(import_chalk22.default.green("\u2705 Server configuration updated"));
|
|
14751
|
+
console.log();
|
|
14752
|
+
console.log(JSON.stringify(config, null, 2));
|
|
14753
|
+
} catch (error) {
|
|
14754
|
+
console.error(import_chalk22.default.red("\u274C Failed to configure server:"), error.message);
|
|
14755
|
+
process.exit(1);
|
|
14756
|
+
}
|
|
14757
|
+
}
|
|
14758
|
+
__name(handleConfigure2, "handleConfigure");
|
|
14759
|
+
function registerServerCommands(program) {
|
|
14760
|
+
const server = program.command("server").description("API server mode for REST API access");
|
|
14761
|
+
server.command("start").description("Start the API server").option("-p, --port <port>", "Port to listen on", "3000").option("-h, --host <host>", "Host to bind to", "127.0.0.1").option("--api-key <key>", "API key for authentication").option("--api-key-required", "Require API key authentication").option("-d, --daemon", "Run in background/daemon mode").action(handleStart2);
|
|
14762
|
+
server.command("stop").description("Stop the running API server").action(handleStop2);
|
|
14763
|
+
server.command("status").description("Check server status").action(handleStatus3);
|
|
14764
|
+
server.command("configure").description("Configure server settings").option("-p, --port <port>", "Default port").option("-h, --host <host>", "Default host").option("--enable-cors <boolean>", "Enable/disable CORS").option("--cache-enabled <boolean>", "Enable/disable caching").option("--cache-ttl <seconds>", "Cache TTL in seconds").action(handleConfigure2);
|
|
14765
|
+
}
|
|
14766
|
+
__name(registerServerCommands, "registerServerCommands");
|
|
14767
|
+
|
|
13890
14768
|
// src/cli/middleware/auth.ts
|
|
13891
14769
|
async function authMiddleware(thisCommand, actionCommand) {
|
|
13892
14770
|
const isConfigCommand = actionCommand.name() === "config" || actionCommand.parent?.name() === "config";
|
|
@@ -13915,7 +14793,7 @@ async function validationMiddleware(thisCommand, actionCommand) {
|
|
|
13915
14793
|
__name(validationMiddleware, "validationMiddleware");
|
|
13916
14794
|
|
|
13917
14795
|
// src/cli/middleware/error-handler.ts
|
|
13918
|
-
var
|
|
14796
|
+
var import_chalk23 = __toESM(require("chalk"));
|
|
13919
14797
|
function errorHandler(error) {
|
|
13920
14798
|
const message = error?.message ?? String(error);
|
|
13921
14799
|
const stack = error?.stack;
|
|
@@ -13923,15 +14801,15 @@ function errorHandler(error) {
|
|
|
13923
14801
|
return;
|
|
13924
14802
|
}
|
|
13925
14803
|
console.error("");
|
|
13926
|
-
console.error(
|
|
14804
|
+
console.error(import_chalk23.default.red("\u2716"), import_chalk23.default.bold("Error:"), message);
|
|
13927
14805
|
if (process.env.DEBUG || process.env.VERBOSE) {
|
|
13928
14806
|
console.error("");
|
|
13929
14807
|
if (stack) {
|
|
13930
|
-
console.error(
|
|
14808
|
+
console.error(import_chalk23.default.gray(stack));
|
|
13931
14809
|
}
|
|
13932
14810
|
} else {
|
|
13933
14811
|
console.error("");
|
|
13934
|
-
console.error(
|
|
14812
|
+
console.error(import_chalk23.default.gray("Run with --verbose for detailed error information"));
|
|
13935
14813
|
}
|
|
13936
14814
|
console.error("");
|
|
13937
14815
|
}
|
|
@@ -13941,7 +14819,7 @@ __name(errorHandler, "errorHandler");
|
|
|
13941
14819
|
function createCLI() {
|
|
13942
14820
|
const program = new import_commander.Command();
|
|
13943
14821
|
program.exitOverride();
|
|
13944
|
-
program.name("infra-cost").description(
|
|
14822
|
+
program.name("infra-cost").description(import_package.default.description).version(import_package.default.version);
|
|
13945
14823
|
program.option("--provider <provider>", "Cloud provider (aws, gcp, azure, alibaba, oracle)", "aws").option("-p, --profile <profile>", "Cloud provider profile", "default").option("-r, --region <region>", "Cloud provider region", "us-east-1").option("-k, --access-key <key>", "Access key for cloud provider").option("-s, --secret-key <key>", "Secret key for cloud provider").option("-T, --session-token <token>", "Session token").option("--project-id <id>", "GCP Project ID").option("--key-file <path>", "Path to service account key file (GCP/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 <fp>", "Oracle Public Key Fingerprint").option("--config-file <path>", "Path to configuration file").option("--config-profile <name>", "Use named profile from config").option("--output <format>", "Output format (json, text, fancy, table)", "fancy").option("--no-color", "Disable colored output").option("--quiet", "Quiet mode - minimal output").option("--verbose", "Verbose output with debug information").option("--no-cache", "Disable caching").option("--cache-ttl <duration>", "Cache TTL (e.g., 4h, 30m)", "4h").option("--log-level <level>", "Logging level (debug, info, warn, error)", "info").option("--log-format <format>", "Log format (pretty, json)", "pretty");
|
|
13946
14824
|
registerNowCommand(program);
|
|
13947
14825
|
registerFreeTierCommand(program);
|
|
@@ -13960,6 +14838,7 @@ function createCLI() {
|
|
|
13960
14838
|
registerRBACCommands(program);
|
|
13961
14839
|
registerSSOCommands(program);
|
|
13962
14840
|
registerPluginCommands(program);
|
|
14841
|
+
registerServerCommands(program);
|
|
13963
14842
|
program.hook("preAction", async (thisCommand, actionCommand) => {
|
|
13964
14843
|
const opts = thisCommand.opts();
|
|
13965
14844
|
const logLevel = opts.verbose ? "debug" : opts.quiet ? "error" : opts.logLevel;
|