Files
sk-az-tools/src/cli.ts
2026-03-06 19:05:34 +01:00

157 lines
4.6 KiB
JavaScript

#!/usr/bin/env node
// SPDX-License-Identifier: MIT
import { parseArgs } from "node:util";
import { runCommand } from "./cli/commands.ts";
import { usageGetToken } from "./cli/commands/get-token.ts";
import { usageListAppGrants } from "./cli/commands/list-app-grants.ts";
import { usageListAppPermissions } from "./cli/commands/list-app-permissions.ts";
import { usageListApps } from "./cli/commands/list-apps.ts";
import { usageListResourcePermissions } from "./cli/commands/list-resource-permissions.ts";
import { usageLogin } from "./cli/commands/login.ts";
import { usageLogout } from "./cli/commands/logout.ts";
import { usageRest } from "./cli/commands/rest.ts";
import {
normalizeOutputFormat,
outputFiltered,
parseHeaderSpec,
renderOutput,
} from "@slawek/sk-tools";
import {
omitPermissionGuidColumns,
} from "./cli/permission-utils.ts";
type CliValues = {
help?: boolean;
type?: string;
method?: string;
url?: string;
"display-name"?: string;
"app-id"?: string;
resources?: string;
"use-device-code"?: boolean;
"no-browser"?: boolean;
browser?: string;
"browser-profile"?: string;
all?: boolean;
resolve?: boolean;
short?: boolean;
filter?: string;
query?: string;
header?: string;
output?: string;
[key: string]: string | boolean | undefined;
};
function usage(): string {
return `Usage: sk-az-tools <command> [options]
Commands:
login Authenticate selected resources
logout Sign out and clear login state
get-token Get access token (azurerm|devops)
rest Call REST API endpoint
list-apps List Entra applications
list-app-permissions List required permissions for an app
list-app-grants List OAuth2 grants for an app
list-resource-permissions List available permissions for a resource app
Global options (all commands):
-q, --query <jmespath>
-o, --output <format> table|t|alignedtable|at|prettytable|pt|tsv
-h, --help
Use: sk-az-tools --help <command>
or: sk-az-tools <command> --help`;
}
function usageCommand(command: string): string {
switch (command) {
case "login":
return usageLogin();
case "list-apps":
return usageListApps();
case "logout":
return usageLogout();
case "get-token":
return usageGetToken();
case "rest":
return usageRest();
case "list-app-permissions":
return usageListAppPermissions();
case "list-app-grants":
return usageListAppGrants();
case "list-resource-permissions":
return usageListResourcePermissions();
default:
return `Unknown command: ${command}\n\n${usage()}`;
}
}
async function main(): Promise<void> {
const argv = process.argv.slice(2);
const command = argv[0];
if (!command) {
console.log(usage());
process.exit(0);
}
if (command === "-h" || command === "--help") {
const helpCommand = argv[1];
console.log(helpCommand ? usageCommand(helpCommand) : usage());
process.exit(0);
}
const { values } = parseArgs({
args: argv.slice(1),
options: {
help: { type: "boolean", short: "h" },
type: { type: "string", short: "t" },
method: { type: "string" },
url: { type: "string" },
"display-name": { type: "string", short: "n" },
"app-id": { type: "string", short: "i" },
resources: { type: "string" },
"use-device-code": { type: "boolean" },
"no-browser": { type: "boolean" },
browser: { type: "string" },
"browser-profile": { type: "string" },
all: { type: "boolean" },
resolve: { type: "boolean", short: "r" },
short: { type: "boolean", short: "s" },
filter: { type: "string", short: "f" },
query: { type: "string", short: "q" },
header: { type: "string", short: "H" },
output: { type: "string", short: "o" },
},
strict: true,
allowPositionals: false,
});
const typedValues = values as CliValues;
if (typedValues.help) {
console.log(usageCommand(command));
process.exit(0);
}
const outputFormat = normalizeOutputFormat(typedValues.output);
const result = await runCommand(command, typedValues);
const filtered = outputFiltered(result, typedValues.query);
const output = command === "list-app-permissions" && typedValues.short
? omitPermissionGuidColumns(filtered)
: filtered;
const headerSpec = command === "rest"
? parseHeaderSpec(undefined)
: parseHeaderSpec(typedValues.header);
renderOutput(command, output, outputFormat, headerSpec);
}
main().catch((err: unknown) => {
const error = err as Error;
console.error(`Error: ${error.message}`);
console.error(usage());
process.exit(1);
});