113 lines
3.5 KiB
TypeScript
113 lines
3.5 KiB
TypeScript
import type { Command } from "commander";
|
|
import {
|
|
getStoredBoardCredential,
|
|
loginBoardCli,
|
|
removeStoredBoardCredential,
|
|
revokeStoredBoardCredential,
|
|
} from "../../client/board-auth.js";
|
|
import {
|
|
addCommonClientOptions,
|
|
handleCommandError,
|
|
printOutput,
|
|
resolveCommandContext,
|
|
type BaseClientOptions,
|
|
} from "./common.js";
|
|
|
|
interface AuthLoginOptions extends BaseClientOptions {
|
|
instanceAdmin?: boolean;
|
|
}
|
|
|
|
interface AuthLogoutOptions extends BaseClientOptions {}
|
|
interface AuthWhoamiOptions extends BaseClientOptions {}
|
|
|
|
export function registerClientAuthCommands(auth: Command): void {
|
|
addCommonClientOptions(
|
|
auth
|
|
.command("login")
|
|
.description("Authenticate the CLI for board-user access")
|
|
.option("--instance-admin", "Request instance-admin approval instead of plain board access", false)
|
|
.action(async (opts: AuthLoginOptions) => {
|
|
try {
|
|
const ctx = resolveCommandContext(opts);
|
|
const login = await loginBoardCli({
|
|
apiBase: ctx.api.apiBase,
|
|
requestedAccess: opts.instanceAdmin ? "instance_admin_required" : "board",
|
|
requestedCompanyId: ctx.companyId ?? null,
|
|
command: "paperclipai auth login",
|
|
});
|
|
printOutput(
|
|
{
|
|
ok: true,
|
|
apiBase: ctx.api.apiBase,
|
|
userId: login.userId ?? null,
|
|
approvalUrl: login.approvalUrl,
|
|
},
|
|
{ json: ctx.json },
|
|
);
|
|
} catch (err) {
|
|
handleCommandError(err);
|
|
}
|
|
}),
|
|
{ includeCompany: true },
|
|
);
|
|
|
|
addCommonClientOptions(
|
|
auth
|
|
.command("logout")
|
|
.description("Remove the stored board-user credential for this API base")
|
|
.action(async (opts: AuthLogoutOptions) => {
|
|
try {
|
|
const ctx = resolveCommandContext(opts);
|
|
const credential = getStoredBoardCredential(ctx.api.apiBase);
|
|
if (!credential) {
|
|
printOutput({ ok: true, apiBase: ctx.api.apiBase, revoked: false, removedLocalCredential: false }, { json: ctx.json });
|
|
return;
|
|
}
|
|
let revoked = false;
|
|
try {
|
|
await revokeStoredBoardCredential({
|
|
apiBase: ctx.api.apiBase,
|
|
token: credential.token,
|
|
});
|
|
revoked = true;
|
|
} catch {
|
|
// Remove the local credential even if the server-side revoke fails.
|
|
}
|
|
const removedLocalCredential = removeStoredBoardCredential(ctx.api.apiBase);
|
|
printOutput(
|
|
{
|
|
ok: true,
|
|
apiBase: ctx.api.apiBase,
|
|
revoked,
|
|
removedLocalCredential,
|
|
},
|
|
{ json: ctx.json },
|
|
);
|
|
} catch (err) {
|
|
handleCommandError(err);
|
|
}
|
|
}),
|
|
);
|
|
|
|
addCommonClientOptions(
|
|
auth
|
|
.command("whoami")
|
|
.description("Show the current board-user identity for this API base")
|
|
.action(async (opts: AuthWhoamiOptions) => {
|
|
try {
|
|
const ctx = resolveCommandContext(opts);
|
|
const me = await ctx.api.get<{
|
|
user: { id: string; name: string; email: string } | null;
|
|
userId: string;
|
|
isInstanceAdmin: boolean;
|
|
companyIds: string[];
|
|
source: string;
|
|
keyId: string | null;
|
|
}>("/api/cli-auth/me");
|
|
printOutput(me, { json: ctx.json });
|
|
} catch (err) {
|
|
handleCommandError(err);
|
|
}
|
|
}),
|
|
);
|
|
}
|