@aigne/afs-aws-cost 1.11.0-beta.12
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/LICENSE.md +26 -0
- package/dist/_virtual/rolldown_runtime.mjs +7 -0
- package/dist/adapter.cjs +158 -0
- package/dist/adapter.d.cts +55 -0
- package/dist/adapter.d.cts.map +1 -0
- package/dist/adapter.d.mts +55 -0
- package/dist/adapter.d.mts.map +1 -0
- package/dist/adapter.mjs +160 -0
- package/dist/adapter.mjs.map +1 -0
- package/dist/index.cjs +102 -0
- package/dist/index.d.cts +34 -0
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +34 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +96 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +59 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Proprietary License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024-2025 ArcBlock, Inc. All Rights Reserved.
|
|
4
|
+
|
|
5
|
+
This software and associated documentation files (the "Software") are proprietary
|
|
6
|
+
and confidential. Unauthorized copying, modification, distribution, or use of
|
|
7
|
+
this Software, via any medium, is strictly prohibited.
|
|
8
|
+
|
|
9
|
+
The Software is provided for internal use only within ArcBlock, Inc. and its
|
|
10
|
+
authorized affiliates.
|
|
11
|
+
|
|
12
|
+
## No License Granted
|
|
13
|
+
|
|
14
|
+
No license, express or implied, is granted to any party for any purpose.
|
|
15
|
+
All rights are reserved by ArcBlock, Inc.
|
|
16
|
+
|
|
17
|
+
## Public Artifact Distribution
|
|
18
|
+
|
|
19
|
+
Portions of this Software may be released publicly under separate open-source
|
|
20
|
+
licenses (such as MIT License) through designated public repositories. Such
|
|
21
|
+
public releases are governed by their respective licenses and do not affect
|
|
22
|
+
the proprietary nature of this repository.
|
|
23
|
+
|
|
24
|
+
## Contact
|
|
25
|
+
|
|
26
|
+
For licensing inquiries, contact: legal@arcblock.io
|
package/dist/adapter.cjs
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
|
|
2
|
+
//#region src/adapter.ts
|
|
3
|
+
const SERVICE_NAME_MAP = {
|
|
4
|
+
"Amazon Elastic Compute Cloud - Compute": "ec2",
|
|
5
|
+
"Amazon Elastic Compute Cloud": "ec2",
|
|
6
|
+
"Amazon Simple Storage Service": "s3",
|
|
7
|
+
"AWS Lambda": "lambda",
|
|
8
|
+
"Amazon Relational Database Service": "rds",
|
|
9
|
+
"Amazon DynamoDB": "dynamodb",
|
|
10
|
+
"Amazon CloudFront": "cloudfront",
|
|
11
|
+
"Amazon Elastic Container Service": "ecs",
|
|
12
|
+
"Amazon Elastic Kubernetes Service": "eks",
|
|
13
|
+
"Amazon ElastiCache": "elasticache",
|
|
14
|
+
"Amazon Elastic Load Balancing": "elb",
|
|
15
|
+
"Amazon Route 53": "route53",
|
|
16
|
+
"Amazon Simple Notification Service": "sns",
|
|
17
|
+
"Amazon Simple Queue Service": "sqs",
|
|
18
|
+
"Amazon CloudWatch": "cloudwatch",
|
|
19
|
+
"AWS Key Management Service": "kms",
|
|
20
|
+
"Amazon Kinesis": "kinesis",
|
|
21
|
+
"Amazon Redshift": "redshift",
|
|
22
|
+
"AWS CodeBuild": "codebuild",
|
|
23
|
+
"Amazon API Gateway": "api-gateway",
|
|
24
|
+
"AWS Secrets Manager": "secrets-manager",
|
|
25
|
+
"Amazon Elastic File System": "efs",
|
|
26
|
+
"AWS Step Functions": "step-functions",
|
|
27
|
+
"AWS Fargate": "fargate",
|
|
28
|
+
"Amazon SageMaker": "sagemaker",
|
|
29
|
+
Tax: "tax"
|
|
30
|
+
};
|
|
31
|
+
function normalizeServiceName(awsName) {
|
|
32
|
+
if (!awsName) return "unknown";
|
|
33
|
+
if (SERVICE_NAME_MAP[awsName]) return SERVICE_NAME_MAP[awsName];
|
|
34
|
+
return awsName.replace(/^(Amazon|AWS)\s+/i, "").replace(/\s*-\s*/g, "-").replace(/\s+/g, "-").toLowerCase();
|
|
35
|
+
}
|
|
36
|
+
var AWSCostAdapter = class {
|
|
37
|
+
cloud = "aws";
|
|
38
|
+
config;
|
|
39
|
+
client;
|
|
40
|
+
commandClass;
|
|
41
|
+
cachedServices = null;
|
|
42
|
+
constructor(config = {}, client) {
|
|
43
|
+
this.config = config;
|
|
44
|
+
this.client = client;
|
|
45
|
+
}
|
|
46
|
+
async isAvailable() {
|
|
47
|
+
try {
|
|
48
|
+
const client = this.getClient();
|
|
49
|
+
if (!client) return false;
|
|
50
|
+
await client.send(this.buildCommand({
|
|
51
|
+
startDate: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
|
|
52
|
+
endDate: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10)
|
|
53
|
+
}));
|
|
54
|
+
return true;
|
|
55
|
+
} catch {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
async fetchCosts(options) {
|
|
60
|
+
const client = this.getClient();
|
|
61
|
+
if (!client) throw new Error("AWS Cost Explorer client not available");
|
|
62
|
+
const records = [];
|
|
63
|
+
let nextToken;
|
|
64
|
+
do {
|
|
65
|
+
const command = this.buildCommand(options, nextToken);
|
|
66
|
+
const response = await client.send(command);
|
|
67
|
+
if (response.ResultsByTime) for (const period of response.ResultsByTime) {
|
|
68
|
+
const date = period.TimePeriod?.Start ?? options.startDate;
|
|
69
|
+
if (period.Groups) for (const group of period.Groups) {
|
|
70
|
+
const serviceName = group.Keys?.[0] ?? "";
|
|
71
|
+
const metrics = group.Metrics ?? {};
|
|
72
|
+
records.push(this.parseRecord(date, serviceName, metrics));
|
|
73
|
+
}
|
|
74
|
+
else if (period.Total) records.push(this.parseRecord(date, "total", period.Total));
|
|
75
|
+
}
|
|
76
|
+
nextToken = response.NextPageToken;
|
|
77
|
+
} while (nextToken);
|
|
78
|
+
this.cachedServices = [...new Set(records.map((r) => r.service))];
|
|
79
|
+
return records;
|
|
80
|
+
}
|
|
81
|
+
async listServices() {
|
|
82
|
+
if (this.cachedServices) return this.cachedServices;
|
|
83
|
+
const end = /* @__PURE__ */ new Date();
|
|
84
|
+
const start = /* @__PURE__ */ new Date();
|
|
85
|
+
start.setDate(start.getDate() - 30);
|
|
86
|
+
const records = await this.fetchCosts({
|
|
87
|
+
startDate: start.toISOString().slice(0, 10),
|
|
88
|
+
endDate: end.toISOString().slice(0, 10)
|
|
89
|
+
});
|
|
90
|
+
return [...new Set(records.map((r) => r.service))];
|
|
91
|
+
}
|
|
92
|
+
capabilities() {
|
|
93
|
+
return {
|
|
94
|
+
supportsAmount: true,
|
|
95
|
+
supportsUsage: true,
|
|
96
|
+
supportsTags: true,
|
|
97
|
+
supportsRegion: true,
|
|
98
|
+
supportsAccount: true,
|
|
99
|
+
supportsForecast: false
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
getClient() {
|
|
103
|
+
if (this.client) return this.client;
|
|
104
|
+
try {
|
|
105
|
+
const sdk = require("@aws-sdk/client-cost-explorer");
|
|
106
|
+
const clientConfig = {};
|
|
107
|
+
if (this.config.region) clientConfig.region = this.config.region;
|
|
108
|
+
if (this.config.accessKeyId && this.config.secretAccessKey) clientConfig.credentials = {
|
|
109
|
+
accessKeyId: this.config.accessKeyId,
|
|
110
|
+
secretAccessKey: this.config.secretAccessKey
|
|
111
|
+
};
|
|
112
|
+
this.client = new sdk.CostExplorerClient(clientConfig);
|
|
113
|
+
this.commandClass = sdk.GetCostAndUsageCommand;
|
|
114
|
+
return this.client;
|
|
115
|
+
} catch {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
buildCommand(options, nextPageToken) {
|
|
120
|
+
const input = {
|
|
121
|
+
TimePeriod: {
|
|
122
|
+
Start: options.startDate,
|
|
123
|
+
End: options.endDate
|
|
124
|
+
},
|
|
125
|
+
Granularity: "DAILY",
|
|
126
|
+
Metrics: ["UnblendedCost", "UsageQuantity"],
|
|
127
|
+
GroupBy: [{
|
|
128
|
+
Type: "DIMENSION",
|
|
129
|
+
Key: "SERVICE"
|
|
130
|
+
}],
|
|
131
|
+
...nextPageToken ? { NextPageToken: nextPageToken } : {}
|
|
132
|
+
};
|
|
133
|
+
return this.commandClass ? new this.commandClass(input) : input;
|
|
134
|
+
}
|
|
135
|
+
parseRecord(date, rawServiceName, metrics) {
|
|
136
|
+
const cost = metrics.UnblendedCost;
|
|
137
|
+
const usage = metrics.UsageQuantity;
|
|
138
|
+
const amount = cost?.Amount ? Number.parseFloat(cost.Amount) : 0;
|
|
139
|
+
const currency = cost?.Unit ?? "USD";
|
|
140
|
+
const usageValue = usage?.Amount ? Number.parseFloat(usage.Amount) : void 0;
|
|
141
|
+
const usageUnit = usage?.Unit ?? "N/A";
|
|
142
|
+
const record = {
|
|
143
|
+
cloud: "aws",
|
|
144
|
+
service: normalizeServiceName(rawServiceName),
|
|
145
|
+
date,
|
|
146
|
+
amount,
|
|
147
|
+
currency
|
|
148
|
+
};
|
|
149
|
+
if (usageValue !== void 0) record.usage = {
|
|
150
|
+
value: usageValue,
|
|
151
|
+
unit: usageUnit
|
|
152
|
+
};
|
|
153
|
+
return record;
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
//#endregion
|
|
158
|
+
exports.AWSCostAdapter = AWSCostAdapter;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { AdapterCapabilities, CostAdapter, CostRecord } from "@aigne/afs-cost-base";
|
|
2
|
+
|
|
3
|
+
//#region src/adapter.d.ts
|
|
4
|
+
/** Minimal Cost Explorer client interface for dependency injection */
|
|
5
|
+
interface CostExplorerClient {
|
|
6
|
+
send(command: unknown): Promise<GetCostAndUsageResponse>;
|
|
7
|
+
}
|
|
8
|
+
interface GetCostAndUsageResponse {
|
|
9
|
+
ResultsByTime?: ResultByTime[];
|
|
10
|
+
NextPageToken?: string;
|
|
11
|
+
}
|
|
12
|
+
interface ResultByTime {
|
|
13
|
+
TimePeriod?: {
|
|
14
|
+
Start?: string;
|
|
15
|
+
End?: string;
|
|
16
|
+
};
|
|
17
|
+
Groups?: Group[];
|
|
18
|
+
Total?: MetricValues;
|
|
19
|
+
}
|
|
20
|
+
interface Group {
|
|
21
|
+
Keys?: string[];
|
|
22
|
+
Metrics?: MetricValues;
|
|
23
|
+
}
|
|
24
|
+
interface MetricValues {
|
|
25
|
+
[key: string]: {
|
|
26
|
+
Amount?: string;
|
|
27
|
+
Unit?: string;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
interface AWSCostConfig {
|
|
31
|
+
accessKeyId?: string;
|
|
32
|
+
secretAccessKey?: string;
|
|
33
|
+
region?: string;
|
|
34
|
+
}
|
|
35
|
+
declare class AWSCostAdapter implements CostAdapter {
|
|
36
|
+
readonly cloud = "aws";
|
|
37
|
+
private config;
|
|
38
|
+
private client?;
|
|
39
|
+
private commandClass?;
|
|
40
|
+
private cachedServices;
|
|
41
|
+
constructor(config?: AWSCostConfig, client?: CostExplorerClient);
|
|
42
|
+
isAvailable(): Promise<boolean>;
|
|
43
|
+
fetchCosts(options: {
|
|
44
|
+
startDate: string;
|
|
45
|
+
endDate: string;
|
|
46
|
+
}): Promise<CostRecord[]>;
|
|
47
|
+
listServices(): Promise<string[]>;
|
|
48
|
+
capabilities(): AdapterCapabilities;
|
|
49
|
+
private getClient;
|
|
50
|
+
private buildCommand;
|
|
51
|
+
private parseRecord;
|
|
52
|
+
}
|
|
53
|
+
//#endregion
|
|
54
|
+
export { AWSCostAdapter, AWSCostConfig, CostExplorerClient, GetCostAndUsageResponse };
|
|
55
|
+
//# sourceMappingURL=adapter.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.d.cts","names":[],"sources":["../src/adapter.ts"],"mappings":";;;;UAOiB,kBAAA;EACf,IAAA,CAAK,OAAA,YAAmB,OAAA,CAAQ,uBAAA;AAAA;AAAA,UAGjB,uBAAA;EACf,aAAA,GAAgB,YAAA;EAChB,aAAA;AAAA;AAAA,UAGQ,YAAA;EACR,UAAA;IAAe,KAAA;IAAgB,GAAA;EAAA;EAC/B,MAAA,GAAS,KAAA;EACT,KAAA,GAAQ,YAAA;AAAA;AAAA,UAGA,KAAA;EACR,IAAA;EACA,OAAA,GAAU,YAAA;AAAA;AAAA,UAGF,YAAA;EAAA,CACP,GAAA;IAAgB,MAAA;IAAiB,IAAA;EAAA;AAAA;AAAA,UAmDnB,aAAA;EACf,WAAA;EACA,eAAA;EACA,MAAA;AAAA;AAAA,cAGW,cAAA,YAA0B,WAAA;EAAA,SAC5B,KAAA;EAAA,QACD,MAAA;EAAA,QACA,MAAA;EAAA,QACA,YAAA;EAAA,QAGA,cAAA;cAEI,MAAA,GAAQ,aAAA,EAAoB,MAAA,GAAS,kBAAA;EAK3C,WAAA,CAAA,GAAe,OAAA;EAiBf,UAAA,CAAW,OAAA;IAAW,SAAA;IAAmB,OAAA;EAAA,IAAoB,OAAA,CAAQ,UAAA;EAmCrE,YAAA,CAAA,GAAgB,OAAA;EAatB,YAAA,CAAA,GAAgB,mBAAA;EAAA,QAWR,SAAA;EAAA,QAsBA,YAAA;EAAA,QAgBA,WAAA;AAAA"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { AdapterCapabilities, CostAdapter, CostRecord } from "@aigne/afs-cost-base";
|
|
2
|
+
|
|
3
|
+
//#region src/adapter.d.ts
|
|
4
|
+
/** Minimal Cost Explorer client interface for dependency injection */
|
|
5
|
+
interface CostExplorerClient {
|
|
6
|
+
send(command: unknown): Promise<GetCostAndUsageResponse>;
|
|
7
|
+
}
|
|
8
|
+
interface GetCostAndUsageResponse {
|
|
9
|
+
ResultsByTime?: ResultByTime[];
|
|
10
|
+
NextPageToken?: string;
|
|
11
|
+
}
|
|
12
|
+
interface ResultByTime {
|
|
13
|
+
TimePeriod?: {
|
|
14
|
+
Start?: string;
|
|
15
|
+
End?: string;
|
|
16
|
+
};
|
|
17
|
+
Groups?: Group[];
|
|
18
|
+
Total?: MetricValues;
|
|
19
|
+
}
|
|
20
|
+
interface Group {
|
|
21
|
+
Keys?: string[];
|
|
22
|
+
Metrics?: MetricValues;
|
|
23
|
+
}
|
|
24
|
+
interface MetricValues {
|
|
25
|
+
[key: string]: {
|
|
26
|
+
Amount?: string;
|
|
27
|
+
Unit?: string;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
interface AWSCostConfig {
|
|
31
|
+
accessKeyId?: string;
|
|
32
|
+
secretAccessKey?: string;
|
|
33
|
+
region?: string;
|
|
34
|
+
}
|
|
35
|
+
declare class AWSCostAdapter implements CostAdapter {
|
|
36
|
+
readonly cloud = "aws";
|
|
37
|
+
private config;
|
|
38
|
+
private client?;
|
|
39
|
+
private commandClass?;
|
|
40
|
+
private cachedServices;
|
|
41
|
+
constructor(config?: AWSCostConfig, client?: CostExplorerClient);
|
|
42
|
+
isAvailable(): Promise<boolean>;
|
|
43
|
+
fetchCosts(options: {
|
|
44
|
+
startDate: string;
|
|
45
|
+
endDate: string;
|
|
46
|
+
}): Promise<CostRecord[]>;
|
|
47
|
+
listServices(): Promise<string[]>;
|
|
48
|
+
capabilities(): AdapterCapabilities;
|
|
49
|
+
private getClient;
|
|
50
|
+
private buildCommand;
|
|
51
|
+
private parseRecord;
|
|
52
|
+
}
|
|
53
|
+
//#endregion
|
|
54
|
+
export { AWSCostAdapter, AWSCostConfig, CostExplorerClient, GetCostAndUsageResponse };
|
|
55
|
+
//# sourceMappingURL=adapter.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.d.mts","names":[],"sources":["../src/adapter.ts"],"mappings":";;;;UAOiB,kBAAA;EACf,IAAA,CAAK,OAAA,YAAmB,OAAA,CAAQ,uBAAA;AAAA;AAAA,UAGjB,uBAAA;EACf,aAAA,GAAgB,YAAA;EAChB,aAAA;AAAA;AAAA,UAGQ,YAAA;EACR,UAAA;IAAe,KAAA;IAAgB,GAAA;EAAA;EAC/B,MAAA,GAAS,KAAA;EACT,KAAA,GAAQ,YAAA;AAAA;AAAA,UAGA,KAAA;EACR,IAAA;EACA,OAAA,GAAU,YAAA;AAAA;AAAA,UAGF,YAAA;EAAA,CACP,GAAA;IAAgB,MAAA;IAAiB,IAAA;EAAA;AAAA;AAAA,UAmDnB,aAAA;EACf,WAAA;EACA,eAAA;EACA,MAAA;AAAA;AAAA,cAGW,cAAA,YAA0B,WAAA;EAAA,SAC5B,KAAA;EAAA,QACD,MAAA;EAAA,QACA,MAAA;EAAA,QACA,YAAA;EAAA,QAGA,cAAA;cAEI,MAAA,GAAQ,aAAA,EAAoB,MAAA,GAAS,kBAAA;EAK3C,WAAA,CAAA,GAAe,OAAA;EAiBf,UAAA,CAAW,OAAA;IAAW,SAAA;IAAmB,OAAA;EAAA,IAAoB,OAAA,CAAQ,UAAA;EAmCrE,YAAA,CAAA,GAAgB,OAAA;EAatB,YAAA,CAAA,GAAgB,mBAAA;EAAA,QAWR,SAAA;EAAA,QAsBA,YAAA;EAAA,QAgBA,WAAA;AAAA"}
|
package/dist/adapter.mjs
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { __require } from "./_virtual/rolldown_runtime.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/adapter.ts
|
|
4
|
+
const SERVICE_NAME_MAP = {
|
|
5
|
+
"Amazon Elastic Compute Cloud - Compute": "ec2",
|
|
6
|
+
"Amazon Elastic Compute Cloud": "ec2",
|
|
7
|
+
"Amazon Simple Storage Service": "s3",
|
|
8
|
+
"AWS Lambda": "lambda",
|
|
9
|
+
"Amazon Relational Database Service": "rds",
|
|
10
|
+
"Amazon DynamoDB": "dynamodb",
|
|
11
|
+
"Amazon CloudFront": "cloudfront",
|
|
12
|
+
"Amazon Elastic Container Service": "ecs",
|
|
13
|
+
"Amazon Elastic Kubernetes Service": "eks",
|
|
14
|
+
"Amazon ElastiCache": "elasticache",
|
|
15
|
+
"Amazon Elastic Load Balancing": "elb",
|
|
16
|
+
"Amazon Route 53": "route53",
|
|
17
|
+
"Amazon Simple Notification Service": "sns",
|
|
18
|
+
"Amazon Simple Queue Service": "sqs",
|
|
19
|
+
"Amazon CloudWatch": "cloudwatch",
|
|
20
|
+
"AWS Key Management Service": "kms",
|
|
21
|
+
"Amazon Kinesis": "kinesis",
|
|
22
|
+
"Amazon Redshift": "redshift",
|
|
23
|
+
"AWS CodeBuild": "codebuild",
|
|
24
|
+
"Amazon API Gateway": "api-gateway",
|
|
25
|
+
"AWS Secrets Manager": "secrets-manager",
|
|
26
|
+
"Amazon Elastic File System": "efs",
|
|
27
|
+
"AWS Step Functions": "step-functions",
|
|
28
|
+
"AWS Fargate": "fargate",
|
|
29
|
+
"Amazon SageMaker": "sagemaker",
|
|
30
|
+
Tax: "tax"
|
|
31
|
+
};
|
|
32
|
+
function normalizeServiceName(awsName) {
|
|
33
|
+
if (!awsName) return "unknown";
|
|
34
|
+
if (SERVICE_NAME_MAP[awsName]) return SERVICE_NAME_MAP[awsName];
|
|
35
|
+
return awsName.replace(/^(Amazon|AWS)\s+/i, "").replace(/\s*-\s*/g, "-").replace(/\s+/g, "-").toLowerCase();
|
|
36
|
+
}
|
|
37
|
+
var AWSCostAdapter = class {
|
|
38
|
+
cloud = "aws";
|
|
39
|
+
config;
|
|
40
|
+
client;
|
|
41
|
+
commandClass;
|
|
42
|
+
cachedServices = null;
|
|
43
|
+
constructor(config = {}, client) {
|
|
44
|
+
this.config = config;
|
|
45
|
+
this.client = client;
|
|
46
|
+
}
|
|
47
|
+
async isAvailable() {
|
|
48
|
+
try {
|
|
49
|
+
const client = this.getClient();
|
|
50
|
+
if (!client) return false;
|
|
51
|
+
await client.send(this.buildCommand({
|
|
52
|
+
startDate: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
|
|
53
|
+
endDate: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10)
|
|
54
|
+
}));
|
|
55
|
+
return true;
|
|
56
|
+
} catch {
|
|
57
|
+
return false;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
async fetchCosts(options) {
|
|
61
|
+
const client = this.getClient();
|
|
62
|
+
if (!client) throw new Error("AWS Cost Explorer client not available");
|
|
63
|
+
const records = [];
|
|
64
|
+
let nextToken;
|
|
65
|
+
do {
|
|
66
|
+
const command = this.buildCommand(options, nextToken);
|
|
67
|
+
const response = await client.send(command);
|
|
68
|
+
if (response.ResultsByTime) for (const period of response.ResultsByTime) {
|
|
69
|
+
const date = period.TimePeriod?.Start ?? options.startDate;
|
|
70
|
+
if (period.Groups) for (const group of period.Groups) {
|
|
71
|
+
const serviceName = group.Keys?.[0] ?? "";
|
|
72
|
+
const metrics = group.Metrics ?? {};
|
|
73
|
+
records.push(this.parseRecord(date, serviceName, metrics));
|
|
74
|
+
}
|
|
75
|
+
else if (period.Total) records.push(this.parseRecord(date, "total", period.Total));
|
|
76
|
+
}
|
|
77
|
+
nextToken = response.NextPageToken;
|
|
78
|
+
} while (nextToken);
|
|
79
|
+
this.cachedServices = [...new Set(records.map((r) => r.service))];
|
|
80
|
+
return records;
|
|
81
|
+
}
|
|
82
|
+
async listServices() {
|
|
83
|
+
if (this.cachedServices) return this.cachedServices;
|
|
84
|
+
const end = /* @__PURE__ */ new Date();
|
|
85
|
+
const start = /* @__PURE__ */ new Date();
|
|
86
|
+
start.setDate(start.getDate() - 30);
|
|
87
|
+
const records = await this.fetchCosts({
|
|
88
|
+
startDate: start.toISOString().slice(0, 10),
|
|
89
|
+
endDate: end.toISOString().slice(0, 10)
|
|
90
|
+
});
|
|
91
|
+
return [...new Set(records.map((r) => r.service))];
|
|
92
|
+
}
|
|
93
|
+
capabilities() {
|
|
94
|
+
return {
|
|
95
|
+
supportsAmount: true,
|
|
96
|
+
supportsUsage: true,
|
|
97
|
+
supportsTags: true,
|
|
98
|
+
supportsRegion: true,
|
|
99
|
+
supportsAccount: true,
|
|
100
|
+
supportsForecast: false
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
getClient() {
|
|
104
|
+
if (this.client) return this.client;
|
|
105
|
+
try {
|
|
106
|
+
const sdk = __require("@aws-sdk/client-cost-explorer");
|
|
107
|
+
const clientConfig = {};
|
|
108
|
+
if (this.config.region) clientConfig.region = this.config.region;
|
|
109
|
+
if (this.config.accessKeyId && this.config.secretAccessKey) clientConfig.credentials = {
|
|
110
|
+
accessKeyId: this.config.accessKeyId,
|
|
111
|
+
secretAccessKey: this.config.secretAccessKey
|
|
112
|
+
};
|
|
113
|
+
this.client = new sdk.CostExplorerClient(clientConfig);
|
|
114
|
+
this.commandClass = sdk.GetCostAndUsageCommand;
|
|
115
|
+
return this.client;
|
|
116
|
+
} catch {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
buildCommand(options, nextPageToken) {
|
|
121
|
+
const input = {
|
|
122
|
+
TimePeriod: {
|
|
123
|
+
Start: options.startDate,
|
|
124
|
+
End: options.endDate
|
|
125
|
+
},
|
|
126
|
+
Granularity: "DAILY",
|
|
127
|
+
Metrics: ["UnblendedCost", "UsageQuantity"],
|
|
128
|
+
GroupBy: [{
|
|
129
|
+
Type: "DIMENSION",
|
|
130
|
+
Key: "SERVICE"
|
|
131
|
+
}],
|
|
132
|
+
...nextPageToken ? { NextPageToken: nextPageToken } : {}
|
|
133
|
+
};
|
|
134
|
+
return this.commandClass ? new this.commandClass(input) : input;
|
|
135
|
+
}
|
|
136
|
+
parseRecord(date, rawServiceName, metrics) {
|
|
137
|
+
const cost = metrics.UnblendedCost;
|
|
138
|
+
const usage = metrics.UsageQuantity;
|
|
139
|
+
const amount = cost?.Amount ? Number.parseFloat(cost.Amount) : 0;
|
|
140
|
+
const currency = cost?.Unit ?? "USD";
|
|
141
|
+
const usageValue = usage?.Amount ? Number.parseFloat(usage.Amount) : void 0;
|
|
142
|
+
const usageUnit = usage?.Unit ?? "N/A";
|
|
143
|
+
const record = {
|
|
144
|
+
cloud: "aws",
|
|
145
|
+
service: normalizeServiceName(rawServiceName),
|
|
146
|
+
date,
|
|
147
|
+
amount,
|
|
148
|
+
currency
|
|
149
|
+
};
|
|
150
|
+
if (usageValue !== void 0) record.usage = {
|
|
151
|
+
value: usageValue,
|
|
152
|
+
unit: usageUnit
|
|
153
|
+
};
|
|
154
|
+
return record;
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
//#endregion
|
|
159
|
+
export { AWSCostAdapter };
|
|
160
|
+
//# sourceMappingURL=adapter.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.mjs","names":[],"sources":["../src/adapter.ts"],"sourcesContent":["import type { AdapterCapabilities, CostAdapter, CostRecord } from \"@aigne/afs-cost-base\";\n\n// ---------------------------------------------------------------------------\n// AWS SDK types (minimal subset to avoid hard dependency)\n// ---------------------------------------------------------------------------\n\n/** Minimal Cost Explorer client interface for dependency injection */\nexport interface CostExplorerClient {\n send(command: unknown): Promise<GetCostAndUsageResponse>;\n}\n\nexport interface GetCostAndUsageResponse {\n ResultsByTime?: ResultByTime[];\n NextPageToken?: string;\n}\n\ninterface ResultByTime {\n TimePeriod?: { Start?: string; End?: string };\n Groups?: Group[];\n Total?: MetricValues;\n}\n\ninterface Group {\n Keys?: string[];\n Metrics?: MetricValues;\n}\n\ninterface MetricValues {\n [key: string]: { Amount?: string; Unit?: string };\n}\n\n// ---------------------------------------------------------------------------\n// Service name normalization\n// ---------------------------------------------------------------------------\n\nconst SERVICE_NAME_MAP: Record<string, string> = {\n \"Amazon Elastic Compute Cloud - Compute\": \"ec2\",\n \"Amazon Elastic Compute Cloud\": \"ec2\",\n \"Amazon Simple Storage Service\": \"s3\",\n \"AWS Lambda\": \"lambda\",\n \"Amazon Relational Database Service\": \"rds\",\n \"Amazon DynamoDB\": \"dynamodb\",\n \"Amazon CloudFront\": \"cloudfront\",\n \"Amazon Elastic Container Service\": \"ecs\",\n \"Amazon Elastic Kubernetes Service\": \"eks\",\n \"Amazon ElastiCache\": \"elasticache\",\n \"Amazon Elastic Load Balancing\": \"elb\",\n \"Amazon Route 53\": \"route53\",\n \"Amazon Simple Notification Service\": \"sns\",\n \"Amazon Simple Queue Service\": \"sqs\",\n \"Amazon CloudWatch\": \"cloudwatch\",\n \"AWS Key Management Service\": \"kms\",\n \"Amazon Kinesis\": \"kinesis\",\n \"Amazon Redshift\": \"redshift\",\n \"AWS CodeBuild\": \"codebuild\",\n \"Amazon API Gateway\": \"api-gateway\",\n \"AWS Secrets Manager\": \"secrets-manager\",\n \"Amazon Elastic File System\": \"efs\",\n \"AWS Step Functions\": \"step-functions\",\n \"AWS Fargate\": \"fargate\",\n \"Amazon SageMaker\": \"sagemaker\",\n Tax: \"tax\",\n};\n\nfunction normalizeServiceName(awsName: string): string {\n if (!awsName) return \"unknown\";\n if (SERVICE_NAME_MAP[awsName]) return SERVICE_NAME_MAP[awsName];\n // Fallback: kebab-case the full name\n return awsName\n .replace(/^(Amazon|AWS)\\s+/i, \"\")\n .replace(/\\s*-\\s*/g, \"-\")\n .replace(/\\s+/g, \"-\")\n .toLowerCase();\n}\n\n// ---------------------------------------------------------------------------\n// AWSCostAdapter\n// ---------------------------------------------------------------------------\n\nexport interface AWSCostConfig {\n accessKeyId?: string;\n secretAccessKey?: string;\n region?: string;\n}\n\nexport class AWSCostAdapter implements CostAdapter {\n readonly cloud = \"aws\";\n private config: AWSCostConfig;\n private client?: CostExplorerClient;\n private commandClass?: new (\n input: unknown,\n ) => unknown;\n private cachedServices: string[] | null = null;\n\n constructor(config: AWSCostConfig = {}, client?: CostExplorerClient) {\n this.config = config;\n this.client = client;\n }\n\n async isAvailable(): Promise<boolean> {\n try {\n const client = this.getClient();\n if (!client) return false;\n // Try a lightweight API call to check credentials\n await client.send(\n this.buildCommand({\n startDate: new Date().toISOString().slice(0, 10),\n endDate: new Date().toISOString().slice(0, 10),\n }),\n );\n return true;\n } catch {\n return false;\n }\n }\n\n async fetchCosts(options: { startDate: string; endDate: string }): Promise<CostRecord[]> {\n const client = this.getClient();\n if (!client) throw new Error(\"AWS Cost Explorer client not available\");\n\n const records: CostRecord[] = [];\n let nextToken: string | undefined;\n\n do {\n const command = this.buildCommand(options, nextToken);\n const response = await client.send(command);\n\n if (response.ResultsByTime) {\n for (const period of response.ResultsByTime) {\n const date = period.TimePeriod?.Start ?? options.startDate;\n\n if (period.Groups) {\n for (const group of period.Groups) {\n const serviceName = group.Keys?.[0] ?? \"\";\n const metrics = group.Metrics ?? {};\n records.push(this.parseRecord(date, serviceName, metrics));\n }\n } else if (period.Total) {\n // Ungrouped response — single total per time period\n records.push(this.parseRecord(date, \"total\", period.Total));\n }\n }\n }\n\n nextToken = response.NextPageToken;\n } while (nextToken);\n\n this.cachedServices = [...new Set(records.map((r) => r.service))];\n return records;\n }\n\n async listServices(): Promise<string[]> {\n if (this.cachedServices) return this.cachedServices;\n // If no cached data, do a fetch for last 30 days\n const end = new Date();\n const start = new Date();\n start.setDate(start.getDate() - 30);\n const records = await this.fetchCosts({\n startDate: start.toISOString().slice(0, 10),\n endDate: end.toISOString().slice(0, 10),\n });\n return [...new Set(records.map((r) => r.service))];\n }\n\n capabilities(): AdapterCapabilities {\n return {\n supportsAmount: true,\n supportsUsage: true,\n supportsTags: true,\n supportsRegion: true,\n supportsAccount: true,\n supportsForecast: false,\n };\n }\n\n private getClient(): CostExplorerClient | undefined {\n if (this.client) return this.client;\n\n try {\n // @aws-sdk/client-cost-explorer is an optional peer dependency\n const sdk = require(\"@aws-sdk/client-cost-explorer\");\n const clientConfig: Record<string, unknown> = {};\n if (this.config.region) clientConfig.region = this.config.region;\n if (this.config.accessKeyId && this.config.secretAccessKey) {\n clientConfig.credentials = {\n accessKeyId: this.config.accessKeyId,\n secretAccessKey: this.config.secretAccessKey,\n };\n }\n this.client = new sdk.CostExplorerClient(clientConfig) as CostExplorerClient;\n this.commandClass = sdk.GetCostAndUsageCommand;\n return this.client;\n } catch {\n return undefined;\n }\n }\n\n private buildCommand(\n options: { startDate: string; endDate: string },\n nextPageToken?: string,\n ): unknown {\n const input = {\n TimePeriod: { Start: options.startDate, End: options.endDate },\n Granularity: \"DAILY\",\n Metrics: [\"UnblendedCost\", \"UsageQuantity\"],\n GroupBy: [{ Type: \"DIMENSION\", Key: \"SERVICE\" }],\n ...(nextPageToken ? { NextPageToken: nextPageToken } : {}),\n };\n // When using real AWS SDK, wrap in GetCostAndUsageCommand\n // When using DI mock, pass plain object (mock client accepts anything)\n return this.commandClass ? new this.commandClass(input) : input;\n }\n\n private parseRecord(date: string, rawServiceName: string, metrics: MetricValues): CostRecord {\n const cost = metrics.UnblendedCost;\n const usage = metrics.UsageQuantity;\n\n const amount = cost?.Amount ? Number.parseFloat(cost.Amount) : 0;\n const currency = cost?.Unit ?? \"USD\";\n const usageValue = usage?.Amount ? Number.parseFloat(usage.Amount) : undefined;\n const usageUnit = usage?.Unit ?? \"N/A\";\n\n const record: CostRecord = {\n cloud: \"aws\",\n service: normalizeServiceName(rawServiceName),\n date,\n amount,\n currency,\n };\n\n if (usageValue !== undefined) {\n record.usage = { value: usageValue, unit: usageUnit };\n }\n\n return record;\n }\n}\n"],"mappings":";;;AAmCA,MAAM,mBAA2C;CAC/C,0CAA0C;CAC1C,gCAAgC;CAChC,iCAAiC;CACjC,cAAc;CACd,sCAAsC;CACtC,mBAAmB;CACnB,qBAAqB;CACrB,oCAAoC;CACpC,qCAAqC;CACrC,sBAAsB;CACtB,iCAAiC;CACjC,mBAAmB;CACnB,sCAAsC;CACtC,+BAA+B;CAC/B,qBAAqB;CACrB,8BAA8B;CAC9B,kBAAkB;CAClB,mBAAmB;CACnB,iBAAiB;CACjB,sBAAsB;CACtB,uBAAuB;CACvB,8BAA8B;CAC9B,sBAAsB;CACtB,eAAe;CACf,oBAAoB;CACpB,KAAK;CACN;AAED,SAAS,qBAAqB,SAAyB;AACrD,KAAI,CAAC,QAAS,QAAO;AACrB,KAAI,iBAAiB,SAAU,QAAO,iBAAiB;AAEvD,QAAO,QACJ,QAAQ,qBAAqB,GAAG,CAChC,QAAQ,YAAY,IAAI,CACxB,QAAQ,QAAQ,IAAI,CACpB,aAAa;;AAalB,IAAa,iBAAb,MAAmD;CACjD,AAAS,QAAQ;CACjB,AAAQ;CACR,AAAQ;CACR,AAAQ;CAGR,AAAQ,iBAAkC;CAE1C,YAAY,SAAwB,EAAE,EAAE,QAA6B;AACnE,OAAK,SAAS;AACd,OAAK,SAAS;;CAGhB,MAAM,cAAgC;AACpC,MAAI;GACF,MAAM,SAAS,KAAK,WAAW;AAC/B,OAAI,CAAC,OAAQ,QAAO;AAEpB,SAAM,OAAO,KACX,KAAK,aAAa;IAChB,4BAAW,IAAI,MAAM,EAAC,aAAa,CAAC,MAAM,GAAG,GAAG;IAChD,0BAAS,IAAI,MAAM,EAAC,aAAa,CAAC,MAAM,GAAG,GAAG;IAC/C,CAAC,CACH;AACD,UAAO;UACD;AACN,UAAO;;;CAIX,MAAM,WAAW,SAAwE;EACvF,MAAM,SAAS,KAAK,WAAW;AAC/B,MAAI,CAAC,OAAQ,OAAM,IAAI,MAAM,yCAAyC;EAEtE,MAAM,UAAwB,EAAE;EAChC,IAAI;AAEJ,KAAG;GACD,MAAM,UAAU,KAAK,aAAa,SAAS,UAAU;GACrD,MAAM,WAAW,MAAM,OAAO,KAAK,QAAQ;AAE3C,OAAI,SAAS,cACX,MAAK,MAAM,UAAU,SAAS,eAAe;IAC3C,MAAM,OAAO,OAAO,YAAY,SAAS,QAAQ;AAEjD,QAAI,OAAO,OACT,MAAK,MAAM,SAAS,OAAO,QAAQ;KACjC,MAAM,cAAc,MAAM,OAAO,MAAM;KACvC,MAAM,UAAU,MAAM,WAAW,EAAE;AACnC,aAAQ,KAAK,KAAK,YAAY,MAAM,aAAa,QAAQ,CAAC;;aAEnD,OAAO,MAEhB,SAAQ,KAAK,KAAK,YAAY,MAAM,SAAS,OAAO,MAAM,CAAC;;AAKjE,eAAY,SAAS;WACd;AAET,OAAK,iBAAiB,CAAC,GAAG,IAAI,IAAI,QAAQ,KAAK,MAAM,EAAE,QAAQ,CAAC,CAAC;AACjE,SAAO;;CAGT,MAAM,eAAkC;AACtC,MAAI,KAAK,eAAgB,QAAO,KAAK;EAErC,MAAM,sBAAM,IAAI,MAAM;EACtB,MAAM,wBAAQ,IAAI,MAAM;AACxB,QAAM,QAAQ,MAAM,SAAS,GAAG,GAAG;EACnC,MAAM,UAAU,MAAM,KAAK,WAAW;GACpC,WAAW,MAAM,aAAa,CAAC,MAAM,GAAG,GAAG;GAC3C,SAAS,IAAI,aAAa,CAAC,MAAM,GAAG,GAAG;GACxC,CAAC;AACF,SAAO,CAAC,GAAG,IAAI,IAAI,QAAQ,KAAK,MAAM,EAAE,QAAQ,CAAC,CAAC;;CAGpD,eAAoC;AAClC,SAAO;GACL,gBAAgB;GAChB,eAAe;GACf,cAAc;GACd,gBAAgB;GAChB,iBAAiB;GACjB,kBAAkB;GACnB;;CAGH,AAAQ,YAA4C;AAClD,MAAI,KAAK,OAAQ,QAAO,KAAK;AAE7B,MAAI;GAEF,MAAM,gBAAc,gCAAgC;GACpD,MAAM,eAAwC,EAAE;AAChD,OAAI,KAAK,OAAO,OAAQ,cAAa,SAAS,KAAK,OAAO;AAC1D,OAAI,KAAK,OAAO,eAAe,KAAK,OAAO,gBACzC,cAAa,cAAc;IACzB,aAAa,KAAK,OAAO;IACzB,iBAAiB,KAAK,OAAO;IAC9B;AAEH,QAAK,SAAS,IAAI,IAAI,mBAAmB,aAAa;AACtD,QAAK,eAAe,IAAI;AACxB,UAAO,KAAK;UACN;AACN;;;CAIJ,AAAQ,aACN,SACA,eACS;EACT,MAAM,QAAQ;GACZ,YAAY;IAAE,OAAO,QAAQ;IAAW,KAAK,QAAQ;IAAS;GAC9D,aAAa;GACb,SAAS,CAAC,iBAAiB,gBAAgB;GAC3C,SAAS,CAAC;IAAE,MAAM;IAAa,KAAK;IAAW,CAAC;GAChD,GAAI,gBAAgB,EAAE,eAAe,eAAe,GAAG,EAAE;GAC1D;AAGD,SAAO,KAAK,eAAe,IAAI,KAAK,aAAa,MAAM,GAAG;;CAG5D,AAAQ,YAAY,MAAc,gBAAwB,SAAmC;EAC3F,MAAM,OAAO,QAAQ;EACrB,MAAM,QAAQ,QAAQ;EAEtB,MAAM,SAAS,MAAM,SAAS,OAAO,WAAW,KAAK,OAAO,GAAG;EAC/D,MAAM,WAAW,MAAM,QAAQ;EAC/B,MAAM,aAAa,OAAO,SAAS,OAAO,WAAW,MAAM,OAAO,GAAG;EACrE,MAAM,YAAY,OAAO,QAAQ;EAEjC,MAAM,SAAqB;GACzB,OAAO;GACP,SAAS,qBAAqB,eAAe;GAC7C;GACA;GACA;GACD;AAED,MAAI,eAAe,OACjB,QAAO,QAAQ;GAAE,OAAO;GAAY,MAAM;GAAW;AAGvD,SAAO"}
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
const require_adapter = require('./adapter.cjs');
|
|
2
|
+
let _aigne_afs_utils_camelize = require("@aigne/afs/utils/camelize");
|
|
3
|
+
let _aigne_afs_cost_base = require("@aigne/afs-cost-base");
|
|
4
|
+
let zod = require("zod");
|
|
5
|
+
|
|
6
|
+
//#region src/index.ts
|
|
7
|
+
var AFSAwsCost = class AFSAwsCost extends _aigne_afs_cost_base.AFSCostBaseProvider {
|
|
8
|
+
cloudName = "aws";
|
|
9
|
+
static schema() {
|
|
10
|
+
return zod.z.object({
|
|
11
|
+
accessKeyId: zod.z.string().optional(),
|
|
12
|
+
secretAccessKey: zod.z.string().optional(),
|
|
13
|
+
region: zod.z.string().optional(),
|
|
14
|
+
name: zod.z.string().optional(),
|
|
15
|
+
description: zod.z.string().optional(),
|
|
16
|
+
accessMode: zod.z.enum(["readonly", "readwrite"]).optional(),
|
|
17
|
+
startDate: zod.z.string().optional(),
|
|
18
|
+
endDate: zod.z.string().optional(),
|
|
19
|
+
cacheTTL: zod.z.number().optional(),
|
|
20
|
+
dataDir: zod.z.string().optional()
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
static manifest() {
|
|
24
|
+
return {
|
|
25
|
+
name: "aws-cost",
|
|
26
|
+
description: "AWS Cost Explorer billing data with by-service and by-date views.",
|
|
27
|
+
uriTemplate: "aws-cost://{label+}",
|
|
28
|
+
category: "cloud",
|
|
29
|
+
schema: zod.z.object({ label: zod.z.string().optional() }),
|
|
30
|
+
tags: [
|
|
31
|
+
"cost",
|
|
32
|
+
"billing",
|
|
33
|
+
"aws"
|
|
34
|
+
],
|
|
35
|
+
capabilityTags: [
|
|
36
|
+
"read-write",
|
|
37
|
+
"search",
|
|
38
|
+
"auth:token",
|
|
39
|
+
"remote"
|
|
40
|
+
],
|
|
41
|
+
security: {
|
|
42
|
+
riskLevel: "external",
|
|
43
|
+
resourceAccess: ["cloud-api", "internet"],
|
|
44
|
+
dataSensitivity: ["credentials"],
|
|
45
|
+
requires: ["cloud-credentials"]
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
static treeSchema() {
|
|
50
|
+
return {
|
|
51
|
+
operations: [
|
|
52
|
+
"list",
|
|
53
|
+
"read",
|
|
54
|
+
"write",
|
|
55
|
+
"search",
|
|
56
|
+
"stat",
|
|
57
|
+
"explain"
|
|
58
|
+
],
|
|
59
|
+
tree: {
|
|
60
|
+
"/": { kind: "aws-cost:root" },
|
|
61
|
+
"/by-service": { kind: "aws-cost:view" },
|
|
62
|
+
"/by-service/{service}": { kind: "aws-cost:service-summary" },
|
|
63
|
+
"/by-date": { kind: "aws-cost:view" },
|
|
64
|
+
"/by-date/{date}": { kind: "aws-cost:date-summary" }
|
|
65
|
+
},
|
|
66
|
+
auth: {
|
|
67
|
+
type: "custom",
|
|
68
|
+
env: ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY"]
|
|
69
|
+
},
|
|
70
|
+
bestFor: [
|
|
71
|
+
"cost visibility",
|
|
72
|
+
"spend tracking",
|
|
73
|
+
"aws billing"
|
|
74
|
+
],
|
|
75
|
+
notFor: ["real-time data", "budget alerts"]
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
static async load({ basePath, config } = {}) {
|
|
79
|
+
const camelized = (0, _aigne_afs_utils_camelize.camelize)(config ?? {});
|
|
80
|
+
return new AFSAwsCost({
|
|
81
|
+
...await AFSAwsCost.schema().parseAsync(camelized),
|
|
82
|
+
cwd: basePath
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
createAdapter(config) {
|
|
86
|
+
return new require_adapter.AWSCostAdapter({
|
|
87
|
+
accessKeyId: config.accessKeyId,
|
|
88
|
+
secretAccessKey: config.secretAccessKey,
|
|
89
|
+
region: config.region
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
//#endregion
|
|
95
|
+
exports.AFSAwsCost = AFSAwsCost;
|
|
96
|
+
exports.AWSCostAdapter = require_adapter.AWSCostAdapter;
|
|
97
|
+
Object.defineProperty(exports, 'MockCostAdapter', {
|
|
98
|
+
enumerable: true,
|
|
99
|
+
get: function () {
|
|
100
|
+
return _aigne_afs_cost_base.MockCostAdapter;
|
|
101
|
+
}
|
|
102
|
+
});
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { AWSCostAdapter, AWSCostConfig, CostExplorerClient, GetCostAndUsageResponse } from "./adapter.cjs";
|
|
2
|
+
import { AFSModuleLoadParams, ProviderManifest, ProviderTreeSchema } from "@aigne/afs";
|
|
3
|
+
import { AFSCostBaseProvider, AdapterCapabilities, CostAdapter, CostAdapter as CostAdapter$1, CostRecord, MockCostAdapter } from "@aigne/afs-cost-base";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
|
|
6
|
+
//#region src/index.d.ts
|
|
7
|
+
declare class AFSAwsCost extends AFSCostBaseProvider {
|
|
8
|
+
readonly cloudName = "aws";
|
|
9
|
+
static schema(): z.ZodObject<{
|
|
10
|
+
accessKeyId: z.ZodOptional<z.ZodString>;
|
|
11
|
+
secretAccessKey: z.ZodOptional<z.ZodString>;
|
|
12
|
+
region: z.ZodOptional<z.ZodString>;
|
|
13
|
+
name: z.ZodOptional<z.ZodString>;
|
|
14
|
+
description: z.ZodOptional<z.ZodString>;
|
|
15
|
+
accessMode: z.ZodOptional<z.ZodEnum<{
|
|
16
|
+
readonly: "readonly";
|
|
17
|
+
readwrite: "readwrite";
|
|
18
|
+
}>>;
|
|
19
|
+
startDate: z.ZodOptional<z.ZodString>;
|
|
20
|
+
endDate: z.ZodOptional<z.ZodString>;
|
|
21
|
+
cacheTTL: z.ZodOptional<z.ZodNumber>;
|
|
22
|
+
dataDir: z.ZodOptional<z.ZodString>;
|
|
23
|
+
}, z.core.$strip>;
|
|
24
|
+
static manifest(): ProviderManifest;
|
|
25
|
+
static treeSchema(): ProviderTreeSchema;
|
|
26
|
+
static load({
|
|
27
|
+
basePath,
|
|
28
|
+
config
|
|
29
|
+
}?: AFSModuleLoadParams): Promise<AFSAwsCost>;
|
|
30
|
+
protected createAdapter(config: Record<string, unknown>): CostAdapter$1;
|
|
31
|
+
}
|
|
32
|
+
//#endregion
|
|
33
|
+
export { AFSAwsCost, AWSCostAdapter, type AWSCostConfig, type AdapterCapabilities, type CostAdapter, type CostExplorerClient, type CostRecord, type GetCostAndUsageResponse, MockCostAdapter };
|
|
34
|
+
//# sourceMappingURL=index.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;;cAoBa,UAAA,SAAmB,mBAAA;EAAA,SACrB,SAAA;EAAA,OAEF,MAAA,CAAA,GAAM,CAAA,CAAA,SAAA;;;;;;;;;;;;;;;SAeN,QAAA,CAAA,GAAY,gBAAA;EAAA,OAkBZ,UAAA,CAAA,GAAc,kBAAA;EAAA,OAgBR,IAAA,CAAA;IAAO,QAAA;IAAU;EAAA,IAAU,mBAAA,GAAwB,OAAA,CAAA,UAAA;EAAA,UAOtD,aAAA,CAAc,MAAA,EAAQ,MAAA,oBAA0B,aAAA;AAAA"}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { AWSCostAdapter, AWSCostConfig, CostExplorerClient, GetCostAndUsageResponse } from "./adapter.mjs";
|
|
2
|
+
import { AFSCostBaseProvider, AdapterCapabilities, CostAdapter, CostAdapter as CostAdapter$1, CostRecord, MockCostAdapter } from "@aigne/afs-cost-base";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { AFSModuleLoadParams, ProviderManifest, ProviderTreeSchema } from "@aigne/afs";
|
|
5
|
+
|
|
6
|
+
//#region src/index.d.ts
|
|
7
|
+
declare class AFSAwsCost extends AFSCostBaseProvider {
|
|
8
|
+
readonly cloudName = "aws";
|
|
9
|
+
static schema(): z.ZodObject<{
|
|
10
|
+
accessKeyId: z.ZodOptional<z.ZodString>;
|
|
11
|
+
secretAccessKey: z.ZodOptional<z.ZodString>;
|
|
12
|
+
region: z.ZodOptional<z.ZodString>;
|
|
13
|
+
name: z.ZodOptional<z.ZodString>;
|
|
14
|
+
description: z.ZodOptional<z.ZodString>;
|
|
15
|
+
accessMode: z.ZodOptional<z.ZodEnum<{
|
|
16
|
+
readonly: "readonly";
|
|
17
|
+
readwrite: "readwrite";
|
|
18
|
+
}>>;
|
|
19
|
+
startDate: z.ZodOptional<z.ZodString>;
|
|
20
|
+
endDate: z.ZodOptional<z.ZodString>;
|
|
21
|
+
cacheTTL: z.ZodOptional<z.ZodNumber>;
|
|
22
|
+
dataDir: z.ZodOptional<z.ZodString>;
|
|
23
|
+
}, z.core.$strip>;
|
|
24
|
+
static manifest(): ProviderManifest;
|
|
25
|
+
static treeSchema(): ProviderTreeSchema;
|
|
26
|
+
static load({
|
|
27
|
+
basePath,
|
|
28
|
+
config
|
|
29
|
+
}?: AFSModuleLoadParams): Promise<AFSAwsCost>;
|
|
30
|
+
protected createAdapter(config: Record<string, unknown>): CostAdapter$1;
|
|
31
|
+
}
|
|
32
|
+
//#endregion
|
|
33
|
+
export { AFSAwsCost, AWSCostAdapter, type AWSCostConfig, type AdapterCapabilities, type CostAdapter, type CostExplorerClient, type CostRecord, type GetCostAndUsageResponse, MockCostAdapter };
|
|
34
|
+
//# sourceMappingURL=index.d.mts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;;cAoBa,UAAA,SAAmB,mBAAA;EAAA,SACrB,SAAA;EAAA,OAEF,MAAA,CAAA,GAAM,CAAA,CAAA,SAAA;;;;;;;;;;;;;;;SAeN,QAAA,CAAA,GAAY,gBAAA;EAAA,OAkBZ,UAAA,CAAA,GAAc,kBAAA;EAAA,OAgBR,IAAA,CAAA;IAAO,QAAA;IAAU;EAAA,IAAU,mBAAA,GAAwB,OAAA,CAAA,UAAA;EAAA,UAOtD,aAAA,CAAc,MAAA,EAAQ,MAAA,oBAA0B,aAAA;AAAA"}
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { AWSCostAdapter } from "./adapter.mjs";
|
|
2
|
+
import { camelize } from "@aigne/afs/utils/camelize";
|
|
3
|
+
import { AFSCostBaseProvider, MockCostAdapter } from "@aigne/afs-cost-base";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
|
|
6
|
+
//#region src/index.ts
|
|
7
|
+
var AFSAwsCost = class AFSAwsCost extends AFSCostBaseProvider {
|
|
8
|
+
cloudName = "aws";
|
|
9
|
+
static schema() {
|
|
10
|
+
return z.object({
|
|
11
|
+
accessKeyId: z.string().optional(),
|
|
12
|
+
secretAccessKey: z.string().optional(),
|
|
13
|
+
region: z.string().optional(),
|
|
14
|
+
name: z.string().optional(),
|
|
15
|
+
description: z.string().optional(),
|
|
16
|
+
accessMode: z.enum(["readonly", "readwrite"]).optional(),
|
|
17
|
+
startDate: z.string().optional(),
|
|
18
|
+
endDate: z.string().optional(),
|
|
19
|
+
cacheTTL: z.number().optional(),
|
|
20
|
+
dataDir: z.string().optional()
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
static manifest() {
|
|
24
|
+
return {
|
|
25
|
+
name: "aws-cost",
|
|
26
|
+
description: "AWS Cost Explorer billing data with by-service and by-date views.",
|
|
27
|
+
uriTemplate: "aws-cost://{label+}",
|
|
28
|
+
category: "cloud",
|
|
29
|
+
schema: z.object({ label: z.string().optional() }),
|
|
30
|
+
tags: [
|
|
31
|
+
"cost",
|
|
32
|
+
"billing",
|
|
33
|
+
"aws"
|
|
34
|
+
],
|
|
35
|
+
capabilityTags: [
|
|
36
|
+
"read-write",
|
|
37
|
+
"search",
|
|
38
|
+
"auth:token",
|
|
39
|
+
"remote"
|
|
40
|
+
],
|
|
41
|
+
security: {
|
|
42
|
+
riskLevel: "external",
|
|
43
|
+
resourceAccess: ["cloud-api", "internet"],
|
|
44
|
+
dataSensitivity: ["credentials"],
|
|
45
|
+
requires: ["cloud-credentials"]
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
static treeSchema() {
|
|
50
|
+
return {
|
|
51
|
+
operations: [
|
|
52
|
+
"list",
|
|
53
|
+
"read",
|
|
54
|
+
"write",
|
|
55
|
+
"search",
|
|
56
|
+
"stat",
|
|
57
|
+
"explain"
|
|
58
|
+
],
|
|
59
|
+
tree: {
|
|
60
|
+
"/": { kind: "aws-cost:root" },
|
|
61
|
+
"/by-service": { kind: "aws-cost:view" },
|
|
62
|
+
"/by-service/{service}": { kind: "aws-cost:service-summary" },
|
|
63
|
+
"/by-date": { kind: "aws-cost:view" },
|
|
64
|
+
"/by-date/{date}": { kind: "aws-cost:date-summary" }
|
|
65
|
+
},
|
|
66
|
+
auth: {
|
|
67
|
+
type: "custom",
|
|
68
|
+
env: ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY"]
|
|
69
|
+
},
|
|
70
|
+
bestFor: [
|
|
71
|
+
"cost visibility",
|
|
72
|
+
"spend tracking",
|
|
73
|
+
"aws billing"
|
|
74
|
+
],
|
|
75
|
+
notFor: ["real-time data", "budget alerts"]
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
static async load({ basePath, config } = {}) {
|
|
79
|
+
const camelized = camelize(config ?? {});
|
|
80
|
+
return new AFSAwsCost({
|
|
81
|
+
...await AFSAwsCost.schema().parseAsync(camelized),
|
|
82
|
+
cwd: basePath
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
createAdapter(config) {
|
|
86
|
+
return new AWSCostAdapter({
|
|
87
|
+
accessKeyId: config.accessKeyId,
|
|
88
|
+
secretAccessKey: config.secretAccessKey,
|
|
89
|
+
region: config.region
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
//#endregion
|
|
95
|
+
export { AFSAwsCost, AWSCostAdapter, MockCostAdapter };
|
|
96
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/index.ts"],"sourcesContent":["import type { AFSModuleLoadParams, ProviderManifest, ProviderTreeSchema } from \"@aigne/afs\";\nimport { camelize } from \"@aigne/afs/utils/camelize\";\nimport {\n type AFSCostBaseOptions,\n AFSCostBaseProvider,\n type CostAdapter,\n} from \"@aigne/afs-cost-base\";\nimport { z } from \"zod\";\n\nimport { AWSCostAdapter, type AWSCostConfig } from \"./adapter.js\";\n\nexport type { AdapterCapabilities, CostAdapter, CostRecord } from \"@aigne/afs-cost-base\";\nexport { MockCostAdapter } from \"@aigne/afs-cost-base\";\nexport type { AWSCostConfig, CostExplorerClient, GetCostAndUsageResponse } from \"./adapter.js\";\nexport { AWSCostAdapter } from \"./adapter.js\";\n\n// =============================================================================\n// AFSAwsCost Provider\n// =============================================================================\n\nexport class AFSAwsCost extends AFSCostBaseProvider {\n readonly cloudName = \"aws\";\n\n static schema() {\n return z.object({\n accessKeyId: z.string().optional(),\n secretAccessKey: z.string().optional(),\n region: z.string().optional(),\n name: z.string().optional(),\n description: z.string().optional(),\n accessMode: z.enum([\"readonly\", \"readwrite\"]).optional(),\n startDate: z.string().optional(),\n endDate: z.string().optional(),\n cacheTTL: z.number().optional(),\n dataDir: z.string().optional(),\n });\n }\n\n static manifest(): ProviderManifest {\n return {\n name: \"aws-cost\",\n description: \"AWS Cost Explorer billing data with by-service and by-date views.\",\n uriTemplate: \"aws-cost://{label+}\",\n category: \"cloud\",\n schema: z.object({ label: z.string().optional() }),\n tags: [\"cost\", \"billing\", \"aws\"],\n capabilityTags: [\"read-write\", \"search\", \"auth:token\", \"remote\"],\n security: {\n riskLevel: \"external\",\n resourceAccess: [\"cloud-api\", \"internet\"],\n dataSensitivity: [\"credentials\"],\n requires: [\"cloud-credentials\"],\n },\n };\n }\n\n static treeSchema(): ProviderTreeSchema {\n return {\n operations: [\"list\", \"read\", \"write\", \"search\", \"stat\", \"explain\"],\n tree: {\n \"/\": { kind: \"aws-cost:root\" },\n \"/by-service\": { kind: \"aws-cost:view\" },\n \"/by-service/{service}\": { kind: \"aws-cost:service-summary\" },\n \"/by-date\": { kind: \"aws-cost:view\" },\n \"/by-date/{date}\": { kind: \"aws-cost:date-summary\" },\n },\n auth: { type: \"custom\", env: [\"AWS_ACCESS_KEY_ID\", \"AWS_SECRET_ACCESS_KEY\"] },\n bestFor: [\"cost visibility\", \"spend tracking\", \"aws billing\"],\n notFor: [\"real-time data\", \"budget alerts\"],\n };\n }\n\n static async load({ basePath, config }: AFSModuleLoadParams = {}) {\n const camelized = camelize(config ?? {});\n const valid = await AFSAwsCost.schema().parseAsync(camelized);\n return new AFSAwsCost({ ...valid, cwd: basePath } as AFSCostBaseOptions &\n Record<string, unknown>);\n }\n\n protected createAdapter(config: Record<string, unknown>): CostAdapter {\n const adapterConfig: AWSCostConfig = {\n accessKeyId: config.accessKeyId as string | undefined,\n secretAccessKey: config.secretAccessKey as string | undefined,\n region: config.region as string | undefined,\n };\n return new AWSCostAdapter(adapterConfig);\n }\n}\n"],"mappings":";;;;;;AAoBA,IAAa,aAAb,MAAa,mBAAmB,oBAAoB;CAClD,AAAS,YAAY;CAErB,OAAO,SAAS;AACd,SAAO,EAAE,OAAO;GACd,aAAa,EAAE,QAAQ,CAAC,UAAU;GAClC,iBAAiB,EAAE,QAAQ,CAAC,UAAU;GACtC,QAAQ,EAAE,QAAQ,CAAC,UAAU;GAC7B,MAAM,EAAE,QAAQ,CAAC,UAAU;GAC3B,aAAa,EAAE,QAAQ,CAAC,UAAU;GAClC,YAAY,EAAE,KAAK,CAAC,YAAY,YAAY,CAAC,CAAC,UAAU;GACxD,WAAW,EAAE,QAAQ,CAAC,UAAU;GAChC,SAAS,EAAE,QAAQ,CAAC,UAAU;GAC9B,UAAU,EAAE,QAAQ,CAAC,UAAU;GAC/B,SAAS,EAAE,QAAQ,CAAC,UAAU;GAC/B,CAAC;;CAGJ,OAAO,WAA6B;AAClC,SAAO;GACL,MAAM;GACN,aAAa;GACb,aAAa;GACb,UAAU;GACV,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,UAAU,EAAE,CAAC;GAClD,MAAM;IAAC;IAAQ;IAAW;IAAM;GAChC,gBAAgB;IAAC;IAAc;IAAU;IAAc;IAAS;GAChE,UAAU;IACR,WAAW;IACX,gBAAgB,CAAC,aAAa,WAAW;IACzC,iBAAiB,CAAC,cAAc;IAChC,UAAU,CAAC,oBAAoB;IAChC;GACF;;CAGH,OAAO,aAAiC;AACtC,SAAO;GACL,YAAY;IAAC;IAAQ;IAAQ;IAAS;IAAU;IAAQ;IAAU;GAClE,MAAM;IACJ,KAAK,EAAE,MAAM,iBAAiB;IAC9B,eAAe,EAAE,MAAM,iBAAiB;IACxC,yBAAyB,EAAE,MAAM,4BAA4B;IAC7D,YAAY,EAAE,MAAM,iBAAiB;IACrC,mBAAmB,EAAE,MAAM,yBAAyB;IACrD;GACD,MAAM;IAAE,MAAM;IAAU,KAAK,CAAC,qBAAqB,wBAAwB;IAAE;GAC7E,SAAS;IAAC;IAAmB;IAAkB;IAAc;GAC7D,QAAQ,CAAC,kBAAkB,gBAAgB;GAC5C;;CAGH,aAAa,KAAK,EAAE,UAAU,WAAgC,EAAE,EAAE;EAChE,MAAM,YAAY,SAAS,UAAU,EAAE,CAAC;AAExC,SAAO,IAAI,WAAW;GAAE,GADV,MAAM,WAAW,QAAQ,CAAC,WAAW,UAAU;GAC3B,KAAK;GAAU,CACvB;;CAG5B,AAAU,cAAc,QAA8C;AAMpE,SAAO,IAAI,eAL0B;GACnC,aAAa,OAAO;GACpB,iBAAiB,OAAO;GACxB,QAAQ,OAAO;GAChB,CACuC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@aigne/afs-aws-cost",
|
|
3
|
+
"version": "1.11.0-beta.12",
|
|
4
|
+
"description": "AIGNE AFS provider for AWS Cost Explorer billing data",
|
|
5
|
+
"license": "UNLICENSED",
|
|
6
|
+
"publishConfig": {
|
|
7
|
+
"access": "public"
|
|
8
|
+
},
|
|
9
|
+
"author": "Arcblock <blocklet@arcblock.io> https://github.com/arcblock",
|
|
10
|
+
"homepage": "https://github.com/arcblock/afs",
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "git+https://github.com/arcblock/afs"
|
|
14
|
+
},
|
|
15
|
+
"bugs": {
|
|
16
|
+
"url": "https://github.com/arcblock/afs/issues"
|
|
17
|
+
},
|
|
18
|
+
"type": "module",
|
|
19
|
+
"main": "./dist/index.cjs",
|
|
20
|
+
"module": "./dist/index.mjs",
|
|
21
|
+
"types": "./dist/index.d.cts",
|
|
22
|
+
"exports": {
|
|
23
|
+
".": {
|
|
24
|
+
"require": "./dist/index.cjs",
|
|
25
|
+
"import": "./dist/index.mjs"
|
|
26
|
+
},
|
|
27
|
+
"./*": "./*"
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"dist",
|
|
31
|
+
"LICENSE",
|
|
32
|
+
"README.md",
|
|
33
|
+
"CHANGELOG.md"
|
|
34
|
+
],
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"@aws-sdk/client-cost-explorer": "^3.0.0",
|
|
37
|
+
"ufo": "^1.6.3",
|
|
38
|
+
"zod": "^4.0.0",
|
|
39
|
+
"@aigne/afs": "^1.11.0-beta.12",
|
|
40
|
+
"@aigne/afs-cost-base": "^1.11.0-beta.12"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@types/bun": "latest",
|
|
44
|
+
"npm-run-all": "^4.1.5",
|
|
45
|
+
"rimraf": "^6.1.2",
|
|
46
|
+
"tsdown": "0.20.0-beta.3",
|
|
47
|
+
"typescript": "5.9.2",
|
|
48
|
+
"@aigne/afs-testing": "1.11.0-beta.12",
|
|
49
|
+
"@aigne/scripts": "0.0.0",
|
|
50
|
+
"@aigne/typescript-config": "0.0.0"
|
|
51
|
+
},
|
|
52
|
+
"scripts": {
|
|
53
|
+
"build": "tsdown",
|
|
54
|
+
"check-types": "tsc --noEmit",
|
|
55
|
+
"clean": "rimraf dist coverage",
|
|
56
|
+
"test": "bun test",
|
|
57
|
+
"test:coverage": "bun test --coverage --coverage-reporter=lcov --coverage-reporter=text"
|
|
58
|
+
}
|
|
59
|
+
}
|