import type { C2cCli, C2cWhoami } from "./c2c-cli.ts"; import { sanitizeAlias, type Identity } from "./identity.ts"; export interface AliasUpdateSuccess { ok: true; alias: string; previousAlias?: string; sessionId: string; sanitized?: boolean; crossRepoRegistered?: boolean; crossRepoError?: string; } export interface AliasUpdateFailure { ok: false; alias?: string; previousAlias?: string; sessionId?: string; sanitized?: boolean; error: string; } export type AliasUpdateResult = AliasUpdateSuccess | AliasUpdateFailure; export type AliasUpdateDetails = Omit | Omit; export function aliasUpdateDetails(result: AliasUpdateResult): AliasUpdateDetails { if (result.ok) { const { ok: _ok, ...details } = result; return details; } const { ok: _ok, ...details } = result; return details; } export function formatAliasUpdateText(result: AliasUpdateSuccess, opts?: { sentenceStart?: boolean }): string { const verb = opts?.sentenceStart ? "Alias" : "alias"; const base = result.previousAlias && result.previousAlias !== result.alias ? `${verb} changed ${result.previousAlias} → ${result.alias}` : `${verb} set to ${result.alias}`; return result.crossRepoError ? `${base}. Cross-repo registration warning: ${result.crossRepoError}` : base; } export function identityWithAlias(identity: Identity, alias: string): Identity { return { ...identity, alias }; } type AliasCli = Pick; export interface AliasBrokerOptions { sessionsBrokerRoot?: string; } function errorMessage(e: unknown): string { return e instanceof Error ? e.message : String(e); } async function mirrorToSessionsBroker( cli: AliasCli, alias: string, sessionId: string, sessionsBrokerRoot: string | undefined, ): Promise> { if (!sessionsBrokerRoot) return {}; try { const whoami = await cli.register(alias, sessionId, { brokerRoot: sessionsBrokerRoot }); if (!whoami) return { crossRepoRegistered: false, crossRepoError: "register returned no identity" }; return { crossRepoRegistered: true }; } catch (e) { return { crossRepoRegistered: false, crossRepoError: errorMessage(e) }; } } export async function registerAliasForIdentity( cli: AliasCli, identity: Identity | null, rawAlias: string, opts: AliasBrokerOptions = {}, ): Promise { const previousAlias = identity?.alias; const sessionId = identity?.sessionId; const trimmed = rawAlias.trim(); const alias = sanitizeAlias(rawAlias); const sanitized = alias !== trimmed; if (!identity || !sessionId) { return { ok: false, alias, previousAlias, sanitized, error: "c2c: not registered yet" }; } if (!alias) { return { ok: false, alias, previousAlias, sessionId, sanitized, error: "alias is empty after sanitization", }; } try { await cli.register(alias, sessionId); } catch (e) { return { ok: false, alias, previousAlias, sessionId, sanitized, error: errorMessage(e) }; } const crossRepo = await mirrorToSessionsBroker(cli, alias, sessionId, opts.sessionsBrokerRoot); return { ok: true, alias, previousAlias, sessionId, sanitized, ...crossRepo, }; } function usableWhoami(whoami: C2cWhoami | null, identity: Identity): C2cWhoami | null { if (!whoami) return null; if (whoami.session_id && whoami.session_id !== identity.sessionId) return null; if (!whoami.alias) return null; return whoami; } export async function refreshAliasFromBroker( cli: AliasCli, identity: Identity | null, opts: AliasBrokerOptions = {}, ): Promise { if (!identity) return null; let whoami: C2cWhoami | null; try { whoami = usableWhoami(await cli.whoami(), identity); } catch { return null; } if (!whoami) return null; const alias = sanitizeAlias(whoami.alias); const sanitized = alias !== whoami.alias.trim(); if (!alias || alias === identity.alias) return null; const crossRepo = await mirrorToSessionsBroker(cli, alias, identity.sessionId, opts.sessionsBrokerRoot); return { ok: true, alias, previousAlias: identity.alias, sessionId: identity.sessionId, sanitized, ...crossRepo, }; }