@slkiser/opencode-quota 5.0.2 → 6.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -46
- package/dist/agent-tools.d.ts +24 -0
- package/dist/agent-tools.d.ts.map +1 -0
- package/dist/agent-tools.js +578 -0
- package/dist/agent-tools.js.map +1 -0
- package/dist/bin/opencode-quota.d.ts.map +1 -1
- package/dist/bin/opencode-quota.js +1 -32
- package/dist/bin/opencode-quota.js.map +1 -1
- package/dist/core/errors.d.ts +12 -0
- package/dist/core/errors.d.ts.map +1 -0
- package/dist/core/errors.js +141 -0
- package/dist/core/errors.js.map +1 -0
- package/dist/core/pace.d.ts +13 -0
- package/dist/core/pace.d.ts.map +1 -0
- package/dist/core/pace.js +52 -0
- package/dist/core/pace.js.map +1 -0
- package/dist/core/persist.d.ts +44 -0
- package/dist/core/persist.d.ts.map +1 -0
- package/dist/core/persist.js +210 -0
- package/dist/core/persist.js.map +1 -0
- package/dist/core/provider-source.d.ts +40 -0
- package/dist/core/provider-source.d.ts.map +1 -0
- package/dist/core/provider-source.js +210 -0
- package/dist/core/provider-source.js.map +1 -0
- package/dist/core/redact.d.ts +17 -0
- package/dist/core/redact.d.ts.map +1 -0
- package/dist/core/redact.js +217 -0
- package/dist/core/redact.js.map +1 -0
- package/dist/core/rows.d.ts +20 -0
- package/dist/core/rows.d.ts.map +1 -0
- package/dist/core/rows.js +142 -0
- package/dist/core/rows.js.map +1 -0
- package/dist/core/selection.d.ts +15 -0
- package/dist/core/selection.d.ts.map +1 -0
- package/dist/core/selection.js +43 -0
- package/dist/core/selection.js.map +1 -0
- package/dist/core/snapshot-publisher.d.ts +15 -0
- package/dist/core/snapshot-publisher.d.ts.map +1 -0
- package/dist/core/snapshot-publisher.js +57 -0
- package/dist/core/snapshot-publisher.js.map +1 -0
- package/dist/core/store.d.ts +93 -0
- package/dist/core/store.d.ts.map +1 -0
- package/dist/core/store.js +563 -0
- package/dist/core/store.js.map +1 -0
- package/dist/core/types.d.ts +186 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +2 -0
- package/dist/core/types.js.map +1 -0
- package/dist/core/usage-ranges.d.ts +46 -0
- package/dist/core/usage-ranges.d.ts.map +1 -0
- package/dist/core/usage-ranges.js +72 -0
- package/dist/core/usage-ranges.js.map +1 -0
- package/dist/core/usage.d.ts +100 -0
- package/dist/core/usage.d.ts.map +1 -0
- package/dist/core/usage.js +373 -0
- package/dist/core/usage.js.map +1 -0
- package/dist/core/validate.d.ts +8 -0
- package/dist/core/validate.d.ts.map +1 -0
- package/dist/core/validate.js +167 -0
- package/dist/core/validate.js.map +1 -0
- package/dist/core/windows.d.ts +24 -0
- package/dist/core/windows.d.ts.map +1 -0
- package/dist/core/windows.js +43 -0
- package/dist/core/windows.js.map +1 -0
- package/dist/data/modelsdev-pricing.min.json +248 -22
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/accounting-format.d.ts +2 -52
- package/dist/lib/accounting-format.d.ts.map +1 -1
- package/dist/lib/accounting-format.js +6 -255
- package/dist/lib/accounting-format.js.map +1 -1
- package/dist/lib/alibaba-auth.d.ts +14 -21
- package/dist/lib/alibaba-auth.d.ts.map +1 -1
- package/dist/lib/alibaba-auth.js +36 -149
- package/dist/lib/alibaba-auth.js.map +1 -1
- package/dist/lib/alibaba-coding-plan-local-quota.d.ts +1 -6
- package/dist/lib/alibaba-coding-plan-local-quota.d.ts.map +1 -1
- package/dist/lib/alibaba-coding-plan-local-quota.js +1 -1
- package/dist/lib/alibaba-coding-plan-local-quota.js.map +1 -1
- package/dist/lib/alibaba-token-plan.d.ts +18 -27
- package/dist/lib/alibaba-token-plan.d.ts.map +1 -1
- package/dist/lib/alibaba-token-plan.js +121 -285
- package/dist/lib/alibaba-token-plan.js.map +1 -1
- package/dist/lib/anthropic-auth.d.ts +3 -5
- package/dist/lib/anthropic-auth.d.ts.map +1 -1
- package/dist/lib/anthropic-auth.js +6 -9
- package/dist/lib/anthropic-auth.js.map +1 -1
- package/dist/lib/anthropic.d.ts +12 -37
- package/dist/lib/anthropic.d.ts.map +1 -1
- package/dist/lib/anthropic.js +348 -698
- package/dist/lib/anthropic.js.map +1 -1
- package/dist/lib/api-key-resolver.d.ts +165 -192
- package/dist/lib/api-key-resolver.d.ts.map +1 -1
- package/dist/lib/api-key-resolver.js +208 -234
- package/dist/lib/api-key-resolver.js.map +1 -1
- package/dist/lib/atomic-json.d.ts +4 -1
- package/dist/lib/atomic-json.d.ts.map +1 -1
- package/dist/lib/atomic-json.js +2 -3
- package/dist/lib/atomic-json.js.map +1 -1
- package/dist/lib/calendar-date.d.ts +15 -0
- package/dist/lib/calendar-date.d.ts.map +1 -0
- package/dist/lib/calendar-date.js +21 -0
- package/dist/lib/calendar-date.js.map +1 -0
- package/dist/lib/chutes.d.ts +0 -1
- package/dist/lib/chutes.d.ts.map +1 -1
- package/dist/lib/chutes.js +2 -4
- package/dist/lib/chutes.js.map +1 -1
- package/dist/lib/cli-reports.d.ts +15 -11
- package/dist/lib/cli-reports.d.ts.map +1 -1
- package/dist/lib/cli-reports.js +34 -107
- package/dist/lib/cli-reports.js.map +1 -1
- package/dist/lib/cli-show.d.ts +9 -22
- package/dist/lib/cli-show.d.ts.map +1 -1
- package/dist/lib/cli-show.js +104 -75
- package/dist/lib/cli-show.js.map +1 -1
- package/dist/lib/config-file-utils.d.ts +3 -15
- package/dist/lib/config-file-utils.d.ts.map +1 -1
- package/dist/lib/config-file-utils.js +4 -63
- package/dist/lib/config-file-utils.js.map +1 -1
- package/dist/lib/config-write-target.d.ts +4 -7
- package/dist/lib/config-write-target.d.ts.map +1 -1
- package/dist/lib/config-write-target.js +4 -18
- package/dist/lib/config-write-target.js.map +1 -1
- package/dist/lib/copilot.d.ts +29 -20
- package/dist/lib/copilot.d.ts.map +1 -1
- package/dist/lib/copilot.js +392 -649
- package/dist/lib/copilot.js.map +1 -1
- package/dist/lib/cursor-detection.d.ts +3 -3
- package/dist/lib/cursor-detection.d.ts.map +1 -1
- package/dist/lib/cursor-detection.js +44 -88
- package/dist/lib/cursor-detection.js.map +1 -1
- package/dist/lib/cursor-pricing.d.ts +71 -8
- package/dist/lib/cursor-pricing.d.ts.map +1 -1
- package/dist/lib/cursor-pricing.js +34 -125
- package/dist/lib/cursor-pricing.js.map +1 -1
- package/dist/lib/cursor-usage.d.ts +6 -4
- package/dist/lib/cursor-usage.d.ts.map +1 -1
- package/dist/lib/cursor-usage.js +27 -40
- package/dist/lib/cursor-usage.js.map +1 -1
- package/dist/lib/deepseek.d.ts +9 -17
- package/dist/lib/deepseek.d.ts.map +1 -1
- package/dist/lib/deepseek.js +24 -54
- package/dist/lib/deepseek.js.map +1 -1
- package/dist/lib/display-sanitize.d.ts +0 -8
- package/dist/lib/display-sanitize.d.ts.map +1 -1
- package/dist/lib/display-sanitize.js +0 -139
- package/dist/lib/display-sanitize.js.map +1 -1
- package/dist/lib/entries.d.ts +13 -239
- package/dist/lib/entries.d.ts.map +1 -1
- package/dist/lib/entries.js +1 -46
- package/dist/lib/entries.js.map +1 -1
- package/dist/lib/file-lock.d.ts +23 -0
- package/dist/lib/file-lock.d.ts.map +1 -0
- package/dist/lib/file-lock.js +122 -0
- package/dist/lib/file-lock.js.map +1 -0
- package/dist/lib/format-utils.d.ts +2 -82
- package/dist/lib/format-utils.d.ts.map +1 -1
- package/dist/lib/format-utils.js +2 -191
- package/dist/lib/format-utils.js.map +1 -1
- package/dist/lib/glm-coding-plan.d.ts +6 -5
- package/dist/lib/glm-coding-plan.d.ts.map +1 -1
- package/dist/lib/glm-coding-plan.js +19 -1
- package/dist/lib/glm-coding-plan.js.map +1 -1
- package/dist/lib/google-agy.d.ts +8 -40
- package/dist/lib/google-agy.d.ts.map +1 -1
- package/dist/lib/google-agy.js +88 -519
- package/dist/lib/google-agy.js.map +1 -1
- package/dist/lib/google-companion-credentials.d.ts +9 -40
- package/dist/lib/google-companion-credentials.d.ts.map +1 -1
- package/dist/lib/google-companion-credentials.js +167 -224
- package/dist/lib/google-companion-credentials.js.map +1 -1
- package/dist/lib/google-gemini-cli.d.ts +5 -38
- package/dist/lib/google-gemini-cli.d.ts.map +1 -1
- package/dist/lib/google-gemini-cli.js +71 -442
- package/dist/lib/google-gemini-cli.js.map +1 -1
- package/dist/lib/google-oauth-quota.d.ts +76 -0
- package/dist/lib/google-oauth-quota.d.ts.map +1 -0
- package/dist/lib/google-oauth-quota.js +297 -0
- package/dist/lib/google-oauth-quota.js.map +1 -0
- package/dist/lib/google-token-cache.d.ts +3 -5
- package/dist/lib/google-token-cache.d.ts.map +1 -1
- package/dist/lib/google-token-cache.js +2 -14
- package/dist/lib/google-token-cache.js.map +1 -1
- package/dist/lib/home-dir-display.d.ts.map +1 -1
- package/dist/lib/home-dir-display.js +21 -39
- package/dist/lib/home-dir-display.js.map +1 -1
- package/dist/lib/http.d.ts +4 -1
- package/dist/lib/http.d.ts.map +1 -1
- package/dist/lib/http.js +2 -1
- package/dist/lib/http.js.map +1 -1
- package/dist/lib/json-guards.d.ts +11 -0
- package/dist/lib/json-guards.d.ts.map +1 -0
- package/dist/lib/json-guards.js +25 -0
- package/dist/lib/json-guards.js.map +1 -0
- package/dist/lib/kilo.d.ts +0 -2
- package/dist/lib/kilo.d.ts.map +1 -1
- package/dist/lib/kilo.js +33 -116
- package/dist/lib/kilo.js.map +1 -1
- package/dist/lib/kimi-endpoints.d.ts +2 -2
- package/dist/lib/kimi-endpoints.d.ts.map +1 -1
- package/dist/lib/kimi-endpoints.js +1 -1
- package/dist/lib/kimi-endpoints.js.map +1 -1
- package/dist/lib/kimi.d.ts.map +1 -1
- package/dist/lib/kimi.js +40 -86
- package/dist/lib/kimi.js.map +1 -1
- package/dist/lib/maintainer-announcements.d.ts +4 -17
- package/dist/lib/maintainer-announcements.d.ts.map +1 -1
- package/dist/lib/maintainer-announcements.js +7 -44
- package/dist/lib/maintainer-announcements.js.map +1 -1
- package/dist/lib/maintainer-home-notice.d.ts +19 -0
- package/dist/lib/maintainer-home-notice.d.ts.map +1 -0
- package/dist/lib/maintainer-home-notice.js +92 -0
- package/dist/lib/maintainer-home-notice.js.map +1 -0
- package/dist/lib/mimo-config.d.ts +6 -9
- package/dist/lib/mimo-config.d.ts.map +1 -1
- package/dist/lib/mimo-config.js +43 -74
- package/dist/lib/mimo-config.js.map +1 -1
- package/dist/lib/mimo.d.ts +2 -5
- package/dist/lib/mimo.d.ts.map +1 -1
- package/dist/lib/mimo.js +57 -109
- package/dist/lib/mimo.js.map +1 -1
- package/dist/lib/minimax-endpoints.d.ts +2 -2
- package/dist/lib/minimax-endpoints.d.ts.map +1 -1
- package/dist/lib/minimax-endpoints.js +1 -1
- package/dist/lib/minimax-endpoints.js.map +1 -1
- package/dist/lib/modelsdev-pricing.d.ts +22 -16
- package/dist/lib/modelsdev-pricing.d.ts.map +1 -1
- package/dist/lib/modelsdev-pricing.js +279 -510
- package/dist/lib/modelsdev-pricing.js.map +1 -1
- package/dist/lib/nanogpt.d.ts +6 -6
- package/dist/lib/nanogpt.d.ts.map +1 -1
- package/dist/lib/nanogpt.js +19 -72
- package/dist/lib/nanogpt.js.map +1 -1
- package/dist/lib/ollama-cloud.d.ts +3 -5
- package/dist/lib/ollama-cloud.d.ts.map +1 -1
- package/dist/lib/ollama-cloud.js +77 -46
- package/dist/lib/ollama-cloud.js.map +1 -1
- package/dist/lib/openai.d.ts +19 -29
- package/dist/lib/openai.d.ts.map +1 -1
- package/dist/lib/openai.js +134 -182
- package/dist/lib/openai.js.map +1 -1
- package/dist/lib/opencode-auth-sqlite.d.ts.map +1 -1
- package/dist/lib/opencode-auth-sqlite.js +22 -23
- package/dist/lib/opencode-auth-sqlite.js.map +1 -1
- package/dist/lib/opencode-auth.d.ts +17 -11
- package/dist/lib/opencode-auth.d.ts.map +1 -1
- package/dist/lib/opencode-auth.js +39 -46
- package/dist/lib/opencode-auth.js.map +1 -1
- package/dist/lib/opencode-config-editor.d.ts +3 -34
- package/dist/lib/opencode-config-editor.d.ts.map +1 -1
- package/dist/lib/opencode-config-editor.js +23 -142
- package/dist/lib/opencode-config-editor.js.map +1 -1
- package/dist/lib/opencode-config-providers.d.ts +4 -3
- package/dist/lib/opencode-config-providers.d.ts.map +1 -1
- package/dist/lib/opencode-config-providers.js +7 -11
- package/dist/lib/opencode-config-providers.js.map +1 -1
- package/dist/lib/opencode-config-read.d.ts +2 -1
- package/dist/lib/opencode-config-read.d.ts.map +1 -1
- package/dist/lib/opencode-config-read.js +3 -2
- package/dist/lib/opencode-config-read.js.map +1 -1
- package/dist/lib/opencode-console-auth.d.ts +2 -3
- package/dist/lib/opencode-console-auth.d.ts.map +1 -1
- package/dist/lib/opencode-console-auth.js +2 -2
- package/dist/lib/opencode-console-auth.js.map +1 -1
- package/dist/lib/opencode-go.d.ts.map +1 -1
- package/dist/lib/opencode-go.js +51 -79
- package/dist/lib/opencode-go.js.map +1 -1
- package/dist/lib/opencode-storage.d.ts +18 -3
- package/dist/lib/opencode-storage.d.ts.map +1 -1
- package/dist/lib/opencode-storage.js +134 -2
- package/dist/lib/opencode-storage.js.map +1 -1
- package/dist/lib/opencode-zen.d.ts +23 -7
- package/dist/lib/opencode-zen.d.ts.map +1 -1
- package/dist/lib/opencode-zen.js +175 -62
- package/dist/lib/opencode-zen.js.map +1 -1
- package/dist/lib/openrouter.d.ts.map +1 -1
- package/dist/lib/openrouter.js +1 -13
- package/dist/lib/openrouter.js.map +1 -1
- package/dist/lib/pricing-key.d.ts +48 -0
- package/dist/lib/pricing-key.d.ts.map +1 -0
- package/dist/lib/pricing-key.js +371 -0
- package/dist/lib/pricing-key.js.map +1 -0
- package/dist/lib/provider-metadata.d.ts +16 -20
- package/dist/lib/provider-metadata.d.ts.map +1 -1
- package/dist/lib/provider-metadata.js +69 -69
- package/dist/lib/provider-metadata.js.map +1 -1
- package/dist/lib/provider-model-matching.d.ts +2 -1
- package/dist/lib/provider-model-matching.d.ts.map +1 -1
- package/dist/lib/provider-registration.d.ts +1 -177
- package/dist/lib/provider-registration.d.ts.map +1 -1
- package/dist/lib/provider-registration.js +1 -163
- package/dist/lib/provider-registration.js.map +1 -1
- package/dist/lib/quota-dialog-commands.d.ts +87 -53
- package/dist/lib/quota-dialog-commands.d.ts.map +1 -1
- package/dist/lib/quota-dialog-commands.js +259 -519
- package/dist/lib/quota-dialog-commands.js.map +1 -1
- package/dist/lib/quota-export-types.d.ts +29 -23
- package/dist/lib/quota-export-types.d.ts.map +1 -1
- package/dist/lib/quota-export.d.ts +16 -26
- package/dist/lib/quota-export.d.ts.map +1 -1
- package/dist/lib/quota-export.js +105 -147
- package/dist/lib/quota-export.js.map +1 -1
- package/dist/lib/quota-providers-local.d.ts +12 -8
- package/dist/lib/quota-providers-local.d.ts.map +1 -1
- package/dist/lib/quota-providers-local.js +150 -223
- package/dist/lib/quota-providers-local.js.map +1 -1
- package/dist/lib/quota-providers-remote.d.ts +3 -8
- package/dist/lib/quota-providers-remote.d.ts.map +1 -1
- package/dist/lib/quota-providers-remote.js +309 -449
- package/dist/lib/quota-providers-remote.js.map +1 -1
- package/dist/lib/quota-providers.d.ts +57 -15
- package/dist/lib/quota-providers.d.ts.map +1 -1
- package/dist/lib/quota-providers.js +157 -64
- package/dist/lib/quota-providers.js.map +1 -1
- package/dist/lib/quota-report-message.d.ts +23 -10
- package/dist/lib/quota-report-message.d.ts.map +1 -1
- package/dist/lib/quota-report-message.js +37 -13
- package/dist/lib/quota-report-message.js.map +1 -1
- package/dist/lib/quota-retry-wait.d.ts +14 -6
- package/dist/lib/quota-retry-wait.d.ts.map +1 -1
- package/dist/lib/quota-retry-wait.js +37 -39
- package/dist/lib/quota-retry-wait.js.map +1 -1
- package/dist/lib/quota-status.d.ts +50 -33
- package/dist/lib/quota-status.d.ts.map +1 -1
- package/dist/lib/quota-status.js +1113 -772
- package/dist/lib/quota-status.js.map +1 -1
- package/dist/lib/quota-telemetry.d.ts +7 -14
- package/dist/lib/quota-telemetry.d.ts.map +1 -1
- package/dist/lib/quota-telemetry.js +46 -97
- package/dist/lib/quota-telemetry.js.map +1 -1
- package/dist/lib/report-document.d.ts +5 -65
- package/dist/lib/report-document.d.ts.map +1 -1
- package/dist/lib/report-document.js +14 -167
- package/dist/lib/report-document.js.map +1 -1
- package/dist/lib/resolved-auth-identity.d.ts +1 -2
- package/dist/lib/resolved-auth-identity.d.ts.map +1 -1
- package/dist/lib/resolved-auth-identity.js +0 -4
- package/dist/lib/resolved-auth-identity.js.map +1 -1
- package/dist/lib/session-tokens.d.ts +19 -19
- package/dist/lib/session-tokens.d.ts.map +1 -1
- package/dist/lib/session-tokens.js +58 -44
- package/dist/lib/session-tokens.js.map +1 -1
- package/dist/lib/synthetic.d.ts +0 -1
- package/dist/lib/synthetic.d.ts.map +1 -1
- package/dist/lib/synthetic.js +43 -94
- package/dist/lib/synthetic.js.map +1 -1
- package/dist/lib/token-buckets.d.ts +0 -1
- package/dist/lib/token-buckets.d.ts.map +1 -1
- package/dist/lib/token-buckets.js +0 -3
- package/dist/lib/token-buckets.js.map +1 -1
- package/dist/lib/token-cost.d.ts +2 -1
- package/dist/lib/token-cost.d.ts.map +1 -1
- package/dist/lib/types.d.ts +46 -340
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/lib/types.js +2 -69
- package/dist/lib/types.js.map +1 -1
- package/dist/lib/xai.d.ts +6 -6
- package/dist/lib/xai.d.ts.map +1 -1
- package/dist/lib/xai.js +51 -90
- package/dist/lib/xai.js.map +1 -1
- package/dist/plugin.d.ts +6 -3
- package/dist/plugin.d.ts.map +1 -1
- package/dist/plugin.js +315 -90
- package/dist/plugin.js.map +1 -1
- package/dist/providers/alibaba-coding-plan.d.ts +4 -0
- package/dist/providers/alibaba-coding-plan.d.ts.map +1 -1
- package/dist/providers/alibaba-coding-plan.js +47 -77
- package/dist/providers/alibaba-coding-plan.js.map +1 -1
- package/dist/providers/alibaba-token-plan.d.ts.map +1 -1
- package/dist/providers/alibaba-token-plan.js +34 -46
- package/dist/providers/alibaba-token-plan.js.map +1 -1
- package/dist/providers/anthropic.d.ts +2 -4
- package/dist/providers/anthropic.d.ts.map +1 -1
- package/dist/providers/anthropic.js +80 -162
- package/dist/providers/anthropic.js.map +1 -1
- package/dist/providers/cache-policies.d.ts +0 -2
- package/dist/providers/cache-policies.d.ts.map +1 -1
- package/dist/providers/cache-policies.js +45 -91
- package/dist/providers/cache-policies.js.map +1 -1
- package/dist/providers/chutes.d.ts +0 -3
- package/dist/providers/chutes.d.ts.map +1 -1
- package/dist/providers/chutes.js +16 -28
- package/dist/providers/chutes.js.map +1 -1
- package/dist/providers/contract.d.ts +128 -0
- package/dist/providers/contract.d.ts.map +1 -0
- package/dist/providers/contract.js +101 -0
- package/dist/providers/contract.js.map +1 -0
- package/dist/providers/copilot.d.ts.map +1 -1
- package/dist/providers/copilot.js +171 -175
- package/dist/providers/copilot.js.map +1 -1
- package/dist/providers/cursor.d.ts.map +1 -1
- package/dist/providers/cursor.js +56 -109
- package/dist/providers/cursor.js.map +1 -1
- package/dist/providers/deepseek.d.ts +1 -6
- package/dist/providers/deepseek.d.ts.map +1 -1
- package/dist/providers/deepseek.js +46 -81
- package/dist/providers/deepseek.js.map +1 -1
- package/dist/providers/glm-coding-plan.d.ts +8 -0
- package/dist/providers/glm-coding-plan.d.ts.map +1 -0
- package/dist/providers/glm-coding-plan.js +106 -0
- package/dist/providers/glm-coding-plan.js.map +1 -0
- package/dist/providers/google-agy.d.ts.map +1 -1
- package/dist/providers/google-agy.js +41 -179
- package/dist/providers/google-agy.js.map +1 -1
- package/dist/providers/google-gemini-cli.d.ts.map +1 -1
- package/dist/providers/google-gemini-cli.js +51 -151
- package/dist/providers/google-gemini-cli.js.map +1 -1
- package/dist/providers/google-oauth-provider.d.ts +39 -0
- package/dist/providers/google-oauth-provider.d.ts.map +1 -0
- package/dist/providers/google-oauth-provider.js +161 -0
- package/dist/providers/google-oauth-provider.js.map +1 -0
- package/dist/providers/kilo.d.ts +0 -3
- package/dist/providers/kilo.d.ts.map +1 -1
- package/dist/providers/kilo.js +69 -123
- package/dist/providers/kilo.js.map +1 -1
- package/dist/providers/kimi-code.d.ts +5 -0
- package/dist/providers/kimi-code.d.ts.map +1 -1
- package/dist/providers/kimi-code.js +91 -136
- package/dist/providers/kimi-code.js.map +1 -1
- package/dist/providers/mimo.d.ts.map +1 -1
- package/dist/providers/mimo.js +55 -94
- package/dist/providers/mimo.js.map +1 -1
- package/dist/providers/minimax-coding-plan.d.ts +2 -23
- package/dist/providers/minimax-coding-plan.d.ts.map +1 -1
- package/dist/providers/minimax-coding-plan.js +161 -277
- package/dist/providers/minimax-coding-plan.js.map +1 -1
- package/dist/providers/nanogpt.d.ts +0 -3
- package/dist/providers/nanogpt.d.ts.map +1 -1
- package/dist/providers/nanogpt.js +61 -95
- package/dist/providers/nanogpt.js.map +1 -1
- package/dist/providers/ollama-cloud.d.ts +0 -5
- package/dist/providers/ollama-cloud.d.ts.map +1 -1
- package/dist/providers/ollama-cloud.js +86 -87
- package/dist/providers/ollama-cloud.js.map +1 -1
- package/dist/providers/openai.d.ts.map +1 -1
- package/dist/providers/openai.js +157 -150
- package/dist/providers/openai.js.map +1 -1
- package/dist/providers/opencode-go.d.ts.map +1 -1
- package/dist/providers/opencode-go.js +157 -199
- package/dist/providers/opencode-go.js.map +1 -1
- package/dist/providers/opencode-zen.d.ts.map +1 -1
- package/dist/providers/opencode-zen.js +104 -141
- package/dist/providers/opencode-zen.js.map +1 -1
- package/dist/providers/openrouter.d.ts +0 -3
- package/dist/providers/openrouter.d.ts.map +1 -1
- package/dist/providers/openrouter.js +26 -13
- package/dist/providers/openrouter.js.map +1 -1
- package/dist/providers/quota-providers.d.ts +7 -3
- package/dist/providers/quota-providers.d.ts.map +1 -1
- package/dist/providers/quota-providers.js +111 -217
- package/dist/providers/quota-providers.js.map +1 -1
- package/dist/providers/registry.d.ts.map +1 -1
- package/dist/providers/registry.js +1 -2
- package/dist/providers/registry.js.map +1 -1
- package/dist/providers/result-helpers.d.ts +10 -37
- package/dist/providers/result-helpers.d.ts.map +1 -1
- package/dist/providers/result-helpers.js +23 -56
- package/dist/providers/result-helpers.js.map +1 -1
- package/dist/providers/synthetic.d.ts +0 -3
- package/dist/providers/synthetic.d.ts.map +1 -1
- package/dist/providers/synthetic.js +16 -58
- package/dist/providers/synthetic.js.map +1 -1
- package/dist/providers/xai.d.ts +2 -1
- package/dist/providers/xai.d.ts.map +1 -1
- package/dist/providers/xai.js +49 -71
- package/dist/providers/xai.js.map +1 -1
- package/dist/rpc.d.ts +110 -38
- package/dist/rpc.d.ts.map +1 -1
- package/dist/rpc.js +125 -37
- package/dist/rpc.js.map +1 -1
- package/dist/settings/convert.d.ts +60 -0
- package/dist/settings/convert.d.ts.map +1 -0
- package/dist/settings/convert.js +473 -0
- package/dist/settings/convert.js.map +1 -0
- package/dist/settings/file.d.ts +89 -0
- package/dist/settings/file.d.ts.map +1 -0
- package/dist/settings/file.js +452 -0
- package/dist/settings/file.js.map +1 -0
- package/dist/settings/index.d.ts +10 -0
- package/dist/settings/index.d.ts.map +1 -0
- package/dist/settings/index.js +3 -0
- package/dist/settings/index.js.map +1 -0
- package/dist/settings/schema.d.ts +607 -0
- package/dist/settings/schema.d.ts.map +1 -0
- package/dist/settings/schema.js +479 -0
- package/dist/settings/schema.js.map +1 -0
- package/dist/tui/client.d.ts +86 -0
- package/dist/tui/client.d.ts.map +1 -0
- package/dist/tui/client.js +229 -0
- package/dist/tui/client.js.map +1 -0
- package/dist/tui/display.d.ts +127 -0
- package/dist/tui/display.d.ts.map +1 -0
- package/dist/tui/display.js +474 -0
- package/dist/tui/display.js.map +1 -0
- package/dist/tui/index.d.ts +21 -0
- package/dist/tui/index.d.ts.map +1 -0
- package/dist/tui/index.js +305 -0
- package/dist/tui/server-settings.d.ts +43 -0
- package/dist/tui/server-settings.d.ts.map +1 -0
- package/dist/tui/server-settings.js +233 -0
- package/dist/tui/server-settings.js.map +1 -0
- package/dist/tui/sidebar.d.ts +41 -0
- package/dist/tui/sidebar.d.ts.map +1 -0
- package/dist/tui/sidebar.js +148 -0
- package/dist/tui/status-line.d.ts +14 -0
- package/dist/tui/status-line.d.ts.map +1 -0
- package/dist/tui/status-line.js +225 -0
- package/dist/tui/theme.d.ts +41 -0
- package/dist/tui/theme.d.ts.map +1 -0
- package/dist/tui/theme.js +52 -0
- package/dist/tui/theme.js.map +1 -0
- package/dist/tui/toast-colors.d.ts +37 -0
- package/dist/tui/toast-colors.d.ts.map +1 -0
- package/dist/tui/toast-colors.js +77 -0
- package/dist/tui/toasts.d.ts +59 -0
- package/dist/tui/toasts.d.ts.map +1 -0
- package/dist/tui/toasts.js +148 -0
- package/dist/tui/toasts.js.map +1 -0
- package/dist/tui/window/drag.d.ts +26 -0
- package/dist/tui/window/drag.d.ts.map +1 -0
- package/dist/tui/window/drag.js +80 -0
- package/dist/tui/window/drag.js.map +1 -0
- package/dist/tui/window/limits.d.ts +15 -0
- package/dist/tui/window/limits.d.ts.map +1 -0
- package/dist/tui/window/limits.js +227 -0
- package/dist/tui/window/limits.js.map +1 -0
- package/dist/tui/window/rail.d.ts +21 -0
- package/dist/tui/window/rail.d.ts.map +1 -0
- package/dist/tui/window/rail.js +81 -0
- package/dist/tui/window/settings.d.ts +164 -0
- package/dist/tui/window/settings.d.ts.map +1 -0
- package/dist/tui/window/settings.js +1679 -0
- package/dist/tui/window/shell.d.ts +108 -0
- package/dist/tui/window/shell.d.ts.map +1 -0
- package/dist/tui/window/shell.js +1003 -0
- package/dist/tui/window/usage.d.ts +75 -0
- package/dist/tui/window/usage.d.ts.map +1 -0
- package/dist/tui/window/usage.js +928 -0
- package/dist/tui/window/views.d.ts +61 -0
- package/dist/tui/window/views.d.ts.map +1 -0
- package/dist/tui/window/views.js +242 -0
- package/dist/tui.d.ts +1 -5
- package/dist/tui.js +1 -894
- package/dist/view/columns.d.ts +15 -0
- package/dist/view/columns.d.ts.map +1 -0
- package/dist/view/columns.js +48 -0
- package/dist/view/columns.js.map +1 -0
- package/dist/view/format.d.ts +82 -0
- package/dist/view/format.d.ts.map +1 -0
- package/dist/view/format.js +201 -0
- package/dist/view/format.js.map +1 -0
- package/dist/view/quick-look.d.ts +28 -0
- package/dist/view/quick-look.d.ts.map +1 -0
- package/dist/view/quick-look.js +150 -0
- package/dist/view/quick-look.js.map +1 -0
- package/dist/view/select.d.ts +67 -0
- package/dist/view/select.d.ts.map +1 -0
- package/dist/view/select.js +87 -0
- package/dist/view/select.js.map +1 -0
- package/dist/view/text.d.ts +153 -0
- package/dist/view/text.d.ts.map +1 -0
- package/dist/view/text.js +642 -0
- package/dist/view/text.js.map +1 -0
- package/dist/view/usage.d.ts +86 -0
- package/dist/view/usage.d.ts.map +1 -0
- package/dist/view/usage.js +724 -0
- package/dist/view/usage.js.map +1 -0
- package/package.json +4 -6
- package/dist/lib/chutes-config.d.ts +0 -15
- package/dist/lib/chutes-config.d.ts.map +0 -1
- package/dist/lib/chutes-config.js +0 -28
- package/dist/lib/chutes-config.js.map +0 -1
- package/dist/lib/cli-status.d.ts +0 -8
- package/dist/lib/cli-status.d.ts.map +0 -1
- package/dist/lib/cli-status.js +0 -98
- package/dist/lib/cli-status.js.map +0 -1
- package/dist/lib/command-parsing.d.ts +0 -43
- package/dist/lib/command-parsing.d.ts.map +0 -1
- package/dist/lib/command-parsing.js +0 -103
- package/dist/lib/command-parsing.js.map +0 -1
- package/dist/lib/config.d.ts +0 -63
- package/dist/lib/config.d.ts.map +0 -1
- package/dist/lib/config.js +0 -1083
- package/dist/lib/config.js.map +0 -1
- package/dist/lib/deepseek-auth.d.ts +0 -15
- package/dist/lib/deepseek-auth.d.ts.map +0 -1
- package/dist/lib/deepseek-auth.js +0 -28
- package/dist/lib/deepseek-auth.js.map +0 -1
- package/dist/lib/format.d.ts +0 -25
- package/dist/lib/format.d.ts.map +0 -1
- package/dist/lib/format.js +0 -300
- package/dist/lib/format.js.map +0 -1
- package/dist/lib/google-agy-companion.d.ts +0 -8
- package/dist/lib/google-agy-companion.d.ts.map +0 -1
- package/dist/lib/google-agy-companion.js +0 -64
- package/dist/lib/google-agy-companion.js.map +0 -1
- package/dist/lib/google-gemini-cli-companion.d.ts +0 -8
- package/dist/lib/google-gemini-cli-companion.d.ts.map +0 -1
- package/dist/lib/google-gemini-cli-companion.js +0 -64
- package/dist/lib/google-gemini-cli-companion.js.map +0 -1
- package/dist/lib/grouped-entry-normalization.d.ts +0 -12
- package/dist/lib/grouped-entry-normalization.d.ts.map +0 -1
- package/dist/lib/grouped-entry-normalization.js +0 -124
- package/dist/lib/grouped-entry-normalization.js.map +0 -1
- package/dist/lib/grouped-header-format.d.ts +0 -5
- package/dist/lib/grouped-header-format.d.ts.map +0 -1
- package/dist/lib/grouped-header-format.js +0 -16
- package/dist/lib/grouped-header-format.js.map +0 -1
- package/dist/lib/init-installer.d.ts +0 -123
- package/dist/lib/init-installer.d.ts.map +0 -1
- package/dist/lib/init-installer.js +0 -1111
- package/dist/lib/init-installer.js.map +0 -1
- package/dist/lib/jsonc.d.ts +0 -17
- package/dist/lib/jsonc.d.ts.map +0 -1
- package/dist/lib/jsonc.js +0 -23
- package/dist/lib/jsonc.js.map +0 -1
- package/dist/lib/kilo-config.d.ts +0 -15
- package/dist/lib/kilo-config.d.ts.map +0 -1
- package/dist/lib/kilo-config.js +0 -27
- package/dist/lib/kilo-config.js.map +0 -1
- package/dist/lib/kimi-auth.d.ts +0 -49
- package/dist/lib/kimi-auth.d.ts.map +0 -1
- package/dist/lib/kimi-auth.js +0 -82
- package/dist/lib/kimi-auth.js.map +0 -1
- package/dist/lib/markdown-table.d.ts +0 -51
- package/dist/lib/markdown-table.d.ts.map +0 -1
- package/dist/lib/markdown-table.js +0 -179
- package/dist/lib/markdown-table.js.map +0 -1
- package/dist/lib/minimax-auth.d.ts +0 -62
- package/dist/lib/minimax-auth.d.ts.map +0 -1
- package/dist/lib/minimax-auth.js +0 -206
- package/dist/lib/minimax-auth.js.map +0 -1
- package/dist/lib/nanogpt-config.d.ts +0 -15
- package/dist/lib/nanogpt-config.d.ts.map +0 -1
- package/dist/lib/nanogpt-config.js +0 -31
- package/dist/lib/nanogpt-config.js.map +0 -1
- package/dist/lib/ollama-cloud-config.d.ts +0 -15
- package/dist/lib/ollama-cloud-config.d.ts.map +0 -1
- package/dist/lib/ollama-cloud-config.js +0 -27
- package/dist/lib/ollama-cloud-config.js.map +0 -1
- package/dist/lib/opencode-go-auth.d.ts +0 -16
- package/dist/lib/opencode-go-auth.d.ts.map +0 -1
- package/dist/lib/opencode-go-auth.js +0 -42
- package/dist/lib/opencode-go-auth.js.map +0 -1
- package/dist/lib/opencode-version.d.ts +0 -6
- package/dist/lib/opencode-version.d.ts.map +0 -1
- package/dist/lib/opencode-version.js +0 -26
- package/dist/lib/opencode-version.js.map +0 -1
- package/dist/lib/provider-add-command.d.ts +0 -35
- package/dist/lib/provider-add-command.d.ts.map +0 -1
- package/dist/lib/provider-add-command.js +0 -204
- package/dist/lib/provider-add-command.js.map +0 -1
- package/dist/lib/provider-add-json-v1-questionnaire.d.ts +0 -32
- package/dist/lib/provider-add-json-v1-questionnaire.d.ts.map +0 -1
- package/dist/lib/provider-add-json-v1-questionnaire.js +0 -336
- package/dist/lib/provider-add-json-v1-questionnaire.js.map +0 -1
- package/dist/lib/provider-add.d.ts +0 -21
- package/dist/lib/provider-add.d.ts.map +0 -1
- package/dist/lib/provider-add.js +0 -219
- package/dist/lib/provider-add.js.map +0 -1
- package/dist/lib/quota-accounting-projection.d.ts +0 -9
- package/dist/lib/quota-accounting-projection.d.ts.map +0 -1
- package/dist/lib/quota-accounting-projection.js +0 -254
- package/dist/lib/quota-accounting-projection.js.map +0 -1
- package/dist/lib/quota-columns.d.ts +0 -38
- package/dist/lib/quota-columns.d.ts.map +0 -1
- package/dist/lib/quota-columns.js +0 -129
- package/dist/lib/quota-columns.js.map +0 -1
- package/dist/lib/quota-command-format.d.ts +0 -34
- package/dist/lib/quota-command-format.d.ts.map +0 -1
- package/dist/lib/quota-command-format.js +0 -240
- package/dist/lib/quota-command-format.js.map +0 -1
- package/dist/lib/quota-dialog-command-specs.d.ts +0 -63
- package/dist/lib/quota-dialog-command-specs.d.ts.map +0 -1
- package/dist/lib/quota-dialog-command-specs.js +0 -145
- package/dist/lib/quota-dialog-command-specs.js.map +0 -1
- package/dist/lib/quota-entry-display.d.ts +0 -7
- package/dist/lib/quota-entry-display.d.ts.map +0 -1
- package/dist/lib/quota-entry-display.js +0 -59
- package/dist/lib/quota-entry-display.js.map +0 -1
- package/dist/lib/quota-exhaustion-projection.d.ts +0 -11
- package/dist/lib/quota-exhaustion-projection.d.ts.map +0 -1
- package/dist/lib/quota-exhaustion-projection.js +0 -105
- package/dist/lib/quota-exhaustion-projection.js.map +0 -1
- package/dist/lib/quota-format-style.d.ts +0 -21
- package/dist/lib/quota-format-style.d.ts.map +0 -1
- package/dist/lib/quota-format-style.js +0 -38
- package/dist/lib/quota-format-style.js.map +0 -1
- package/dist/lib/quota-render-data.d.ts +0 -102
- package/dist/lib/quota-render-data.d.ts.map +0 -1
- package/dist/lib/quota-render-data.js +0 -419
- package/dist/lib/quota-render-data.js.map +0 -1
- package/dist/lib/quota-reset-notifications.d.ts +0 -20
- package/dist/lib/quota-reset-notifications.d.ts.map +0 -1
- package/dist/lib/quota-reset-notifications.js +0 -331
- package/dist/lib/quota-reset-notifications.js.map +0 -1
- package/dist/lib/quota-runtime-context.d.ts +0 -54
- package/dist/lib/quota-runtime-context.d.ts.map +0 -1
- package/dist/lib/quota-runtime-context.js +0 -110
- package/dist/lib/quota-runtime-context.js.map +0 -1
- package/dist/lib/quota-state-codec.d.ts +0 -16
- package/dist/lib/quota-state-codec.d.ts.map +0 -1
- package/dist/lib/quota-state-codec.js +0 -412
- package/dist/lib/quota-state-codec.js.map +0 -1
- package/dist/lib/quota-state.d.ts +0 -28
- package/dist/lib/quota-state.d.ts.map +0 -1
- package/dist/lib/quota-state.js +0 -473
- package/dist/lib/quota-state.js.map +0 -1
- package/dist/lib/quota-stats-format.d.ts +0 -29
- package/dist/lib/quota-stats-format.d.ts.map +0 -1
- package/dist/lib/quota-stats-format.js +0 -459
- package/dist/lib/quota-stats-format.js.map +0 -1
- package/dist/lib/quota-stats.d.ts +0 -126
- package/dist/lib/quota-stats.d.ts.map +0 -1
- package/dist/lib/quota-stats.js +0 -695
- package/dist/lib/quota-stats.js.map +0 -1
- package/dist/lib/quota-surface-data.d.ts +0 -26
- package/dist/lib/quota-surface-data.d.ts.map +0 -1
- package/dist/lib/quota-surface-data.js +0 -299
- package/dist/lib/quota-surface-data.js.map +0 -1
- package/dist/lib/scoped-update-migration.d.ts +0 -103
- package/dist/lib/scoped-update-migration.d.ts.map +0 -1
- package/dist/lib/scoped-update-migration.js +0 -344
- package/dist/lib/scoped-update-migration.js.map +0 -1
- package/dist/lib/scoped-update.d.ts +0 -77
- package/dist/lib/scoped-update.d.ts.map +0 -1
- package/dist/lib/scoped-update.js +0 -590
- package/dist/lib/scoped-update.js.map +0 -1
- package/dist/lib/session-tokens-format.d.ts +0 -36
- package/dist/lib/session-tokens-format.d.ts.map +0 -1
- package/dist/lib/session-tokens-format.js +0 -111
- package/dist/lib/session-tokens-format.js.map +0 -1
- package/dist/lib/synthetic-config.d.ts +0 -15
- package/dist/lib/synthetic-config.d.ts.map +0 -1
- package/dist/lib/synthetic-config.js +0 -28
- package/dist/lib/synthetic-config.js.map +0 -1
- package/dist/lib/toast-format-grouped.d.ts +0 -25
- package/dist/lib/toast-format-grouped.d.ts.map +0 -1
- package/dist/lib/toast-format-grouped.js +0 -296
- package/dist/lib/toast-format-grouped.js.map +0 -1
- package/dist/lib/tui-compact-format.d.ts +0 -10
- package/dist/lib/tui-compact-format.d.ts.map +0 -1
- package/dist/lib/tui-compact-format.js +0 -460
- package/dist/lib/tui-compact-format.js.map +0 -1
- package/dist/lib/tui-panel-state.d.ts +0 -15
- package/dist/lib/tui-panel-state.d.ts.map +0 -1
- package/dist/lib/tui-panel-state.js +0 -2
- package/dist/lib/tui-panel-state.js.map +0 -1
- package/dist/lib/tui-prompt-bar-format.d.ts +0 -25
- package/dist/lib/tui-prompt-bar-format.d.ts.map +0 -1
- package/dist/lib/tui-prompt-bar-format.js +0 -206
- package/dist/lib/tui-prompt-bar-format.js.map +0 -1
- package/dist/lib/tui-sidebar-format.d.ts +0 -13
- package/dist/lib/tui-sidebar-format.d.ts.map +0 -1
- package/dist/lib/tui-sidebar-format.js +0 -34
- package/dist/lib/tui-sidebar-format.js.map +0 -1
- package/dist/lib/zai-auth.d.ts +0 -15
- package/dist/lib/zai-auth.d.ts.map +0 -1
- package/dist/lib/zai-auth.js +0 -37
- package/dist/lib/zai-auth.js.map +0 -1
- package/dist/lib/zai.d.ts +0 -5
- package/dist/lib/zai.d.ts.map +0 -1
- package/dist/lib/zai.js +0 -14
- package/dist/lib/zai.js.map +0 -1
- package/dist/lib/zhipu-auth.d.ts +0 -15
- package/dist/lib/zhipu-auth.d.ts.map +0 -1
- package/dist/lib/zhipu-auth.js +0 -42
- package/dist/lib/zhipu-auth.js.map +0 -1
- package/dist/lib/zhipu.d.ts +0 -5
- package/dist/lib/zhipu.d.ts.map +0 -1
- package/dist/lib/zhipu.js +0 -13
- package/dist/lib/zhipu.js.map +0 -1
- package/dist/providers/glm-coding-plan-provider.d.ts +0 -56
- package/dist/providers/glm-coding-plan-provider.d.ts.map +0 -1
- package/dist/providers/glm-coding-plan-provider.js +0 -115
- package/dist/providers/glm-coding-plan-provider.js.map +0 -1
- package/dist/providers/google-account-format.d.ts +0 -7
- package/dist/providers/google-account-format.d.ts.map +0 -1
- package/dist/providers/google-account-format.js +0 -52
- package/dist/providers/google-account-format.js.map +0 -1
- package/dist/providers/zai.d.ts +0 -2
- package/dist/providers/zai.d.ts.map +0 -1
- package/dist/providers/zai.js +0 -24
- package/dist/providers/zai.js.map +0 -1
- package/dist/providers/zhipu.d.ts +0 -2
- package/dist/providers/zhipu.d.ts.map +0 -1
- package/dist/providers/zhipu.js +0 -21
- package/dist/providers/zhipu.js.map +0 -1
- package/dist/tui-v2.d.ts +0 -5
- package/dist/tui-v2.d.ts.map +0 -1
- package/dist/tui-v2.jsx +0 -624
- package/dist/tui-v2.jsx.map +0 -1
- package/dist/tui.d.ts.map +0 -1
|
@@ -0,0 +1,724 @@
|
|
|
1
|
+
import { usageRangeLabel } from "../core/usage-ranges.js";
|
|
2
|
+
import { getQuotaProviderDisplayLabel } from "../lib/provider-metadata.js";
|
|
3
|
+
import { columnsOf, padEndColumns, padStartColumns, sliceColumns, truncateColumns, } from "./columns.js";
|
|
4
|
+
import { cacheHit, formatShare, formatTokens, formatUsd, LOW_CACHE_MARK, } from "./format.js";
|
|
5
|
+
/** The chart sits beside the total and the providers, like T3's page, when it gets this much. */
|
|
6
|
+
const BESIDE_CHART_MIN_WIDTH = 32;
|
|
7
|
+
/** The total and the provider list never get wider than this, so their numbers stay close. */
|
|
8
|
+
const LIST_MAX_WIDTH = 48;
|
|
9
|
+
const COLUMN_GAP = 3;
|
|
10
|
+
/** Between the total and providers on the left and the chart on the right. */
|
|
11
|
+
const SIDE_GAP = 4;
|
|
12
|
+
/** Narrower views are laid out at this width and then cut. */
|
|
13
|
+
const LAYOUT_MIN_WIDTH = 32;
|
|
14
|
+
/** Under the providers the chart has this many rows; beside them, at least this many. */
|
|
15
|
+
const CHART_ROWS = 6;
|
|
16
|
+
const DETAIL_CHART_ROWS = 5;
|
|
17
|
+
/** A day's column is at most this wide, so a short range does not draw a few huge blocks. */
|
|
18
|
+
const CHART_COLUMN_MAX = 16;
|
|
19
|
+
/** From this many cells, a chart column leaves its last cell empty, so the days stand apart. */
|
|
20
|
+
const CHART_GAP_FROM = 3;
|
|
21
|
+
/** One to eight eighths of a chart cell. */
|
|
22
|
+
const BLOCKS = "▁▂▃▄▅▆▇█";
|
|
23
|
+
/** The "by type" bar and its legend stay short, like T3's, on a wide view. */
|
|
24
|
+
const BY_TYPE_MAX_WIDTH = 64;
|
|
25
|
+
/** The provider color mark, before a provider and its models. */
|
|
26
|
+
const DOT = "● ";
|
|
27
|
+
const TEXT_MAX_WIDTH = 64;
|
|
28
|
+
const TEXT_BREAKDOWN_ROWS = 10;
|
|
29
|
+
/** The TUI's "by type" bar and its legend marks: a thin line, its parts told apart by color. */
|
|
30
|
+
const THIN_BAR = "━";
|
|
31
|
+
const THIN_MARK = "━━";
|
|
32
|
+
/** A breakdown row's bar needs this much room, else the row has none. */
|
|
33
|
+
const ROW_BAR_MIN_WIDTH = 8;
|
|
34
|
+
/** The breakdown's names get at most this wide when the rows have bars. */
|
|
35
|
+
const ROW_NAME_MAX_WIDTH = 40;
|
|
36
|
+
/** At the end of each model row in the TUI: enter or a click opens the model's details. */
|
|
37
|
+
const OPEN_MARK = " ▸";
|
|
38
|
+
const CACHE_HEADER = "Cache";
|
|
39
|
+
/** A model's Cache cell without input tokens or cache numbers. */
|
|
40
|
+
const NO_CACHE_HIT = "—";
|
|
41
|
+
/** The model breakdown drops its Cache column before its names get narrower than this. */
|
|
42
|
+
const CACHE_NAME_MIN_WIDTH = 8;
|
|
43
|
+
/** Shade marks, one per part, so the "by type" bar also reads without colors. */
|
|
44
|
+
const PART_MARKS = {
|
|
45
|
+
input: "▒",
|
|
46
|
+
cacheRead: "░",
|
|
47
|
+
cacheWrite: "▓",
|
|
48
|
+
output: "█",
|
|
49
|
+
};
|
|
50
|
+
const PART_LABELS = {
|
|
51
|
+
input: "Input",
|
|
52
|
+
cacheRead: "Cache read",
|
|
53
|
+
cacheWrite: "Cache write",
|
|
54
|
+
output: "Output",
|
|
55
|
+
};
|
|
56
|
+
const MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
|
57
|
+
const WEEKDAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
|
|
58
|
+
function span(text, role) {
|
|
59
|
+
return { text, role };
|
|
60
|
+
}
|
|
61
|
+
function lineWidth(line) {
|
|
62
|
+
return line.reduce((sum, part) => sum + columnsOf(part.text), 0);
|
|
63
|
+
}
|
|
64
|
+
/** `line` cut to `width` columns: the last guard that keeps every line inside the view. */
|
|
65
|
+
function clip(line, width) {
|
|
66
|
+
const clipped = [];
|
|
67
|
+
let room = width;
|
|
68
|
+
for (const part of line) {
|
|
69
|
+
const text = sliceColumns(part.text, Math.max(0, room));
|
|
70
|
+
if (text)
|
|
71
|
+
clipped.push({ ...part, text });
|
|
72
|
+
room -= columnsOf(text);
|
|
73
|
+
}
|
|
74
|
+
return clipped;
|
|
75
|
+
}
|
|
76
|
+
function plural(count, word) {
|
|
77
|
+
return `${count} ${count === 1 ? word : `${word}s`}`;
|
|
78
|
+
}
|
|
79
|
+
/** A row's cost, or "no price" when all its tokens come from models without a price. */
|
|
80
|
+
export function costText(row) {
|
|
81
|
+
return row.tokens > 0 && row.unpricedTokens >= row.tokens ? "no price" : formatUsd(row.costUsd);
|
|
82
|
+
}
|
|
83
|
+
/** "Sep 11", or "Sep 11, 2025" outside `thisYear`. */
|
|
84
|
+
function shortDate(date, thisYear) {
|
|
85
|
+
const text = `${MONTHS[date.getMonth()]} ${date.getDate()}`;
|
|
86
|
+
return date.getFullYear() === thisYear ? text : `${text}, ${date.getFullYear()}`;
|
|
87
|
+
}
|
|
88
|
+
/** The local midnight of a report day like "2026-09-11". */
|
|
89
|
+
function dayDate(day) {
|
|
90
|
+
const [year, month, date] = day.split("-").map(Number);
|
|
91
|
+
return new Date(year, month - 1, date);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* The days a report covers, like "Oct 7 – Oct 13", or one day like "Oct 13"; undefined for
|
|
95
|
+
* "all" and "session", which have no time window.
|
|
96
|
+
*/
|
|
97
|
+
export function rangeDates(report) {
|
|
98
|
+
if (report.sinceMs === undefined || report.untilMs === undefined)
|
|
99
|
+
return undefined;
|
|
100
|
+
const thisYear = new Date(report.generatedAtMs).getFullYear();
|
|
101
|
+
const first = shortDate(new Date(report.sinceMs), thisYear);
|
|
102
|
+
const last = shortDate(new Date(report.untilMs), thisYear);
|
|
103
|
+
return first === last ? first : `${first} – ${last}`;
|
|
104
|
+
}
|
|
105
|
+
function rangeTitle(report) {
|
|
106
|
+
if (report.range !== "between" || report.sinceMs === undefined || report.untilMs === undefined) {
|
|
107
|
+
return usageRangeLabel(report.range);
|
|
108
|
+
}
|
|
109
|
+
const thisYear = new Date(report.generatedAtMs).getFullYear();
|
|
110
|
+
return `${shortDate(new Date(report.sinceMs), thisYear)} to ${shortDate(new Date(report.untilMs), thisYear)}`;
|
|
111
|
+
}
|
|
112
|
+
/** The report's rows in the mode's order: the report's own (most cost first), or most tokens. */
|
|
113
|
+
function inModeOrder(rows, mode) {
|
|
114
|
+
return mode === "cost" ? rows : [...rows].sort((a, b) => b.tokens - a.tokens);
|
|
115
|
+
}
|
|
116
|
+
function providerRole(report, providerID) {
|
|
117
|
+
const index = report.providers.findIndex((row) => row.providerID === providerID);
|
|
118
|
+
return index < 0 ? "muted" : `provider:${index}`;
|
|
119
|
+
}
|
|
120
|
+
/** The total on one line, or two when it does not fit. */
|
|
121
|
+
function totalLines(view, width) {
|
|
122
|
+
const { totals } = view.report;
|
|
123
|
+
const sessions = plural(totals.sessions, "session");
|
|
124
|
+
const title = view.mode === "cost"
|
|
125
|
+
? span(formatUsd(totals.costUsd), "title")
|
|
126
|
+
: span(`${formatTokens(totals.tokens.processed)} tokens`, "title");
|
|
127
|
+
const detail = view.mode === "cost"
|
|
128
|
+
? `${sessions} · API estimate`
|
|
129
|
+
: `${sessions} · ${formatUsd(totals.costUsd)} API estimate`;
|
|
130
|
+
const fits = columnsOf(title.text) + 2 + columnsOf(detail) <= width;
|
|
131
|
+
return fits ? [[title, span(` ${detail}`, "muted")]] : [[title], [span(detail, "muted")]];
|
|
132
|
+
}
|
|
133
|
+
/** "Unpriced tokens excluded: 4.0% (exo-free)", when any tokens have no price. */
|
|
134
|
+
function unpricedLines(view, width) {
|
|
135
|
+
const { totals, models } = view.report;
|
|
136
|
+
if (totals.unpricedShare <= 0)
|
|
137
|
+
return [];
|
|
138
|
+
const label = "Unpriced tokens excluded: ";
|
|
139
|
+
const share = formatShare(totals.unpricedShare);
|
|
140
|
+
const names = models.filter((row) => !row.priced && row.tokens > 0).map((row) => row.modelID);
|
|
141
|
+
const shown = names.length > 3
|
|
142
|
+
? `${names.slice(0, 3).join(", ")} +${names.length - 3} more`
|
|
143
|
+
: names.join(", ");
|
|
144
|
+
const room = width - columnsOf(label) - columnsOf(share) - " ()".length;
|
|
145
|
+
const line = [span(label, "muted"), span(share, "warning")];
|
|
146
|
+
if (shown && room >= 2)
|
|
147
|
+
line.push(span(` (${truncateColumns(shown, room)})`, "muted"));
|
|
148
|
+
return [line];
|
|
149
|
+
}
|
|
150
|
+
const METRIC_HEADERS = { cost: "Cost", share: "Share", tokens: "Tokens" };
|
|
151
|
+
/** A table's number columns: the mode's amount, its share, then the other amount. */
|
|
152
|
+
function metricsOf(mode) {
|
|
153
|
+
return mode === "cost" ? ["cost", "share", "tokens"] : ["tokens", "share", "cost"];
|
|
154
|
+
}
|
|
155
|
+
function metricText(view, metric, row) {
|
|
156
|
+
if (metric === "cost")
|
|
157
|
+
return costText(row);
|
|
158
|
+
if (metric === "tokens")
|
|
159
|
+
return formatTokens(row.tokens);
|
|
160
|
+
return formatShare(view.mode === "cost" ? row.costShare : row.tokenShare);
|
|
161
|
+
}
|
|
162
|
+
function metricRole(view, metric, text) {
|
|
163
|
+
if (metric === "share" || text === "no price")
|
|
164
|
+
return "muted";
|
|
165
|
+
return metric === view.mode ? "value" : "text";
|
|
166
|
+
}
|
|
167
|
+
/** Each metric column's width: its header or its widest number. */
|
|
168
|
+
function metricWidths(view, metrics, rows) {
|
|
169
|
+
return metrics.map((metric) => Math.max(columnsOf(METRIC_HEADERS[metric]), ...rows.map((row) => columnsOf(metricText(view, metric, row)))));
|
|
170
|
+
}
|
|
171
|
+
/** The metric cells of one table line: right-aligned, two spaces before each. */
|
|
172
|
+
function metricSpans(view, metrics, widths, row) {
|
|
173
|
+
return metrics.map((metric, index) => {
|
|
174
|
+
if (!row)
|
|
175
|
+
return span(` ${padStartColumns(METRIC_HEADERS[metric], widths[index])}`, "muted");
|
|
176
|
+
const text = metricText(view, metric, row);
|
|
177
|
+
return span(` ${padStartColumns(text, widths[index])}`, metricRole(view, metric, text));
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* The providers as a table under a header: name, the mode's amount, its share and the other
|
|
182
|
+
* amount. A narrow list drops the other amount, then the share, before names get short.
|
|
183
|
+
*/
|
|
184
|
+
function providerLines(view, width) {
|
|
185
|
+
const rows = inModeOrder(view.report.providers, view.mode);
|
|
186
|
+
const names = rows.map((row) => getQuotaProviderDisplayLabel(row.providerID));
|
|
187
|
+
const nameWidest = Math.max(columnsOf("Provider") - columnsOf(DOT), ...names.map(columnsOf));
|
|
188
|
+
let metrics = metricsOf(view.mode);
|
|
189
|
+
let widths = metricWidths(view, metrics, rows);
|
|
190
|
+
const room = () => width - columnsOf(DOT) - widths.reduce((sum, item) => sum + item + 2, 0);
|
|
191
|
+
while (metrics.length > 1 && room() < Math.min(nameWidest, 8)) {
|
|
192
|
+
metrics = metrics.slice(0, -1);
|
|
193
|
+
widths = widths.slice(0, -1);
|
|
194
|
+
}
|
|
195
|
+
const nameWidth = Math.max(1, Math.min(nameWidest, room()));
|
|
196
|
+
return [
|
|
197
|
+
[
|
|
198
|
+
span(padEndColumns("Provider", columnsOf(DOT) + nameWidth), "muted"),
|
|
199
|
+
...metricSpans(view, metrics, widths, undefined),
|
|
200
|
+
],
|
|
201
|
+
...rows.map((row, index) => [
|
|
202
|
+
span(DOT, providerRole(view.report, row.providerID)),
|
|
203
|
+
span(padEndColumns(truncateColumns(names[index], nameWidth), nameWidth), "text"),
|
|
204
|
+
...metricSpans(view, metrics, widths, row),
|
|
205
|
+
]),
|
|
206
|
+
];
|
|
207
|
+
}
|
|
208
|
+
/** "Sep 11 … Sep 26 … Oct 10" under the chart: first, middle (when it fits) and last day. */
|
|
209
|
+
function dateAxis(columns, columnStart, chartWidth, thisYear) {
|
|
210
|
+
const first = shortDate(dayDate(columns[0].first), thisYear);
|
|
211
|
+
const last = shortDate(dayDate(columns[columns.length - 1].last), thisYear);
|
|
212
|
+
const lastAt = chartWidth - columnsOf(last);
|
|
213
|
+
if (lastAt < columnsOf(first) + 2)
|
|
214
|
+
return truncateColumns(first, chartWidth);
|
|
215
|
+
const middleIndex = Math.floor(columns.length / 2);
|
|
216
|
+
const middle = shortDate(dayDate(columns[middleIndex].first), thisYear);
|
|
217
|
+
const middleCenter = (columnStart(middleIndex) + columnStart(middleIndex + 1)) / 2;
|
|
218
|
+
const middleAt = Math.round(middleCenter - columnsOf(middle) / 2);
|
|
219
|
+
const middleFits = middleAt >= columnsOf(first) + 2 && middleAt + columnsOf(middle) + 2 <= lastAt;
|
|
220
|
+
const axis = columns.length >= 3 && middleFits ? padEndColumns(first, middleAt) + middle : first;
|
|
221
|
+
return padEndColumns(axis, lastAt) + last;
|
|
222
|
+
}
|
|
223
|
+
/**
|
|
224
|
+
* `days` in at most `plotWidth` columns: one day per column, or the same number of days per
|
|
225
|
+
* column (the last may hold fewer) when the days do not fit.
|
|
226
|
+
*/
|
|
227
|
+
function chartColumns(days, plotWidth) {
|
|
228
|
+
const daysPerColumn = Math.ceil(days.length / Math.max(1, plotWidth));
|
|
229
|
+
const columns = [];
|
|
230
|
+
for (let index = 0; index < days.length; index += daysPerColumn) {
|
|
231
|
+
const group = days.slice(index, index + daysPerColumn);
|
|
232
|
+
columns.push({
|
|
233
|
+
first: group[0].date,
|
|
234
|
+
last: group[group.length - 1].date,
|
|
235
|
+
amount: group.reduce((sum, day) => sum + day.amount, 0),
|
|
236
|
+
parts: group[0].parts.map((_, part) => group.reduce((sum, day) => sum + day.parts[part], 0)),
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
return { columns, daysPerColumn, max: Math.max(...columns.map((column) => column.amount)) };
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* The report's days with the mode's amount: all of them, or one model's (0 on its other days),
|
|
243
|
+
* each split by provider from the models' days.
|
|
244
|
+
*/
|
|
245
|
+
function chartDays(report, mode, model) {
|
|
246
|
+
const amountOf = (row) => mode === "cost" ? row.costUsd : row.tokens;
|
|
247
|
+
const parts = new Map(report.days.map((day) => [day.date, report.providers.map(() => 0)]));
|
|
248
|
+
for (const row of model ? [model] : report.models) {
|
|
249
|
+
const provider = report.providers.findIndex((item) => item.providerID === row.providerID);
|
|
250
|
+
if (provider < 0)
|
|
251
|
+
continue;
|
|
252
|
+
for (const day of row.days) {
|
|
253
|
+
const dayParts = parts.get(day.date);
|
|
254
|
+
if (dayParts)
|
|
255
|
+
dayParts[provider] += amountOf(day);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
const modelDays = new Map(model?.days.map((day) => [day.date, amountOf(day)]));
|
|
259
|
+
return report.days.map((day) => ({
|
|
260
|
+
date: day.date,
|
|
261
|
+
amount: model ? (modelDays.get(day.date) ?? 0) : amountOf(day),
|
|
262
|
+
parts: parts.get(day.date) ?? [],
|
|
263
|
+
}));
|
|
264
|
+
}
|
|
265
|
+
/** `column` as a stack `height` eighths tall, its parts in proportion to their amounts. */
|
|
266
|
+
function chartStack(column, height) {
|
|
267
|
+
const known = column.parts.reduce((sum, part) => sum + part, 0);
|
|
268
|
+
const amounts = [...column.parts, Math.max(0, column.amount - known)];
|
|
269
|
+
const total = known + amounts[amounts.length - 1];
|
|
270
|
+
let below = 0;
|
|
271
|
+
const segments = amounts.map((amount, index) => {
|
|
272
|
+
below += amount;
|
|
273
|
+
const role = index < column.parts.length ? `provider:${index}` : "bar";
|
|
274
|
+
const top = index === amounts.length - 1 ? height : Math.round((below / total) * height);
|
|
275
|
+
return { role, top: Math.min(height, top) };
|
|
276
|
+
});
|
|
277
|
+
return { height, segments };
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* The cell of `stack` that starts `bottom` eighths up. A cell has two colors: a block where one
|
|
281
|
+
* part ends inside a full cell is that part's color, and above it is the color of the part with
|
|
282
|
+
* the most of the rest of the cell. Where the column ends inside the cell, the part with the
|
|
283
|
+
* most of the cell colors its block.
|
|
284
|
+
*/
|
|
285
|
+
function chartCell(stack, bottom) {
|
|
286
|
+
const fill = Math.min(8, Math.max(0, stack.height - bottom));
|
|
287
|
+
if (fill === 0)
|
|
288
|
+
return span(" ", "muted");
|
|
289
|
+
const inCell = [];
|
|
290
|
+
stack.segments.forEach((segment, index) => {
|
|
291
|
+
const start = Math.max(index === 0 ? 0 : stack.segments[index - 1].top, bottom);
|
|
292
|
+
const size = Math.min(segment.top, bottom + fill) - start;
|
|
293
|
+
if (size > 0)
|
|
294
|
+
inCell.push({ role: segment.role, size });
|
|
295
|
+
});
|
|
296
|
+
const lower = inCell[0];
|
|
297
|
+
if (inCell.length === 1)
|
|
298
|
+
return span(BLOCKS[fill - 1], lower.role);
|
|
299
|
+
if (fill < 8) {
|
|
300
|
+
const most = inCell.reduce((largest, part) => (part.size > largest.size ? part : largest));
|
|
301
|
+
return span(BLOCKS[fill - 1], most.role);
|
|
302
|
+
}
|
|
303
|
+
const upper = inCell
|
|
304
|
+
.slice(1)
|
|
305
|
+
.reduce((largest, part) => (part.size > largest.size ? part : largest));
|
|
306
|
+
return { text: BLOCKS[lower.size - 1], role: lower.role, back: upper.role };
|
|
307
|
+
}
|
|
308
|
+
/** Adds `part` to `line`, joined with the last span when both color the same way. */
|
|
309
|
+
function pushSpan(line, part) {
|
|
310
|
+
const last = line[line.length - 1];
|
|
311
|
+
if (last && last.role === part.role && last.back === part.back)
|
|
312
|
+
last.text += part.text;
|
|
313
|
+
else
|
|
314
|
+
line.push(part);
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* The daily chart: a title, `rows` rows of block characters (one column per day, or per few
|
|
318
|
+
* days when the days do not fit) with the top value on the left, a baseline with 0, and a date
|
|
319
|
+
* axis. Each column stacks its providers, the first (most cost) at the bottom, in their colors.
|
|
320
|
+
* Columns of 3 cells or more keep their last cell empty, so the days stand apart.
|
|
321
|
+
* Nothing when the range has fewer than two days or nothing to draw.
|
|
322
|
+
*/
|
|
323
|
+
function chartLines(days, mode, thisYear, width, rows) {
|
|
324
|
+
const formatAmount = mode === "cost" ? formatUsd : formatTokens;
|
|
325
|
+
if (days.length < 2 || days.every((day) => day.amount <= 0))
|
|
326
|
+
return [];
|
|
327
|
+
// The label column decides the room for the columns, and the columns decide the top value:
|
|
328
|
+
// start at the width of "0" and widen the label column until the top value fits in it.
|
|
329
|
+
let labelWidth = 1;
|
|
330
|
+
let grouping = chartColumns(days, width - labelWidth - 1);
|
|
331
|
+
while (columnsOf(formatAmount(grouping.max)) > labelWidth) {
|
|
332
|
+
labelWidth = columnsOf(formatAmount(grouping.max));
|
|
333
|
+
grouping = chartColumns(days, width - labelWidth - 1);
|
|
334
|
+
}
|
|
335
|
+
const { columns, daysPerColumn, max } = grouping;
|
|
336
|
+
// `columns` were grouped for this plot width, so every column gets at least one cell.
|
|
337
|
+
const plotWidth = Math.max(1, width - labelWidth - 1);
|
|
338
|
+
const chartWidth = Math.min(plotWidth, columns.length * CHART_COLUMN_MAX);
|
|
339
|
+
// Column `index` covers [columnStart(index), columnStart(index + 1)): widths differ by one at most.
|
|
340
|
+
const columnStart = (index) => Math.floor((index * chartWidth) / columns.length);
|
|
341
|
+
const gap = Math.floor(chartWidth / columns.length) >= CHART_GAP_FROM;
|
|
342
|
+
// Each column's height in eighths of a row (a day above 0 always shows), split by provider.
|
|
343
|
+
const stacks = columns.map((column) => chartStack(column, column.amount <= 0 ? 0 : Math.max(1, Math.round((column.amount / max) * rows * 8))));
|
|
344
|
+
const [daily, amount] = mode === "cost" ? ["Daily cost", "Cost"] : ["Daily tokens", "Tokens"];
|
|
345
|
+
const title = daysPerColumn === 1 ? daily : `${amount} per ${daysPerColumn} days`;
|
|
346
|
+
const lines = [[span(title, "heading")]];
|
|
347
|
+
for (let row = 0; row < rows; row += 1) {
|
|
348
|
+
const below = (rows - 1 - row) * 8;
|
|
349
|
+
const label = row === 0 ? formatAmount(max) : "";
|
|
350
|
+
const cells = [];
|
|
351
|
+
stacks.forEach((stack, index) => {
|
|
352
|
+
const cell = chartCell(stack, below);
|
|
353
|
+
const width = columnStart(index + 1) - columnStart(index);
|
|
354
|
+
pushSpan(cells, { ...cell, text: cell.text.repeat(gap ? width - 1 : width) });
|
|
355
|
+
if (gap)
|
|
356
|
+
pushSpan(cells, span(" ", "muted"));
|
|
357
|
+
});
|
|
358
|
+
lines.push([span(`${padStartColumns(label, labelWidth)} `, "muted"), ...cells]);
|
|
359
|
+
}
|
|
360
|
+
lines.push([
|
|
361
|
+
span(`${padStartColumns("0", labelWidth)} `, "muted"),
|
|
362
|
+
span("─".repeat(chartWidth), "muted"),
|
|
363
|
+
]);
|
|
364
|
+
const axis = dateAxis(columns, columnStart, chartWidth, thisYear);
|
|
365
|
+
lines.push([span(`${" ".repeat(labelWidth + 1)}${axis}`, "muted")]);
|
|
366
|
+
return lines;
|
|
367
|
+
}
|
|
368
|
+
/** Label-over-value cells in as few rows as fit in `width`, the rows evenly filled. */
|
|
369
|
+
function grid(items, width) {
|
|
370
|
+
const cellMin = Math.max(...items.flatMap(([label, value]) => [columnsOf(label), columnsOf(value)])) + 2;
|
|
371
|
+
const rows = Math.ceil(items.length / Math.max(1, Math.floor(width / cellMin)));
|
|
372
|
+
const perRow = Math.ceil(items.length / rows);
|
|
373
|
+
const cellWidth = Math.floor(width / perRow);
|
|
374
|
+
const lines = [];
|
|
375
|
+
for (let index = 0; index < items.length; index += perRow) {
|
|
376
|
+
const row = items.slice(index, index + perRow);
|
|
377
|
+
lines.push(row.map(([label]) => span(padEndColumns(label, cellWidth), "muted")));
|
|
378
|
+
lines.push(row.map(([, value]) => span(padEndColumns(value, cellWidth), "value")));
|
|
379
|
+
}
|
|
380
|
+
return lines;
|
|
381
|
+
}
|
|
382
|
+
function totalsLines(view, width) {
|
|
383
|
+
const { tokens, cacheSavingsUsd } = view.report.totals;
|
|
384
|
+
return [
|
|
385
|
+
[span("Totals", "heading")],
|
|
386
|
+
...grid([
|
|
387
|
+
["Processed tokens", formatTokens(tokens.processed)],
|
|
388
|
+
["Cached input", formatTokens(tokens.cachedInput)],
|
|
389
|
+
["Uncached input", formatTokens(tokens.uncachedInput)],
|
|
390
|
+
["Output", formatTokens(tokens.output)],
|
|
391
|
+
["Cache savings", formatUsd(cacheSavingsUsd)],
|
|
392
|
+
], width),
|
|
393
|
+
];
|
|
394
|
+
}
|
|
395
|
+
/**
|
|
396
|
+
* `width` cells split in proportion to `amounts` (largest remainder); a part above 0 gets at
|
|
397
|
+
* least one cell, taken from the widest part.
|
|
398
|
+
*/
|
|
399
|
+
function splitWidth(amounts, width) {
|
|
400
|
+
const total = amounts.reduce((sum, amount) => sum + amount, 0);
|
|
401
|
+
const exact = amounts.map((amount) => (amount / total) * width);
|
|
402
|
+
const cells = exact.map(Math.floor);
|
|
403
|
+
let left = width - cells.reduce((sum, count) => sum + count, 0);
|
|
404
|
+
const byRemainder = amounts
|
|
405
|
+
.map((_, index) => index)
|
|
406
|
+
.sort((a, b) => exact[b] - cells[b] - (exact[a] - cells[a]));
|
|
407
|
+
for (const index of byRemainder) {
|
|
408
|
+
if (left <= 0)
|
|
409
|
+
break;
|
|
410
|
+
cells[index] += 1;
|
|
411
|
+
left -= 1;
|
|
412
|
+
}
|
|
413
|
+
amounts.forEach((amount, index) => {
|
|
414
|
+
const widest = cells.indexOf(Math.max(...cells));
|
|
415
|
+
if (amount > 0 && cells[index] === 0 && cells[widest] > 1) {
|
|
416
|
+
cells[widest] -= 1;
|
|
417
|
+
cells[index] = 1;
|
|
418
|
+
}
|
|
419
|
+
});
|
|
420
|
+
return cells;
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* One stacked bar and its legend: cost by type, or tokens by type. Text gets a bar of shade
|
|
424
|
+
* marks of at most 64 columns; `colored` (the TUI) a thin line as wide as `width`, a space
|
|
425
|
+
* between its parts.
|
|
426
|
+
*/
|
|
427
|
+
function byTypeLines(kind, amounts, width, colored) {
|
|
428
|
+
const barWidth = colored ? width : Math.min(width, BY_TYPE_MAX_WIDTH);
|
|
429
|
+
const parts = ["input", "cacheRead", "cacheWrite", "output"].map((part) => ({
|
|
430
|
+
part,
|
|
431
|
+
amount: amounts[part],
|
|
432
|
+
}));
|
|
433
|
+
if (parts.every((part) => part.amount <= 0))
|
|
434
|
+
return [];
|
|
435
|
+
const shown = parts.filter((part) => part.amount > 0).length;
|
|
436
|
+
const gaps = colored ? shown - 1 : 0;
|
|
437
|
+
const cells = splitWidth(parts.map((part) => part.amount), barWidth - gaps);
|
|
438
|
+
const bar = [];
|
|
439
|
+
parts.forEach((part, index) => {
|
|
440
|
+
if (cells[index] === 0)
|
|
441
|
+
return;
|
|
442
|
+
if (colored && bar.length > 0)
|
|
443
|
+
bar.push(span(" ", "muted"));
|
|
444
|
+
const mark = colored ? THIN_BAR : PART_MARKS[part.part];
|
|
445
|
+
bar.push(span(mark.repeat(cells[index]), `part:${part.part}`));
|
|
446
|
+
});
|
|
447
|
+
const legend = [];
|
|
448
|
+
for (const part of parts) {
|
|
449
|
+
const amount = kind === "cost" ? formatUsd(part.amount) : formatTokens(part.amount);
|
|
450
|
+
const item = [
|
|
451
|
+
span(colored ? THIN_MARK : PART_MARKS[part.part], `part:${part.part}`),
|
|
452
|
+
span(` ${PART_LABELS[part.part]} `, "muted"),
|
|
453
|
+
span(amount, "value"),
|
|
454
|
+
];
|
|
455
|
+
const current = legend[legend.length - 1];
|
|
456
|
+
if (current && lineWidth(current) + 3 + lineWidth(item) <= barWidth) {
|
|
457
|
+
current.push(span(" ", "muted"), ...item);
|
|
458
|
+
}
|
|
459
|
+
else {
|
|
460
|
+
legend.push(item);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
const title = kind === "cost" ? "Cost by type" : "Tokens by type";
|
|
464
|
+
return [[span(title, "heading")], bar, ...legend];
|
|
465
|
+
}
|
|
466
|
+
function breakdownEntries(view) {
|
|
467
|
+
const { report } = view;
|
|
468
|
+
if (view.breakdown === "day") {
|
|
469
|
+
return report.days
|
|
470
|
+
.filter((row) => row.tokens > 0)
|
|
471
|
+
.reverse()
|
|
472
|
+
.map((row) => {
|
|
473
|
+
const date = dayDate(row.date);
|
|
474
|
+
return {
|
|
475
|
+
name: `${WEEKDAYS[date.getDay()]}, ${shortDate(date, view.thisYear)}`,
|
|
476
|
+
dot: undefined,
|
|
477
|
+
row,
|
|
478
|
+
};
|
|
479
|
+
});
|
|
480
|
+
}
|
|
481
|
+
return breakdownModels(report, view.mode).map((row) => ({
|
|
482
|
+
name: modelName(report, row),
|
|
483
|
+
dot: providerRole(report, row.providerID),
|
|
484
|
+
row,
|
|
485
|
+
cache: cacheHit(row.tokensByType),
|
|
486
|
+
}));
|
|
487
|
+
}
|
|
488
|
+
/** A model's id, with its provider when more than one provider serves that id. */
|
|
489
|
+
function modelName(report, model) {
|
|
490
|
+
const providers = report.models.filter((row) => row.modelID === model.modelID).length;
|
|
491
|
+
return providers > 1 ? `${model.providerID}/${model.modelID}` : model.modelID;
|
|
492
|
+
}
|
|
493
|
+
/**
|
|
494
|
+
* The models of the model breakdown, in its order. In `usageView` with the model breakdown,
|
|
495
|
+
* these models' rows follow the Breakdown heading and its column names, one each, in this order.
|
|
496
|
+
*/
|
|
497
|
+
export function breakdownModels(report, mode) {
|
|
498
|
+
return inModeOrder(report.models.filter((row) => row.tokens > 0), mode);
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* The model breakdown's Cache column: each model's cache hit, right-aligned under "Cache", in
|
|
502
|
+
* the good color, or the warning color with " ⚠" after it when it is low, or muted before 200K
|
|
503
|
+
* input tokens; "—" without one.
|
|
504
|
+
*/
|
|
505
|
+
function cacheColumn(shown) {
|
|
506
|
+
const texts = shown.map((entry) => entry.cache?.text ?? NO_CACHE_HIT);
|
|
507
|
+
const percentWidth = Math.max(columnsOf(CACHE_HEADER), ...texts.map(columnsOf));
|
|
508
|
+
const lowAfter = ` ${LOW_CACHE_MARK}`;
|
|
509
|
+
const hang = shown.some((entry) => entry.cache?.low) ? columnsOf(lowAfter) : 0;
|
|
510
|
+
const spans = (entry) => {
|
|
511
|
+
if (!entry) {
|
|
512
|
+
const header = ` ${padStartColumns(CACHE_HEADER, percentWidth)}${" ".repeat(hang)}`;
|
|
513
|
+
return [span(header, "muted")];
|
|
514
|
+
}
|
|
515
|
+
const { cache } = entry;
|
|
516
|
+
let role = "muted";
|
|
517
|
+
if (cache?.judged)
|
|
518
|
+
role = cache.low ? "warning" : "good";
|
|
519
|
+
const cell = span(` ${padStartColumns(cache?.text ?? NO_CACHE_HIT, percentWidth)}`, role);
|
|
520
|
+
if (hang === 0)
|
|
521
|
+
return [cell];
|
|
522
|
+
return [cell, cache?.low ? span(lowAfter, "warning") : span(" ".repeat(hang), "muted")];
|
|
523
|
+
};
|
|
524
|
+
return { width: 2 + percentWidth + hang, spans };
|
|
525
|
+
}
|
|
526
|
+
/**
|
|
527
|
+
* The breakdown table: #, Model or Day, then the mode's amount, Share and the other amount, and
|
|
528
|
+
* for models their cache hit (`cacheColumn`) while the names keep 8 columns or their full width.
|
|
529
|
+
* `colored` (the TUI) puts a bar of each row's amount between the names and the numbers, the
|
|
530
|
+
* biggest row's bar full, when there is room for it, and ends each model row with ▸.
|
|
531
|
+
*/
|
|
532
|
+
function breakdownLines(view, fullWidth, maxRows, colored) {
|
|
533
|
+
const mark = colored && view.breakdown === "model" ? OPEN_MARK : "";
|
|
534
|
+
const width = fullWidth - columnsOf(mark);
|
|
535
|
+
const entries = breakdownEntries(view);
|
|
536
|
+
const shown = entries.slice(0, maxRows);
|
|
537
|
+
const metrics = metricsOf(view.mode);
|
|
538
|
+
const widths = metricWidths(view, metrics, shown.map((entry) => entry.row));
|
|
539
|
+
const numberWidth = String(shown.length).length;
|
|
540
|
+
const dotWidth = view.breakdown === "model" ? columnsOf(DOT) : 0;
|
|
541
|
+
const metricsWidth = widths.reduce((sum, metricWidth) => sum + metricWidth + 2, 0);
|
|
542
|
+
const nameWidest = Math.min(ROW_NAME_MAX_WIDTH, Math.max(4, ...shown.map((entry) => columnsOf(entry.name))));
|
|
543
|
+
const withoutCache = numberWidth + 2 + dotWidth + metricsWidth;
|
|
544
|
+
const cache = cacheColumn(shown);
|
|
545
|
+
const withCache = view.breakdown === "model" &&
|
|
546
|
+
width - withoutCache - cache.width >= Math.min(nameWidest, CACHE_NAME_MIN_WIDTH);
|
|
547
|
+
const fixed = withoutCache + (withCache ? cache.width : 0);
|
|
548
|
+
const barRoom = width - fixed - nameWidest - 2;
|
|
549
|
+
const barWidth = colored && barRoom >= ROW_BAR_MIN_WIDTH ? barRoom : 0;
|
|
550
|
+
const nameWidth = barWidth > 0 ? nameWidest : Math.max(4, width - fixed);
|
|
551
|
+
const amountOf = (row) => (view.mode === "cost" ? row.costUsd : row.tokens);
|
|
552
|
+
const most = Math.max(0, ...shown.map((entry) => amountOf(entry.row)));
|
|
553
|
+
const barSpans = (entry) => {
|
|
554
|
+
if (barWidth === 0)
|
|
555
|
+
return [];
|
|
556
|
+
const amount = amountOf(entry.row);
|
|
557
|
+
const filled = most > 0 && amount > 0 ? Math.max(1, Math.round((amount / most) * barWidth)) : 0;
|
|
558
|
+
return [
|
|
559
|
+
span(" ", "muted"),
|
|
560
|
+
span(THIN_BAR.repeat(filled), entry.dot ?? "bar"),
|
|
561
|
+
span(" ".repeat(barWidth - filled), "muted"),
|
|
562
|
+
].filter((part) => part.text.length > 0);
|
|
563
|
+
};
|
|
564
|
+
const nameHeader = view.breakdown === "model" ? "Model" : "Day";
|
|
565
|
+
const lines = [
|
|
566
|
+
[span("Breakdown", "heading")],
|
|
567
|
+
[
|
|
568
|
+
span(`${padStartColumns("#", numberWidth)} ${padEndColumns(nameHeader, dotWidth + nameWidth)}${barWidth > 0 ? " ".repeat(barWidth + 2) : ""}`, "muted"),
|
|
569
|
+
...metricSpans(view, metrics, widths, undefined),
|
|
570
|
+
...(withCache ? cache.spans(undefined) : []),
|
|
571
|
+
...(mark ? [span(" ".repeat(columnsOf(mark)), "muted")] : []),
|
|
572
|
+
],
|
|
573
|
+
];
|
|
574
|
+
shown.forEach((entry, position) => {
|
|
575
|
+
lines.push([
|
|
576
|
+
span(`${padStartColumns(String(position + 1), numberWidth)} `, "muted"),
|
|
577
|
+
...(entry.dot ? [span(DOT, entry.dot)] : []),
|
|
578
|
+
span(padEndColumns(truncateColumns(entry.name, nameWidth), nameWidth), "text"),
|
|
579
|
+
...barSpans(entry),
|
|
580
|
+
...metricSpans(view, metrics, widths, entry.row),
|
|
581
|
+
...(withCache ? cache.spans(entry) : []),
|
|
582
|
+
...(mark ? [span(mark, "muted")] : []),
|
|
583
|
+
]);
|
|
584
|
+
});
|
|
585
|
+
if (entries.length > shown.length) {
|
|
586
|
+
lines.push([span(`and ${entries.length - shown.length} more`, "muted")]);
|
|
587
|
+
}
|
|
588
|
+
return lines;
|
|
589
|
+
}
|
|
590
|
+
/** `right` beside `left`, starting at column `rightStart`. */
|
|
591
|
+
function sideBySide(left, right, rightStart) {
|
|
592
|
+
return Array.from({ length: Math.max(left.length, right.length) }, (_, index) => {
|
|
593
|
+
const leftLine = left[index] ?? [];
|
|
594
|
+
const rightLine = right[index] ?? [];
|
|
595
|
+
if (rightLine.length === 0)
|
|
596
|
+
return leftLine;
|
|
597
|
+
const gap = " ".repeat(Math.max(0, rightStart - lineWidth(leftLine)));
|
|
598
|
+
return [...leftLine, span(gap, "muted"), ...rightLine];
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
function buildView(report, options, contents) {
|
|
602
|
+
const width = Math.max(0, Math.floor(options.width));
|
|
603
|
+
if (report.totals.tokens.processed <= 0) {
|
|
604
|
+
return [clip([span("No usage in this range.", "muted")], width)];
|
|
605
|
+
}
|
|
606
|
+
const view = {
|
|
607
|
+
report,
|
|
608
|
+
mode: options.mode,
|
|
609
|
+
breakdown: options.breakdown,
|
|
610
|
+
thisYear: new Date(report.generatedAtMs).getFullYear(),
|
|
611
|
+
};
|
|
612
|
+
const layoutWidth = Math.max(LAYOUT_MIN_WIDTH, width);
|
|
613
|
+
const listWidth = Math.min(LIST_MAX_WIDTH, layoutWidth);
|
|
614
|
+
const list = [...totalLines(view, listWidth), [], ...providerLines(view, listWidth)].map((line) => clip(line, listWidth));
|
|
615
|
+
let top = list;
|
|
616
|
+
if (contents.chart) {
|
|
617
|
+
const listUsed = Math.max(...list.map(lineWidth));
|
|
618
|
+
if (layoutWidth - listUsed - SIDE_GAP >= BESIDE_CHART_MIN_WIDTH) {
|
|
619
|
+
// As tall as the list: the chart's title, baseline and dates take 3 lines.
|
|
620
|
+
const rows = Math.max(CHART_ROWS, list.length - 3);
|
|
621
|
+
const chart = chartLines(chartDays(report, view.mode), view.mode, view.thisYear, layoutWidth - listUsed - SIDE_GAP, rows);
|
|
622
|
+
top = sideBySide(list, chart, listUsed + SIDE_GAP);
|
|
623
|
+
}
|
|
624
|
+
else {
|
|
625
|
+
const chart = chartLines(chartDays(report, view.mode), view.mode, view.thisYear, layoutWidth, CHART_ROWS);
|
|
626
|
+
if (chart.length > 0)
|
|
627
|
+
top = [...list, [], ...chart];
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
const sections = [
|
|
631
|
+
top,
|
|
632
|
+
totalsLines(view, layoutWidth),
|
|
633
|
+
byTypeLines(view.mode, view.mode === "cost" ? report.totals.costByType : report.totals.tokensByType, layoutWidth, contents.colored),
|
|
634
|
+
breakdownLines(view, layoutWidth, contents.breakdownRows, contents.colored),
|
|
635
|
+
unpricedLines(view, layoutWidth),
|
|
636
|
+
].filter((lines) => lines.length > 0);
|
|
637
|
+
const gap = Array.from({ length: contents.sectionGap }, () => []);
|
|
638
|
+
return sections
|
|
639
|
+
.flatMap((lines, index) => (index === 0 ? lines : [...gap, ...lines]))
|
|
640
|
+
.map((line) => clip(line, width));
|
|
641
|
+
}
|
|
642
|
+
/** The usage view for the TUI Usage tab: lines of role-tagged spans, each at most `width`. */
|
|
643
|
+
export function usageView(report, options) {
|
|
644
|
+
return buildView(report, options, {
|
|
645
|
+
chart: true,
|
|
646
|
+
breakdownRows: Number.POSITIVE_INFINITY,
|
|
647
|
+
colored: true,
|
|
648
|
+
sectionGap: 2,
|
|
649
|
+
});
|
|
650
|
+
}
|
|
651
|
+
/** Below this width per side, cost by type and tokens by type stack in the model detail. */
|
|
652
|
+
const DETAIL_SIDE_MIN_WIDTH = 30;
|
|
653
|
+
/** The model detail's first line: the provider's dot and the model's name, cut to `width`. */
|
|
654
|
+
export function modelDetailTitle(report, model, width) {
|
|
655
|
+
const room = Math.max(0, Math.floor(width));
|
|
656
|
+
const name = truncateColumns(modelName(report, model), room - columnsOf(DOT));
|
|
657
|
+
return clip([span(DOT, providerRole(report, model.providerID)), span(name, "title")], room);
|
|
658
|
+
}
|
|
659
|
+
/**
|
|
660
|
+
* One model of the breakdown, like T3 Code's model popup: its name, provider and share, then
|
|
661
|
+
* Cost, Tokens, cost per 1M tokens and cache hit (cache reads ÷ all input tokens), its daily
|
|
662
|
+
* chart over the report's days, and cost by type and tokens by type, side by side when they
|
|
663
|
+
* fit. Every line fits in `width`.
|
|
664
|
+
*/
|
|
665
|
+
export function modelDetailView(report, model, options) {
|
|
666
|
+
const width = Math.max(0, Math.floor(options.width));
|
|
667
|
+
const layoutWidth = Math.max(LAYOUT_MIN_WIDTH, width);
|
|
668
|
+
const { mode } = options;
|
|
669
|
+
const thisYear = new Date(report.generatedAtMs).getFullYear();
|
|
670
|
+
const share = mode === "cost"
|
|
671
|
+
? `${formatShare(model.costShare)} of cost`
|
|
672
|
+
: `${formatShare(model.tokenShare)} of tokens`;
|
|
673
|
+
const { input, cacheRead, cacheWrite } = model.tokensByType;
|
|
674
|
+
const allInput = input + cacheRead + cacheWrite;
|
|
675
|
+
let perMillion = "—";
|
|
676
|
+
if (!model.priced)
|
|
677
|
+
perMillion = "no price";
|
|
678
|
+
else if (model.tokens > 0)
|
|
679
|
+
perMillion = formatUsd((model.costUsd / model.tokens) * 1_000_000);
|
|
680
|
+
const lines = [
|
|
681
|
+
modelDetailTitle(report, model, width),
|
|
682
|
+
[span(`${getQuotaProviderDisplayLabel(model.providerID)} · ${share}`, "muted")],
|
|
683
|
+
[],
|
|
684
|
+
...grid([
|
|
685
|
+
["Cost", costText(model)],
|
|
686
|
+
["Tokens", formatTokens(model.tokens)],
|
|
687
|
+
["Per 1M tokens", perMillion],
|
|
688
|
+
["Cache hit", allInput > 0 ? formatShare(cacheRead / allInput) : "—"],
|
|
689
|
+
], layoutWidth),
|
|
690
|
+
];
|
|
691
|
+
const chart = chartLines(chartDays(report, mode, model), mode, thisYear, layoutWidth, DETAIL_CHART_ROWS);
|
|
692
|
+
if (chart.length > 0)
|
|
693
|
+
lines.push([], ...chart);
|
|
694
|
+
const sideWidth = Math.floor((layoutWidth - COLUMN_GAP) / 2);
|
|
695
|
+
const sideBySideFits = sideWidth >= DETAIL_SIDE_MIN_WIDTH;
|
|
696
|
+
const byTypeWidth = sideBySideFits ? sideWidth : layoutWidth;
|
|
697
|
+
const costByType = byTypeLines("cost", model.costByType, byTypeWidth, true);
|
|
698
|
+
const tokensByType = byTypeLines("tokens", model.tokensByType, byTypeWidth, true);
|
|
699
|
+
if (sideBySideFits && costByType.length > 0) {
|
|
700
|
+
lines.push([], ...sideBySide(costByType, tokensByType, sideWidth + COLUMN_GAP));
|
|
701
|
+
}
|
|
702
|
+
else {
|
|
703
|
+
for (const section of [costByType, tokensByType]) {
|
|
704
|
+
if (section.length > 0)
|
|
705
|
+
lines.push([], ...section);
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
return lines.map((line) => clip(line, width));
|
|
709
|
+
}
|
|
710
|
+
/**
|
|
711
|
+
* The usage view as plain text for Web `/quota` and the CLI: a range title, no chart, the top
|
|
712
|
+
* 10 breakdown rows, at most 64 columns.
|
|
713
|
+
*/
|
|
714
|
+
export function usageText(report, options) {
|
|
715
|
+
const width = Math.min(TEXT_MAX_WIDTH, options.width ?? TEXT_MAX_WIDTH);
|
|
716
|
+
const lines = buildView(report, { mode: options.mode, breakdown: options.breakdown, width }, { chart: false, breakdownRows: TEXT_BREAKDOWN_ROWS, colored: false, sectionGap: 1 });
|
|
717
|
+
const title = truncateColumns(`Usage · ${rangeTitle(report)}`, width);
|
|
718
|
+
const texts = lines.map((line) => line
|
|
719
|
+
.map((part) => part.text)
|
|
720
|
+
.join("")
|
|
721
|
+
.trimEnd());
|
|
722
|
+
return [title, ...texts].join("\n");
|
|
723
|
+
}
|
|
724
|
+
//# sourceMappingURL=usage.js.map
|