import { defineCommand } from "citty"; import { createInterface } from "node:readline/promises"; import { stdin, stdout } from "node:process"; import { apiRequest } from "../lib/api"; import { readSession, removeSessions, writeSession, type KuberSession, } from "../lib/session"; type LoginResponse = KuberSession; async function promptUsername(): Promise { const readline = createInterface({ input: stdin, output: stdout }); try { return (await readline.question("Username: ")).trim(); } finally { readline.close(); } } async function promptPassword(): Promise { if (!stdin.isTTY || !stdin.setRawMode) { throw new Error("Password input requires an interactive terminal"); } stdout.write("Password: "); stdin.setRawMode(true); stdin.resume(); return new Promise((resolve, reject) => { let password = ""; const cleanup = () => { stdin.off("data", onData); stdin.setRawMode(false); stdin.pause(); stdout.write("\n"); }; const onData = (chunk: Buffer) => { const value = chunk.toString("utf8"); if (value === "\u0003") { cleanup(); reject(new Error("Login cancelled")); return; } if (value === "\r" || value === "\n") { cleanup(); resolve(password); return; } if (value === "\u007f" || value === "\b") { password = password.slice(0, -1); return; } password += value; }; stdin.on("data", onData); }); } export async function loginUser( username: string, password: string, persistent: boolean, ): Promise { const session = await apiRequest( "/login", { method: "POST", body: JSON.stringify({ username, password, persistent }), }, { authenticated: false }, ); await writeSession(session, persistent); return session; } export const login = defineCommand({ meta: { name: "login", description: "Log in to kuber.astrxl.dev", }, args: { persist: { type: "boolean", description: "Keep the login across reboots", }, }, async run({ args }) { const username = String(args._[0] ?? "").trim() || (await promptUsername()); if (!username) throw new Error("Username is required"); const session = await loginUser( username, await promptPassword(), Boolean(args.persist), ); console.log(`Logged in as ${session.user.username}`); }, }); export const logout = defineCommand({ meta: { name: "logout", description: "Log out of kuber.astrxl.dev", }, async run() { const session = await readSession(); if (session) { try { await apiRequest( "/logout", { method: "POST", }, { session }, ); } finally { await removeSessions(); } } else { await removeSessions(); } console.log("Logged out"); }, }); export const whoami = defineCommand({ meta: { name: "whoami", description: "Show the current kuber user", }, async run() { const result = await apiRequest("/me"); console.log(`${result.username} (${result.roles.join(", ")})`); }, });