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