11import { defineCommand , detectOutputFormat , unwrapResponse } from "bailian-cli-core" ;
2- import {
3- ansi ,
4- displayWidth ,
5- emitResult ,
6- type AnsiStyles ,
7- type TextStyle ,
8- } from "bailian-cli-runtime" ;
9- import { formatDateTime } from "./shared.ts" ;
2+ import { emitResult } from "bailian-cli-runtime" ;
3+ import { printQuotaBox , readNumber } from "./quota-box.ts" ;
104
115const TOKEN_PLAN_USAGE_API = "zeldaHttp.apikeyMgr./tokenplan/personal/api/v2/usage" ;
12- const BOX_WIDTH = 76 ;
13- const PROGRESS_WIDTH = 32 ;
146
157interface TokenPlanUsage {
168 per5HourPercentage ?: number ;
@@ -19,16 +11,6 @@ interface TokenPlanUsage {
1911 per1WeekResetTime ?: number ;
2012}
2113
22- interface QuotaWindow {
23- percentage ?: number ;
24- resetTime ?: number ;
25- }
26-
27- /** Accept only finite numbers; anything else counts as absent (possibly unlimited). */
28- function readNumber ( value : unknown ) : number | undefined {
29- return typeof value === "number" && Number . isFinite ( value ) ? value : undefined ;
30- }
31-
3214function readUsage ( result : unknown ) : TokenPlanUsage {
3315 const response = unwrapResponse ( result as Record < string , unknown > ) ;
3416 const usage : TokenPlanUsage = { } ;
@@ -45,78 +27,27 @@ function readUsage(result: unknown): TokenPlanUsage {
4527 return usage ;
4628}
4729
48- function formatPercentage ( ratio : number ) : string {
49- return `${ ( ratio * 100 ) . toFixed ( 2 ) } %` ;
50- }
51-
52- function formatRemainingTime ( resetTime : number , now : number ) : string {
53- const remainingMs = Math . max ( 0 , resetTime - now ) ;
54- const totalMinutes = Math . floor ( remainingMs / 60_000 ) ;
55- if ( totalMinutes === 0 ) return "now" ;
56-
57- const days = Math . floor ( totalMinutes / ( 24 * 60 ) ) ;
58- const hours = Math . floor ( ( totalMinutes % ( 24 * 60 ) ) / 60 ) ;
59- const minutes = totalMinutes % 60 ;
60- const parts : string [ ] = [ ] ;
61- if ( days > 0 ) parts . push ( `${ days } d` ) ;
62- if ( hours > 0 ) parts . push ( `${ hours } h` ) ;
63- if ( minutes > 0 || parts . length === 0 ) parts . push ( `${ minutes } m` ) ;
64- return parts . join ( " " ) ;
65- }
66-
67- function progressBar ( ratio : number ) : string {
68- const clampedRatio = Math . min ( 1 , Math . max ( 0 , ratio ) ) ;
69- const filled = Math . round ( clampedRatio * PROGRESS_WIDTH ) ;
70- return `[${ "█" . repeat ( filled ) } ${ "░" . repeat ( PROGRESS_WIDTH - filled ) } ]` ;
71- }
72-
73- function progressStyle ( percentage : number , color : AnsiStyles ) : TextStyle {
74- if ( percentage >= 0.9 ) return color . red ;
75- if ( percentage >= 0.75 ) return color . yellow ;
76- return color . green ;
77- }
78-
7930function printView ( usage : TokenPlanUsage , generatedAt : number ) : void {
80- const color = ansi ( process . stdout ) ;
81- const writeLine = ( text = "" , style ?: TextStyle ) => {
82- const padding = Math . max ( 0 , BOX_WIDTH - displayWidth ( ` ${ text } ` ) ) ;
83- process . stdout . write ( `│ ${ style ? style ( text ) : text } ${ " " . repeat ( padding ) } │\n` ) ;
84- } ;
85- const writeQuota = ( label : string , unlimitedMessage : string , window : QuotaWindow ) => {
86- writeLine ( label , color . bold ) ;
87- if ( window . percentage === undefined ) {
88- writeLine ( unlimitedMessage , color . dim ) ;
89- return ;
90- }
91-
92- const percentageText = formatPercentage ( window . percentage ) ;
93- const bar = progressBar ( window . percentage ) ;
94- writeLine ( `${ percentageText } used ${ bar } ` , progressStyle ( window . percentage , color ) ) ;
95- if ( window . resetTime === undefined ) {
96- writeLine ( "Resets: not applicable (no usage yet)" , color . dim ) ;
97- return ;
98- }
99-
100- const resetText = `Resets: ${ formatDateTime ( window . resetTime ) } (in ${ formatRemainingTime ( window . resetTime , generatedAt ) } )` ;
101- writeLine ( resetText , color . dim ) ;
102- } ;
103-
104- process . stdout . write ( `┌${ "─" . repeat ( BOX_WIDTH ) } ┐\n` ) ;
105- writeLine ( "Token Plan Usage" , color . cyan ) ;
106- writeLine ( `Generated at: ${ formatDateTime ( generatedAt ) } (local time)` , color . dim ) ;
107- process . stdout . write ( `├${ "─" . repeat ( BOX_WIDTH ) } ┤\n` ) ;
108- writeQuota (
109- "5-hour quota" ,
110- "The 5-hour limit may be unlimited; verify in the Bailian Token Plan console." ,
111- { percentage : usage . per5HourPercentage , resetTime : usage . per5HourResetTime } ,
112- ) ;
113- process . stdout . write ( `├${ "─" . repeat ( BOX_WIDTH ) } ┤\n` ) ;
114- writeQuota (
115- "1-week quota" ,
116- "The 1-week limit may be unlimited; verify in the Bailian Token Plan console." ,
117- { percentage : usage . per1WeekPercentage , resetTime : usage . per1WeekResetTime } ,
31+ printQuotaBox (
32+ "Token Plan Usage" ,
33+ [
34+ {
35+ label : "5-hour quota" ,
36+ emptyMessage :
37+ "The 5-hour limit may be unlimited; verify in the Bailian Token Plan console." ,
38+ percentage : usage . per5HourPercentage ,
39+ resetTime : usage . per5HourResetTime ,
40+ } ,
41+ {
42+ label : "1-week quota" ,
43+ emptyMessage :
44+ "The 1-week limit may be unlimited; verify in the Bailian Token Plan console." ,
45+ percentage : usage . per1WeekPercentage ,
46+ resetTime : usage . per1WeekResetTime ,
47+ } ,
48+ ] ,
49+ generatedAt ,
11850 ) ;
119- process . stdout . write ( `└${ "─" . repeat ( BOX_WIDTH ) } ┘\n` ) ;
12051}
12152
12253export default defineCommand ( {
0 commit comments