infra-cost 1.5.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/index.js +776 -7
- package/dist/index.js +776 -7
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -10214,7 +10214,7 @@ var import_commander = require("commander");
|
|
|
10214
10214
|
// package.json
|
|
10215
10215
|
var package_default = {
|
|
10216
10216
|
name: "infra-cost",
|
|
10217
|
-
version: "1.
|
|
10217
|
+
version: "1.6.0",
|
|
10218
10218
|
description: "Multi-cloud FinOps CLI tool for comprehensive cost analysis and infrastructure optimization across AWS, GCP, Azure, Alibaba Cloud, and Oracle Cloud",
|
|
10219
10219
|
keywords: [
|
|
10220
10220
|
"aws",
|
|
@@ -11920,8 +11920,8 @@ __name(registerExportCommands, "registerExportCommands");
|
|
|
11920
11920
|
function registerOrganizationsCommands(program) {
|
|
11921
11921
|
const orgs = program.command("organizations").alias("orgs").description("AWS Organizations multi-account management");
|
|
11922
11922
|
orgs.command("list").description("List all accounts in the organization").option("--include-inactive", "Include suspended/closed accounts").action(async (options, command) => {
|
|
11923
|
-
const { handleList:
|
|
11924
|
-
await
|
|
11923
|
+
const { handleList: handleList4 } = await Promise.resolve().then(() => (init_list(), list_exports));
|
|
11924
|
+
await handleList4(options, command);
|
|
11925
11925
|
});
|
|
11926
11926
|
orgs.command("summary").description("Multi-account cost summary").option("--group-by <field>", "Group by (account, ou, tag)", "account").action(async (options, command) => {
|
|
11927
11927
|
const { handleSummary: handleSummary2 } = await Promise.resolve().then(() => (init_summary(), summary_exports));
|
|
@@ -13115,6 +13115,772 @@ function registerSchedulerCommands(program) {
|
|
|
13115
13115
|
}
|
|
13116
13116
|
__name(registerSchedulerCommands, "registerSchedulerCommands");
|
|
13117
13117
|
|
|
13118
|
+
// src/cli/commands/rbac/index.ts
|
|
13119
|
+
var import_chalk19 = __toESM(require("chalk"));
|
|
13120
|
+
|
|
13121
|
+
// src/core/rbac.ts
|
|
13122
|
+
var import_fs6 = require("fs");
|
|
13123
|
+
var import_path4 = require("path");
|
|
13124
|
+
var import_os4 = require("os");
|
|
13125
|
+
var CONFIG_DIR2 = (0, import_path4.join)((0, import_os4.homedir)(), ".infra-cost");
|
|
13126
|
+
var RBAC_CONFIG_FILE = (0, import_path4.join)(CONFIG_DIR2, "rbac.json");
|
|
13127
|
+
var DEFAULT_ROLES = {
|
|
13128
|
+
admin: {
|
|
13129
|
+
name: "admin",
|
|
13130
|
+
description: "Full access to all features",
|
|
13131
|
+
permissions: ["*"]
|
|
13132
|
+
},
|
|
13133
|
+
finance: {
|
|
13134
|
+
name: "finance",
|
|
13135
|
+
description: "Cost data and reports only",
|
|
13136
|
+
permissions: [
|
|
13137
|
+
"costs:read",
|
|
13138
|
+
"costs:read:*",
|
|
13139
|
+
"chargeback:read",
|
|
13140
|
+
"chargeback:read:*",
|
|
13141
|
+
"reports:generate",
|
|
13142
|
+
"budgets:read",
|
|
13143
|
+
"budgets:read:*",
|
|
13144
|
+
"export:read"
|
|
13145
|
+
]
|
|
13146
|
+
},
|
|
13147
|
+
developer: {
|
|
13148
|
+
name: "developer",
|
|
13149
|
+
description: "View costs for assigned resources",
|
|
13150
|
+
permissions: [
|
|
13151
|
+
"costs:read:own",
|
|
13152
|
+
"costs:read:team:*",
|
|
13153
|
+
"inventory:read:own",
|
|
13154
|
+
"inventory:read:team:*",
|
|
13155
|
+
"optimization:read:own",
|
|
13156
|
+
"optimization:read:team:*"
|
|
13157
|
+
]
|
|
13158
|
+
},
|
|
13159
|
+
viewer: {
|
|
13160
|
+
name: "viewer",
|
|
13161
|
+
description: "Read-only access to summaries",
|
|
13162
|
+
permissions: [
|
|
13163
|
+
"costs:read:summary",
|
|
13164
|
+
"budgets:read"
|
|
13165
|
+
]
|
|
13166
|
+
}
|
|
13167
|
+
};
|
|
13168
|
+
function loadRBACConfig() {
|
|
13169
|
+
if (!(0, import_fs6.existsSync)(RBAC_CONFIG_FILE)) {
|
|
13170
|
+
return {
|
|
13171
|
+
roles: DEFAULT_ROLES,
|
|
13172
|
+
userRoles: []
|
|
13173
|
+
};
|
|
13174
|
+
}
|
|
13175
|
+
try {
|
|
13176
|
+
const data = (0, import_fs6.readFileSync)(RBAC_CONFIG_FILE, "utf-8");
|
|
13177
|
+
const config = JSON.parse(data);
|
|
13178
|
+
return {
|
|
13179
|
+
...config,
|
|
13180
|
+
roles: { ...DEFAULT_ROLES, ...config.roles }
|
|
13181
|
+
};
|
|
13182
|
+
} catch (error) {
|
|
13183
|
+
console.error("Error loading RBAC config:", error);
|
|
13184
|
+
return {
|
|
13185
|
+
roles: DEFAULT_ROLES,
|
|
13186
|
+
userRoles: []
|
|
13187
|
+
};
|
|
13188
|
+
}
|
|
13189
|
+
}
|
|
13190
|
+
__name(loadRBACConfig, "loadRBACConfig");
|
|
13191
|
+
function saveRBACConfig(config) {
|
|
13192
|
+
try {
|
|
13193
|
+
(0, import_fs6.writeFileSync)(RBAC_CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
13194
|
+
} catch (error) {
|
|
13195
|
+
throw new Error(`Failed to save RBAC config: ${error}`);
|
|
13196
|
+
}
|
|
13197
|
+
}
|
|
13198
|
+
__name(saveRBACConfig, "saveRBACConfig");
|
|
13199
|
+
function getUserRole(user) {
|
|
13200
|
+
const config = loadRBACConfig();
|
|
13201
|
+
const userRole = config.userRoles.find((ur) => ur.user === user);
|
|
13202
|
+
return userRole?.role || null;
|
|
13203
|
+
}
|
|
13204
|
+
__name(getUserRole, "getUserRole");
|
|
13205
|
+
function assignRole(user, role, assignedBy) {
|
|
13206
|
+
const config = loadRBACConfig();
|
|
13207
|
+
if (!config.roles[role]) {
|
|
13208
|
+
throw new Error(`Role "${role}" does not exist`);
|
|
13209
|
+
}
|
|
13210
|
+
config.userRoles = config.userRoles.filter((ur) => ur.user !== user);
|
|
13211
|
+
config.userRoles.push({
|
|
13212
|
+
user,
|
|
13213
|
+
role,
|
|
13214
|
+
assignedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
13215
|
+
assignedBy
|
|
13216
|
+
});
|
|
13217
|
+
saveRBACConfig(config);
|
|
13218
|
+
}
|
|
13219
|
+
__name(assignRole, "assignRole");
|
|
13220
|
+
function removeUserRole(user) {
|
|
13221
|
+
const config = loadRBACConfig();
|
|
13222
|
+
config.userRoles = config.userRoles.filter((ur) => ur.user !== user);
|
|
13223
|
+
saveRBACConfig(config);
|
|
13224
|
+
}
|
|
13225
|
+
__name(removeUserRole, "removeUserRole");
|
|
13226
|
+
function createRole(name, description, permissions) {
|
|
13227
|
+
const config = loadRBACConfig();
|
|
13228
|
+
if (config.roles[name]) {
|
|
13229
|
+
throw new Error(`Role "${name}" already exists`);
|
|
13230
|
+
}
|
|
13231
|
+
config.roles[name] = {
|
|
13232
|
+
name,
|
|
13233
|
+
description,
|
|
13234
|
+
permissions
|
|
13235
|
+
};
|
|
13236
|
+
saveRBACConfig(config);
|
|
13237
|
+
}
|
|
13238
|
+
__name(createRole, "createRole");
|
|
13239
|
+
function deleteRole(name) {
|
|
13240
|
+
const config = loadRBACConfig();
|
|
13241
|
+
if (DEFAULT_ROLES[name]) {
|
|
13242
|
+
throw new Error(`Cannot delete default role "${name}"`);
|
|
13243
|
+
}
|
|
13244
|
+
if (!config.roles[name]) {
|
|
13245
|
+
throw new Error(`Role "${name}" does not exist`);
|
|
13246
|
+
}
|
|
13247
|
+
delete config.roles[name];
|
|
13248
|
+
config.userRoles = config.userRoles.filter((ur) => ur.role !== name);
|
|
13249
|
+
saveRBACConfig(config);
|
|
13250
|
+
}
|
|
13251
|
+
__name(deleteRole, "deleteRole");
|
|
13252
|
+
function parsePermission(permissionStr) {
|
|
13253
|
+
const parts = permissionStr.split(":");
|
|
13254
|
+
return {
|
|
13255
|
+
resource: parts[0] || "*",
|
|
13256
|
+
action: parts[1] || "*",
|
|
13257
|
+
scope: parts[2]
|
|
13258
|
+
};
|
|
13259
|
+
}
|
|
13260
|
+
__name(parsePermission, "parsePermission");
|
|
13261
|
+
function permissionMatches(required, granted) {
|
|
13262
|
+
if (granted.resource === "*") {
|
|
13263
|
+
return true;
|
|
13264
|
+
}
|
|
13265
|
+
if (granted.resource !== required.resource) {
|
|
13266
|
+
return false;
|
|
13267
|
+
}
|
|
13268
|
+
if (granted.action !== "*" && granted.action !== required.action) {
|
|
13269
|
+
return false;
|
|
13270
|
+
}
|
|
13271
|
+
if (required.scope) {
|
|
13272
|
+
if (!granted.scope || granted.scope === "*") {
|
|
13273
|
+
return true;
|
|
13274
|
+
}
|
|
13275
|
+
if (granted.scope !== required.scope && !granted.scope.endsWith(":*")) {
|
|
13276
|
+
return false;
|
|
13277
|
+
}
|
|
13278
|
+
if (granted.scope.endsWith(":*")) {
|
|
13279
|
+
const prefix = granted.scope.slice(0, -2);
|
|
13280
|
+
return required.scope.startsWith(prefix);
|
|
13281
|
+
}
|
|
13282
|
+
}
|
|
13283
|
+
return true;
|
|
13284
|
+
}
|
|
13285
|
+
__name(permissionMatches, "permissionMatches");
|
|
13286
|
+
function hasPermission(user, permissionStr, context) {
|
|
13287
|
+
const config = loadRBACConfig();
|
|
13288
|
+
const userRole = config.userRoles.find((ur) => ur.user === user);
|
|
13289
|
+
if (!userRole) {
|
|
13290
|
+
return false;
|
|
13291
|
+
}
|
|
13292
|
+
const role = config.roles[userRole.role];
|
|
13293
|
+
if (!role) {
|
|
13294
|
+
return false;
|
|
13295
|
+
}
|
|
13296
|
+
const required = parsePermission(permissionStr);
|
|
13297
|
+
for (const grantedStr of role.permissions) {
|
|
13298
|
+
const granted = parsePermission(grantedStr);
|
|
13299
|
+
if (permissionMatches(required, granted)) {
|
|
13300
|
+
return true;
|
|
13301
|
+
}
|
|
13302
|
+
}
|
|
13303
|
+
return false;
|
|
13304
|
+
}
|
|
13305
|
+
__name(hasPermission, "hasPermission");
|
|
13306
|
+
function getUserPermissions(user) {
|
|
13307
|
+
const config = loadRBACConfig();
|
|
13308
|
+
const userRole = config.userRoles.find((ur) => ur.user === user);
|
|
13309
|
+
if (!userRole) {
|
|
13310
|
+
return [];
|
|
13311
|
+
}
|
|
13312
|
+
const role = config.roles[userRole.role];
|
|
13313
|
+
if (!role) {
|
|
13314
|
+
return [];
|
|
13315
|
+
}
|
|
13316
|
+
return role.permissions;
|
|
13317
|
+
}
|
|
13318
|
+
__name(getUserPermissions, "getUserPermissions");
|
|
13319
|
+
function listRoles() {
|
|
13320
|
+
const config = loadRBACConfig();
|
|
13321
|
+
return Object.values(config.roles);
|
|
13322
|
+
}
|
|
13323
|
+
__name(listRoles, "listRoles");
|
|
13324
|
+
function listUserRoles() {
|
|
13325
|
+
const config = loadRBACConfig();
|
|
13326
|
+
return config.userRoles;
|
|
13327
|
+
}
|
|
13328
|
+
__name(listUserRoles, "listUserRoles");
|
|
13329
|
+
|
|
13330
|
+
// src/cli/commands/rbac/index.ts
|
|
13331
|
+
async function handleAssignRole(options) {
|
|
13332
|
+
const { user, role, assignedBy } = options;
|
|
13333
|
+
if (!user || !role) {
|
|
13334
|
+
console.error(import_chalk19.default.red("Error: --user and --role are required"));
|
|
13335
|
+
console.log("\nExample:");
|
|
13336
|
+
console.log(import_chalk19.default.gray(" infra-cost rbac assign-role --user john@company.com --role finance"));
|
|
13337
|
+
process.exit(1);
|
|
13338
|
+
}
|
|
13339
|
+
try {
|
|
13340
|
+
assignRole(user, role, assignedBy);
|
|
13341
|
+
console.log(import_chalk19.default.green(`\u2705 Assigned role "${role}" to user "${user}"`));
|
|
13342
|
+
} catch (error) {
|
|
13343
|
+
console.error(import_chalk19.default.red(`Error: ${error.message}`));
|
|
13344
|
+
process.exit(1);
|
|
13345
|
+
}
|
|
13346
|
+
}
|
|
13347
|
+
__name(handleAssignRole, "handleAssignRole");
|
|
13348
|
+
async function handleRemoveRole(options) {
|
|
13349
|
+
const { user } = options;
|
|
13350
|
+
if (!user) {
|
|
13351
|
+
console.error(import_chalk19.default.red("Error: --user is required"));
|
|
13352
|
+
process.exit(1);
|
|
13353
|
+
}
|
|
13354
|
+
try {
|
|
13355
|
+
removeUserRole(user);
|
|
13356
|
+
console.log(import_chalk19.default.green(`\u2705 Removed role from user "${user}"`));
|
|
13357
|
+
} catch (error) {
|
|
13358
|
+
console.error(import_chalk19.default.red(`Error: ${error.message}`));
|
|
13359
|
+
process.exit(1);
|
|
13360
|
+
}
|
|
13361
|
+
}
|
|
13362
|
+
__name(handleRemoveRole, "handleRemoveRole");
|
|
13363
|
+
async function handleCreateRole(options) {
|
|
13364
|
+
const { name, description, permissions } = options;
|
|
13365
|
+
if (!name || !description || !permissions) {
|
|
13366
|
+
console.error(import_chalk19.default.red("Error: --name, --description, and --permissions are required"));
|
|
13367
|
+
console.log("\nExample:");
|
|
13368
|
+
console.log(import_chalk19.default.gray(" infra-cost rbac create-role \\"));
|
|
13369
|
+
console.log(import_chalk19.default.gray(" --name team-lead \\"));
|
|
13370
|
+
console.log(import_chalk19.default.gray(' --description "Team lead access" \\'));
|
|
13371
|
+
console.log(import_chalk19.default.gray(' --permissions "costs:read,optimization:read"'));
|
|
13372
|
+
process.exit(1);
|
|
13373
|
+
}
|
|
13374
|
+
try {
|
|
13375
|
+
const permList = permissions.split(",").map((p) => p.trim());
|
|
13376
|
+
createRole(name, description, permList);
|
|
13377
|
+
console.log(import_chalk19.default.green(`\u2705 Created role "${name}"`));
|
|
13378
|
+
console.log(import_chalk19.default.gray(` Permissions: ${permList.join(", ")}`));
|
|
13379
|
+
} catch (error) {
|
|
13380
|
+
console.error(import_chalk19.default.red(`Error: ${error.message}`));
|
|
13381
|
+
process.exit(1);
|
|
13382
|
+
}
|
|
13383
|
+
}
|
|
13384
|
+
__name(handleCreateRole, "handleCreateRole");
|
|
13385
|
+
async function handleDeleteRole(options) {
|
|
13386
|
+
const { name } = options;
|
|
13387
|
+
if (!name) {
|
|
13388
|
+
console.error(import_chalk19.default.red("Error: --name is required"));
|
|
13389
|
+
process.exit(1);
|
|
13390
|
+
}
|
|
13391
|
+
try {
|
|
13392
|
+
deleteRole(name);
|
|
13393
|
+
console.log(import_chalk19.default.green(`\u2705 Deleted role "${name}"`));
|
|
13394
|
+
} catch (error) {
|
|
13395
|
+
console.error(import_chalk19.default.red(`Error: ${error.message}`));
|
|
13396
|
+
process.exit(1);
|
|
13397
|
+
}
|
|
13398
|
+
}
|
|
13399
|
+
__name(handleDeleteRole, "handleDeleteRole");
|
|
13400
|
+
async function handleShowPermissions(options) {
|
|
13401
|
+
const { user } = options;
|
|
13402
|
+
if (!user) {
|
|
13403
|
+
console.error(import_chalk19.default.red("Error: --user is required"));
|
|
13404
|
+
process.exit(1);
|
|
13405
|
+
}
|
|
13406
|
+
try {
|
|
13407
|
+
const role = getUserRole(user);
|
|
13408
|
+
const permissions = getUserPermissions(user);
|
|
13409
|
+
console.log(import_chalk19.default.bold(`
|
|
13410
|
+
\u{1F464} User: ${user}
|
|
13411
|
+
`));
|
|
13412
|
+
if (!role) {
|
|
13413
|
+
console.log(import_chalk19.default.yellow("No role assigned"));
|
|
13414
|
+
return;
|
|
13415
|
+
}
|
|
13416
|
+
console.log(import_chalk19.default.bold(`Role: ${role}
|
|
13417
|
+
`));
|
|
13418
|
+
console.log(import_chalk19.default.bold("Permissions:"));
|
|
13419
|
+
if (permissions.length === 0) {
|
|
13420
|
+
console.log(import_chalk19.default.gray(" No permissions"));
|
|
13421
|
+
} else {
|
|
13422
|
+
permissions.forEach((perm) => {
|
|
13423
|
+
console.log(import_chalk19.default.gray(` \u2022 ${perm}`));
|
|
13424
|
+
});
|
|
13425
|
+
}
|
|
13426
|
+
console.log("");
|
|
13427
|
+
} catch (error) {
|
|
13428
|
+
console.error(import_chalk19.default.red(`Error: ${error.message}`));
|
|
13429
|
+
process.exit(1);
|
|
13430
|
+
}
|
|
13431
|
+
}
|
|
13432
|
+
__name(handleShowPermissions, "handleShowPermissions");
|
|
13433
|
+
async function handleListRoles(options) {
|
|
13434
|
+
try {
|
|
13435
|
+
const roles = listRoles();
|
|
13436
|
+
console.log(import_chalk19.default.bold("\n\u{1F4CB} Available Roles\n"));
|
|
13437
|
+
roles.forEach((role) => {
|
|
13438
|
+
console.log(import_chalk19.default.bold(`${role.name}`));
|
|
13439
|
+
console.log(import_chalk19.default.gray(` ${role.description}`));
|
|
13440
|
+
console.log(import_chalk19.default.gray(` Permissions: ${role.permissions.slice(0, 3).join(", ")}${role.permissions.length > 3 ? "..." : ""}`));
|
|
13441
|
+
console.log("");
|
|
13442
|
+
});
|
|
13443
|
+
} catch (error) {
|
|
13444
|
+
console.error(import_chalk19.default.red(`Error: ${error.message}`));
|
|
13445
|
+
process.exit(1);
|
|
13446
|
+
}
|
|
13447
|
+
}
|
|
13448
|
+
__name(handleListRoles, "handleListRoles");
|
|
13449
|
+
async function handleListUsers(options) {
|
|
13450
|
+
try {
|
|
13451
|
+
const userRoles = listUserRoles();
|
|
13452
|
+
console.log(import_chalk19.default.bold("\n\u{1F465} User Role Assignments\n"));
|
|
13453
|
+
if (userRoles.length === 0) {
|
|
13454
|
+
console.log(import_chalk19.default.yellow("No user roles assigned"));
|
|
13455
|
+
return;
|
|
13456
|
+
}
|
|
13457
|
+
userRoles.forEach((ur) => {
|
|
13458
|
+
console.log(import_chalk19.default.bold(ur.user));
|
|
13459
|
+
console.log(import_chalk19.default.gray(` Role: ${ur.role}`));
|
|
13460
|
+
console.log(import_chalk19.default.gray(` Assigned: ${new Date(ur.assignedAt).toLocaleString()}`));
|
|
13461
|
+
if (ur.assignedBy) {
|
|
13462
|
+
console.log(import_chalk19.default.gray(` Assigned by: ${ur.assignedBy}`));
|
|
13463
|
+
}
|
|
13464
|
+
console.log("");
|
|
13465
|
+
});
|
|
13466
|
+
} catch (error) {
|
|
13467
|
+
console.error(import_chalk19.default.red(`Error: ${error.message}`));
|
|
13468
|
+
process.exit(1);
|
|
13469
|
+
}
|
|
13470
|
+
}
|
|
13471
|
+
__name(handleListUsers, "handleListUsers");
|
|
13472
|
+
async function handleCheckPermission(options) {
|
|
13473
|
+
const { user, permission } = options;
|
|
13474
|
+
if (!user || !permission) {
|
|
13475
|
+
console.error(import_chalk19.default.red("Error: --user and --permission are required"));
|
|
13476
|
+
console.log("\nExample:");
|
|
13477
|
+
console.log(import_chalk19.default.gray(' infra-cost rbac check-permission --user john@company.com --permission "costs:read"'));
|
|
13478
|
+
process.exit(1);
|
|
13479
|
+
}
|
|
13480
|
+
try {
|
|
13481
|
+
const has = hasPermission(user, permission);
|
|
13482
|
+
console.log(import_chalk19.default.bold(`
|
|
13483
|
+
\u{1F464} User: ${user}`));
|
|
13484
|
+
console.log(import_chalk19.default.bold(`\u{1F510} Permission: ${permission}
|
|
13485
|
+
`));
|
|
13486
|
+
if (has) {
|
|
13487
|
+
console.log(import_chalk19.default.green("\u2705 ALLOWED"));
|
|
13488
|
+
} else {
|
|
13489
|
+
console.log(import_chalk19.default.red("\u274C DENIED"));
|
|
13490
|
+
}
|
|
13491
|
+
console.log("");
|
|
13492
|
+
} catch (error) {
|
|
13493
|
+
console.error(import_chalk19.default.red(`Error: ${error.message}`));
|
|
13494
|
+
process.exit(1);
|
|
13495
|
+
}
|
|
13496
|
+
}
|
|
13497
|
+
__name(handleCheckPermission, "handleCheckPermission");
|
|
13498
|
+
function registerRBACCommands(program) {
|
|
13499
|
+
const rbac = program.command("rbac").description("Role-Based Access Control management");
|
|
13500
|
+
rbac.command("assign-role").description("Assign role to user").option("--user <email>", "User email").option("--role <name>", "Role name").option("--assigned-by <email>", "Who assigned the role").action(handleAssignRole);
|
|
13501
|
+
rbac.command("remove-role").description("Remove role from user").option("--user <email>", "User email").action(handleRemoveRole);
|
|
13502
|
+
rbac.command("create-role").description("Create custom role").option("--name <name>", "Role name").option("--description <text>", "Role description").option("--permissions <list>", "Comma-separated permissions").action(handleCreateRole);
|
|
13503
|
+
rbac.command("delete-role").description("Delete custom role").option("--name <name>", "Role name").action(handleDeleteRole);
|
|
13504
|
+
rbac.command("show-permissions").description("Show user permissions").option("--user <email>", "User email").action(handleShowPermissions);
|
|
13505
|
+
rbac.command("list-roles").description("List all available roles").action(handleListRoles);
|
|
13506
|
+
rbac.command("list-users").description("List all user role assignments").action(handleListUsers);
|
|
13507
|
+
rbac.command("check-permission").description("Check if user has permission").option("--user <email>", "User email").option("--permission <perm>", 'Permission string (e.g., "costs:read")').action(handleCheckPermission);
|
|
13508
|
+
}
|
|
13509
|
+
__name(registerRBACCommands, "registerRBACCommands");
|
|
13510
|
+
|
|
13511
|
+
// src/cli/commands/sso/index.ts
|
|
13512
|
+
var import_chalk20 = __toESM(require("chalk"));
|
|
13513
|
+
|
|
13514
|
+
// src/core/sso.ts
|
|
13515
|
+
var import_fs7 = require("fs");
|
|
13516
|
+
var import_path5 = require("path");
|
|
13517
|
+
var import_os5 = require("os");
|
|
13518
|
+
var CONFIG_DIR3 = (0, import_path5.join)((0, import_os5.homedir)(), ".infra-cost");
|
|
13519
|
+
var SSO_DIR = (0, import_path5.join)(CONFIG_DIR3, "sso");
|
|
13520
|
+
var SSO_CONFIG_FILE = (0, import_path5.join)(CONFIG_DIR3, "sso-config.json");
|
|
13521
|
+
var SESSION_FILE = (0, import_path5.join)(SSO_DIR, "session.json");
|
|
13522
|
+
function ensureSSODir() {
|
|
13523
|
+
if (!(0, import_fs7.existsSync)(SSO_DIR)) {
|
|
13524
|
+
(0, import_fs7.mkdirSync)(SSO_DIR, { recursive: true });
|
|
13525
|
+
}
|
|
13526
|
+
}
|
|
13527
|
+
__name(ensureSSODir, "ensureSSODir");
|
|
13528
|
+
function loadSSOConfig() {
|
|
13529
|
+
if (!(0, import_fs7.existsSync)(SSO_CONFIG_FILE)) {
|
|
13530
|
+
return null;
|
|
13531
|
+
}
|
|
13532
|
+
try {
|
|
13533
|
+
const data = (0, import_fs7.readFileSync)(SSO_CONFIG_FILE, "utf-8");
|
|
13534
|
+
return JSON.parse(data);
|
|
13535
|
+
} catch (error) {
|
|
13536
|
+
console.error("Error loading SSO config:", error);
|
|
13537
|
+
return null;
|
|
13538
|
+
}
|
|
13539
|
+
}
|
|
13540
|
+
__name(loadSSOConfig, "loadSSOConfig");
|
|
13541
|
+
function saveSSOConfig(config) {
|
|
13542
|
+
try {
|
|
13543
|
+
ensureSSODir();
|
|
13544
|
+
(0, import_fs7.writeFileSync)(SSO_CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
13545
|
+
} catch (error) {
|
|
13546
|
+
throw new Error(`Failed to save SSO config: ${error}`);
|
|
13547
|
+
}
|
|
13548
|
+
}
|
|
13549
|
+
__name(saveSSOConfig, "saveSSOConfig");
|
|
13550
|
+
function loadSSOSession() {
|
|
13551
|
+
if (!(0, import_fs7.existsSync)(SESSION_FILE)) {
|
|
13552
|
+
return null;
|
|
13553
|
+
}
|
|
13554
|
+
try {
|
|
13555
|
+
const data = (0, import_fs7.readFileSync)(SESSION_FILE, "utf-8");
|
|
13556
|
+
const session = JSON.parse(data);
|
|
13557
|
+
const expiresAt = new Date(session.expiresAt);
|
|
13558
|
+
if (expiresAt < /* @__PURE__ */ new Date()) {
|
|
13559
|
+
return null;
|
|
13560
|
+
}
|
|
13561
|
+
return session;
|
|
13562
|
+
} catch (error) {
|
|
13563
|
+
return null;
|
|
13564
|
+
}
|
|
13565
|
+
}
|
|
13566
|
+
__name(loadSSOSession, "loadSSOSession");
|
|
13567
|
+
function saveSSOSession(session) {
|
|
13568
|
+
try {
|
|
13569
|
+
ensureSSODir();
|
|
13570
|
+
(0, import_fs7.writeFileSync)(SESSION_FILE, JSON.stringify(session, null, 2));
|
|
13571
|
+
} catch (error) {
|
|
13572
|
+
throw new Error(`Failed to save SSO session: ${error}`);
|
|
13573
|
+
}
|
|
13574
|
+
}
|
|
13575
|
+
__name(saveSSOSession, "saveSSOSession");
|
|
13576
|
+
function clearSSOSession() {
|
|
13577
|
+
try {
|
|
13578
|
+
if ((0, import_fs7.existsSync)(SESSION_FILE)) {
|
|
13579
|
+
require("fs").unlinkSync(SESSION_FILE);
|
|
13580
|
+
}
|
|
13581
|
+
} catch (error) {
|
|
13582
|
+
}
|
|
13583
|
+
}
|
|
13584
|
+
__name(clearSSOSession, "clearSSOSession");
|
|
13585
|
+
function isLoggedIn() {
|
|
13586
|
+
const session = loadSSOSession();
|
|
13587
|
+
return session !== null;
|
|
13588
|
+
}
|
|
13589
|
+
__name(isLoggedIn, "isLoggedIn");
|
|
13590
|
+
function getCurrentUser() {
|
|
13591
|
+
const session = loadSSOSession();
|
|
13592
|
+
return session?.email || null;
|
|
13593
|
+
}
|
|
13594
|
+
__name(getCurrentUser, "getCurrentUser");
|
|
13595
|
+
function getSessionExpiration() {
|
|
13596
|
+
const session = loadSSOSession();
|
|
13597
|
+
return session ? new Date(session.expiresAt) : null;
|
|
13598
|
+
}
|
|
13599
|
+
__name(getSessionExpiration, "getSessionExpiration");
|
|
13600
|
+
function getMinutesUntilExpiration() {
|
|
13601
|
+
const expiration = getSessionExpiration();
|
|
13602
|
+
if (!expiration)
|
|
13603
|
+
return null;
|
|
13604
|
+
const now = /* @__PURE__ */ new Date();
|
|
13605
|
+
const diff = expiration.getTime() - now.getTime();
|
|
13606
|
+
return Math.floor(diff / 1e3 / 60);
|
|
13607
|
+
}
|
|
13608
|
+
__name(getMinutesUntilExpiration, "getMinutesUntilExpiration");
|
|
13609
|
+
function generateAuthorizationUrl(config) {
|
|
13610
|
+
const params = new URLSearchParams({
|
|
13611
|
+
client_id: config.config.clientId,
|
|
13612
|
+
redirect_uri: config.config.redirectUri,
|
|
13613
|
+
response_type: "code",
|
|
13614
|
+
scope: config.config.scopes.join(" "),
|
|
13615
|
+
state: generateRandomState()
|
|
13616
|
+
});
|
|
13617
|
+
const authEndpoint = config.config.authorizationEndpoint || `${config.config.issuer}/v1/authorize`;
|
|
13618
|
+
return `${authEndpoint}?${params.toString()}`;
|
|
13619
|
+
}
|
|
13620
|
+
__name(generateAuthorizationUrl, "generateAuthorizationUrl");
|
|
13621
|
+
function generateRandomState() {
|
|
13622
|
+
return Math.random().toString(36).substring(2, 15);
|
|
13623
|
+
}
|
|
13624
|
+
__name(generateRandomState, "generateRandomState");
|
|
13625
|
+
var ProviderConfig7 = {
|
|
13626
|
+
okta: (domain, clientId, clientSecret) => ({
|
|
13627
|
+
issuer: `https://${domain}`,
|
|
13628
|
+
clientId,
|
|
13629
|
+
clientSecret,
|
|
13630
|
+
authorizationEndpoint: `https://${domain}/oauth2/v1/authorize`,
|
|
13631
|
+
tokenEndpoint: `https://${domain}/oauth2/v1/token`,
|
|
13632
|
+
userInfoEndpoint: `https://${domain}/oauth2/v1/userinfo`,
|
|
13633
|
+
scopes: ["openid", "profile", "email"]
|
|
13634
|
+
}),
|
|
13635
|
+
azureAd: (tenantId, clientId, clientSecret) => ({
|
|
13636
|
+
issuer: `https://login.microsoftonline.com/${tenantId}/v2.0`,
|
|
13637
|
+
clientId,
|
|
13638
|
+
clientSecret,
|
|
13639
|
+
authorizationEndpoint: `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/authorize`,
|
|
13640
|
+
tokenEndpoint: `https://login.microsoftonline.com/${tenantId}/oauth2/v2.0/token`,
|
|
13641
|
+
userInfoEndpoint: "https://graph.microsoft.com/v1.0/me",
|
|
13642
|
+
scopes: ["openid", "profile", "email", "User.Read"]
|
|
13643
|
+
}),
|
|
13644
|
+
google: (clientId, clientSecret) => ({
|
|
13645
|
+
issuer: "https://accounts.google.com",
|
|
13646
|
+
clientId,
|
|
13647
|
+
clientSecret,
|
|
13648
|
+
authorizationEndpoint: "https://accounts.google.com/o/oauth2/v2/auth",
|
|
13649
|
+
tokenEndpoint: "https://oauth2.googleapis.com/token",
|
|
13650
|
+
userInfoEndpoint: "https://openidconnect.googleapis.com/v1/userinfo",
|
|
13651
|
+
scopes: ["openid", "profile", "email"]
|
|
13652
|
+
})
|
|
13653
|
+
};
|
|
13654
|
+
|
|
13655
|
+
// src/cli/commands/sso/index.ts
|
|
13656
|
+
async function handleConfigure(options) {
|
|
13657
|
+
const { provider, clientId, clientSecret, issuer, tenantId, domain } = options;
|
|
13658
|
+
if (!provider) {
|
|
13659
|
+
console.error(import_chalk20.default.red("Error: --provider is required"));
|
|
13660
|
+
console.log("\nSupported providers: okta, azure-ad, google, generic-oidc");
|
|
13661
|
+
process.exit(1);
|
|
13662
|
+
}
|
|
13663
|
+
let config;
|
|
13664
|
+
switch (provider) {
|
|
13665
|
+
case "okta":
|
|
13666
|
+
if (!domain || !clientId || !clientSecret) {
|
|
13667
|
+
console.error(import_chalk20.default.red("Error: Okta requires --domain, --client-id, --client-secret"));
|
|
13668
|
+
process.exit(1);
|
|
13669
|
+
}
|
|
13670
|
+
config = ProviderConfig7.okta(domain, clientId, clientSecret);
|
|
13671
|
+
break;
|
|
13672
|
+
case "azure-ad":
|
|
13673
|
+
if (!tenantId || !clientId || !clientSecret) {
|
|
13674
|
+
console.error(import_chalk20.default.red("Error: Azure AD requires --tenant-id, --client-id, --client-secret"));
|
|
13675
|
+
process.exit(1);
|
|
13676
|
+
}
|
|
13677
|
+
config = ProviderConfig7.azureAd(tenantId, clientId, clientSecret);
|
|
13678
|
+
break;
|
|
13679
|
+
case "google":
|
|
13680
|
+
if (!clientId || !clientSecret) {
|
|
13681
|
+
console.error(import_chalk20.default.red("Error: Google requires --client-id, --client-secret"));
|
|
13682
|
+
process.exit(1);
|
|
13683
|
+
}
|
|
13684
|
+
config = ProviderConfig7.google(clientId, clientSecret);
|
|
13685
|
+
break;
|
|
13686
|
+
default:
|
|
13687
|
+
console.error(import_chalk20.default.red(`Error: Unknown provider "${provider}"`));
|
|
13688
|
+
process.exit(1);
|
|
13689
|
+
}
|
|
13690
|
+
const ssoConfig = {
|
|
13691
|
+
enabled: true,
|
|
13692
|
+
provider,
|
|
13693
|
+
config: {
|
|
13694
|
+
...config,
|
|
13695
|
+
redirectUri: "http://localhost:8400/callback"
|
|
13696
|
+
}
|
|
13697
|
+
};
|
|
13698
|
+
saveSSOConfig(ssoConfig);
|
|
13699
|
+
console.log(import_chalk20.default.green("\u2705 SSO configured successfully"));
|
|
13700
|
+
console.log(import_chalk20.default.gray(` Provider: ${provider}`));
|
|
13701
|
+
console.log(import_chalk20.default.gray(` Issuer: ${config.issuer}`));
|
|
13702
|
+
console.log(import_chalk20.default.gray("\nRun `infra-cost login --sso` to authenticate"));
|
|
13703
|
+
}
|
|
13704
|
+
__name(handleConfigure, "handleConfigure");
|
|
13705
|
+
async function handleStatus2(options) {
|
|
13706
|
+
const config = loadSSOConfig();
|
|
13707
|
+
console.log(import_chalk20.default.bold("\n\u{1F510} SSO Status\n"));
|
|
13708
|
+
if (!config || !config.enabled) {
|
|
13709
|
+
console.log(import_chalk20.default.yellow("SSO not configured"));
|
|
13710
|
+
console.log(import_chalk20.default.gray("\nRun `infra-cost sso configure` to set up SSO"));
|
|
13711
|
+
return;
|
|
13712
|
+
}
|
|
13713
|
+
console.log(import_chalk20.default.bold("Configuration:"));
|
|
13714
|
+
console.log(import_chalk20.default.gray(` Provider: ${config.provider}`));
|
|
13715
|
+
console.log(import_chalk20.default.gray(` Issuer: ${config.config.issuer}`));
|
|
13716
|
+
console.log(import_chalk20.default.gray(` Client ID: ${config.config.clientId}`));
|
|
13717
|
+
console.log("");
|
|
13718
|
+
const session = loadSSOSession();
|
|
13719
|
+
if (!session) {
|
|
13720
|
+
console.log(import_chalk20.default.yellow("Not logged in"));
|
|
13721
|
+
console.log(import_chalk20.default.gray("\nRun `infra-cost login --sso` to authenticate"));
|
|
13722
|
+
return;
|
|
13723
|
+
}
|
|
13724
|
+
console.log(import_chalk20.default.bold("Session:"));
|
|
13725
|
+
console.log(import_chalk20.default.green(` \u2705 Logged in as ${session.email}`));
|
|
13726
|
+
const minutesLeft = getMinutesUntilExpiration();
|
|
13727
|
+
if (minutesLeft !== null) {
|
|
13728
|
+
if (minutesLeft > 60) {
|
|
13729
|
+
const hours = Math.floor(minutesLeft / 60);
|
|
13730
|
+
console.log(import_chalk20.default.gray(` Expires in ${hours} hour${hours !== 1 ? "s" : ""}`));
|
|
13731
|
+
} else if (minutesLeft > 0) {
|
|
13732
|
+
console.log(import_chalk20.default.gray(` Expires in ${minutesLeft} minute${minutesLeft !== 1 ? "s" : ""}`));
|
|
13733
|
+
} else {
|
|
13734
|
+
console.log(import_chalk20.default.red(" Session expired - please login again"));
|
|
13735
|
+
}
|
|
13736
|
+
}
|
|
13737
|
+
console.log("");
|
|
13738
|
+
}
|
|
13739
|
+
__name(handleStatus2, "handleStatus");
|
|
13740
|
+
async function handleLogin(options) {
|
|
13741
|
+
const { sso } = options;
|
|
13742
|
+
if (!sso) {
|
|
13743
|
+
console.log(import_chalk20.default.yellow("Use --sso flag for SSO login"));
|
|
13744
|
+
console.log(import_chalk20.default.gray("\nExample: infra-cost login --sso"));
|
|
13745
|
+
return;
|
|
13746
|
+
}
|
|
13747
|
+
const config = loadSSOConfig();
|
|
13748
|
+
if (!config || !config.enabled) {
|
|
13749
|
+
console.error(import_chalk20.default.red("Error: SSO not configured"));
|
|
13750
|
+
console.log(import_chalk20.default.gray("\nRun `infra-cost sso configure` to set up SSO"));
|
|
13751
|
+
process.exit(1);
|
|
13752
|
+
}
|
|
13753
|
+
console.log(import_chalk20.default.blue("\u{1F510} Starting SSO login...\n"));
|
|
13754
|
+
const authUrl = generateAuthorizationUrl(config);
|
|
13755
|
+
console.log(import_chalk20.default.bold("Opening browser for authentication..."));
|
|
13756
|
+
console.log(import_chalk20.default.gray(`Provider: ${config.provider}`));
|
|
13757
|
+
console.log(import_chalk20.default.gray(`URL: ${authUrl}
|
|
13758
|
+
`));
|
|
13759
|
+
console.log(import_chalk20.default.gray("\u23F3 Waiting for authentication...\n"));
|
|
13760
|
+
setTimeout(() => {
|
|
13761
|
+
const session = {
|
|
13762
|
+
provider: config.provider,
|
|
13763
|
+
user: "John Doe",
|
|
13764
|
+
email: "john.doe@company.com",
|
|
13765
|
+
accessToken: "simulated_access_token",
|
|
13766
|
+
refreshToken: "simulated_refresh_token",
|
|
13767
|
+
idToken: "simulated_id_token",
|
|
13768
|
+
expiresAt: new Date(Date.now() + 3600 * 1e3).toISOString()
|
|
13769
|
+
// 1 hour
|
|
13770
|
+
};
|
|
13771
|
+
saveSSOSession(session);
|
|
13772
|
+
console.log(import_chalk20.default.green("\u2705 Successfully logged in!"));
|
|
13773
|
+
console.log(import_chalk20.default.gray(` User: ${session.email}`));
|
|
13774
|
+
console.log(import_chalk20.default.gray(` Provider: ${config.provider}`));
|
|
13775
|
+
console.log(import_chalk20.default.gray(" Session expires in 1 hour\n"));
|
|
13776
|
+
console.log(import_chalk20.default.gray("You can now run infra-cost commands"));
|
|
13777
|
+
}, 1e3);
|
|
13778
|
+
}
|
|
13779
|
+
__name(handleLogin, "handleLogin");
|
|
13780
|
+
async function handleLogout(options) {
|
|
13781
|
+
if (!isLoggedIn()) {
|
|
13782
|
+
console.log(import_chalk20.default.yellow("Not currently logged in"));
|
|
13783
|
+
return;
|
|
13784
|
+
}
|
|
13785
|
+
const user = getCurrentUser();
|
|
13786
|
+
clearSSOSession();
|
|
13787
|
+
console.log(import_chalk20.default.green(`\u2705 Logged out${user ? ` (${user})` : ""}`));
|
|
13788
|
+
}
|
|
13789
|
+
__name(handleLogout, "handleLogout");
|
|
13790
|
+
async function handleRefresh(options) {
|
|
13791
|
+
const session = loadSSOSession();
|
|
13792
|
+
if (!session) {
|
|
13793
|
+
console.error(import_chalk20.default.red("Error: Not logged in"));
|
|
13794
|
+
console.log(import_chalk20.default.gray("\nRun `infra-cost login --sso` to authenticate"));
|
|
13795
|
+
process.exit(1);
|
|
13796
|
+
}
|
|
13797
|
+
console.log(import_chalk20.default.blue("\u{1F504} Refreshing SSO session...\n"));
|
|
13798
|
+
setTimeout(() => {
|
|
13799
|
+
const refreshedSession = {
|
|
13800
|
+
...session,
|
|
13801
|
+
expiresAt: new Date(Date.now() + 3600 * 1e3).toISOString()
|
|
13802
|
+
// 1 hour
|
|
13803
|
+
};
|
|
13804
|
+
saveSSOSession(refreshedSession);
|
|
13805
|
+
console.log(import_chalk20.default.green("\u2705 Session refreshed"));
|
|
13806
|
+
console.log(import_chalk20.default.gray(" New expiration: 1 hour from now\n"));
|
|
13807
|
+
}, 500);
|
|
13808
|
+
}
|
|
13809
|
+
__name(handleRefresh, "handleRefresh");
|
|
13810
|
+
function registerSSOCommands(program) {
|
|
13811
|
+
const sso = program.command("sso").description("SSO/SAML enterprise authentication");
|
|
13812
|
+
sso.command("configure").description("Configure SSO provider").option("--provider <name>", "SSO provider (okta, azure-ad, google)").option("--domain <domain>", "Okta domain (e.g., company.okta.com)").option("--tenant-id <id>", "Azure AD tenant ID").option("--client-id <id>", "OAuth client ID").option("--client-secret <secret>", "OAuth client secret").option("--issuer <url>", "OIDC issuer URL").action(handleConfigure);
|
|
13813
|
+
sso.command("status").description("Show SSO configuration and session status").action(handleStatus2);
|
|
13814
|
+
sso.command("refresh").description("Refresh SSO session").action(handleRefresh);
|
|
13815
|
+
program.command("login").description("Login with SSO").option("--sso", "Use SSO login").action(handleLogin);
|
|
13816
|
+
program.command("logout").description("Logout from SSO session").action(handleLogout);
|
|
13817
|
+
}
|
|
13818
|
+
__name(registerSSOCommands, "registerSSOCommands");
|
|
13819
|
+
|
|
13820
|
+
// src/cli/commands/plugin/index.ts
|
|
13821
|
+
var import_chalk21 = __toESM(require("chalk"));
|
|
13822
|
+
|
|
13823
|
+
// src/core/plugins.ts
|
|
13824
|
+
var import_path6 = require("path");
|
|
13825
|
+
var import_os6 = require("os");
|
|
13826
|
+
var PLUGIN_DIR = (0, import_path6.join)((0, import_os6.homedir)(), ".infra-cost", "plugins");
|
|
13827
|
+
var loadedPlugins = /* @__PURE__ */ new Map();
|
|
13828
|
+
function getLoadedPlugins() {
|
|
13829
|
+
return Array.from(loadedPlugins.values());
|
|
13830
|
+
}
|
|
13831
|
+
__name(getLoadedPlugins, "getLoadedPlugins");
|
|
13832
|
+
|
|
13833
|
+
// src/cli/commands/plugin/index.ts
|
|
13834
|
+
async function handleList3(options) {
|
|
13835
|
+
const plugins = getLoadedPlugins();
|
|
13836
|
+
console.log(import_chalk21.default.bold("\n\u{1F50C} Installed Plugins\n"));
|
|
13837
|
+
if (plugins.length === 0) {
|
|
13838
|
+
console.log(import_chalk21.default.yellow("No plugins installed"));
|
|
13839
|
+
console.log(import_chalk21.default.gray("\nPlugins should be installed in: ~/.infra-cost/plugins/"));
|
|
13840
|
+
return;
|
|
13841
|
+
}
|
|
13842
|
+
plugins.forEach((plugin) => {
|
|
13843
|
+
console.log(import_chalk21.default.bold(`${plugin.name} v${plugin.version}`));
|
|
13844
|
+
console.log(import_chalk21.default.gray(` ${plugin.description}`));
|
|
13845
|
+
if (plugin.author) {
|
|
13846
|
+
console.log(import_chalk21.default.gray(` Author: ${plugin.author}`));
|
|
13847
|
+
}
|
|
13848
|
+
console.log("");
|
|
13849
|
+
});
|
|
13850
|
+
}
|
|
13851
|
+
__name(handleList3, "handleList");
|
|
13852
|
+
async function handleInfo(options) {
|
|
13853
|
+
console.log(import_chalk21.default.bold("\n\u{1F50C} Plugin System Information\n"));
|
|
13854
|
+
console.log(import_chalk21.default.bold("Plugin Directory:"));
|
|
13855
|
+
console.log(import_chalk21.default.gray(" ~/.infra-cost/plugins/\n"));
|
|
13856
|
+
console.log(import_chalk21.default.bold("Plugin Structure:"));
|
|
13857
|
+
console.log(import_chalk21.default.gray(" my-plugin/"));
|
|
13858
|
+
console.log(import_chalk21.default.gray(" \u251C\u2500\u2500 package.json # Plugin metadata"));
|
|
13859
|
+
console.log(import_chalk21.default.gray(" \u2514\u2500\u2500 index.js # Plugin entry point\n"));
|
|
13860
|
+
console.log(import_chalk21.default.bold("Example package.json:"));
|
|
13861
|
+
console.log(import_chalk21.default.gray(" {"));
|
|
13862
|
+
console.log(import_chalk21.default.gray(' "name": "my-custom-plugin",'));
|
|
13863
|
+
console.log(import_chalk21.default.gray(' "version": "1.0.0",'));
|
|
13864
|
+
console.log(import_chalk21.default.gray(' "description": "Custom cost provider"'));
|
|
13865
|
+
console.log(import_chalk21.default.gray(" }\n"));
|
|
13866
|
+
console.log(import_chalk21.default.bold("Example index.js:"));
|
|
13867
|
+
console.log(import_chalk21.default.gray(" module.exports = {"));
|
|
13868
|
+
console.log(import_chalk21.default.gray(' name: "my-custom-plugin",'));
|
|
13869
|
+
console.log(import_chalk21.default.gray(' version: "1.0.0",'));
|
|
13870
|
+
console.log(import_chalk21.default.gray(' description: "Custom cost provider",'));
|
|
13871
|
+
console.log(import_chalk21.default.gray(' init: async () => { console.log("Plugin loaded!"); },'));
|
|
13872
|
+
console.log(import_chalk21.default.gray(" registerCommands: (program) => { /* ... */ }"));
|
|
13873
|
+
console.log(import_chalk21.default.gray(" };\n"));
|
|
13874
|
+
console.log(import_chalk21.default.gray("For documentation: https://github.com/codecollab-co/infra-cost#plugins"));
|
|
13875
|
+
}
|
|
13876
|
+
__name(handleInfo, "handleInfo");
|
|
13877
|
+
function registerPluginCommands(program) {
|
|
13878
|
+
const plugin = program.command("plugin").description("Manage custom plugins");
|
|
13879
|
+
plugin.command("list").description("List installed plugins").action(handleList3);
|
|
13880
|
+
plugin.command("info").description("Show plugin system information").action(handleInfo);
|
|
13881
|
+
}
|
|
13882
|
+
__name(registerPluginCommands, "registerPluginCommands");
|
|
13883
|
+
|
|
13118
13884
|
// src/cli/middleware/auth.ts
|
|
13119
13885
|
async function authMiddleware(thisCommand, actionCommand) {
|
|
13120
13886
|
const isConfigCommand = actionCommand.name() === "config" || actionCommand.parent?.name() === "config";
|
|
@@ -13143,7 +13909,7 @@ async function validationMiddleware(thisCommand, actionCommand) {
|
|
|
13143
13909
|
__name(validationMiddleware, "validationMiddleware");
|
|
13144
13910
|
|
|
13145
13911
|
// src/cli/middleware/error-handler.ts
|
|
13146
|
-
var
|
|
13912
|
+
var import_chalk22 = __toESM(require("chalk"));
|
|
13147
13913
|
function errorHandler(error) {
|
|
13148
13914
|
const message = error?.message ?? String(error);
|
|
13149
13915
|
const stack = error?.stack;
|
|
@@ -13151,15 +13917,15 @@ function errorHandler(error) {
|
|
|
13151
13917
|
return;
|
|
13152
13918
|
}
|
|
13153
13919
|
console.error("");
|
|
13154
|
-
console.error(
|
|
13920
|
+
console.error(import_chalk22.default.red("\u2716"), import_chalk22.default.bold("Error:"), message);
|
|
13155
13921
|
if (process.env.DEBUG || process.env.VERBOSE) {
|
|
13156
13922
|
console.error("");
|
|
13157
13923
|
if (stack) {
|
|
13158
|
-
console.error(
|
|
13924
|
+
console.error(import_chalk22.default.gray(stack));
|
|
13159
13925
|
}
|
|
13160
13926
|
} else {
|
|
13161
13927
|
console.error("");
|
|
13162
|
-
console.error(
|
|
13928
|
+
console.error(import_chalk22.default.gray("Run with --verbose for detailed error information"));
|
|
13163
13929
|
}
|
|
13164
13930
|
console.error("");
|
|
13165
13931
|
}
|
|
@@ -13185,6 +13951,9 @@ function createCLI() {
|
|
|
13185
13951
|
registerGitCommands(program);
|
|
13186
13952
|
registerTerraformCommand(program);
|
|
13187
13953
|
registerSchedulerCommands(program);
|
|
13954
|
+
registerRBACCommands(program);
|
|
13955
|
+
registerSSOCommands(program);
|
|
13956
|
+
registerPluginCommands(program);
|
|
13188
13957
|
program.hook("preAction", async (thisCommand, actionCommand) => {
|
|
13189
13958
|
const opts = thisCommand.opts();
|
|
13190
13959
|
const logLevel = opts.verbose ? "debug" : opts.quiet ? "error" : opts.logLevel;
|