Files
kuber/command/main.ts
T
2026-10-07 10:33:21 +00:00

121 lines
4.1 KiB
TypeScript

import { defineCommand } from "citty";
import { wrapCommandErrors } from "../lib/error";
function lazyCommand<T extends object>(
load: () => Promise<T>,
options: { hidden?: boolean } = {},
): () => Promise<T> {
let pending: Promise<T> | undefined;
return () =>
(pending ??= load()
.then((command) => {
// Keep the exported command objects pristine: tests and callers may
// import them directly while the CLI needs wrapped handlers.
const cliCommand = cloneCommand(command);
if (options.hidden) {
const definition = cliCommand as T & {
meta?: Record<string, unknown>;
};
definition.meta = { ...definition.meta, hidden: true };
}
wrapCommandErrors(cliCommand);
return cliCommand;
})
.catch((error) => {
pending = undefined;
throw error;
}));
}
function cloneCommand<T extends object>(command: T): T {
const clone = { ...command } as T & { subCommands?: unknown };
const subCommands = clone.subCommands;
if (subCommands && typeof subCommands === "object") {
clone.subCommands = Object.fromEntries(
Object.entries(subCommands).map(([name, child]) => [
name,
child && typeof child === "object" ? cloneCommand(child) : child,
]),
);
}
return clone;
}
export const main = defineCommand({
meta: {
name: "kuber",
version: "2.7.1",
description: "Deploy and manage projects on Kubernetes",
},
args: {
config: {
type: "string",
description: "Path to a kuber configuration file",
},
},
subCommands: {
add: lazyCommand(() => import("./add").then((m) => m.add)),
audit: lazyCommand(() => import("./audit").then((m) => m.audit), {
hidden: true,
}),
ci: lazyCommand(() => import("./ci").then((m) => m.ci), { hidden: true }),
db: lazyCommand(() => import("./db").then((m) => m.db)),
down: lazyCommand(() => import("./down").then((m) => m.down)),
export: lazyCommand(() => import("./export").then((m) => m.exportCommand)),
exec: lazyCommand(() => import("./exec").then((m) => m.exec)),
init: lazyCommand(() => import("./init").then((m) => m.init)),
logs: lazyCommand(() => import("./logs").then((m) => m.logs)),
maintenance: lazyCommand(
() => import("./maintenance").then((m) => m.maintenance),
{ hidden: true },
),
login: lazyCommand(() => import("./auth").then((m) => m.login)),
logout: lazyCommand(() => import("./auth").then((m) => m.logout), {
hidden: true,
}),
operations: lazyCommand(
() => import("./operations").then((m) => m.operations),
{ hidden: true },
),
ps: lazyCommand(() => import("./ps").then((m) => m.ps)),
restart: lazyCommand(() => import("./restart").then((m) => m.restart)),
rollback: lazyCommand(() => import("./rollback").then((m) => m.rollback)),
s3: lazyCommand(() => import("./s3").then((m) => m.s3)),
start: lazyCommand(() => import("./start").then((m) => m.start)),
stop: lazyCommand(() => import("./stop").then((m) => m.stop)),
up: lazyCommand(() => import("./up").then((m) => m.up)),
users: lazyCommand(() => import("./users").then((m) => m.users), {
hidden: true,
}),
trust: lazyCommand(() => import("./trust").then((m) => m.trust)),
whoami: lazyCommand(() => import("./auth").then((m) => m.whoami), {
hidden: true,
}),
},
});
type Completion = Awaited<
ReturnType<(typeof import("@bomb.sh/tab/citty"))["default"]>
>;
let completion: Promise<Completion> | undefined;
export function initializeCompletion() {
completion ??= (async () => {
// tab inspects command objects directly, unlike citty's lazy resolver.
const commands = main.subCommands;
if (commands && typeof commands !== "function") {
main.subCommands = Object.fromEntries(
await Promise.all(
Object.entries(commands).map(async ([name, command]) => [
name,
typeof command === "function" ? await command() : await command,
]),
),
);
}
const { default: tab } = await import("@bomb.sh/tab/citty");
return tab(main);
})();
return completion;
}