@opscompass/skills 0.1.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/README.md +78 -0
- package/manifest.json +35 -0
- package/package.json +23 -0
- package/skills/opscompass-oracle-licensing/.opscompass-runtime/tools/cli-entrypoint.js +15 -0
- package/skills/opscompass-oracle-licensing/SKILL.md +211 -0
- package/skills/opscompass-oracle-licensing/api/entitlement-compliance/entitlement-compliance.json +189 -0
- package/skills/opscompass-oracle-licensing/api/entitlement-deployment/entitlement-deployment +26 -0
- package/skills/opscompass-oracle-licensing/api/entitlement-usage/entitlement-usage.json +114 -0
- package/skills/opscompass-oracle-licensing/assets/mock-entitlements/deployment-details.json +61 -0
- package/skills/opscompass-oracle-licensing/assets/mock-entitlements/license-usage-map.json +70 -0
- package/skills/opscompass-oracle-licensing/assets/mock-entitlements/overall-compliance.json +36 -0
- package/skills/opscompass-oracle-licensing/package.json +3 -0
- package/skills/opscompass-oracle-licensing/references/evidence-model.md +90 -0
- package/skills/opscompass-oracle-licensing/references/report-structure.md +54 -0
- package/skills/opscompass-oracle-licensing/scripts/fake-entitlements-api.tool.js +31 -0
- package/skills/opscompass-oracle-licensing/scripts/fake_entitlements_api.js +72 -0
- package/skills/opscompass-oracle-licensing/scripts/tools.js +2 -0
- package/skills/opscompass-resources/.opscompass-runtime/tools/cli-entrypoint.js +15 -0
- package/skills/opscompass-resources/.opscompass-runtime/tools/index.js +2 -0
- package/skills/opscompass-resources/.opscompass-runtime/tools/opsc-cli-context.js +24 -0
- package/skills/opscompass-resources/.opscompass-runtime/tools/opsc-cli-defaults.js +125 -0
- package/skills/opscompass-resources/.opscompass-runtime/tools/opsc-cli.js +90 -0
- package/skills/opscompass-resources/SKILL.md +335 -0
- package/skills/opscompass-resources/package.json +3 -0
- package/skills/opscompass-resources/references/resources-commands.md +178 -0
- package/skills/opscompass-resources/scripts/build-resources-command.tool.js +53 -0
- package/skills/opscompass-resources/scripts/build_resources_command.js +240 -0
- package/skills/opscompass-resources/scripts/list-and-fetch-latest-configs.tool.js +116 -0
- package/skills/opscompass-resources/scripts/list_and_fetch_latest_configs.js +714 -0
- package/skills/opscompass-resources/scripts/map-oracle-workloads-to-platform-cpus.tool.js +24 -0
- package/skills/opscompass-resources/scripts/map_oracle_workloads_to_platform_cpus.js +273 -0
- package/skills/opscompass-resources/scripts/opscompass-defaults.tool.js +80 -0
- package/skills/opscompass-resources/scripts/opscompass-resources.tool.js +51 -0
- package/skills/opscompass-resources/scripts/summarize-latest-configuration.tool.js +24 -0
- package/skills/opscompass-resources/scripts/summarize-resources-list.tool.js +24 -0
- package/skills/opscompass-resources/scripts/summarize_latest_configuration.js +254 -0
- package/skills/opscompass-resources/scripts/summarize_resources_list.js +126 -0
- package/skills/opscompass-resources/scripts/tools.js +16 -0
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { isMainModule } from "../.opscompass-runtime/tools/cli-entrypoint.js";
|
|
3
|
+
const COMMON_DOC_KEYS = [
|
|
4
|
+
"hostname",
|
|
5
|
+
"name",
|
|
6
|
+
"type",
|
|
7
|
+
"platform",
|
|
8
|
+
"version",
|
|
9
|
+
"edition",
|
|
10
|
+
"created",
|
|
11
|
+
"mostRecentCollection",
|
|
12
|
+
"lastSampleDate",
|
|
13
|
+
"dbid",
|
|
14
|
+
"engine",
|
|
15
|
+
"engineVersion",
|
|
16
|
+
"instanceType",
|
|
17
|
+
"size",
|
|
18
|
+
];
|
|
19
|
+
function parseArgs(argv) {
|
|
20
|
+
const args = { top: 5, input: null };
|
|
21
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
22
|
+
if (argv[i] === "--input") {
|
|
23
|
+
if (++i >= argv.length)
|
|
24
|
+
throw new Error("argument --input: expected one argument");
|
|
25
|
+
args.input = argv[i];
|
|
26
|
+
}
|
|
27
|
+
else if (argv[i] === "--top") {
|
|
28
|
+
if (++i >= argv.length)
|
|
29
|
+
throw new Error("argument --top: expected one argument");
|
|
30
|
+
args.top = Number(argv[i]);
|
|
31
|
+
if (!Number.isInteger(args.top))
|
|
32
|
+
throw new Error(`argument --top: invalid int value: '${argv[i]}'`);
|
|
33
|
+
}
|
|
34
|
+
else if (argv[i] === "--help" || argv[i] === "-h") {
|
|
35
|
+
console.log("Summarize OpsCompass get-latest-configuration output.");
|
|
36
|
+
process.exit(0);
|
|
37
|
+
}
|
|
38
|
+
else
|
|
39
|
+
throw new Error(`unrecognized arguments: ${argv[i]}`);
|
|
40
|
+
}
|
|
41
|
+
return args;
|
|
42
|
+
}
|
|
43
|
+
async function loadPayload(filePath) {
|
|
44
|
+
if (filePath)
|
|
45
|
+
return JSON.parse(readFileSync(filePath, "utf8"));
|
|
46
|
+
const chunks = [];
|
|
47
|
+
for await (const chunk of process.stdin)
|
|
48
|
+
chunks.push(chunk);
|
|
49
|
+
return JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
50
|
+
}
|
|
51
|
+
export function asConfigList(payload) {
|
|
52
|
+
if (payload !== null &&
|
|
53
|
+
typeof payload === "object" &&
|
|
54
|
+
!Array.isArray(payload))
|
|
55
|
+
return [payload];
|
|
56
|
+
if (Array.isArray(payload))
|
|
57
|
+
return payload.filter((item) => item !== null && typeof item === "object" && !Array.isArray(item));
|
|
58
|
+
throw new Error("expected a JSON object or array of objects");
|
|
59
|
+
}
|
|
60
|
+
const getDict = (obj, key) => obj[key] !== null && typeof obj[key] === "object" && !Array.isArray(obj[key])
|
|
61
|
+
? obj[key]
|
|
62
|
+
: {};
|
|
63
|
+
export function scalarPreview(value) {
|
|
64
|
+
if (value === null || value === undefined)
|
|
65
|
+
return "null";
|
|
66
|
+
if (typeof value === "boolean")
|
|
67
|
+
return value ? "true" : "false";
|
|
68
|
+
const text = String(value);
|
|
69
|
+
return text.length <= 80 ? text : `${text.slice(0, 77)}...`;
|
|
70
|
+
}
|
|
71
|
+
export function summarizeUsageConcern(name, value) {
|
|
72
|
+
if (value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
73
|
+
const concern = value;
|
|
74
|
+
const details = [
|
|
75
|
+
"name",
|
|
76
|
+
"feature",
|
|
77
|
+
"product",
|
|
78
|
+
"option",
|
|
79
|
+
"pack",
|
|
80
|
+
"value",
|
|
81
|
+
"usage",
|
|
82
|
+
"detected",
|
|
83
|
+
"isConcerning",
|
|
84
|
+
"message",
|
|
85
|
+
]
|
|
86
|
+
.filter((key) => Object.hasOwn(concern, key) &&
|
|
87
|
+
(concern[key] === null || typeof concern[key] !== "object"))
|
|
88
|
+
.map((key) => `${key}=${scalarPreview(concern[key])}`);
|
|
89
|
+
return details.length
|
|
90
|
+
? `${name}: ${details.join(", ")}`
|
|
91
|
+
: `${name}: object (${Object.keys(value).length} keys)`;
|
|
92
|
+
}
|
|
93
|
+
return `${name}: ${scalarPreview(value)}`;
|
|
94
|
+
}
|
|
95
|
+
export function summarizeOne(cfg, topN) {
|
|
96
|
+
const lines = [], doc = getDict(cfg, "document");
|
|
97
|
+
const cfgId = scalarPreview(cfg.id), cfgName = scalarPreview(cfg.name), cfgType = scalarPreview(cfg.type), cfgLocation = scalarPreview(cfg.location), accountId = scalarPreview(cfg.accountId);
|
|
98
|
+
lines.push(`Resource: ${cfgName} (type=${cfgType}, id=${cfgId})`, `- accountId: ${accountId}`, `- location: ${cfgLocation}`);
|
|
99
|
+
if (Object.keys(doc).length) {
|
|
100
|
+
lines.push("- document scalar fields:");
|
|
101
|
+
let shown = false;
|
|
102
|
+
for (const key of COMMON_DOC_KEYS)
|
|
103
|
+
if (Object.hasOwn(doc, key) &&
|
|
104
|
+
(doc[key] === null || typeof doc[key] !== "object")) {
|
|
105
|
+
lines.push(` - ${key}: ${scalarPreview(doc[key])}`);
|
|
106
|
+
shown = true;
|
|
107
|
+
}
|
|
108
|
+
const extras = Object.keys(doc)
|
|
109
|
+
.filter((key) => !COMMON_DOC_KEYS.includes(key) &&
|
|
110
|
+
(doc[key] === null || typeof doc[key] !== "object"))
|
|
111
|
+
.sort();
|
|
112
|
+
for (const key of extras.slice(0, Math.max(0, topN - (shown ? 3 : 0)))) {
|
|
113
|
+
lines.push(` - ${key}: ${scalarPreview(doc[key])}`);
|
|
114
|
+
shown = true;
|
|
115
|
+
}
|
|
116
|
+
if (!shown)
|
|
117
|
+
lines.push(" - none");
|
|
118
|
+
const nested = Object.keys(doc)
|
|
119
|
+
.filter((key) => doc[key] !== null && typeof doc[key] === "object")
|
|
120
|
+
.sort();
|
|
121
|
+
if (nested.length) {
|
|
122
|
+
lines.push("- document nested sections:");
|
|
123
|
+
for (const key of nested.slice(0, topN)) {
|
|
124
|
+
const value = doc[key];
|
|
125
|
+
lines.push(` - ${key}: ${Array.isArray(value) ? `array (${value.length} items)` : `object (${Object.keys(value).length} keys)`}`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
const concerns = getDict(doc, "productUsageConcerns");
|
|
129
|
+
if (Object.keys(concerns).length) {
|
|
130
|
+
lines.push(`- productUsageConcerns: ${Object.keys(concerns).length} concern(s)`);
|
|
131
|
+
for (const [name, value] of Object.entries(concerns).slice(0, topN))
|
|
132
|
+
lines.push(` - ${summarizeUsageConcern(name, value)}`);
|
|
133
|
+
}
|
|
134
|
+
else if (cfgType === "databases/oracle")
|
|
135
|
+
lines.push("- productUsageConcerns: none");
|
|
136
|
+
const parameters = getDict(doc, "parameters");
|
|
137
|
+
if (Object.keys(parameters).length) {
|
|
138
|
+
const concerning = Object.entries(parameters)
|
|
139
|
+
.filter((entry) => {
|
|
140
|
+
const value = entry[1];
|
|
141
|
+
return (value !== null &&
|
|
142
|
+
typeof value === "object" &&
|
|
143
|
+
!Array.isArray(value) &&
|
|
144
|
+
"isConcerning" in value &&
|
|
145
|
+
value.isConcerning === true);
|
|
146
|
+
})
|
|
147
|
+
.map(([name, value]) => [name, scalarPreview(value.value)]);
|
|
148
|
+
lines.push(`- parameters: ${Object.keys(parameters).length} total, ${concerning.length} concerning`);
|
|
149
|
+
if (concerning.length) {
|
|
150
|
+
lines.push("- concerning parameters:");
|
|
151
|
+
for (const [name, value] of concerning.slice(0, topN))
|
|
152
|
+
lines.push(` - ${name}: ${value}`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
else
|
|
157
|
+
lines.push("- document: missing or not an object");
|
|
158
|
+
return lines;
|
|
159
|
+
}
|
|
160
|
+
function bump(map, key) {
|
|
161
|
+
map.set(key, (map.get(key) || 0) + 1);
|
|
162
|
+
}
|
|
163
|
+
export function summarizeMany(configs, topN) {
|
|
164
|
+
if (configs.length === 1)
|
|
165
|
+
return summarizeOne(configs[0], topN).join("\n");
|
|
166
|
+
const byType = new Map(), byLocation = new Map(), scalarByType = new Map(), params = [], usage = [];
|
|
167
|
+
for (const cfg of configs) {
|
|
168
|
+
const type = String(cfg.type || "unknown");
|
|
169
|
+
bump(byType, type);
|
|
170
|
+
bump(byLocation, String(cfg.location || "unknown"));
|
|
171
|
+
const doc = getDict(cfg, "document");
|
|
172
|
+
if (!scalarByType.has(type))
|
|
173
|
+
scalarByType.set(type, new Map());
|
|
174
|
+
for (const [key, value] of Object.entries(doc))
|
|
175
|
+
if (value === null || typeof value !== "object")
|
|
176
|
+
bump(scalarByType.get(type), key);
|
|
177
|
+
const parameterValues = getDict(doc, "parameters");
|
|
178
|
+
const concerning = Object.values(parameterValues).filter((value) => value !== null &&
|
|
179
|
+
typeof value === "object" &&
|
|
180
|
+
!Array.isArray(value) &&
|
|
181
|
+
"isConcerning" in value &&
|
|
182
|
+
value.isConcerning === true).length;
|
|
183
|
+
const name = String(cfg.name || "(unnamed)");
|
|
184
|
+
if (concerning > 0)
|
|
185
|
+
params.push([name, concerning]);
|
|
186
|
+
const concerns = getDict(doc, "productUsageConcerns");
|
|
187
|
+
if (Object.keys(concerns).length)
|
|
188
|
+
usage.push([name, Object.keys(concerns).length]);
|
|
189
|
+
}
|
|
190
|
+
const mostCommon = (map, limit) => [...map].sort((a, b) => b[1] - a[1]).slice(0, limit);
|
|
191
|
+
const lines = [
|
|
192
|
+
`Total configurations: ${configs.length}`,
|
|
193
|
+
"",
|
|
194
|
+
"Configurations by type:",
|
|
195
|
+
];
|
|
196
|
+
for (const [key, count] of mostCommon(byType, Infinity))
|
|
197
|
+
lines.push(`- ${key}: ${count}`);
|
|
198
|
+
lines.push("", "Top locations:");
|
|
199
|
+
for (const [key, count] of mostCommon(byLocation, topN))
|
|
200
|
+
lines.push(`- ${key}: ${count}`);
|
|
201
|
+
lines.push("", "Document scalar fields commonly present by type:");
|
|
202
|
+
for (const [type, map] of [...scalarByType].sort((a, b) => a[0].localeCompare(b[0])))
|
|
203
|
+
lines.push(`- ${type}: ${mostCommon(map, topN)
|
|
204
|
+
.map(([key]) => key)
|
|
205
|
+
.join(", ") || "none"}`);
|
|
206
|
+
lines.push("");
|
|
207
|
+
if (params.length) {
|
|
208
|
+
params.sort((a, b) => b[1] - a[1]);
|
|
209
|
+
lines.push(`Top ${topN} resources by concerning parameter count:`);
|
|
210
|
+
for (const [name, count] of params.slice(0, topN))
|
|
211
|
+
lines.push(`- ${name}: ${count}`);
|
|
212
|
+
}
|
|
213
|
+
else
|
|
214
|
+
lines.push("No concerning parameters found in provided configurations.");
|
|
215
|
+
lines.push("");
|
|
216
|
+
if (usage.length) {
|
|
217
|
+
usage.sort((a, b) => b[1] - a[1]);
|
|
218
|
+
lines.push(`Top ${topN} resources by product usage concern count:`);
|
|
219
|
+
for (const [name, count] of usage.slice(0, topN))
|
|
220
|
+
lines.push(`- ${name}: ${count}`);
|
|
221
|
+
}
|
|
222
|
+
else
|
|
223
|
+
lines.push("No product usage concerns found in provided configurations.");
|
|
224
|
+
lines.push("", "Per-resource snapshots:");
|
|
225
|
+
for (const cfg of configs.slice(0, topN))
|
|
226
|
+
for (const line of summarizeOne(cfg, 3))
|
|
227
|
+
lines.push(line.startsWith("Resource:") ? `- ${line}` : ` ${line}`);
|
|
228
|
+
return lines.join("\n");
|
|
229
|
+
}
|
|
230
|
+
export async function main(argv = process.argv.slice(2)) {
|
|
231
|
+
try {
|
|
232
|
+
const args = parseArgs(argv);
|
|
233
|
+
if (args.top <= 0) {
|
|
234
|
+
console.error("Error: --top must be greater than 0.");
|
|
235
|
+
return 2;
|
|
236
|
+
}
|
|
237
|
+
const configs = asConfigList(await loadPayload(args.input));
|
|
238
|
+
if (!configs.length) {
|
|
239
|
+
console.log("No configuration objects found.");
|
|
240
|
+
return 0;
|
|
241
|
+
}
|
|
242
|
+
console.log(summarizeMany(configs, args.top));
|
|
243
|
+
return 0;
|
|
244
|
+
}
|
|
245
|
+
catch (error) {
|
|
246
|
+
console.error(error instanceof SyntaxError
|
|
247
|
+
? `Error: invalid JSON: ${error.message}`
|
|
248
|
+
: `Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
249
|
+
return 2;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
if (isMainModule(import.meta.url)) {
|
|
253
|
+
process.exitCode = await main();
|
|
254
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { isMainModule } from "../.opscompass-runtime/tools/cli-entrypoint.js";
|
|
3
|
+
function parseArgs(argv) {
|
|
4
|
+
const args = { top: 10, input: null };
|
|
5
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
6
|
+
const flag = argv[i];
|
|
7
|
+
if (flag === "--input") {
|
|
8
|
+
if (++i >= argv.length)
|
|
9
|
+
throw new Error("argument --input: expected one argument");
|
|
10
|
+
args.input = argv[i];
|
|
11
|
+
}
|
|
12
|
+
else if (flag === "--top") {
|
|
13
|
+
if (++i >= argv.length)
|
|
14
|
+
throw new Error("argument --top: expected one argument");
|
|
15
|
+
args.top = Number(argv[i]);
|
|
16
|
+
if (!Number.isInteger(args.top))
|
|
17
|
+
throw new Error(`argument --top: invalid int value: '${argv[i]}'`);
|
|
18
|
+
}
|
|
19
|
+
else if (flag === "--help" || flag === "-h") {
|
|
20
|
+
console.log("Summarize OpsCompass resources list output.");
|
|
21
|
+
process.exit(0);
|
|
22
|
+
}
|
|
23
|
+
else
|
|
24
|
+
throw new Error(`unrecognized arguments: ${flag}`);
|
|
25
|
+
}
|
|
26
|
+
return args;
|
|
27
|
+
}
|
|
28
|
+
export function loadResources(inputPath, inputText) {
|
|
29
|
+
const payload = inputPath
|
|
30
|
+
? JSON.parse(readFileSync(inputPath, "utf8"))
|
|
31
|
+
: JSON.parse(inputText ?? "");
|
|
32
|
+
if (!Array.isArray(payload))
|
|
33
|
+
throw new Error("expected top-level JSON array.");
|
|
34
|
+
return payload.filter((item) => item !== null && typeof item === "object" && !Array.isArray(item));
|
|
35
|
+
}
|
|
36
|
+
async function loadResourcesAsync(inputPath) {
|
|
37
|
+
if (inputPath)
|
|
38
|
+
return loadResources(inputPath);
|
|
39
|
+
const chunks = [];
|
|
40
|
+
for await (const chunk of process.stdin)
|
|
41
|
+
chunks.push(chunk);
|
|
42
|
+
return loadResources(null, Buffer.concat(chunks).toString("utf8"));
|
|
43
|
+
}
|
|
44
|
+
const text = (value) => String(value);
|
|
45
|
+
const num = (value) => Number(value || 0);
|
|
46
|
+
const resourceName = (resource) => text(resource.name || "(unnamed)");
|
|
47
|
+
const resourceId = (resource) => text(resource.id || "");
|
|
48
|
+
const nestedId = (value) => value !== null && typeof value === "object"
|
|
49
|
+
? value.id
|
|
50
|
+
: undefined;
|
|
51
|
+
export function summarize(resources, topN) {
|
|
52
|
+
const byProvider = new Map(), byRegion = new Map(), byAccount = new Map();
|
|
53
|
+
let changesTotal = 0, complianceTotal = 0;
|
|
54
|
+
const withChanges = [], withCompliance = [];
|
|
55
|
+
const bump = (map, key) => {
|
|
56
|
+
map.set(key, (map.get(key) || 0) + 1);
|
|
57
|
+
};
|
|
58
|
+
for (const resource of resources) {
|
|
59
|
+
const provider = text(resource.cloud_provider || "unknown");
|
|
60
|
+
const region = text(nestedId(resource.region) || "unknown");
|
|
61
|
+
const account = text(nestedId(resource.account) || "unknown");
|
|
62
|
+
const changes = num(resource.unacknowledged_changes), compliance = num(resource.compliance_problems);
|
|
63
|
+
bump(byProvider, provider);
|
|
64
|
+
bump(byRegion, region);
|
|
65
|
+
bump(byAccount, account);
|
|
66
|
+
changesTotal += changes;
|
|
67
|
+
complianceTotal += compliance;
|
|
68
|
+
if (changes > 0)
|
|
69
|
+
withChanges.push(resource);
|
|
70
|
+
if (compliance > 0)
|
|
71
|
+
withCompliance.push(resource);
|
|
72
|
+
}
|
|
73
|
+
const sortCounts = (a, b) => b[1] - a[1] || a[0].localeCompare(b[0]);
|
|
74
|
+
withChanges.sort((a, b) => num(b.unacknowledged_changes) - num(a.unacknowledged_changes));
|
|
75
|
+
withCompliance.sort((a, b) => num(b.compliance_problems) - num(a.compliance_problems));
|
|
76
|
+
const lines = [
|
|
77
|
+
`Total resources: ${resources.length}`,
|
|
78
|
+
`Total unacknowledged changes: ${changesTotal}`,
|
|
79
|
+
`Total compliance problems: ${complianceTotal}`,
|
|
80
|
+
"",
|
|
81
|
+
"Resources by provider:",
|
|
82
|
+
];
|
|
83
|
+
for (const [key, count] of [...byProvider].sort(sortCounts))
|
|
84
|
+
lines.push(`- ${key}: ${count}`);
|
|
85
|
+
lines.push("", "Top regions by resource count:");
|
|
86
|
+
for (const [key, count] of [...byRegion].sort(sortCounts).slice(0, topN))
|
|
87
|
+
lines.push(`- ${key}: ${count}`);
|
|
88
|
+
lines.push("", "Top accounts by resource count:");
|
|
89
|
+
for (const [key, count] of [...byAccount].sort(sortCounts).slice(0, topN))
|
|
90
|
+
lines.push(`- ${key}: ${count}`);
|
|
91
|
+
lines.push("", `Top ${topN} resources by unacknowledged changes:`);
|
|
92
|
+
if (withChanges.length)
|
|
93
|
+
for (const item of withChanges.slice(0, topN))
|
|
94
|
+
lines.push(`- ${resourceName(item)} (id=${resourceId(item)}): ${num(item.unacknowledged_changes)}`);
|
|
95
|
+
else
|
|
96
|
+
lines.push("- none");
|
|
97
|
+
lines.push("", `Top ${topN} resources by compliance problems:`);
|
|
98
|
+
if (withCompliance.length)
|
|
99
|
+
for (const item of withCompliance.slice(0, topN))
|
|
100
|
+
lines.push(`- ${resourceName(item)} (id=${resourceId(item)}): ${num(item.compliance_problems)}`);
|
|
101
|
+
else
|
|
102
|
+
lines.push("- none");
|
|
103
|
+
return lines.join("\n");
|
|
104
|
+
}
|
|
105
|
+
export async function main(argv = process.argv.slice(2)) {
|
|
106
|
+
try {
|
|
107
|
+
const args = parseArgs(argv);
|
|
108
|
+
if (args.top <= 0) {
|
|
109
|
+
console.error("Error: --top must be greater than 0.");
|
|
110
|
+
return 2;
|
|
111
|
+
}
|
|
112
|
+
const resources = await loadResourcesAsync(args.input);
|
|
113
|
+
console.log(summarize(resources, args.top));
|
|
114
|
+
return 0;
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
if (error instanceof SyntaxError)
|
|
118
|
+
console.error(`Error: invalid JSON: ${error.message}`);
|
|
119
|
+
else
|
|
120
|
+
console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
121
|
+
return 2;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (isMainModule(import.meta.url)) {
|
|
125
|
+
process.exitCode = await main();
|
|
126
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { buildResourcesCommandTool } from "./build-resources-command.tool.js";
|
|
2
|
+
import { listAndFetchLatestConfigsTool } from "./list-and-fetch-latest-configs.tool.js";
|
|
3
|
+
import { mapOracleWorkloadsToPlatformCpusTool } from "./map-oracle-workloads-to-platform-cpus.tool.js";
|
|
4
|
+
import { opscompassDefaultsTool } from "./opscompass-defaults.tool.js";
|
|
5
|
+
import { opscompassResourcesTool } from "./opscompass-resources.tool.js";
|
|
6
|
+
import { summarizeLatestConfigurationTool } from "./summarize-latest-configuration.tool.js";
|
|
7
|
+
import { summarizeResourcesListTool } from "./summarize-resources-list.tool.js";
|
|
8
|
+
export const tools = [
|
|
9
|
+
opscompassResourcesTool,
|
|
10
|
+
buildResourcesCommandTool,
|
|
11
|
+
listAndFetchLatestConfigsTool,
|
|
12
|
+
mapOracleWorkloadsToPlatformCpusTool,
|
|
13
|
+
opscompassDefaultsTool,
|
|
14
|
+
summarizeLatestConfigurationTool,
|
|
15
|
+
summarizeResourcesListTool,
|
|
16
|
+
];
|