11import { defineCommand , detectOutputFormat , unwrapResponse } from "bailian-cli-core" ;
22import { emitResult } from "bailian-cli-runtime" ;
3- import { printQuotaBox , readNumber } from "./quota-box.ts" ;
3+ import { printQuotaBox , readNumber , type QuotaSection } from "./quota-box.ts" ;
44
55const TOKEN_PLAN_USAGE_API = "zeldaHttp.apikeyMgr./tokenplan/personal/api/v2/usage" ;
66
7- interface TokenPlanUsage {
8- per1MonthPercentage ?: number ;
9- per1MonthResetTime ?: number ;
7+ /** Quota window fields look like per5HourPercentage / per1WeekResetTime / per1MonthPercentage. */
8+ const WINDOW_FIELD_PATTERN = / ^ p e r ( \d + ) ( H o u r | D a y | W e e k | M o n t h ) ( P e r c e n t a g e | R e s e t T i m e ) $ / ;
9+
10+ /** Personal plans are metered monthly; render an empty monthly window when the API returns none. */
11+ const DEFAULT_WINDOW_KEY = "per1Month" ;
12+
13+ const UNIT_HOURS : Record < string , number > = { Hour : 1 , Day : 24 , Week : 24 * 7 , Month : 24 * 30 } ;
14+
15+ interface TokenPlanWindow {
16+ /** Field prefix, e.g. per1Month. */
17+ key : string ;
18+ count : number ;
19+ unit : string ;
20+ percentage ?: number ;
21+ resetTime ?: number ;
1022}
1123
12- function readUsage ( result : unknown ) : TokenPlanUsage {
13- const response = unwrapResponse ( result as Record < string , unknown > ) ;
14- const usage : TokenPlanUsage = { } ;
24+ /**
25+ * JSON output: keeps the API's per* quota fields as-is; hasQuotaData tells whether any usage
26+ * ratio was read, so consumers can tell "no data" apart from a normal result.
27+ */
28+ type TokenPlanUsage = Record < string , number | boolean > ;
1529
16- const per1MonthPercentage = readNumber ( response . per1MonthPercentage ) ;
17- if ( per1MonthPercentage !== undefined ) usage . per1MonthPercentage = per1MonthPercentage ;
18- const per1MonthResetTime = readNumber ( response . per1MonthResetTime ) ;
19- if ( per1MonthResetTime !== undefined ) usage . per1MonthResetTime = per1MonthResetTime ;
30+ /** Discover windows from the per* fields actually present, so metering changes don't break parsing. */
31+ function readWindows ( response : Record < string , unknown > ) : TokenPlanWindow [ ] {
32+ const windows = new Map < string , TokenPlanWindow > ( ) ;
33+ for ( const [ field , value ] of Object . entries ( response ) ) {
34+ const match = WINDOW_FIELD_PATTERN . exec ( field ) ;
35+ if ( ! match ) continue ;
36+ const numberValue = readNumber ( value ) ;
37+ if ( numberValue === undefined ) continue ;
38+
39+ const [ , count , unit , kind ] = match ;
40+ const key = `per${ count } ${ unit } ` ;
41+ const window = windows . get ( key ) ?? { key, count : Number ( count ) , unit } ;
42+ if ( kind === "Percentage" ) window . percentage = numberValue ;
43+ else window . resetTime = numberValue ;
44+ windows . set ( key , window ) ;
45+ }
46+ return [ ...windows . values ( ) ] . sort (
47+ ( left , right ) => left . count * UNIT_HOURS [ left . unit ] - right . count * UNIT_HOURS [ right . unit ] ,
48+ ) ;
49+ }
2050
51+ function toJson ( windows : TokenPlanWindow [ ] ) : TokenPlanUsage {
52+ const usage : TokenPlanUsage = { } ;
53+ for ( const window of windows ) {
54+ if ( window . percentage !== undefined ) usage [ `${ window . key } Percentage` ] = window . percentage ;
55+ if ( window . resetTime !== undefined ) usage [ `${ window . key } ResetTime` ] = window . resetTime ;
56+ }
57+ usage . hasQuotaData = windows . some ( ( window ) => window . percentage !== undefined ) ;
2158 return usage ;
2259}
2360
24- function printView ( usage : TokenPlanUsage , generatedAt : number ) : void {
25- printQuotaBox (
26- "Token Plan Usage" ,
27- [
28- {
29- label : "Monthly quota" ,
30- emptyMessage :
31- "The monthly limit may be unlimited; verify in the Bailian Token Plan console." ,
32- percentage : usage . per1MonthPercentage ,
33- resetTime : usage . per1MonthResetTime ,
34- } ,
35- ] ,
36- generatedAt ,
37- ) ;
61+ function windowName ( window : Pick < TokenPlanWindow , "count" | "unit" > ) : string {
62+ if ( window . count === 1 && window . unit === "Month" ) return "monthly" ;
63+ return `${ window . count } -${ window . unit . toLowerCase ( ) } ` ;
64+ }
65+
66+ function toSection ( window : TokenPlanWindow ) : QuotaSection {
67+ const name = windowName ( window ) ;
68+ return {
69+ label : `${ name . charAt ( 0 ) . toUpperCase ( ) } ${ name . slice ( 1 ) } quota` ,
70+ emptyMessage : `The ${ name } limit may be unlimited; verify in the Bailian Token Plan console.` ,
71+ percentage : window . percentage ,
72+ resetTime : window . resetTime ,
73+ } ;
74+ }
75+
76+ function printView ( windows : TokenPlanWindow [ ] , generatedAt : number ) : void {
77+ const visibleWindows =
78+ windows . length > 0 ? windows : [ { key : DEFAULT_WINDOW_KEY , count : 1 , unit : "Month" } ] ;
79+ printQuotaBox ( "Token Plan Usage" , visibleWindows . map ( toSection ) , generatedAt ) ;
3880}
3981
4082export default defineCommand ( {
@@ -55,13 +97,13 @@ export default defineCommand({
5597 }
5698
5799 const result = await ctx . client . console ( TOKEN_PLAN_USAGE_API , { } ) ;
58- const usage = readUsage ( result ) ;
100+ const windows = readWindows ( unwrapResponse ( result as Record < string , unknown > ) ) ;
59101
60102 if ( format === "json" ) {
61- emitResult ( usage , format ) ;
103+ emitResult ( toJson ( windows ) , format ) ;
62104 return ;
63105 }
64106
65- printView ( usage , Date . now ( ) ) ;
107+ printView ( windows , Date . now ( ) ) ;
66108 } ,
67109} ) ;
0 commit comments