@slkiser/opencode-quota 5.0.3 → 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 +30 -44
- 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/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 +33 -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 -560
- 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 -6
- package/dist/lib/ollama-cloud.d.ts.map +1 -1
- package/dist/lib/ollama-cloud.js +26 -37
- package/dist/lib/ollama-cloud.js.map +1 -1
- package/dist/lib/openai.d.ts +18 -29
- package/dist/lib/openai.d.ts.map +1 -1
- package/dist/lib/openai.js +108 -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 +15 -12
- package/dist/lib/opencode-zen.d.ts.map +1 -1
- package/dist/lib/opencode-zen.js +139 -147
- 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 -773
- 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 +25 -333
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/lib/types.js +2 -70
- 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 +38 -88
- 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 +0 -1
- package/dist/providers/google-agy.d.ts.map +1 -1
- package/dist/providers/google-agy.js +41 -192
- 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 -6
- package/dist/providers/ollama-cloud.d.ts.map +1 -1
- package/dist/providers/ollama-cloud.js +86 -112
- package/dist/providers/ollama-cloud.js.map +1 -1
- package/dist/providers/openai.d.ts.map +1 -1
- package/dist/providers/openai.js +144 -166
- 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 +100 -150
- 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 -1092
- 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 -21
- package/dist/lib/quota-surface-data.d.ts.map +0 -1
- package/dist/lib/quota-surface-data.js +0 -362
- 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
package/dist/lib/copilot.js
CHANGED
|
@@ -7,16 +7,14 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { existsSync, readFileSync } from "fs";
|
|
9
9
|
import { join } from "path";
|
|
10
|
-
import { sanitizeDisplaySnippet, sanitizeDisplayText } from "./display-sanitize.js";
|
|
10
|
+
import { sanitizeDisplaySnippet, sanitizeDisplayText, sanitizeSingleLineDisplaySnippet, } from "./display-sanitize.js";
|
|
11
11
|
import { fetchWithTimeout } from "./http.js";
|
|
12
|
+
import { finiteNumber } from "./json-guards.js";
|
|
12
13
|
import { readAuthFile } from "./opencode-auth.js";
|
|
13
14
|
import { getOpencodeRuntimeDirs } from "./opencode-runtime-paths.js";
|
|
14
15
|
import { deriveResolvedAuthIdentity } from "./resolved-auth-identity.js";
|
|
15
16
|
const GITHUB_API_BASE_URL = "https://api.github.com";
|
|
16
|
-
const GITHUB_API_VERSION = "2026-03-10";
|
|
17
17
|
const COPILOT_QUOTA_CONFIG_FILENAME = "copilot-quota-token.json";
|
|
18
|
-
const USER_AGENT = "opencode-quota/copilot-billing";
|
|
19
|
-
const COPILOT_INTERNAL_USER_PATH = "/copilot_internal/user";
|
|
20
18
|
/** Integration ids that can hold the Copilot OAuth login, in precedence order. */
|
|
21
19
|
export const COPILOT_AUTH_KEYS = [
|
|
22
20
|
"github-copilot",
|
|
@@ -24,10 +22,33 @@ export const COPILOT_AUTH_KEYS = [
|
|
|
24
22
|
"copilot-chat",
|
|
25
23
|
"github-copilot-chat",
|
|
26
24
|
];
|
|
25
|
+
const VALID_TIERS = [
|
|
26
|
+
"free",
|
|
27
|
+
"student",
|
|
28
|
+
"pro",
|
|
29
|
+
"pro+",
|
|
30
|
+
"max",
|
|
31
|
+
"business",
|
|
32
|
+
"enterprise",
|
|
33
|
+
];
|
|
27
34
|
const LEGACY_PREMIUM_REQUEST_TOTALS = {
|
|
28
35
|
pro: 300,
|
|
29
36
|
"pro+": 1500,
|
|
30
37
|
};
|
|
38
|
+
/** More specific budgets win when several apply to the same target. */
|
|
39
|
+
const BUDGET_SCOPE_SPECIFICITY = {
|
|
40
|
+
user: 5,
|
|
41
|
+
multi_user_cost_center: 4,
|
|
42
|
+
multi_user_customer: 3,
|
|
43
|
+
organization: 2,
|
|
44
|
+
enterprise: 1,
|
|
45
|
+
};
|
|
46
|
+
function errorText(error) {
|
|
47
|
+
return error instanceof Error ? error.message : String(error);
|
|
48
|
+
}
|
|
49
|
+
function toQuotaError(message) {
|
|
50
|
+
return { success: false, error: message };
|
|
51
|
+
}
|
|
31
52
|
function validateEnterpriseHost(value) {
|
|
32
53
|
if (value === undefined || value === null)
|
|
33
54
|
return {};
|
|
@@ -56,7 +77,7 @@ function validateEnterpriseHost(value) {
|
|
|
56
77
|
url.username ||
|
|
57
78
|
url.password ||
|
|
58
79
|
url.port ||
|
|
59
|
-
|
|
80
|
+
url.pathname !== "/" ||
|
|
60
81
|
url.search ||
|
|
61
82
|
url.hash) {
|
|
62
83
|
return invalid;
|
|
@@ -80,9 +101,6 @@ function validateEnterpriseHost(value) {
|
|
|
80
101
|
function getApiBaseUrl(enterpriseHost) {
|
|
81
102
|
return enterpriseHost ? `https://api.${enterpriseHost}` : GITHUB_API_BASE_URL;
|
|
82
103
|
}
|
|
83
|
-
function getApiHost(apiBaseUrl) {
|
|
84
|
-
return new URL(apiBaseUrl).hostname;
|
|
85
|
-
}
|
|
86
104
|
function classifyPatTokenKind(token) {
|
|
87
105
|
if (token.startsWith("github_pat_"))
|
|
88
106
|
return "github_pat";
|
|
@@ -94,101 +112,50 @@ function classifyPatTokenKind(token) {
|
|
|
94
112
|
return "ghs";
|
|
95
113
|
return "other";
|
|
96
114
|
}
|
|
97
|
-
function getCurrentBillingPeriod(now = new Date()) {
|
|
98
|
-
return {
|
|
99
|
-
year: now.getUTCFullYear(),
|
|
100
|
-
month: now.getUTCMonth() + 1,
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
function getApproxNextResetIso(nowMs = Date.now()) {
|
|
104
|
-
const now = new Date(nowMs);
|
|
105
|
-
return new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth() + 1, 1)).toISOString();
|
|
106
|
-
}
|
|
107
|
-
function formatBillingPeriod(period) {
|
|
108
|
-
return `${period.year}-${String(period.month).padStart(2, "0")}`;
|
|
109
|
-
}
|
|
110
115
|
function computePercentRemainingFromUsed(used, total) {
|
|
111
116
|
if (!Number.isFinite(used) || used < 0 || !Number.isFinite(total) || total <= 0) {
|
|
112
117
|
return undefined;
|
|
113
118
|
}
|
|
114
119
|
return Math.max(0, Math.min(100, Math.floor(((total - used) * 100) / total)));
|
|
115
120
|
}
|
|
116
|
-
|
|
117
|
-
|
|
121
|
+
/** Why this tier/billing model cannot be combined with the configured scope, if it cannot. */
|
|
122
|
+
function getPatScopeError(config) {
|
|
123
|
+
const hasManagedScope = Boolean(config.organization || config.enterprise);
|
|
118
124
|
if (config.billingModel === "legacy_premium_requests") {
|
|
119
125
|
if (config.tier !== "pro" && config.tier !== "pro+") {
|
|
120
|
-
return
|
|
121
|
-
target: null,
|
|
122
|
-
error: "Legacy premium-request billing is only available to Copilot Pro or Pro+ subscribers on an existing annual plan that remained on legacy billing after June 1, 2026.",
|
|
123
|
-
};
|
|
126
|
+
return "Legacy premium-request billing is only available to Copilot Pro or Pro+ subscribers on an existing annual plan that remained on legacy billing after June 1, 2026.";
|
|
124
127
|
}
|
|
125
|
-
if (
|
|
126
|
-
return
|
|
127
|
-
target: null,
|
|
128
|
-
error: "Legacy premium-request billing is personal-only. Remove organization and enterprise from copilot-quota-token.json.",
|
|
129
|
-
};
|
|
128
|
+
if (hasManagedScope) {
|
|
129
|
+
return "Legacy premium-request billing is personal-only. Remove organization and enterprise from copilot-quota-token.json.";
|
|
130
130
|
}
|
|
131
131
|
}
|
|
132
132
|
if (config.tier === "business") {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
error: 'Copilot Business AI Credit usage requires "organization" and does not accept "enterprise" in copilot-quota-token.json.',
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
return {
|
|
140
|
-
target: {
|
|
141
|
-
scope: "organization",
|
|
142
|
-
organization: config.organization,
|
|
143
|
-
username: config.username,
|
|
144
|
-
billingPeriod,
|
|
145
|
-
},
|
|
146
|
-
};
|
|
133
|
+
return !config.organization || config.enterprise
|
|
134
|
+
? 'Copilot Business AI Credit usage requires "organization" and does not accept "enterprise" in copilot-quota-token.json.'
|
|
135
|
+
: undefined;
|
|
147
136
|
}
|
|
148
137
|
if (config.tier === "enterprise") {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
scope: "enterprise",
|
|
153
|
-
enterprise: config.enterprise,
|
|
154
|
-
organization: config.organization,
|
|
155
|
-
username: config.username,
|
|
156
|
-
billingPeriod,
|
|
157
|
-
},
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
if (config.organization) {
|
|
161
|
-
return {
|
|
162
|
-
target: {
|
|
163
|
-
scope: "organization",
|
|
164
|
-
organization: config.organization,
|
|
165
|
-
username: config.username,
|
|
166
|
-
billingPeriod,
|
|
167
|
-
},
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
|
-
return {
|
|
171
|
-
target: null,
|
|
172
|
-
error: 'Copilot Enterprise AI Credit usage requires "enterprise" or "organization" in copilot-quota-token.json.',
|
|
173
|
-
};
|
|
138
|
+
return hasManagedScope
|
|
139
|
+
? undefined
|
|
140
|
+
: 'Copilot Enterprise AI Credit usage requires "enterprise" or "organization" in copilot-quota-token.json.';
|
|
174
141
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
142
|
+
return hasManagedScope
|
|
143
|
+
? `Copilot ${config.tier} AI Credit usage is personal. Remove organization and enterprise from copilot-quota-token.json.`
|
|
144
|
+
: undefined;
|
|
145
|
+
}
|
|
146
|
+
/** Billing target of a config that already passed `getPatScopeError`. */
|
|
147
|
+
function resolvePatBillingTarget(config) {
|
|
148
|
+
const now = new Date();
|
|
149
|
+
const billingPeriod = { year: now.getUTCFullYear(), month: now.getUTCMonth() + 1 };
|
|
150
|
+
const { username, organization, enterprise } = config;
|
|
151
|
+
if (enterprise) {
|
|
152
|
+
return { scope: "enterprise", enterprise, organization, username, billingPeriod };
|
|
180
153
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
username: config.username,
|
|
185
|
-
billingPeriod,
|
|
186
|
-
},
|
|
187
|
-
};
|
|
154
|
+
if (organization)
|
|
155
|
+
return { scope: "organization", organization, username, billingPeriod };
|
|
156
|
+
return { scope: "user", username, billingPeriod };
|
|
188
157
|
}
|
|
189
158
|
function validatePatTargetCompatibility(target, tokenKind) {
|
|
190
|
-
if (!tokenKind)
|
|
191
|
-
return null;
|
|
192
159
|
if (target.scope === "user" && tokenKind === "ghs") {
|
|
193
160
|
return ("GitHub's personal AI Credit report supports GitHub App user access tokens, " +
|
|
194
161
|
"but not GitHub App installation access tokens. Use a GitHub App user token, fine-grained PAT with Plan (read), or supported classic credential.");
|
|
@@ -200,13 +167,9 @@ function validatePatTargetCompatibility(target, tokenKind) {
|
|
|
200
167
|
}
|
|
201
168
|
return null;
|
|
202
169
|
}
|
|
203
|
-
export function getCopilotPatConfigCandidatePaths() {
|
|
204
|
-
return [join(getOpencodeRuntimeDirs().configDir, COPILOT_QUOTA_CONFIG_FILENAME)];
|
|
205
|
-
}
|
|
206
170
|
function validateQuotaConfig(raw) {
|
|
207
|
-
if (!raw || typeof raw !== "object")
|
|
208
|
-
return {
|
|
209
|
-
}
|
|
171
|
+
if (!raw || typeof raw !== "object")
|
|
172
|
+
return { error: "Config must be a JSON object" };
|
|
210
173
|
const obj = raw;
|
|
211
174
|
const token = typeof obj.token === "string" ? obj.token.trim() : "";
|
|
212
175
|
const tier = typeof obj.tier === "string" ? obj.tier.trim().toLowerCase() : "";
|
|
@@ -215,110 +178,69 @@ function validateQuotaConfig(raw) {
|
|
|
215
178
|
: typeof obj.billingModel === "string"
|
|
216
179
|
? obj.billingModel.trim()
|
|
217
180
|
: "";
|
|
218
|
-
if (!token)
|
|
219
|
-
return {
|
|
220
|
-
|
|
221
|
-
const validTiers = [
|
|
222
|
-
"free",
|
|
223
|
-
"student",
|
|
224
|
-
"pro",
|
|
225
|
-
"pro+",
|
|
226
|
-
"max",
|
|
227
|
-
"business",
|
|
228
|
-
"enterprise",
|
|
229
|
-
];
|
|
230
|
-
if (!validTiers.includes(tier)) {
|
|
181
|
+
if (!token)
|
|
182
|
+
return { error: "Missing required string field: token" };
|
|
183
|
+
if (!VALID_TIERS.includes(tier)) {
|
|
231
184
|
return {
|
|
232
|
-
config: null,
|
|
233
185
|
error: "Invalid tier; expected one of: free, student, pro, pro+, max, business, enterprise",
|
|
234
186
|
};
|
|
235
187
|
}
|
|
236
188
|
if (billingModel !== "ai_credits" && billingModel !== "legacy_premium_requests") {
|
|
237
|
-
return {
|
|
238
|
-
config: null,
|
|
239
|
-
error: "Invalid billingModel; expected ai_credits or legacy_premium_requests",
|
|
240
|
-
};
|
|
189
|
+
return { error: "Invalid billingModel; expected ai_credits or legacy_premium_requests" };
|
|
241
190
|
}
|
|
242
|
-
const
|
|
191
|
+
const enterpriseHost = validateEnterpriseHost(obj.enterpriseUrl);
|
|
192
|
+
if (enterpriseHost.error)
|
|
193
|
+
return { error: enterpriseHost.error };
|
|
194
|
+
const optional = {};
|
|
195
|
+
for (const key of ["username", "organization", "enterprise"]) {
|
|
243
196
|
const value = obj[key];
|
|
244
197
|
if (value == null)
|
|
245
|
-
|
|
198
|
+
continue;
|
|
246
199
|
if (typeof value !== "string" || !value.trim()) {
|
|
247
|
-
|
|
200
|
+
return { error: `${key} must be a non-empty string when provided` };
|
|
248
201
|
}
|
|
249
|
-
|
|
202
|
+
optional[key] = value.trim();
|
|
203
|
+
}
|
|
204
|
+
const config = {
|
|
205
|
+
token,
|
|
206
|
+
tier: tier,
|
|
207
|
+
billingModel,
|
|
208
|
+
username: optional.username,
|
|
209
|
+
organization: optional.organization,
|
|
210
|
+
enterprise: optional.enterprise,
|
|
211
|
+
enterpriseUrl: enterpriseHost.host,
|
|
250
212
|
};
|
|
213
|
+
const scopeError = getPatScopeError(config);
|
|
214
|
+
return scopeError ? { error: scopeError } : { config };
|
|
215
|
+
}
|
|
216
|
+
function readQuotaConfigWithMeta() {
|
|
217
|
+
const path = join(getOpencodeRuntimeDirs().configDir, COPILOT_QUOTA_CONFIG_FILENAME);
|
|
218
|
+
const checkedPaths = [path];
|
|
219
|
+
if (!existsSync(path))
|
|
220
|
+
return { state: "absent", checkedPaths };
|
|
251
221
|
try {
|
|
252
|
-
const
|
|
253
|
-
if (
|
|
254
|
-
|
|
255
|
-
const config = {
|
|
256
|
-
token,
|
|
257
|
-
tier: tier,
|
|
258
|
-
billingModel,
|
|
259
|
-
username: readOptionalString("username"),
|
|
260
|
-
organization: readOptionalString("organization"),
|
|
261
|
-
enterprise: readOptionalString("enterprise"),
|
|
262
|
-
enterpriseUrl: enterpriseHost.host,
|
|
263
|
-
};
|
|
264
|
-
const resolved = resolvePatBillingTarget(config);
|
|
265
|
-
if (!resolved.target) {
|
|
266
|
-
return { config: null, error: resolved.error };
|
|
222
|
+
const validated = validateQuotaConfig(JSON.parse(readFileSync(path, "utf-8")));
|
|
223
|
+
if ("error" in validated) {
|
|
224
|
+
return { state: "invalid", checkedPaths, selectedPath: path, error: validated.error };
|
|
267
225
|
}
|
|
268
|
-
return {
|
|
226
|
+
return {
|
|
227
|
+
state: "valid",
|
|
228
|
+
checkedPaths,
|
|
229
|
+
selectedPath: path,
|
|
230
|
+
config: validated.config,
|
|
231
|
+
tokenKind: classifyPatTokenKind(validated.config.token),
|
|
232
|
+
};
|
|
269
233
|
}
|
|
270
234
|
catch (error) {
|
|
271
|
-
return {
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
export function readQuotaConfigWithMeta() {
|
|
275
|
-
const checkedPaths = getCopilotPatConfigCandidatePaths();
|
|
276
|
-
for (const path of checkedPaths) {
|
|
277
|
-
if (!existsSync(path))
|
|
278
|
-
continue;
|
|
279
|
-
try {
|
|
280
|
-
const parsed = JSON.parse(readFileSync(path, "utf-8"));
|
|
281
|
-
const validated = validateQuotaConfig(parsed);
|
|
282
|
-
if (!validated.config) {
|
|
283
|
-
return {
|
|
284
|
-
state: "invalid",
|
|
285
|
-
checkedPaths,
|
|
286
|
-
selectedPath: path,
|
|
287
|
-
error: validated.error ?? "Invalid config",
|
|
288
|
-
};
|
|
289
|
-
}
|
|
290
|
-
return {
|
|
291
|
-
state: "valid",
|
|
292
|
-
checkedPaths,
|
|
293
|
-
selectedPath: path,
|
|
294
|
-
config: validated.config,
|
|
295
|
-
tokenKind: classifyPatTokenKind(validated.config.token),
|
|
296
|
-
};
|
|
297
|
-
}
|
|
298
|
-
catch (error) {
|
|
299
|
-
return {
|
|
300
|
-
state: "invalid",
|
|
301
|
-
checkedPaths,
|
|
302
|
-
selectedPath: path,
|
|
303
|
-
error: error instanceof Error ? error.message : String(error),
|
|
304
|
-
};
|
|
305
|
-
}
|
|
235
|
+
return { state: "invalid", checkedPaths, selectedPath: path, error: errorText(error) };
|
|
306
236
|
}
|
|
307
|
-
return { state: "absent", checkedPaths };
|
|
308
237
|
}
|
|
309
238
|
function getCopilotOAuthToken(auth) {
|
|
310
239
|
return auth?.access?.trim() || auth?.refresh?.trim() || null;
|
|
311
240
|
}
|
|
312
241
|
function selectCopilotAuth(authData) {
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
const candidates = [
|
|
316
|
-
["github-copilot", authData["github-copilot"]],
|
|
317
|
-
["copilot", authData.copilot],
|
|
318
|
-
["copilot-chat", authData["copilot-chat"]],
|
|
319
|
-
["github-copilot-chat", authData["github-copilot-chat"]],
|
|
320
|
-
];
|
|
321
|
-
for (const [keyName, auth] of candidates) {
|
|
242
|
+
for (const keyName of COPILOT_AUTH_KEYS) {
|
|
243
|
+
const auth = authData?.[keyName];
|
|
322
244
|
if (!auth || auth.type !== "oauth")
|
|
323
245
|
continue;
|
|
324
246
|
// A login OpenCode could not return still counts, so it shows as an error.
|
|
@@ -328,36 +250,15 @@ function selectCopilotAuth(authData) {
|
|
|
328
250
|
}
|
|
329
251
|
return { auth: null, keyName: null };
|
|
330
252
|
}
|
|
331
|
-
function getRemainingTotalsState(target, config) {
|
|
332
|
-
if (!target || !config)
|
|
333
|
-
return "unavailable";
|
|
334
|
-
if (target.scope === "organization")
|
|
335
|
-
return "not_available_from_org_usage";
|
|
336
|
-
if (target.scope === "enterprise")
|
|
337
|
-
return "not_available_from_enterprise_usage";
|
|
338
|
-
if (config.billingModel !== "legacy_premium_requests") {
|
|
339
|
-
return "value_only_without_denominator";
|
|
340
|
-
}
|
|
341
|
-
return LEGACY_PREMIUM_REQUEST_TOTALS[config.tier]
|
|
342
|
-
? "available"
|
|
343
|
-
: "value_only_without_denominator";
|
|
344
|
-
}
|
|
345
253
|
export function getCopilotQuotaAuthDiagnostics(authData) {
|
|
346
254
|
const pat = readQuotaConfigWithMeta();
|
|
347
255
|
const { auth, keyName } = selectCopilotAuth(authData);
|
|
348
256
|
const oauthEnterpriseHost = validateEnterpriseHost(auth?.enterpriseUrl);
|
|
349
|
-
const
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
const compatibilityError = resolved.target && pat.state === "valid"
|
|
353
|
-
? validatePatTargetCompatibility(resolved.target, pat.tokenKind)
|
|
354
|
-
: null;
|
|
257
|
+
const target = pat.config ? resolvePatBillingTarget(pat.config) : null;
|
|
258
|
+
const compatibilityError = target ? validatePatTargetCompatibility(target, pat.tokenKind) : null;
|
|
259
|
+
// Any PAT config, even an invalid one, is authoritative over OAuth.
|
|
355
260
|
const patBlocksOAuth = pat.state !== "absent";
|
|
356
|
-
const effectiveSource = patBlocksOAuth
|
|
357
|
-
? "pat"
|
|
358
|
-
: auth
|
|
359
|
-
? "oauth"
|
|
360
|
-
: "none";
|
|
261
|
+
const effectiveSource = patBlocksOAuth ? "pat" : auth ? "oauth" : "none";
|
|
361
262
|
const effectiveEnterpriseHost = effectiveSource === "pat"
|
|
362
263
|
? pat.config?.enterpriseUrl
|
|
363
264
|
: effectiveSource === "oauth"
|
|
@@ -377,25 +278,9 @@ export function getCopilotQuotaAuthDiagnostics(authData) {
|
|
|
377
278
|
: effectiveSource === "pat" && pat.state !== "valid"
|
|
378
279
|
? "none"
|
|
379
280
|
: "github.com";
|
|
380
|
-
const apiBaseUrl = deployment === "github.com" || deployment === "ghe.com"
|
|
381
|
-
? getApiBaseUrl(effectiveEnterpriseHost)
|
|
382
|
-
: null;
|
|
383
281
|
const billingModel = pat.config?.billingModel ?? "ai_credits";
|
|
384
|
-
const oauthAvailable = effectiveSource === "oauth" &&
|
|
385
|
-
const
|
|
386
|
-
? billingModel === "legacy_premium_requests"
|
|
387
|
-
? "github_legacy_premium_request_api"
|
|
388
|
-
: "github_ai_credit_api"
|
|
389
|
-
: oauthAvailable
|
|
390
|
-
? "copilot_internal_user"
|
|
391
|
-
: "none";
|
|
392
|
-
const billingMode = resolved.target?.scope === "organization"
|
|
393
|
-
? "organization_usage"
|
|
394
|
-
: resolved.target?.scope === "enterprise"
|
|
395
|
-
? "enterprise_usage"
|
|
396
|
-
: resolved.target?.scope === "user" || oauthAvailable
|
|
397
|
-
? "user_quota"
|
|
398
|
-
: "none";
|
|
282
|
+
const oauthAvailable = effectiveSource === "oauth" && !oauthEnterpriseHost.error;
|
|
283
|
+
const billingScope = target?.scope ?? (oauthAvailable ? "user" : "none");
|
|
399
284
|
return {
|
|
400
285
|
pat,
|
|
401
286
|
oauth: {
|
|
@@ -406,35 +291,49 @@ export function getCopilotQuotaAuthDiagnostics(authData) {
|
|
|
406
291
|
hasEnterpriseUrl: auth?.enterpriseUrl !== undefined,
|
|
407
292
|
},
|
|
408
293
|
deployment,
|
|
409
|
-
apiHost:
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
? "oauth"
|
|
414
|
-
: "none",
|
|
294
|
+
apiHost: deployment === "github.com" || deployment === "ghe.com"
|
|
295
|
+
? new URL(getApiBaseUrl(effectiveEnterpriseHost)).hostname
|
|
296
|
+
: null,
|
|
297
|
+
enterpriseHostSource: effectiveEnterpriseHost ? effectiveSource : "none",
|
|
415
298
|
enterpriseHostError,
|
|
416
299
|
effectiveSource,
|
|
417
300
|
override: patBlocksOAuth && auth ? "pat_overrides_oauth" : "none",
|
|
418
|
-
quotaApi
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
301
|
+
quotaApi: target
|
|
302
|
+
? billingModel === "legacy_premium_requests"
|
|
303
|
+
? "github_legacy_premium_request_api"
|
|
304
|
+
: "github_ai_credit_api"
|
|
305
|
+
: oauthAvailable
|
|
306
|
+
? "copilot_internal_user"
|
|
307
|
+
: "none",
|
|
308
|
+
billingMode: billingScope === "organization"
|
|
309
|
+
? "organization_usage"
|
|
310
|
+
: billingScope === "enterprise"
|
|
311
|
+
? "enterprise_usage"
|
|
312
|
+
: billingScope === "user"
|
|
313
|
+
? "user_quota"
|
|
314
|
+
: "none",
|
|
315
|
+
billingScope,
|
|
316
|
+
billingApiAccessLikely: oauthAvailable || (target !== null && !compatibilityError),
|
|
423
317
|
remainingTotalsState: oauthAvailable
|
|
424
318
|
? "reported_by_copilot_internal_user"
|
|
425
|
-
:
|
|
426
|
-
|
|
319
|
+
: billingScope === "organization"
|
|
320
|
+
? "not_available_from_org_usage"
|
|
321
|
+
: billingScope === "enterprise"
|
|
322
|
+
? "not_available_from_enterprise_usage"
|
|
323
|
+
: billingScope === "none"
|
|
324
|
+
? "unavailable"
|
|
325
|
+
: billingModel === "legacy_premium_requests"
|
|
326
|
+
? "available"
|
|
327
|
+
: "value_only_without_denominator",
|
|
328
|
+
queryPeriod: target?.billingPeriod,
|
|
427
329
|
usernameFilter: pat.config?.username,
|
|
428
|
-
billingTargetError: resolved.error,
|
|
429
330
|
tokenCompatibilityError: compatibilityError ?? undefined,
|
|
430
331
|
billingModel,
|
|
431
|
-
budgetApi:
|
|
432
|
-
? "
|
|
433
|
-
:
|
|
434
|
-
? "
|
|
435
|
-
:
|
|
436
|
-
? "enterprise_budgets"
|
|
437
|
-
: "not_available",
|
|
332
|
+
budgetApi: billingScope === "organization"
|
|
333
|
+
? "organization_budgets"
|
|
334
|
+
: billingScope === "enterprise"
|
|
335
|
+
? "enterprise_budgets"
|
|
336
|
+
: "not_available",
|
|
438
337
|
oauthAccountingState: !auth
|
|
439
338
|
? "not_configured"
|
|
440
339
|
: oauthEnterpriseHost.error
|
|
@@ -442,116 +341,107 @@ export function getCopilotQuotaAuthDiagnostics(authData) {
|
|
|
442
341
|
: "available_via_copilot_internal_user",
|
|
443
342
|
};
|
|
444
343
|
}
|
|
445
|
-
function
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
"X-GitHub-Api-Version": GITHUB_API_VERSION,
|
|
450
|
-
"User-Agent": USER_AGENT,
|
|
451
|
-
};
|
|
452
|
-
}
|
|
453
|
-
function redactGitHubErrorText(text, secrets) {
|
|
454
|
-
let sanitized = sanitizeDisplayText(text);
|
|
455
|
-
for (const secret of secrets) {
|
|
456
|
-
if (!secret)
|
|
457
|
-
continue;
|
|
458
|
-
sanitized = sanitized.split(secret).join("[redacted]");
|
|
459
|
-
const sanitizedSecret = sanitizeDisplayText(secret);
|
|
460
|
-
if (sanitizedSecret) {
|
|
461
|
-
sanitized = sanitized.split(sanitizedSecret).join("[redacted]");
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
return sanitized;
|
|
344
|
+
function redactSecret(text, secret) {
|
|
345
|
+
const redacted = sanitizeDisplayText(text).replaceAll(secret, "[redacted]");
|
|
346
|
+
const sanitizedSecret = sanitizeDisplayText(secret);
|
|
347
|
+
return sanitizedSecret ? redacted.replaceAll(sanitizedSecret, "[redacted]") : redacted;
|
|
465
348
|
}
|
|
466
|
-
async function
|
|
349
|
+
async function readGitHubErrorMessage(response, token) {
|
|
467
350
|
const text = await response.text();
|
|
468
351
|
try {
|
|
469
352
|
const parsed = JSON.parse(text);
|
|
470
353
|
const message = typeof parsed.message === "string" ? parsed.message : null;
|
|
471
354
|
const documentationUrl = typeof parsed.documentation_url === "string" ? parsed.documentation_url : null;
|
|
472
|
-
if (message && documentationUrl)
|
|
473
|
-
return
|
|
474
|
-
}
|
|
355
|
+
if (message && documentationUrl)
|
|
356
|
+
return redactSecret(`${message} (${documentationUrl})`, token);
|
|
475
357
|
if (message)
|
|
476
|
-
return
|
|
358
|
+
return redactSecret(message, token);
|
|
477
359
|
}
|
|
478
360
|
catch {
|
|
479
361
|
// Fall through to a bounded plain-text snippet.
|
|
480
362
|
}
|
|
481
|
-
return sanitizeDisplaySnippet(
|
|
363
|
+
return sanitizeDisplaySnippet(redactSecret(text, token), 160);
|
|
482
364
|
}
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
365
|
+
/** GET a GitHub JSON endpoint; failures become errors that never contain `token`. */
|
|
366
|
+
async function fetchGitHubJson(params) {
|
|
367
|
+
const { apiName, token } = params;
|
|
368
|
+
return await fetchWithTimeout(params.url, {
|
|
369
|
+
request: { headers: params.headers },
|
|
370
|
+
timeoutMs: params.requestTimeoutMs,
|
|
487
371
|
consume: async (response) => {
|
|
488
372
|
if (!response.ok) {
|
|
489
|
-
const message = await
|
|
490
|
-
const rateLimit =
|
|
373
|
+
const message = await readGitHubErrorMessage(response, token);
|
|
374
|
+
const rateLimit = apiName === "GitHub API" &&
|
|
375
|
+
response.status === 403 &&
|
|
376
|
+
response.headers.get("x-ratelimit-remaining") === "0"
|
|
491
377
|
? " (GitHub API rate limit exhausted)"
|
|
492
378
|
: "";
|
|
493
|
-
throw new Error(
|
|
379
|
+
throw new Error(`${apiName} error ${response.status}: ${message}${rateLimit}`);
|
|
494
380
|
}
|
|
495
381
|
try {
|
|
496
382
|
return (await response.json());
|
|
497
383
|
}
|
|
498
384
|
catch {
|
|
499
|
-
throw new Error(
|
|
385
|
+
throw new Error(`${apiName} returned malformed JSON`);
|
|
500
386
|
}
|
|
501
387
|
},
|
|
502
388
|
});
|
|
503
389
|
}
|
|
390
|
+
async function fetchGitHubRestJson(url, token, requestTimeoutMs) {
|
|
391
|
+
return await fetchGitHubJson({
|
|
392
|
+
url,
|
|
393
|
+
apiName: "GitHub API",
|
|
394
|
+
headers: {
|
|
395
|
+
Accept: "application/vnd.github+json",
|
|
396
|
+
Authorization: `Bearer ${token}`,
|
|
397
|
+
"X-GitHub-Api-Version": "2026-03-10",
|
|
398
|
+
"User-Agent": "opencode-quota/copilot-billing",
|
|
399
|
+
},
|
|
400
|
+
token,
|
|
401
|
+
requestTimeoutMs,
|
|
402
|
+
});
|
|
403
|
+
}
|
|
504
404
|
async function resolveGitHubUsername(apiBaseUrl, token, requestTimeoutMs) {
|
|
505
|
-
const
|
|
506
|
-
const login =
|
|
507
|
-
if (!login)
|
|
405
|
+
const viewer = await fetchGitHubRestJson(`${apiBaseUrl}/user`, token, requestTimeoutMs);
|
|
406
|
+
const login = viewer.login?.trim();
|
|
407
|
+
if (!login)
|
|
508
408
|
throw new Error("GitHub /user response did not include a login");
|
|
509
|
-
}
|
|
510
409
|
return login;
|
|
511
410
|
}
|
|
512
|
-
function
|
|
513
|
-
const query = new URLSearchParams({
|
|
514
|
-
year: String(target.billingPeriod.year),
|
|
515
|
-
month: String(target.billingPeriod.month),
|
|
516
|
-
});
|
|
517
|
-
if (target.scope !== "user" && target.username)
|
|
518
|
-
query.set("user", target.username);
|
|
519
|
-
if (target.scope === "enterprise" && target.organization) {
|
|
520
|
-
query.set("organization", target.organization);
|
|
521
|
-
}
|
|
522
|
-
return query;
|
|
523
|
-
}
|
|
524
|
-
function getBillingUsageUrl(apiBaseUrl, target, billingModel) {
|
|
525
|
-
const report = billingModel === "legacy_premium_requests" ? "premium_request" : "ai_credit";
|
|
526
|
-
const query = buildBillingQuery(target);
|
|
411
|
+
function billingSettingsUrl(apiBaseUrl, target) {
|
|
527
412
|
if (target.scope === "enterprise") {
|
|
528
|
-
return `${apiBaseUrl}/enterprises/${encodeURIComponent(target.enterprise)}/settings/billing
|
|
413
|
+
return `${apiBaseUrl}/enterprises/${encodeURIComponent(target.enterprise)}/settings/billing`;
|
|
529
414
|
}
|
|
530
415
|
if (target.scope === "organization") {
|
|
531
|
-
return `${apiBaseUrl}/organizations/${encodeURIComponent(target.organization)}/settings/billing
|
|
416
|
+
return `${apiBaseUrl}/organizations/${encodeURIComponent(target.organization)}/settings/billing`;
|
|
532
417
|
}
|
|
533
|
-
return `${apiBaseUrl}/users/${encodeURIComponent(target.username)}/settings/billing
|
|
418
|
+
return `${apiBaseUrl}/users/${encodeURIComponent(target.username)}/settings/billing`;
|
|
534
419
|
}
|
|
535
420
|
async function fetchBillingUsage(params) {
|
|
421
|
+
const { apiBaseUrl, token, requestTimeoutMs } = params;
|
|
536
422
|
const target = params.target.scope === "user"
|
|
537
423
|
? {
|
|
538
424
|
...params.target,
|
|
539
425
|
username: params.target.username ??
|
|
540
|
-
(await resolveGitHubUsername(
|
|
426
|
+
(await resolveGitHubUsername(apiBaseUrl, token, requestTimeoutMs)),
|
|
541
427
|
}
|
|
542
428
|
: params.target;
|
|
429
|
+
const query = new URLSearchParams({
|
|
430
|
+
year: String(target.billingPeriod.year),
|
|
431
|
+
month: String(target.billingPeriod.month),
|
|
432
|
+
});
|
|
433
|
+
if (target.scope !== "user" && target.username)
|
|
434
|
+
query.set("user", target.username);
|
|
435
|
+
if (target.scope === "enterprise" && target.organization) {
|
|
436
|
+
query.set("organization", target.organization);
|
|
437
|
+
}
|
|
438
|
+
const report = params.billingModel === "legacy_premium_requests" ? "premium_request" : "ai_credit";
|
|
439
|
+
const url = `${billingSettingsUrl(apiBaseUrl, target)}/${report}/usage?${query}`;
|
|
543
440
|
return {
|
|
544
|
-
response: await fetchGitHubRestJson(
|
|
441
|
+
response: await fetchGitHubRestJson(url, token, requestTimeoutMs),
|
|
545
442
|
target,
|
|
546
443
|
};
|
|
547
444
|
}
|
|
548
|
-
function getResponsePeriod(response, fallback) {
|
|
549
|
-
const period = response.timePeriod ?? response.time_period;
|
|
550
|
-
return {
|
|
551
|
-
year: typeof period?.year === "number" ? period.year : fallback.year,
|
|
552
|
-
month: typeof period?.month === "number" ? period.month : fallback.month,
|
|
553
|
-
};
|
|
554
|
-
}
|
|
555
445
|
function getUsageItems(response) {
|
|
556
446
|
if (Array.isArray(response.usageItems))
|
|
557
447
|
return response.usageItems;
|
|
@@ -559,27 +449,28 @@ function getUsageItems(response) {
|
|
|
559
449
|
return response.usage_items;
|
|
560
450
|
throw new Error("GitHub billing response did not include a usageItems array");
|
|
561
451
|
}
|
|
562
|
-
function
|
|
563
|
-
return
|
|
564
|
-
}
|
|
565
|
-
function quantitiesDiffer(left, right) {
|
|
566
|
-
return (Math.abs(left - right) > Number.EPSILON * 16 * Math.max(1, Math.abs(left), Math.abs(right)));
|
|
567
|
-
}
|
|
568
|
-
function isAiCreditItem(item) {
|
|
569
|
-
const text = [item.product, item.sku, item.unitType, item.unit_type]
|
|
452
|
+
function usageItemText(item) {
|
|
453
|
+
return [item.product, item.sku, item.unitType, item.unit_type]
|
|
570
454
|
.filter((value) => typeof value === "string")
|
|
571
455
|
.join(" ")
|
|
572
456
|
.toLowerCase();
|
|
573
|
-
const identifiesCopilot = text.includes("copilot");
|
|
574
|
-
const identifiesAiCredits = text.includes("ai credit") || text.includes("ai-credit");
|
|
575
|
-
return identifiesCopilot && identifiesAiCredits;
|
|
576
457
|
}
|
|
577
|
-
function
|
|
578
|
-
const text =
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
return
|
|
458
|
+
function isAiCreditItem(item) {
|
|
459
|
+
const text = usageItemText(item);
|
|
460
|
+
return text.includes("copilot") && (text.includes("ai credit") || text.includes("ai-credit"));
|
|
461
|
+
}
|
|
462
|
+
function quantitiesDiffer(left, right) {
|
|
463
|
+
return (Math.abs(left - right) > Number.EPSILON * 16 * Math.max(1, Math.abs(left), Math.abs(right)));
|
|
464
|
+
}
|
|
465
|
+
function hasInconsistentQuantities(gross, discount, net) {
|
|
466
|
+
if (gross === undefined)
|
|
467
|
+
return false;
|
|
468
|
+
if (discount !== undefined && net !== undefined) {
|
|
469
|
+
return quantitiesDiffer(gross, discount + net);
|
|
470
|
+
}
|
|
471
|
+
// With one part missing, only a part larger than gross is a contradiction.
|
|
472
|
+
const part = discount ?? net;
|
|
473
|
+
return part !== undefined && part > gross && quantitiesDiffer(gross, part);
|
|
583
474
|
}
|
|
584
475
|
function parseAiCreditTotals(response) {
|
|
585
476
|
const items = getUsageItems(response);
|
|
@@ -587,16 +478,17 @@ function parseAiCreditTotals(response) {
|
|
|
587
478
|
if (matches.length === 0 && items.length > 0) {
|
|
588
479
|
throw new Error("GitHub billing response did not contain a Copilot AI Credit usage item");
|
|
589
480
|
}
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
481
|
+
const totals = {
|
|
482
|
+
used: 0,
|
|
483
|
+
includedUsed: 0,
|
|
484
|
+
billedUsed: 0,
|
|
485
|
+
billedAmountUsd: undefined,
|
|
486
|
+
authority: "provider_reported",
|
|
487
|
+
};
|
|
596
488
|
for (const item of matches) {
|
|
597
|
-
const gross =
|
|
598
|
-
const discount =
|
|
599
|
-
const net =
|
|
489
|
+
const gross = finiteNumber(item.grossQuantity ?? item.gross_quantity);
|
|
490
|
+
const discount = finiteNumber(item.discountQuantity ?? item.discount_quantity);
|
|
491
|
+
const net = finiteNumber(item.netQuantity ?? item.net_quantity);
|
|
600
492
|
const quantities = [gross, discount, net].filter((value) => value !== undefined);
|
|
601
493
|
if (quantities.length === 0) {
|
|
602
494
|
throw new Error("GitHub Copilot AI Credit usage item did not include quantity fields");
|
|
@@ -604,65 +496,36 @@ function parseAiCreditTotals(response) {
|
|
|
604
496
|
if (quantities.some((value) => value < 0)) {
|
|
605
497
|
throw new Error("GitHub Copilot AI Credit usage item contained a negative quantity");
|
|
606
498
|
}
|
|
607
|
-
if (gross
|
|
608
|
-
discount !== undefined &&
|
|
609
|
-
net !== undefined &&
|
|
610
|
-
quantitiesDiffer(gross, discount + net)) {
|
|
499
|
+
if (hasInconsistentQuantities(gross, discount, net)) {
|
|
611
500
|
throw new Error("GitHub Copilot AI Credit usage item contained inconsistent quantities");
|
|
612
501
|
}
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
else if (gross !== undefined && discount === undefined && net !== undefined) {
|
|
623
|
-
if (net > gross && quantitiesDiffer(gross, net)) {
|
|
624
|
-
throw new Error("GitHub Copilot AI Credit usage item contained inconsistent quantities");
|
|
625
|
-
}
|
|
626
|
-
normalizedIncluded = Math.max(0, gross - net);
|
|
627
|
-
}
|
|
628
|
-
else if (gross === undefined && discount !== undefined && net !== undefined) {
|
|
629
|
-
normalizedGross = discount + net;
|
|
630
|
-
}
|
|
631
|
-
normalizedIncluded ??= 0;
|
|
632
|
-
normalizedBilled ??= Math.max(0, (normalizedGross ?? 0) - normalizedIncluded);
|
|
633
|
-
normalizedGross ??= normalizedIncluded + normalizedBilled;
|
|
634
|
-
used += normalizedGross;
|
|
635
|
-
includedUsed += normalizedIncluded;
|
|
636
|
-
billedUsed += normalizedBilled;
|
|
637
|
-
if (gross === undefined || discount === undefined || net === undefined) {
|
|
638
|
-
authority = "locally_derived";
|
|
639
|
-
}
|
|
640
|
-
const netAmount = readFiniteNumber(item.netAmount ?? item.net_amount);
|
|
502
|
+
// gross = included (discount) + billed (net); fill in whichever part is missing.
|
|
503
|
+
const included = discount ?? (gross !== undefined && net !== undefined ? Math.max(0, gross - net) : 0);
|
|
504
|
+
const billed = net ?? Math.max(0, (gross ?? 0) - included);
|
|
505
|
+
totals.used += gross ?? included + billed;
|
|
506
|
+
totals.includedUsed += included;
|
|
507
|
+
totals.billedUsed += billed;
|
|
508
|
+
if (quantities.length < 3)
|
|
509
|
+
totals.authority = "locally_derived";
|
|
510
|
+
const netAmount = finiteNumber(item.netAmount ?? item.net_amount);
|
|
641
511
|
if (netAmount !== undefined) {
|
|
642
512
|
if (netAmount < 0) {
|
|
643
513
|
throw new Error("GitHub Copilot AI Credit usage item contained a negative net amount");
|
|
644
514
|
}
|
|
645
|
-
billedAmountUsd
|
|
646
|
-
hasBilledAmount = true;
|
|
515
|
+
totals.billedAmountUsd = (totals.billedAmountUsd ?? 0) + netAmount;
|
|
647
516
|
}
|
|
648
517
|
}
|
|
649
|
-
return
|
|
650
|
-
used,
|
|
651
|
-
includedUsed,
|
|
652
|
-
billedUsed,
|
|
653
|
-
billedAmountUsd: hasBilledAmount ? billedAmountUsd : undefined,
|
|
654
|
-
authority,
|
|
655
|
-
};
|
|
518
|
+
return totals;
|
|
656
519
|
}
|
|
657
520
|
function parseLegacyPremiumRequestUsage(response) {
|
|
658
521
|
const items = getUsageItems(response);
|
|
659
|
-
const matches = items.filter(
|
|
522
|
+
const matches = items.filter((item) => usageItemText(item).includes("premium request"));
|
|
660
523
|
if (matches.length === 0 && items.length > 0) {
|
|
661
524
|
throw new Error("GitHub billing response did not contain a premium-request usage item");
|
|
662
525
|
}
|
|
663
526
|
return matches.reduce((sum, item) => {
|
|
664
|
-
const gross =
|
|
665
|
-
const net =
|
|
527
|
+
const gross = finiteNumber(item.grossQuantity ?? item.gross_quantity);
|
|
528
|
+
const net = finiteNumber(item.netQuantity ?? item.net_quantity);
|
|
666
529
|
if (gross === undefined && net === undefined) {
|
|
667
530
|
throw new Error("GitHub premium-request usage item did not include quantity fields");
|
|
668
531
|
}
|
|
@@ -682,67 +545,36 @@ function budgetMatchesTarget(budget, target) {
|
|
|
682
545
|
const scope = budget.budget_scope;
|
|
683
546
|
const entity = budget.budget_entity_name?.toLowerCase();
|
|
684
547
|
const user = (budget.user ?? budget.budget_entity_name)?.toLowerCase();
|
|
685
|
-
if (target.
|
|
686
|
-
if (
|
|
548
|
+
if (target.username) {
|
|
549
|
+
if (scope === "user")
|
|
687
550
|
return !user || user === target.username.toLowerCase();
|
|
688
|
-
|
|
689
|
-
if (target.username && scope === "multi_user_customer")
|
|
551
|
+
if (scope === "multi_user_customer")
|
|
690
552
|
return true;
|
|
691
|
-
|
|
692
|
-
}
|
|
693
|
-
if (target.scope === "enterprise") {
|
|
694
|
-
if (target.username && scope === "user") {
|
|
695
|
-
return !user || user === target.username.toLowerCase();
|
|
696
|
-
}
|
|
697
|
-
if (target.username &&
|
|
698
|
-
(scope === "multi_user_customer" || scope === "multi_user_cost_center")) {
|
|
553
|
+
if (scope === "multi_user_cost_center" && target.scope === "enterprise")
|
|
699
554
|
return true;
|
|
700
|
-
}
|
|
701
|
-
if (target.organization && scope === "organization") {
|
|
702
|
-
return !entity || entity === target.organization.toLowerCase();
|
|
703
|
-
}
|
|
704
|
-
return scope === "enterprise";
|
|
705
555
|
}
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
case "user":
|
|
711
|
-
return 5;
|
|
712
|
-
case "multi_user_cost_center":
|
|
713
|
-
return 4;
|
|
714
|
-
case "multi_user_customer":
|
|
715
|
-
return 3;
|
|
716
|
-
case "organization":
|
|
717
|
-
return 2;
|
|
718
|
-
case "enterprise":
|
|
719
|
-
return 1;
|
|
720
|
-
default:
|
|
721
|
-
return 0;
|
|
556
|
+
if (target.scope === "organization")
|
|
557
|
+
return scope === "organization";
|
|
558
|
+
if (target.organization && scope === "organization") {
|
|
559
|
+
return !entity || entity === target.organization.toLowerCase();
|
|
722
560
|
}
|
|
723
|
-
|
|
724
|
-
function getBudgetsUrl(apiBaseUrl, target, page) {
|
|
725
|
-
const base = target.scope === "organization"
|
|
726
|
-
? `${apiBaseUrl}/organizations/${encodeURIComponent(target.organization)}/settings/billing/budgets`
|
|
727
|
-
: `${apiBaseUrl}/enterprises/${encodeURIComponent(target.enterprise)}/settings/billing/budgets`;
|
|
728
|
-
const query = new URLSearchParams({ page: String(page), per_page: "100" });
|
|
729
|
-
if (target.username)
|
|
730
|
-
query.set("user", target.username);
|
|
731
|
-
return `${base}?${query}`;
|
|
561
|
+
return scope === "enterprise";
|
|
732
562
|
}
|
|
733
563
|
async function fetchApplicableBudget(params) {
|
|
734
564
|
const budgets = [];
|
|
735
565
|
for (let page = 1; page <= 100; page += 1) {
|
|
736
|
-
const
|
|
566
|
+
const query = new URLSearchParams({ page: String(page), per_page: "100" });
|
|
567
|
+
if (params.target.username)
|
|
568
|
+
query.set("user", params.target.username);
|
|
569
|
+
const response = await fetchGitHubRestJson(`${billingSettingsUrl(params.apiBaseUrl, params.target)}/budgets?${query}`, params.token, params.requestTimeoutMs);
|
|
737
570
|
if (!Array.isArray(response.budgets)) {
|
|
738
571
|
throw new Error("GitHub budgets response did not include a budgets array");
|
|
739
572
|
}
|
|
740
573
|
budgets.push(...response.budgets);
|
|
741
574
|
if (!response.has_next_page)
|
|
742
575
|
break;
|
|
743
|
-
if (page === 100)
|
|
576
|
+
if (page === 100)
|
|
744
577
|
throw new Error("GitHub budgets response exceeded 100 pages");
|
|
745
|
-
}
|
|
746
578
|
}
|
|
747
579
|
const selected = budgets
|
|
748
580
|
.filter((budget) => isAiCreditBudget(budget) &&
|
|
@@ -750,7 +582,8 @@ async function fetchApplicableBudget(params) {
|
|
|
750
582
|
typeof budget.budget_amount === "number" &&
|
|
751
583
|
Number.isFinite(budget.budget_amount) &&
|
|
752
584
|
budget.budget_amount >= 0)
|
|
753
|
-
.sort((a, b) =>
|
|
585
|
+
.sort((a, b) => (BUDGET_SCOPE_SPECIFICITY[b.budget_scope ?? ""] ?? 0) -
|
|
586
|
+
(BUDGET_SCOPE_SPECIFICITY[a.budget_scope ?? ""] ?? 0))[0];
|
|
754
587
|
if (!selected || selected.budget_amount === undefined)
|
|
755
588
|
return undefined;
|
|
756
589
|
const percentRemaining = params.spentUsd === undefined
|
|
@@ -764,67 +597,55 @@ async function fetchApplicableBudget(params) {
|
|
|
764
597
|
authority: percentRemaining === undefined ? "provider_reported" : "locally_derived",
|
|
765
598
|
};
|
|
766
599
|
}
|
|
767
|
-
function makeBudgetWarning(error) {
|
|
768
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
769
|
-
return `AI Credit usage loaded, but the budget report failed: ${message}`;
|
|
770
|
-
}
|
|
771
600
|
async function toAiCreditResult(params) {
|
|
601
|
+
const { target } = params;
|
|
772
602
|
const totals = parseAiCreditTotals(params.response);
|
|
773
|
-
const
|
|
603
|
+
const responsePeriod = params.response.timePeriod ?? params.response.time_period;
|
|
604
|
+
const period = {
|
|
605
|
+
year: typeof responsePeriod?.year === "number" ? responsePeriod.year : target.billingPeriod.year,
|
|
606
|
+
month: typeof responsePeriod?.month === "number" ? responsePeriod.month : target.billingPeriod.month,
|
|
607
|
+
};
|
|
608
|
+
if (target.scope === "user") {
|
|
609
|
+
return { success: true, mode: "user_quota", unit: "ai_credits", period, ...totals };
|
|
610
|
+
}
|
|
611
|
+
// The budget is optional context: its failure becomes a warning, not a failed usage read.
|
|
774
612
|
let budget;
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
});
|
|
785
|
-
}
|
|
786
|
-
catch (error) {
|
|
787
|
-
warnings.push(makeBudgetWarning(error));
|
|
788
|
-
}
|
|
613
|
+
let warnings;
|
|
614
|
+
try {
|
|
615
|
+
budget = await fetchApplicableBudget({
|
|
616
|
+
apiBaseUrl: params.apiBaseUrl,
|
|
617
|
+
token: params.token,
|
|
618
|
+
target,
|
|
619
|
+
spentUsd: totals.billedAmountUsd,
|
|
620
|
+
requestTimeoutMs: params.requestTimeoutMs,
|
|
621
|
+
});
|
|
789
622
|
}
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
success: true,
|
|
793
|
-
mode: "organization_usage",
|
|
794
|
-
organization: params.target.organization,
|
|
795
|
-
username: params.target.username,
|
|
796
|
-
period,
|
|
797
|
-
unit: "ai_credits",
|
|
798
|
-
...totals,
|
|
799
|
-
budget,
|
|
800
|
-
warnings: warnings.length ? warnings : undefined,
|
|
801
|
-
};
|
|
623
|
+
catch (error) {
|
|
624
|
+
warnings = [`AI Credit usage loaded, but the budget report failed: ${errorText(error)}`];
|
|
802
625
|
}
|
|
803
|
-
|
|
626
|
+
const usage = { period, unit: "ai_credits", ...totals, budget, warnings };
|
|
627
|
+
if (target.scope === "organization") {
|
|
804
628
|
return {
|
|
805
629
|
success: true,
|
|
806
|
-
mode: "
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
period,
|
|
811
|
-
unit: "ai_credits",
|
|
812
|
-
...totals,
|
|
813
|
-
budget,
|
|
814
|
-
warnings: warnings.length ? warnings : undefined,
|
|
630
|
+
mode: "organization_usage",
|
|
631
|
+
organization: target.organization,
|
|
632
|
+
username: target.username,
|
|
633
|
+
...usage,
|
|
815
634
|
};
|
|
816
635
|
}
|
|
817
636
|
return {
|
|
818
637
|
success: true,
|
|
819
|
-
mode: "
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
638
|
+
mode: "enterprise_usage",
|
|
639
|
+
enterprise: target.enterprise,
|
|
640
|
+
organization: target.organization,
|
|
641
|
+
username: target.username,
|
|
642
|
+
...usage,
|
|
823
643
|
};
|
|
824
644
|
}
|
|
825
645
|
function toLegacyResult(response, config) {
|
|
826
646
|
const used = parseLegacyPremiumRequestUsage(response);
|
|
827
647
|
const total = LEGACY_PREMIUM_REQUEST_TOTALS[config.tier];
|
|
648
|
+
const now = new Date();
|
|
828
649
|
return {
|
|
829
650
|
success: true,
|
|
830
651
|
mode: "user_quota",
|
|
@@ -834,28 +655,10 @@ function toLegacyResult(response, config) {
|
|
|
834
655
|
total,
|
|
835
656
|
percentRemaining: total ? computePercentRemainingFromUsed(used, total) : undefined,
|
|
836
657
|
plan: config.tier,
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
}
|
|
840
|
-
function toQuotaError(message) {
|
|
841
|
-
return { success: false, error: message };
|
|
842
|
-
}
|
|
843
|
-
function buildCopilotInternalHeaders(token) {
|
|
844
|
-
return {
|
|
845
|
-
Accept: "application/json",
|
|
846
|
-
Authorization: `token ${token}`,
|
|
847
|
-
"Editor-Version": "vscode/1.96.2",
|
|
848
|
-
"Editor-Plugin-Version": "copilot-chat/0.26.7",
|
|
849
|
-
"User-Agent": "GitHubCopilotChat/0.26.7",
|
|
850
|
-
"X-GitHub-Api-Version": "2025-04-01",
|
|
658
|
+
// Approximate: the start of the next UTC month.
|
|
659
|
+
resetTimeIso: new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth() + 1, 1)).toISOString(),
|
|
851
660
|
};
|
|
852
661
|
}
|
|
853
|
-
function normalizeCopilotPlan(value) {
|
|
854
|
-
if (typeof value !== "string")
|
|
855
|
-
return undefined;
|
|
856
|
-
const plan = sanitizeDisplayText(value).replace(/\s+/gu, " ").trim().slice(0, 64);
|
|
857
|
-
return plan || undefined;
|
|
858
|
-
}
|
|
859
662
|
function normalizeResetTime(value) {
|
|
860
663
|
if (typeof value !== "string" || !value.trim())
|
|
861
664
|
return undefined;
|
|
@@ -863,32 +666,11 @@ function normalizeResetTime(value) {
|
|
|
863
666
|
return Number.isFinite(timestamp) ? new Date(timestamp).toISOString() : undefined;
|
|
864
667
|
}
|
|
865
668
|
function parseCopilotInternalUser(response) {
|
|
866
|
-
const plan =
|
|
669
|
+
const plan = typeof response.copilot_plan === "string"
|
|
670
|
+
? sanitizeSingleLineDisplaySnippet(response.copilot_plan, 64) || undefined
|
|
671
|
+
: undefined;
|
|
867
672
|
const resetTimeIso = normalizeResetTime(response.quota_reset_date_utc ?? response.quota_reset_date);
|
|
868
673
|
const snapshot = response.quota_snapshots?.premium_interactions;
|
|
869
|
-
const entitlement = readFiniteNumber(snapshot?.entitlement);
|
|
870
|
-
const remaining = readFiniteNumber(snapshot?.quota_remaining ?? snapshot?.remaining);
|
|
871
|
-
const reportedPercentRemaining = readFiniteNumber(snapshot?.percent_remaining);
|
|
872
|
-
const hasPercentRemaining = snapshot?.percent_remaining !== undefined;
|
|
873
|
-
const hasReportedPercent = reportedPercentRemaining !== undefined &&
|
|
874
|
-
reportedPercentRemaining >= 0 &&
|
|
875
|
-
reportedPercentRemaining <= 100;
|
|
876
|
-
const tokenBasedBilling = response.token_based_billing === true;
|
|
877
|
-
const isPlaceholder = tokenBasedBilling &&
|
|
878
|
-
(!snapshot ||
|
|
879
|
-
(snapshot.unlimited !== true &&
|
|
880
|
-
entitlement === 0 &&
|
|
881
|
-
remaining === 0 &&
|
|
882
|
-
(!hasReportedPercent || reportedPercentRemaining === 0)));
|
|
883
|
-
if (isPlaceholder) {
|
|
884
|
-
return {
|
|
885
|
-
success: true,
|
|
886
|
-
mode: "user_plan",
|
|
887
|
-
authority: "provider_reported",
|
|
888
|
-
plan,
|
|
889
|
-
resetTimeIso,
|
|
890
|
-
};
|
|
891
|
-
}
|
|
892
674
|
if (snapshot?.unlimited === true) {
|
|
893
675
|
return {
|
|
894
676
|
success: true,
|
|
@@ -901,82 +683,90 @@ function parseCopilotInternalUser(response) {
|
|
|
901
683
|
resetTimeIso,
|
|
902
684
|
};
|
|
903
685
|
}
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
:
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
entitlement
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
}
|
|
686
|
+
const entitlement = finiteNumber(snapshot?.entitlement);
|
|
687
|
+
const remaining = finiteNumber(snapshot?.quota_remaining ?? snapshot?.remaining);
|
|
688
|
+
const reportedPercent = finiteNumber(snapshot?.percent_remaining);
|
|
689
|
+
const validReportedPercent = reportedPercent !== undefined && reportedPercent >= 0 && reportedPercent <= 100
|
|
690
|
+
? reportedPercent
|
|
691
|
+
: undefined;
|
|
692
|
+
const tokenBasedBilling = response.token_based_billing === true;
|
|
693
|
+
if (entitlement !== undefined && entitlement > 0 && remaining !== undefined) {
|
|
694
|
+
if (remaining >= 0 && remaining <= entitlement) {
|
|
695
|
+
return {
|
|
696
|
+
success: true,
|
|
697
|
+
mode: "user_quota",
|
|
698
|
+
unit: "premium_interactions",
|
|
699
|
+
used: entitlement - remaining,
|
|
700
|
+
authority: "locally_derived",
|
|
701
|
+
total: entitlement,
|
|
702
|
+
// An invalid reported percentage is dropped, not replaced by a derived one.
|
|
703
|
+
percentRemaining: validReportedPercent ??
|
|
704
|
+
(snapshot?.percent_remaining === undefined
|
|
705
|
+
? Math.min(100, Math.max(0, (remaining / entitlement) * 100))
|
|
706
|
+
: undefined),
|
|
707
|
+
plan,
|
|
708
|
+
resetTimeIso,
|
|
709
|
+
};
|
|
710
|
+
}
|
|
711
|
+
// Token-based billing reports a negative remaining once usage goes over the
|
|
712
|
+
// entitlement, alongside a provider-reported clamp in [0, 100].
|
|
713
|
+
if (tokenBasedBilling && remaining < 0 && validReportedPercent !== undefined) {
|
|
714
|
+
const creditsUsed = finiteNumber(snapshot?.credits_used);
|
|
715
|
+
const hasCreditsUsed = creditsUsed !== undefined && creditsUsed >= 0;
|
|
716
|
+
return {
|
|
717
|
+
success: true,
|
|
718
|
+
mode: "user_quota",
|
|
719
|
+
unit: hasCreditsUsed ? "ai_credits" : "premium_interactions",
|
|
720
|
+
used: hasCreditsUsed ? creditsUsed : entitlement - remaining,
|
|
721
|
+
authority: hasCreditsUsed ? "provider_reported" : "locally_derived",
|
|
722
|
+
total: entitlement,
|
|
723
|
+
percentRemaining: validReportedPercent,
|
|
724
|
+
plan,
|
|
725
|
+
resetTimeIso,
|
|
726
|
+
};
|
|
727
|
+
}
|
|
946
728
|
}
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
mode: "user_plan",
|
|
951
|
-
authority: "provider_reported",
|
|
952
|
-
plan,
|
|
953
|
-
resetTimeIso,
|
|
954
|
-
};
|
|
729
|
+
// Token-based billing without usable numbers (e.g. an all-zero placeholder) is plan-only.
|
|
730
|
+
if (tokenBasedBilling) {
|
|
731
|
+
return { success: true, mode: "user_plan", authority: "provider_reported", plan, resetTimeIso };
|
|
955
732
|
}
|
|
956
733
|
throw new Error("GitHub Copilot user response did not include a usable quota snapshot");
|
|
957
734
|
}
|
|
958
|
-
async function
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
735
|
+
async function queryCopilotOAuthQuota(authData, requestTimeoutMs) {
|
|
736
|
+
const { auth } = selectCopilotAuth(authData ?? (await readAuthFile({ integrationIds: COPILOT_AUTH_KEYS })));
|
|
737
|
+
if (auth?.resolveError !== undefined) {
|
|
738
|
+
return toQuotaError(`Copilot sign-in could not be read: ${auth.resolveError}. Run \`opencode auth login github-copilot\`.`);
|
|
739
|
+
}
|
|
740
|
+
const token = getCopilotOAuthToken(auth);
|
|
741
|
+
if (!auth || !token)
|
|
742
|
+
return null;
|
|
743
|
+
const enterpriseHost = validateEnterpriseHost(auth.enterpriseUrl);
|
|
744
|
+
if (enterpriseHost.error) {
|
|
745
|
+
return toQuotaError(`Invalid OpenCode Copilot enterpriseUrl: ${enterpriseHost.error}`);
|
|
746
|
+
}
|
|
747
|
+
try {
|
|
748
|
+
const response = await fetchGitHubJson({
|
|
749
|
+
url: `${getApiBaseUrl(enterpriseHost.host)}/copilot_internal/user`,
|
|
750
|
+
apiName: "GitHub Copilot API",
|
|
751
|
+
headers: {
|
|
752
|
+
Accept: "application/json",
|
|
753
|
+
Authorization: `token ${token}`,
|
|
754
|
+
"Editor-Version": "vscode/1.96.2",
|
|
755
|
+
"Editor-Plugin-Version": "copilot-chat/0.26.7",
|
|
756
|
+
"User-Agent": "GitHubCopilotChat/0.26.7",
|
|
757
|
+
"X-GitHub-Api-Version": "2025-04-01",
|
|
758
|
+
},
|
|
759
|
+
token,
|
|
760
|
+
requestTimeoutMs,
|
|
761
|
+
});
|
|
762
|
+
if (!response || typeof response !== "object") {
|
|
763
|
+
throw new Error("GitHub Copilot API returned malformed JSON");
|
|
764
|
+
}
|
|
765
|
+
return parseCopilotInternalUser(response);
|
|
766
|
+
}
|
|
767
|
+
catch (error) {
|
|
768
|
+
return toQuotaError(errorText(error));
|
|
769
|
+
}
|
|
980
770
|
}
|
|
981
771
|
/**
|
|
982
772
|
* Query GitHub Copilot accounting.
|
|
@@ -987,71 +777,48 @@ async function fetchCopilotInternalUser(params) {
|
|
|
987
777
|
export async function queryCopilotQuota(options = {}) {
|
|
988
778
|
const pat = readQuotaConfigWithMeta();
|
|
989
779
|
if (pat.state === "invalid") {
|
|
990
|
-
return toQuotaError(`Invalid copilot-quota-token.json: ${pat.error
|
|
991
|
-
}
|
|
992
|
-
if (pat.state === "absent" || !pat.config) {
|
|
993
|
-
const { auth } = selectCopilotAuth(options.authData ?? (await readAuthFile({ integrationIds: COPILOT_AUTH_KEYS })));
|
|
994
|
-
if (auth?.resolveError !== undefined) {
|
|
995
|
-
return toQuotaError(`Copilot sign-in could not be read: ${auth.resolveError}. Run \`opencode auth login github-copilot\`.`);
|
|
996
|
-
}
|
|
997
|
-
const token = getCopilotOAuthToken(auth);
|
|
998
|
-
if (!auth || !token)
|
|
999
|
-
return null;
|
|
1000
|
-
const enterpriseHost = validateEnterpriseHost(auth.enterpriseUrl);
|
|
1001
|
-
if (enterpriseHost.error) {
|
|
1002
|
-
return toQuotaError(`Invalid OpenCode Copilot enterpriseUrl: ${enterpriseHost.error}`);
|
|
1003
|
-
}
|
|
1004
|
-
try {
|
|
1005
|
-
return await fetchCopilotInternalUser({
|
|
1006
|
-
apiBaseUrl: getApiBaseUrl(enterpriseHost.host),
|
|
1007
|
-
token,
|
|
1008
|
-
requestTimeoutMs: options.requestTimeoutMs,
|
|
1009
|
-
});
|
|
1010
|
-
}
|
|
1011
|
-
catch (error) {
|
|
1012
|
-
return toQuotaError(error instanceof Error ? error.message : String(error));
|
|
1013
|
-
}
|
|
1014
|
-
}
|
|
1015
|
-
const resolved = resolvePatBillingTarget(pat.config);
|
|
1016
|
-
if (!resolved.target) {
|
|
1017
|
-
return toQuotaError(resolved.error ?? "Unable to resolve Copilot billing scope.");
|
|
780
|
+
return toQuotaError(`Invalid copilot-quota-token.json: ${pat.error} (${pat.selectedPath})`);
|
|
1018
781
|
}
|
|
1019
|
-
|
|
782
|
+
if (!pat.config)
|
|
783
|
+
return await queryCopilotOAuthQuota(options.authData, options.requestTimeoutMs);
|
|
784
|
+
const config = pat.config;
|
|
785
|
+
const billingTarget = resolvePatBillingTarget(config);
|
|
786
|
+
const compatibilityError = validatePatTargetCompatibility(billingTarget, pat.tokenKind);
|
|
1020
787
|
if (compatibilityError)
|
|
1021
788
|
return toQuotaError(compatibilityError);
|
|
1022
789
|
try {
|
|
1023
|
-
const billingModel =
|
|
1024
|
-
const apiBaseUrl = getApiBaseUrl(
|
|
790
|
+
const billingModel = config.billingModel ?? "ai_credits";
|
|
791
|
+
const apiBaseUrl = getApiBaseUrl(config.enterpriseUrl);
|
|
1025
792
|
const { response, target } = await fetchBillingUsage({
|
|
1026
793
|
apiBaseUrl,
|
|
1027
|
-
token:
|
|
1028
|
-
target:
|
|
794
|
+
token: config.token,
|
|
795
|
+
target: billingTarget,
|
|
1029
796
|
billingModel,
|
|
1030
797
|
requestTimeoutMs: options.requestTimeoutMs,
|
|
1031
798
|
});
|
|
1032
799
|
return billingModel === "legacy_premium_requests"
|
|
1033
|
-
? toLegacyResult(response,
|
|
800
|
+
? toLegacyResult(response, config)
|
|
1034
801
|
: await toAiCreditResult({
|
|
1035
802
|
apiBaseUrl,
|
|
1036
803
|
response,
|
|
1037
804
|
target,
|
|
1038
|
-
token:
|
|
805
|
+
token: config.token,
|
|
1039
806
|
requestTimeoutMs: options.requestTimeoutMs,
|
|
1040
807
|
});
|
|
1041
808
|
}
|
|
1042
809
|
catch (error) {
|
|
1043
|
-
return toQuotaError(
|
|
810
|
+
return toQuotaError(errorText(error));
|
|
1044
811
|
}
|
|
1045
812
|
}
|
|
1046
813
|
export async function resolveCopilotAuthIdentity() {
|
|
1047
814
|
const pat = readQuotaConfigWithMeta();
|
|
1048
815
|
if (pat.state === "invalid")
|
|
1049
816
|
return null;
|
|
1050
|
-
if (pat.
|
|
1051
|
-
const
|
|
1052
|
-
if (
|
|
817
|
+
if (pat.config) {
|
|
818
|
+
const target = resolvePatBillingTarget(pat.config);
|
|
819
|
+
if (validatePatTargetCompatibility(target, pat.tokenKind))
|
|
1053
820
|
return null;
|
|
1054
|
-
}
|
|
821
|
+
const { year, month } = target.billingPeriod;
|
|
1055
822
|
return deriveResolvedAuthIdentity({
|
|
1056
823
|
providerId: "copilot:pat",
|
|
1057
824
|
principal: { kind: "credential", value: pat.config.token },
|
|
@@ -1059,11 +826,11 @@ export async function resolveCopilotAuthIdentity() {
|
|
|
1059
826
|
`api=${getApiBaseUrl(pat.config.enterpriseUrl)}`,
|
|
1060
827
|
`billingModel=${pat.config.billingModel ?? "ai_credits"}`,
|
|
1061
828
|
`tier=${pat.config.tier}`,
|
|
1062
|
-
`scope=${
|
|
1063
|
-
`username=${
|
|
1064
|
-
`organization=${"organization" in
|
|
1065
|
-
`enterprise=${"enterprise" in
|
|
1066
|
-
`period=${
|
|
829
|
+
`scope=${target.scope}`,
|
|
830
|
+
`username=${target.username ?? ""}`,
|
|
831
|
+
`organization=${"organization" in target ? target.organization : ""}`,
|
|
832
|
+
`enterprise=${"enterprise" in target ? target.enterprise : ""}`,
|
|
833
|
+
`period=${year}-${String(month).padStart(2, "0")}`,
|
|
1067
834
|
],
|
|
1068
835
|
});
|
|
1069
836
|
}
|
|
@@ -1084,28 +851,4 @@ export async function hasCopilotQuotaRuntimeAvailable() {
|
|
|
1084
851
|
const diagnostics = getCopilotQuotaAuthDiagnostics(await readAuthFile({ integrationIds: COPILOT_AUTH_KEYS }));
|
|
1085
852
|
return diagnostics.billingApiAccessLikely;
|
|
1086
853
|
}
|
|
1087
|
-
export function formatCopilotQuota(result) {
|
|
1088
|
-
if (!result || !result.success)
|
|
1089
|
-
return null;
|
|
1090
|
-
if (result.mode === "user_plan") {
|
|
1091
|
-
return result.plan ? `Copilot Plan ${result.plan}` : "Copilot Plan available";
|
|
1092
|
-
}
|
|
1093
|
-
const unit = result.unit === "ai_credits"
|
|
1094
|
-
? "AI Credits"
|
|
1095
|
-
: result.unit === "premium_interactions"
|
|
1096
|
-
? "Premium Interactions"
|
|
1097
|
-
: "Premium Requests";
|
|
1098
|
-
if (result.mode === "organization_usage") {
|
|
1099
|
-
return `Copilot Org (${result.organization}) ${result.used} ${unit} | ${formatBillingPeriod(result.period)}`;
|
|
1100
|
-
}
|
|
1101
|
-
if (result.mode === "enterprise_usage") {
|
|
1102
|
-
return `Copilot Enterprise (${result.enterprise}) ${result.used} ${unit} | ${formatBillingPeriod(result.period)}`;
|
|
1103
|
-
}
|
|
1104
|
-
if (result.unlimited)
|
|
1105
|
-
return `Copilot ${unit} Unlimited`;
|
|
1106
|
-
if (result.total !== undefined) {
|
|
1107
|
-
return `Copilot ${unit} ${result.used}/${result.total}`;
|
|
1108
|
-
}
|
|
1109
|
-
return `Copilot ${unit} ${result.used} used`;
|
|
1110
|
-
}
|
|
1111
854
|
//# sourceMappingURL=copilot.js.map
|