107 lines
3.6 KiB
TypeScript
107 lines
3.6 KiB
TypeScript
import { homedir } from "node:os";
|
|
import type { ModuleContext, RenderedModule } from "../types";
|
|
|
|
function formatTokens(count: number): string {
|
|
if (count < 1000) return count.toString();
|
|
if (count < 10000) return `${(count / 1000).toFixed(1)}k`;
|
|
if (count < 1000000) return `${Math.round(count / 1000)}k`;
|
|
if (count < 10000000) return `${(count / 1000000).toFixed(1)}M`;
|
|
return `${Math.round(count / 1000000)}M`;
|
|
}
|
|
|
|
function sessionEntries(ctx: any): any[] {
|
|
try {
|
|
return ctx.sessionManager?.getEntries?.() ?? [];
|
|
} catch {
|
|
return [];
|
|
}
|
|
}
|
|
|
|
function totalCost(ctx: any): number {
|
|
return sessionEntries(ctx).reduce((total, entry) => {
|
|
const cost =
|
|
entry?.type === "message" && entry.message?.role === "assistant"
|
|
? entry.message.usage?.cost?.total
|
|
: undefined;
|
|
return (
|
|
total + (typeof cost === "number" && Number.isFinite(cost) ? cost : 0)
|
|
);
|
|
}, 0);
|
|
}
|
|
|
|
function isUsingSubscription(ctx: any): boolean {
|
|
const provider = ctx.model?.provider;
|
|
if (!provider) return false;
|
|
try {
|
|
return Boolean(ctx.modelRegistry?.isUsingOAuth?.(ctx.model));
|
|
} catch {
|
|
return provider === "anthropic" || provider === "openai-codex";
|
|
}
|
|
}
|
|
|
|
export function directoryModule(moduleCtx: ModuleContext): RenderedModule {
|
|
const cwd = moduleCtx.ctx.sessionManager?.getCwd?.() ?? process.cwd();
|
|
const home = homedir();
|
|
let text = home && cwd.startsWith(home) ? `~${cwd.slice(home.length)}` : cwd;
|
|
|
|
// Git And Session Name
|
|
const options = moduleCtx.config.modules.directory;
|
|
if (options?.showGitBranch) {
|
|
const branch = moduleCtx.footerData?.getGitBranch?.();
|
|
if (branch) text = `${text} (${branch})`;
|
|
}
|
|
if (options?.showSessionName) {
|
|
const sessionName = moduleCtx.ctx.sessionManager?.getSessionName?.();
|
|
if (sessionName) text = `${text} • ${sessionName}`;
|
|
}
|
|
|
|
return { text };
|
|
}
|
|
|
|
export function contextModule(moduleCtx: ModuleContext): RenderedModule {
|
|
const usage = moduleCtx.ctx.getContextUsage?.();
|
|
const contextWindow =
|
|
usage?.contextWindow ?? moduleCtx.ctx.model?.contextWindow ?? 0;
|
|
const percent = usage?.percent;
|
|
const showWindow = moduleCtx.config.modules.context?.showWindow ?? false;
|
|
const value =
|
|
percent === null || percent === undefined
|
|
? "?"
|
|
: `${Number(percent).toFixed(1)}%`;
|
|
return {
|
|
text: showWindow ? `${value} / ${formatTokens(contextWindow)}` : `${value}`,
|
|
};
|
|
}
|
|
|
|
export function modelModule(moduleCtx: ModuleContext): RenderedModule {
|
|
const model = moduleCtx.ctx.model;
|
|
if (!model) return { text: "no-model" };
|
|
|
|
// Model Label
|
|
const options = moduleCtx.config.modules.model;
|
|
const provider = options?.showProvider ? `(${model.provider}) ` : "";
|
|
let text = `${provider}${model.id}`;
|
|
if (options?.showThinking && model.reasoning) {
|
|
const thinking = moduleCtx.state.thinkingLevel ?? "off";
|
|
text = thinking === "off" ? `${text} thinking off` : `${text} ${thinking}`;
|
|
}
|
|
return { text };
|
|
}
|
|
|
|
export function thinkingModule(moduleCtx: ModuleContext): RenderedModule {
|
|
if (!moduleCtx.ctx.model?.reasoning) return { text: "" };
|
|
const level = moduleCtx.state.thinkingLevel ?? "off";
|
|
if (level === "off" && moduleCtx.config.modules.thinking?.hideWhenOff)
|
|
return { text: "" };
|
|
return { text: `think ${level}` };
|
|
}
|
|
|
|
export function costModule(moduleCtx: ModuleContext): RenderedModule {
|
|
const cost = totalCost(moduleCtx.ctx);
|
|
const subscription = isUsingSubscription(moduleCtx.ctx);
|
|
if (!cost && !subscription) return { text: "" };
|
|
if (subscription && moduleCtx.config.modules.cost?.showSubscription !== false)
|
|
return { text: cost ? `$${cost.toFixed(3)} sub` : "sub" };
|
|
return { text: `$${cost.toFixed(3)}` };
|
|
}
|