import type { ExtensionAPI, ExtensionCommandContext } from "@earendil-works/pi-coding-agent"; import type { Range } from "./aggregate.ts"; import { TokenUsePanel } from "./panel.ts"; import { loadPriceBook } from "./pricing.ts"; import { scanUsage } from "./scan.ts"; const CUSTOM_RANGE = /^(\d{4}-\d{2}-\d{2})\.\.(\d{4}-\d{2}-\d{2})$/; /** 面板内的 t 键只在四档预设间循环;自定义区间通过命令参数传入 */ export function parseRange(args: string): Range { const arg = args.trim(); if (arg === "today" || arg === "7d" || arg === "30d" || arg === "all") return arg; const m = CUSTOM_RANGE.exec(arg); if (m) { const from = Date.parse(`${m[1]}T00:00:00`); const to = Date.parse(`${m[2]}T23:59:59.999`); if (!Number.isNaN(from) && !Number.isNaN(to)) return { from, to }; } return "all"; } export default function (pi: ExtensionAPI) { pi.registerCommand("token-use", { description: "交互式浏览本机 pi 的 token 用量(按时间/项目/模型钻取)", handler: async (args: string, ctx: ExtensionCommandContext) => { let records: ReturnType; try { records = scanUsage(); } catch (err) { ctx.ui.notify(`读取会话目录失败:${err instanceof Error ? err.message : String(err)}`, "error"); return; } if (records.length === 0) { ctx.ui.notify("没有找到任何带用量的会话记录", "info"); return; } // 每次打开都重新加载定价配置,改完文件重开面板即生效 const book = loadPriceBook(); const range = parseRange(args); await ctx.ui.custom( (tui, theme, _keybindings, done) => new TokenUsePanel(tui, records, ctx.cwd, theme, book, () => done(undefined), range), { // 铺满整屏:overlay 本身不清屏,只有占满才能盖住背后的对话内容 overlay: true, overlayOptions: { width: "100%", maxHeight: "100%", anchor: "center", margin: 0 }, }, ); }, }); }