@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 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
@@ -0,0 +1,7 @@
1
+ import { createRequire } from "node:module";
2
+
3
+ //#region rolldown:runtime
4
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
5
+
6
+ //#endregion
7
+ export { __require };
@@ -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"}
@@ -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":"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"}
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
+ });
@@ -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"}
@@ -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
+ }