cartethyia 2.7.4 → 2.7.6
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/.env.example +5 -0
- package/README.md +18 -6
- package/dist/dashboard/assets/BannedPage-B1N603cM.js +1 -0
- package/dist/dashboard/assets/BannedPage-B1N603cM.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-9q4_4NoK.js +3 -0
- package/dist/dashboard/assets/CliToolDetailPage-9q4_4NoK.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-DwshGtK-.js +1 -0
- package/dist/dashboard/assets/CliToolsPage-DwshGtK-.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-D762MIkp.js +1 -0
- package/dist/dashboard/assets/CombosPage-D762MIkp.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-KRYjHZsX.js +1 -0
- package/dist/dashboard/assets/ConsoleLogPage-KRYjHZsX.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-DXsxk5uM.js +1 -0
- package/dist/dashboard/assets/CustomizationPage-DXsxk5uM.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-BEjAjD4F.js +1 -0
- package/dist/dashboard/assets/HealthEventsModal-BEjAjD4F.js.gz +0 -0
- package/dist/dashboard/assets/InjectSystemPromptPanel-d5v3K_Yc.js +899 -0
- package/dist/dashboard/assets/InjectSystemPromptPanel-d5v3K_Yc.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-Ccv4tjNS.js +1 -0
- package/dist/dashboard/assets/LoginPage-Ccv4tjNS.js.gz +0 -0
- package/dist/dashboard/assets/{ModelPicker-BMS2t8BA.js → ModelPicker-C6WlpU5E.js} +1 -1
- package/dist/dashboard/assets/ModelPicker-C6WlpU5E.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-DN9fYNWA.js +1 -0
- package/dist/dashboard/assets/OverviewPage-DN9fYNWA.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-BY0ieUgz.js +5 -0
- package/dist/dashboard/assets/ProviderDetailPage-BY0ieUgz.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-jjqXJDA3.js +1 -0
- package/dist/dashboard/assets/ProviderIcon-jjqXJDA3.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-DNsbfea2.js +1 -0
- package/dist/dashboard/assets/ProvidersPage-DNsbfea2.js.gz +0 -0
- package/dist/dashboard/assets/{ProxyPage-lsrx7NYg.js → ProxyPage-DxjvUzSD.js} +1 -1
- package/dist/dashboard/assets/ProxyPage-DxjvUzSD.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-OaHScjpX.js +1 -0
- package/dist/dashboard/assets/QuotaPage-OaHScjpX.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-BW4AKj2H.js +1 -0
- package/dist/dashboard/assets/SettingsPage-BW4AKj2H.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-44BZrxFC.js +1 -0
- package/dist/dashboard/assets/SetupPage-44BZrxFC.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-Cq5tW7hW.js → StudioPage-DRPavCdU.js} +4 -4
- package/dist/dashboard/assets/StudioPage-DRPavCdU.js.gz +0 -0
- package/dist/dashboard/assets/ToolIcon-CfeLA-Z_.js +1 -0
- package/dist/dashboard/assets/ToolIcon-CfeLA-Z_.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-B7M7LvMM.js +56 -0
- package/dist/dashboard/assets/UsagePage-B7M7LvMM.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-KvX83mRe.js +1 -0
- package/dist/dashboard/assets/api-keys-KvX83mRe.js.gz +0 -0
- package/dist/dashboard/assets/app-DRnQLhsc.js +9 -0
- package/dist/dashboard/assets/app-DRnQLhsc.js.gz +0 -0
- package/dist/dashboard/assets/app-mweRcsKp.css +2 -0
- package/dist/dashboard/assets/app-mweRcsKp.css.gz +0 -0
- package/dist/dashboard/assets/assert-CidQVoNo.js +1 -0
- package/dist/dashboard/assets/assert-CidQVoNo.js.gz +0 -0
- package/dist/dashboard/assets/{badge-D9CS6dZk.js → badge-BaX2GTrn.js} +1 -1
- package/dist/dashboard/assets/badge-BaX2GTrn.js.gz +0 -0
- package/dist/dashboard/assets/{banner-DympBNq4.js → banner-DFH4Bkte.js} +1 -1
- package/dist/dashboard/assets/banner-DFH4Bkte.js.gz +0 -0
- package/dist/dashboard/assets/brand/login_register.jpg +0 -0
- package/dist/dashboard/assets/brand/login_register_initial.jpg +0 -0
- package/dist/dashboard/assets/brand/og_bansos.jpg +0 -0
- package/dist/dashboard/assets/brand/og_image.jpg +0 -0
- package/dist/dashboard/assets/{card-ibRWU_uT.js → card-CxDw0lK6.js} +1 -1
- package/dist/dashboard/assets/card-CxDw0lK6.js.gz +0 -0
- package/dist/dashboard/assets/chevron-left-Bf6CaTSJ.js +1 -0
- package/dist/dashboard/assets/chevron-left-Bf6CaTSJ.js.gz +0 -0
- package/dist/dashboard/assets/circle-question-mark-Cz4un8sH.js +1 -0
- package/dist/dashboard/assets/circle-question-mark-Cz4un8sH.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-Bwv5-zGZ.js +1 -0
- package/dist/dashboard/assets/cli-tools-Bwv5-zGZ.js.gz +0 -0
- package/dist/dashboard/assets/{clipboard-button-DJm-9pz7.js → clipboard-button-DfS1ntiq.js} +1 -1
- package/dist/dashboard/assets/clipboard-button-DfS1ntiq.js.gz +0 -0
- package/dist/dashboard/assets/dialog-BK-NRr5u.js +1 -0
- package/dist/dashboard/assets/dialog-BK-NRr5u.js.gz +0 -0
- package/dist/dashboard/assets/{drawer-CqGYeOrd.js → drawer-QqPul6sC.js} +1 -1
- package/dist/dashboard/assets/drawer-QqPul6sC.js.gz +0 -0
- package/dist/dashboard/assets/entry-BMlYJVFj.js +1 -0
- package/dist/dashboard/assets/entry-BMlYJVFj.js.gz +0 -0
- package/dist/dashboard/assets/entry-D6WvojeD.js +4 -0
- package/dist/dashboard/assets/entry-D6WvojeD.js.gz +0 -0
- package/dist/dashboard/assets/entry-Dmx7gBiN.js +1 -0
- package/dist/dashboard/assets/entry-Dmx7gBiN.js.gz +0 -0
- package/dist/dashboard/assets/{formatters-D-MTJxMr.js → formatters-u7JPB5BX.js} +1 -1
- package/dist/dashboard/assets/formatters-u7JPB5BX.js.gz +0 -0
- package/dist/dashboard/assets/house-h9AZlJuT.js +1 -0
- package/dist/dashboard/assets/house-h9AZlJuT.js.gz +0 -0
- package/dist/dashboard/assets/{index-DBNPOfhV.js → index-Ko4dLRnV.js} +2 -2
- package/dist/dashboard/assets/index-Ko4dLRnV.js.gz +0 -0
- package/dist/dashboard/assets/{inline-BT0zntEr.js → inline-CHJQwxLa.js} +1 -1
- package/dist/dashboard/assets/inline-CHJQwxLa.js.gz +0 -0
- package/dist/dashboard/assets/{input-Da2ZViWg.js → input-BlmlizVV.js} +1 -1
- package/dist/dashboard/assets/input-BlmlizVV.js.gz +0 -0
- package/dist/dashboard/assets/{layout-BXNeLw6M.js → layout-Bz93I-yE.js} +1 -1
- package/dist/dashboard/assets/layout-Bz93I-yE.js.gz +0 -0
- package/dist/dashboard/assets/{list-row-BycOrrIV.js → list-row-Ml16KCfu.js} +1 -1
- package/dist/dashboard/assets/list-row-Ml16KCfu.js.gz +0 -0
- package/dist/dashboard/assets/live-7_QpnYGu.js +1 -0
- package/dist/dashboard/assets/live-7_QpnYGu.js.gz +0 -0
- package/dist/dashboard/assets/{mutation-Rr0TIvcj.js → mutation-GU7XdEjz.js} +1 -1
- package/dist/dashboard/assets/mutation-GU7XdEjz.js.gz +0 -0
- package/dist/dashboard/assets/network-CuMDaTzT.js +1 -0
- package/dist/dashboard/assets/network-CuMDaTzT.js.gz +0 -0
- package/dist/dashboard/assets/numbers-CoqpaDMx.js +2 -0
- package/dist/dashboard/assets/numbers-CoqpaDMx.js.gz +0 -0
- package/dist/dashboard/assets/pagination-B5aOoweZ.js +1 -0
- package/dist/dashboard/assets/pagination-B5aOoweZ.js.gz +0 -0
- package/dist/dashboard/assets/providers/zai.webp +0 -0
- package/dist/dashboard/assets/providers/zcode.webp +0 -0
- package/dist/dashboard/assets/{query-keys-BloKtaXJ.js → query-keys-YSoQOUh6.js} +1 -1
- package/dist/dashboard/assets/{query-keys-BloKtaXJ.js.gz → query-keys-YSoQOUh6.js.gz} +0 -0
- package/dist/dashboard/assets/quota-DJ1QlAOq.js +1 -0
- package/dist/dashboard/assets/quota-DJ1QlAOq.js.gz +0 -0
- package/dist/dashboard/assets/routing-fnCsxgNO.js +1 -0
- package/dist/dashboard/assets/routing-fnCsxgNO.js.gz +0 -0
- package/dist/dashboard/assets/{select-BIYIBkMU.js → select-BHqpd-bg.js} +2 -2
- package/dist/dashboard/assets/select-BHqpd-bg.js.gz +0 -0
- package/dist/dashboard/assets/settings-BxslsErn.js +1 -0
- package/dist/dashboard/assets/settings-BxslsErn.js.gz +0 -0
- package/dist/dashboard/assets/shield-BMQNxORh.js +1 -0
- package/dist/dashboard/assets/shield-BMQNxORh.js.gz +0 -0
- package/dist/dashboard/assets/{stack-DfgnFDWY.js → stack-j7cXjgYX.js} +1 -1
- package/dist/dashboard/assets/stack-j7cXjgYX.js.gz +0 -0
- package/dist/dashboard/assets/{state-CkfdYWel.js → state-DHmEnLa2.js} +1 -1
- package/dist/dashboard/assets/state-DHmEnLa2.js.gz +0 -0
- package/dist/dashboard/assets/{switch-SYm237HG.js → switch-DmZcyTIC.js} +1 -1
- package/dist/dashboard/assets/switch-DmZcyTIC.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-BQqCRNIi.js → useMutation-CbCP4w1o.js} +1 -1
- package/dist/dashboard/assets/useMutation-CbCP4w1o.js.gz +0 -0
- package/dist/dashboard/console-backgrounds.jpg +0 -0
- package/dist/dashboard/console-backgrounds_initial.jpg +0 -0
- package/dist/dashboard/index.html +2 -2
- package/dist/dashboard/index.html.gz +0 -0
- package/dist/dashboard/precompressed-assets.json +60 -59
- package/migrations/full-postgres/0036_system_prompt_presets_refs.sql +10 -0
- package/migrations/full-postgres/0037_share_concurrency_scope.sql +3 -0
- package/migrations/full-postgres/0038_add_accounts_quota_snapshot.sql +7 -0
- package/migrations/full-postgres/0039_add_telemetry_provider_model_indexes.sql +13 -0
- package/migrations/full-postgres/0040_add_models_dialect.sql +3 -0
- package/migrations/full-postgres/0041_add_daily_checkin_ledger_account_fk.sql +7 -0
- package/migrations/full-postgres/0042_add_telemetry_degenerate_stop.sql +14 -0
- package/migrations/lite-sqlite3/0036_system_prompt_presets_refs.sql +14 -0
- package/migrations/lite-sqlite3/0037_share_concurrency_scope.sql +3 -0
- package/migrations/lite-sqlite3/0038_add_accounts_quota_snapshot.sql +4 -0
- package/migrations/lite-sqlite3/0039_add_telemetry_provider_model_indexes.sql +6 -0
- package/migrations/lite-sqlite3/0040_add_models_dialect.sql +3 -0
- package/migrations/lite-sqlite3/0041_add_daily_checkin_ledger_account_fk.sql +26 -0
- package/migrations/lite-sqlite3/0042_add_telemetry_degenerate_stop.sql +12 -0
- package/package.json +4 -6
- package/scripts/cli/update.ts +15 -41
- package/src/boot/app.ts +58 -2
- package/src/boot/dependencies.ts +794 -732
- package/src/boot/lifecycle.ts +11 -12
- package/src/config/cooldowns.ts +34 -55
- package/src/config/network.ts +1 -1
- package/src/config/package-paths.ts +1 -1
- package/src/config/providers.ts +19 -14
- package/src/config/resources.ts +0 -5
- package/src/config/runtime.ts +2 -5
- package/src/config/spec.ts +33 -6
- package/src/config/usage.ts +27 -0
- package/src/console/api-keys/ban-constants.ts +38 -0
- package/src/console/api-keys/contracts.ts +71 -62
- package/src/console/api-keys/routes.ts +56 -24
- package/src/console/auth/access.ts +4 -4
- package/src/console/auth/account-reset.ts +1 -1
- package/src/console/auth/audit-recorder.ts +1 -1
- package/src/console/auth/lockout.ts +7 -3
- package/src/console/auth/row-coercion.ts +57 -12
- package/src/console/auth/session-resolver.ts +1 -1
- package/src/console/auth/session-service.ts +8 -2
- package/src/console/auth/session.ts +37 -31
- package/src/console/auth/setup-service.ts +4 -3
- package/src/console/auth/user-store.ts +1 -1
- package/src/console/backup/9router-map.ts +0 -3
- package/src/console/backup/contracts.ts +9 -9
- package/src/console/backup/export-endpoint.ts +5 -6
- package/src/console/backup/nine-router.ts +1 -7
- package/src/console/backup/routes.ts +29 -33
- package/src/console/backup/service.ts +468 -468
- package/src/console/backup/store.ts +47 -29
- package/src/console/backup/validate.ts +11 -1
- package/src/console/cli-tools/contracts.ts +43 -299
- package/src/console/cli-tools/fs-ops.ts +29 -0
- package/src/console/cli-tools/injectors/claude.ts +3 -3
- package/src/console/cli-tools/injectors/codex.ts +181 -181
- package/src/console/cli-tools/injectors/driver.ts +150 -247
- package/src/console/cli-tools/injectors/kilo.ts +146 -21
- package/src/console/cli-tools/injectors/opencode.ts +176 -153
- package/src/console/cli-tools/routes.ts +7 -1
- package/src/console/cli-tools/service.ts +8 -7
- package/src/console/cli-tools/store.ts +2 -2
- package/src/console/console-router.ts +32 -0
- package/src/console/dashboard-assets.ts +9 -9
- package/src/console/domain-registration.ts +16 -2
- package/src/console/inferhub/client.ts +0 -7
- package/src/console/inferhub/key-store.ts +1 -1
- package/src/console/inferhub/normalize.ts +2 -2
- package/src/console/inferhub/routes.ts +1 -1
- package/src/console/inferhub/types.ts +1 -1
- package/src/console/observability/contracts.ts +7 -1
- package/src/console/observability/live.ts +31 -18
- package/src/console/observability/logs.ts +1 -1
- package/src/console/observability/sse.ts +62 -8
- package/src/console/observability/store.ts +23 -4
- package/src/console/providers/catalog/catalog-projections.ts +1 -1
- package/src/console/providers/catalog/contracts.ts +39 -0
- package/src/console/providers/catalog/model-operations.ts +1 -1
- package/src/console/providers/catalog/provider-operations.ts +95 -17
- package/src/console/providers/catalog/public-model-store.ts +0 -0
- package/src/console/providers/catalog/routes.ts +27 -3
- package/src/console/providers/catalog/store.ts +147 -8
- package/src/console/providers/detail/routes.ts +1 -1
- package/src/console/providers/oauth/callback-listener.ts +100 -18
- package/src/console/providers/oauth/routes.ts +92 -13
- package/src/console/quota/account-quota-global.ts +41 -3
- package/src/console/quota/account-quota-shared.ts +10 -3
- package/src/console/quota/account-quota-tenant.ts +259 -66
- package/src/console/quota/daily-growth-job.ts +308 -0
- package/src/console/quota/native-identity.ts +2 -2
- package/src/console/quota/quota-view.ts +1 -1
- package/src/console/remote-support/routes.ts +1 -1
- package/src/console/remote-support/service.ts +2 -2
- package/src/console/routing/model/contracts.ts +1 -1
- package/src/console/routing/pools/contracts.ts +2 -2
- package/src/console/routing/pools/import-parse.ts +3 -3
- package/src/console/routing/pools/relay-deploy.ts +3 -3
- package/src/console/settings/contracts.ts +29 -2
- package/src/console/settings/store.ts +20 -0
- package/src/console/share/share-router.ts +100 -32
- package/src/console/share/share-stats.ts +11 -13
- package/src/console/share/share-usage.ts +1 -1
- package/src/console/shared/errors.ts +2 -2
- package/src/console/shared/object.ts +18 -0
- package/src/console/shared/read-through-cache.ts +4 -4
- package/src/console/shared/transaction.ts +14 -0
- package/src/console/system-prompt/contracts.ts +89 -12
- package/src/console/system-prompt/routes.ts +98 -45
- package/src/console/updates/apply-update.ts +244 -0
- package/src/console/updates/routes.ts +252 -97
- package/src/main.ts +11 -5
- package/src/network/http2-bidi.ts +1 -1
- package/src/network/ip-utils.ts +298 -0
- package/src/network/outbound-fetch.ts +1 -1
- package/src/network/pool/agent.ts +7 -6
- package/src/network/pool/byte-accounting.ts +1 -1
- package/src/network/pool/resolver.ts +16 -12
- package/src/network/pool/selector.ts +150 -136
- package/src/network/pool-health-machine.ts +1 -1
- package/src/network/rate-limit-verdict.ts +1 -1
- package/src/network/retry.ts +1 -1
- package/src/network/ssrf.ts +20 -71
- package/src/observability/event-log.ts +9 -2
- package/src/observability/event-query.ts +61 -291
- package/src/observability/log-sanitize.ts +1 -1
- package/src/observability/metrics.ts +4 -4
- package/src/observability/payload-capture.ts +6 -5
- package/src/observability/payload-store.ts +1 -1
- package/src/observability/runtime-metrics.ts +1 -1
- package/src/observability/telemetry-buffer.ts +19 -3
- package/src/observability/telemetry-reconciliation.ts +3 -3
- package/src/observability/token-speed.ts +1 -1
- package/src/persistence/connection-url.ts +1 -1
- package/src/persistence/db-handle.ts +1 -1
- package/src/persistence/db-pg.ts +2 -2
- package/src/persistence/db-sqlite.ts +3 -3
- package/src/persistence/dialect.ts +5 -1
- package/src/persistence/readiness.ts +1 -1
- package/src/persistence/redis.ts +1 -1
- package/src/persistence/schema.ts +121 -40
- package/src/persistence/shared-cache.ts +2 -2
- package/src/persistence/sort-order.ts +1 -1
- package/src/persistence/stores/api-key-store.ts +29 -2
- package/src/persistence/stores/share-store.ts +20 -8
- package/src/persistence/system-prompt-store.ts +63 -18
- package/src/persistence/tenant-preferences.ts +1 -1
- package/src/persistence/ttl-cache.ts +3 -3
- package/src/persistence/usage-flush.ts +7 -2
- package/src/protocol/canonical-model.ts +1 -1
- package/src/protocol/codecs.ts +6 -10
- package/src/protocol/dsml.ts +8 -125
- package/src/protocol/image-clamp.ts +2 -2
- package/src/protocol/reasoning-text.ts +1 -1
- package/src/protocol/reasoning.ts +7 -52
- package/src/protocol/registry.ts +50 -15
- package/src/protocol/request/chat.ts +46 -19
- package/src/protocol/request/codex.ts +1 -1
- package/src/protocol/request/messages.ts +46 -14
- package/src/protocol/request/responses.ts +19 -10
- package/src/protocol/response/chat.ts +25 -10
- package/src/protocol/response/codex.ts +1 -1
- package/src/protocol/response/messages.ts +1 -1
- package/src/protocol/response/omp-tool-call-markup.ts +1 -1
- package/src/protocol/response/responses.ts +1 -1
- package/src/protocol/sse-decoder.ts +50 -1
- package/src/protocol/stop-reason.ts +2 -2
- package/src/protocol/think-split.ts +347 -0
- package/src/protocol/translation/attachment-integrity.ts +3 -3
- package/src/protocol/translation/cache-controls.ts +8 -20
- package/src/protocol/translation/capabilities.ts +6 -6
- package/src/protocol/translation/dialect/deepseek.ts +10 -0
- package/src/protocol/translation/dialect/inband.ts +39 -0
- package/src/protocol/translation/dialect/model-thinking.ts +89 -0
- package/src/protocol/translation/dialect/qwen.ts +114 -0
- package/src/protocol/translation/dialect/resolver.ts +83 -0
- package/src/protocol/translation/dialect/rules.ts +93 -0
- package/src/protocol/translation/dialect/thinking-ladder.ts +245 -0
- package/src/protocol/translation/dialect/types.ts +58 -0
- package/src/protocol/translation/quirks.ts +18 -4
- package/src/protocol/translation/thinking.ts +12 -252
- package/src/protocol/translation/tool-id.ts +3 -3
- package/src/protocol/translation/tool-repair.ts +1 -1
- package/src/protocol/upstream/post-json.ts +6 -1
- package/src/providers/adapters/compatible-adapter.ts +71 -4
- package/src/providers/authentication/jwt-validator.ts +0 -5
- package/src/providers/authentication/oauth-client.ts +254 -253
- package/src/providers/authentication/oauth-flow-store.ts +42 -7
- package/src/providers/bundled-discovery-providers.ts +6 -30
- package/src/providers/configured-openai-providers.ts +82 -97
- package/src/providers/default-registry.ts +35 -37
- package/src/providers/discovery/catalog/catalog-format.ts +13 -2
- package/src/providers/discovery/discovery-types.ts +0 -1
- package/src/providers/discovery/openai-model-discovery.ts +2 -2
- package/src/providers/discovery/probe-phases.ts +48 -13
- package/src/providers/discovery/probing-service.ts +6 -8
- package/src/providers/integrations/agentrouter/agentrouter.ts +12 -2
- package/src/providers/integrations/antigravity/antigravity-oauth.ts +0 -1
- package/src/providers/integrations/antigravity/antigravity-protocol.ts +88 -28
- package/src/providers/integrations/antigravity/antigravity-quota.ts +18 -6
- package/src/providers/integrations/antigravity/antigravity.ts +22 -13
- package/src/providers/integrations/buddy/buddy-catalog-shared.ts +1 -1
- package/src/providers/integrations/buddy/buddy-chat-shared.ts +20 -4
- package/src/providers/integrations/buddy/buddy-cli-headers.ts +19 -52
- package/src/providers/integrations/buddy/buddy-identity.ts +86 -0
- package/src/providers/integrations/buddy/buddy-oauth-shared.ts +1 -1
- package/src/providers/integrations/buddy/codebuddy/cn-provider.ts +1 -2
- package/src/providers/integrations/buddy/codebuddy/provider.ts +4 -15
- package/src/providers/integrations/buddy/codebuddy/shared.ts +30 -9
- package/src/providers/integrations/buddy/workbuddy/provider.ts +5 -9
- package/src/providers/integrations/buddy/workbuddy/shared.ts +21 -3
- package/src/providers/integrations/claude/claude-cch.ts +148 -148
- package/src/providers/integrations/claude/fallback-credit.ts +3 -7
- package/src/providers/integrations/cline/cline.ts +1 -1
- package/src/providers/integrations/codex/codex-device-code.ts +101 -101
- package/src/providers/integrations/codex/codex-oauth.ts +0 -1
- package/src/providers/integrations/commandcode/commandcode.ts +75 -115
- package/src/providers/integrations/commandcode/quota.ts +131 -51
- package/src/providers/integrations/cursor/cursor-http2.ts +0 -3
- package/src/providers/integrations/cursor/cursor-quota.ts +2 -6
- package/src/providers/integrations/cursor/cursor-request.ts +0 -87
- package/src/providers/integrations/cursor/cursor-shared.ts +0 -4
- package/src/providers/integrations/cursor/cursor.ts +35 -3
- package/src/providers/integrations/cursor/protocol/framing.ts +0 -56
- package/src/providers/integrations/cursor/protocol/messages.ts +0 -96
- package/src/providers/integrations/factory/factory.ts +9 -9
- package/src/providers/integrations/github/github-discovery.ts +70 -12
- package/src/providers/integrations/github/github-oauth.ts +184 -14
- package/src/providers/integrations/github/github.ts +134 -26
- package/src/providers/integrations/grok/grok-oauth.ts +261 -261
- package/src/providers/integrations/grok/grok-quota-frame.ts +1 -1
- package/src/providers/integrations/grok/grok-quota.ts +16 -7
- package/src/providers/integrations/grok/grok-ratelimit-store.ts +0 -5
- package/src/providers/integrations/grok/grok-ratelimit.ts +3 -0
- package/src/providers/integrations/grok/grok.ts +86 -36
- package/src/providers/integrations/kilo/kilo-oauth.ts +1 -1
- package/src/providers/integrations/kiro/kiro-cache.ts +31 -0
- package/src/providers/integrations/kiro/kiro-catalog.ts +31 -5
- package/src/providers/integrations/kiro/kiro-discovery.ts +47 -43
- package/src/providers/integrations/kiro/kiro-machine-id.ts +6 -3
- package/src/providers/integrations/kiro/kiro-oauth.ts +523 -255
- package/src/providers/integrations/kiro/kiro-quota.ts +35 -47
- package/src/providers/integrations/kiro/kiro-request.ts +8 -11
- package/src/providers/integrations/kiro/kiro-stream.ts +13 -0
- package/src/providers/integrations/kiro/kiro.ts +80 -68
- package/src/providers/integrations/muse/muse-oauth.ts +0 -1
- package/src/providers/integrations/muse/muse-quota.ts +0 -4
- package/src/providers/integrations/opencode/fingerprint.ts +7 -5
- package/src/providers/integrations/opencode/provider.ts +17 -24
- package/src/providers/integrations/qoder/qoder-catalog.ts +94 -11
- package/src/providers/integrations/qoder/qoder-oauth.ts +165 -25
- package/src/providers/integrations/qoder/qoder-quota.ts +48 -6
- package/src/providers/integrations/qoder/qoder-realm.ts +10 -31
- package/src/providers/integrations/qoder/qoder-sse.ts +159 -0
- package/src/providers/integrations/qoder/qoder-umid.ts +21 -25
- package/src/providers/integrations/qoder/qoder-virtual.ts +12 -0
- package/src/providers/integrations/qoder/qoder.ts +422 -71
- package/src/providers/integrations/shared/connect.ts +0 -54
- package/src/providers/integrations/shared/hash.ts +15 -0
- package/src/providers/integrations/shared/session-cache.ts +136 -0
- package/src/providers/integrations/trae/trae-cache.ts +7 -87
- package/src/providers/integrations/trae/trae-catalog.ts +1 -6
- package/src/providers/integrations/trae/trae-protocol.ts +17 -8
- package/src/providers/integrations/trae/trae-realm.ts +0 -7
- package/src/providers/integrations/trae/trae.ts +51 -8
- package/src/providers/integrations/xai/xai-oauth.ts +5 -5
- package/src/providers/integrations/zcode-start-plan/gifts.ts +265 -0
- package/src/providers/integrations/zcode-start-plan/oauth.ts +154 -0
- package/src/providers/integrations/zcode-start-plan/provider.ts +131 -0
- package/src/providers/integrations/zcode-start-plan/quota.ts +152 -0
- package/src/providers/integrations/zcode-start-plan/zcode_prompt.json +10 -0
- package/src/providers/operations/account-error-classification.ts +198 -133
- package/src/providers/operations/account-health-service.ts +31 -39
- package/src/providers/operations/account-reset-service.ts +58 -1
- package/src/providers/operations/byok-wire-profile.ts +19 -15
- package/src/providers/operations/cli-platform.ts +31 -5
- package/src/providers/operations/client-version-resolver.ts +4 -6
- package/src/providers/operations/client-versions.ts +10 -7
- package/src/providers/operations/provider-catalog-seeder.ts +2 -0
- package/src/providers/operations/provider-catalog-service.ts +18 -2
- package/src/providers/operations/provider-credential-service.ts +19 -2
- package/src/providers/provider-contracts.ts +18 -7
- package/src/providers/provider-metadata.ts +17 -2
- package/src/providers/provider-model-definition.ts +22 -1
- package/src/providers/provider-probe.ts +4 -2
- package/src/providers/provider-usage.ts +50 -19
- package/src/providers/quota/cache.ts +29 -0
- package/src/providers/quota/quota-contracts.ts +4 -0
- package/src/providers/quota/refresh.ts +27 -4
- package/src/providers/quota/snapshot.ts +94 -0
- package/src/providers/search/search-provider.ts +1 -3
- package/src/providers/search/search-providers.ts +2 -1
- package/src/security/abuse.ts +4 -4
- package/src/security/admission/service.ts +13 -6
- package/src/security/admission/ttl.ts +61 -61
- package/src/security/api-key-auth.ts +17 -3
- package/src/security/client-router-fingerprint.ts +4 -4
- package/src/security/credential-codec.ts +3 -3
- package/src/security/csrf.ts +2 -2
- package/src/security/ip-boundary.ts +16 -189
- package/src/security/model-access-rule.ts +1 -1
- package/src/security/outbound-headers.ts +18 -0
- package/src/shared/invoke-guard.ts +47 -0
- package/src/transport/contracts.ts +13 -4
- package/src/transport/dispatch/attempt-finalize.ts +70 -22
- package/src/transport/dispatch/attempt-loop.ts +22 -0
- package/src/transport/dispatch/capture-policy.ts +34 -37
- package/src/transport/dispatch/empty-stream.ts +23 -20
- package/src/transport/dispatch/generation.ts +25 -45
- package/src/transport/dispatch/native-shared.ts +127 -0
- package/src/transport/dispatch/proxy-request.ts +3 -1
- package/src/transport/dispatch/responses-compact.ts +25 -46
- package/src/transport/dispatch/streaming/encoder.ts +18 -72
- package/src/transport/dispatch/streaming/finalize.ts +2 -2
- package/src/transport/dispatch/streaming/stream.ts +7 -3
- package/src/transport/dispatch/streaming/thinking-loop.ts +1 -1
- package/src/transport/dispatch/systemone.ts +20 -48
- package/src/transport/dispatch/token-count.ts +41 -38
- package/src/transport/dispatch/websearch.ts +19 -45
- package/src/transport/drain-endpoint.ts +1 -1
- package/src/transport/failure-policy.ts +21 -1
- package/src/transport/guards/auth-guard.ts +0 -6
- package/src/transport/guards/body-policy.ts +3 -3
- package/src/transport/guards/error-lifecycle.ts +4 -0
- package/src/transport/guards/headroom-guard.ts +1 -1
- package/src/transport/guards/pipeline.ts +1 -1
- package/src/transport/guards/request-context.ts +12 -17
- package/src/transport/request/preparer.ts +286 -7
- package/src/transport/request/rtk/compress-request.ts +3 -3
- package/src/transport/request/rtk/rtk-constants.ts +2 -2
- package/src/transport/request/rtk/rtk-filters.ts +8 -8
- package/src/transport/request/state.ts +7 -13
- package/src/transport/request/system-prompt-injection.ts +74 -11
- package/src/transport/request/tenant-preferences.ts +22 -0
- package/src/transport/routing/candidate-index.ts +2 -2
- package/src/transport/routing/round-robin.ts +1 -1
- package/src/transport/routing/route-catalog.ts +17 -18
- package/src/transport/routing/route-model.ts +126 -33
- package/src/transport/routing/router.ts +159 -37
- package/src/transport/shutdown-notice.ts +2 -2
- package/src/transport/surface/adapters.ts +3 -3
- package/src/transport/surface/chat/encode.ts +7 -7
- package/src/transport/surface/chat/parse.ts +7 -7
- package/src/transport/surface/completion.ts +2 -2
- package/src/transport/surface/content-parts.ts +1 -1
- package/src/transport/surface/dialects.ts +2 -2
- package/src/transport/surface/messages/parse.ts +3 -3
- package/src/transport/surface/reasoning-summary.ts +9 -14
- package/src/transport/surface/responses/adapter.ts +1 -1
- package/src/transport/surface/responses/errors.ts +0 -9
- package/src/transport/vision-handoff-cache.ts +58 -0
- package/src/transport/vision-handoff.ts +431 -0
- package/src/workers/account-identity-sweep.ts +2 -2
- package/src/workers/buddy-growth-tasks.ts +392 -346
- package/src/workers/daily-checkin.ts +102 -49
- package/src/workers/maintenance-lease.ts +2 -35
- package/src/workers/oauth-refresh-sweep.ts +8 -1
- package/src/workers/quota-refresh-sweep.ts +120 -82
- package/src/workers/scheduler.ts +67 -4
- package/src/workers/sweep-runner.ts +24 -5
- package/src/workers/zcode-gift-claim.ts +108 -0
- package/dist/dashboard/assets/AccountCooldown-Bjo92Qat.js +0 -1
- package/dist/dashboard/assets/AccountCooldown-Bjo92Qat.js.gz +0 -0
- package/dist/dashboard/assets/BannedPage-ChG5svFb.js +0 -1
- package/dist/dashboard/assets/BannedPage-ChG5svFb.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-BIcWQun7.js +0 -1
- package/dist/dashboard/assets/CliToolDetailPage-BIcWQun7.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-Bf2VelDe.js +0 -1
- package/dist/dashboard/assets/CliToolsPage-Bf2VelDe.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-DCgm5g6g.js +0 -1
- package/dist/dashboard/assets/CombosPage-DCgm5g6g.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-a8mD0QF1.js +0 -1
- package/dist/dashboard/assets/ConsoleLogPage-a8mD0QF1.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-ggeMSjte.js +0 -1
- package/dist/dashboard/assets/CustomizationPage-ggeMSjte.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-DzSWzE_j.js +0 -1
- package/dist/dashboard/assets/HealthEventsModal-DzSWzE_j.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-U6AKaJcF.js +0 -1
- package/dist/dashboard/assets/LoginPage-U6AKaJcF.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-BMS2t8BA.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-D2D4IiHl.js +0 -1
- package/dist/dashboard/assets/OverviewPage-D2D4IiHl.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-CrNf7j_X.js +0 -5
- package/dist/dashboard/assets/ProviderDetailPage-CrNf7j_X.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-FB6yS4v1.js +0 -1
- package/dist/dashboard/assets/ProviderIcon-FB6yS4v1.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-BSDbquc1.js +0 -1
- package/dist/dashboard/assets/ProvidersPage-BSDbquc1.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-lsrx7NYg.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-L0skzML8.js +0 -1
- package/dist/dashboard/assets/QuotaPage-L0skzML8.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-CmAbjRHW.js +0 -1
- package/dist/dashboard/assets/SettingsPage-CmAbjRHW.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-f0DE1h0-.js +0 -1
- package/dist/dashboard/assets/SetupPage-f0DE1h0-.js.gz +0 -0
- package/dist/dashboard/assets/SortableList-ClzpwyET.js +0 -1
- package/dist/dashboard/assets/SortableList-ClzpwyET.js.gz +0 -0
- package/dist/dashboard/assets/StudioPage-Cq5tW7hW.js.gz +0 -0
- package/dist/dashboard/assets/ToolIcon-K1ckcDKg.js +0 -1
- package/dist/dashboard/assets/ToolIcon-K1ckcDKg.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-Dfc9rWQn.js +0 -56
- package/dist/dashboard/assets/UsagePage-Dfc9rWQn.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-C38iXoO4.js +0 -1
- package/dist/dashboard/assets/api-keys-C38iXoO4.js.gz +0 -0
- package/dist/dashboard/assets/app-BayTTy01.js +0 -9
- package/dist/dashboard/assets/app-BayTTy01.js.gz +0 -0
- package/dist/dashboard/assets/app-a159k0ub.css +0 -2
- package/dist/dashboard/assets/app-a159k0ub.css.gz +0 -0
- package/dist/dashboard/assets/assert-DbRA8EG2.js +0 -15
- package/dist/dashboard/assets/assert-DbRA8EG2.js.gz +0 -0
- package/dist/dashboard/assets/badge-D9CS6dZk.js.gz +0 -0
- package/dist/dashboard/assets/banner-DympBNq4.js.gz +0 -0
- package/dist/dashboard/assets/card-ibRWU_uT.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-63qBGaRw.js +0 -1
- package/dist/dashboard/assets/cli-tools-63qBGaRw.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-DJm-9pz7.js.gz +0 -0
- package/dist/dashboard/assets/dialog-8fiE6tec.js +0 -1
- package/dist/dashboard/assets/dialog-8fiE6tec.js.gz +0 -0
- package/dist/dashboard/assets/drawer-CqGYeOrd.js.gz +0 -0
- package/dist/dashboard/assets/entry-BSAsYsmV.js +0 -1
- package/dist/dashboard/assets/entry-BSAsYsmV.js.gz +0 -0
- package/dist/dashboard/assets/entry-CiIQULZD.js +0 -4
- package/dist/dashboard/assets/entry-CiIQULZD.js.gz +0 -0
- package/dist/dashboard/assets/entry-Daz0lJ-W.js +0 -1
- package/dist/dashboard/assets/entry-Daz0lJ-W.js.gz +0 -0
- package/dist/dashboard/assets/formatters-D-MTJxMr.js.gz +0 -0
- package/dist/dashboard/assets/github-badge-CPK47xQx.js +0 -1
- package/dist/dashboard/assets/github-badge-CPK47xQx.js.gz +0 -0
- package/dist/dashboard/assets/index-DBNPOfhV.js.gz +0 -0
- package/dist/dashboard/assets/inline-BT0zntEr.js.gz +0 -0
- package/dist/dashboard/assets/input-Da2ZViWg.js.gz +0 -0
- package/dist/dashboard/assets/layout-BXNeLw6M.js.gz +0 -0
- package/dist/dashboard/assets/list-row-BycOrrIV.js.gz +0 -0
- package/dist/dashboard/assets/live-DCQNZpF7.js +0 -1
- package/dist/dashboard/assets/live-DCQNZpF7.js.gz +0 -0
- package/dist/dashboard/assets/mutation-Rr0TIvcj.js.gz +0 -0
- package/dist/dashboard/assets/network-9Yj2ZyLo.js +0 -1
- package/dist/dashboard/assets/network-9Yj2ZyLo.js.gz +0 -0
- package/dist/dashboard/assets/numbers-CEagj6ZZ.js +0 -2
- package/dist/dashboard/assets/numbers-CEagj6ZZ.js.gz +0 -0
- package/dist/dashboard/assets/providers/zai-coding-plan.svg +0 -6
- package/dist/dashboard/assets/providers/zai-coding-plan.svg.gz +0 -0
- package/dist/dashboard/assets/quota-Caw7wqci.js +0 -1
- package/dist/dashboard/assets/quota-Caw7wqci.js.gz +0 -0
- package/dist/dashboard/assets/routing-CO9zCUus.js +0 -1
- package/dist/dashboard/assets/routing-CO9zCUus.js.gz +0 -0
- package/dist/dashboard/assets/select-BIYIBkMU.js.gz +0 -0
- package/dist/dashboard/assets/settings-jS3-PNSA.js +0 -1
- package/dist/dashboard/assets/settings-jS3-PNSA.js.gz +0 -0
- package/dist/dashboard/assets/stack-DfgnFDWY.js.gz +0 -0
- package/dist/dashboard/assets/state-CkfdYWel.js.gz +0 -0
- package/dist/dashboard/assets/stream-retry-5QnVuSSo.js +0 -1
- package/dist/dashboard/assets/stream-retry-5QnVuSSo.js.gz +0 -0
- package/dist/dashboard/assets/switch-SYm237HG.js.gz +0 -0
- package/dist/dashboard/assets/useMutation-BQqCRNIi.js.gz +0 -0
- package/src/console/cli-tools/injectors/cline.ts +0 -110
- package/src/console/cli-tools/injectors/copilot.ts +0 -108
- package/src/console/cli-tools/injectors/cowork.ts +0 -64
- package/src/console/cli-tools/injectors/deepseek-tui.ts +0 -85
- package/src/console/cli-tools/injectors/droid.ts +0 -96
- package/src/console/cli-tools/injectors/grok-build.ts +0 -70
- package/src/console/cli-tools/injectors/hermes.ts +0 -101
- package/src/console/cli-tools/injectors/jcode.ts +0 -115
- package/src/console/cli-tools/injectors/openclaw.ts +0 -161
- package/src/network/response-headers.ts +0 -39
- package/src/protocol/response/dsml.ts +0 -38
- package/src/providers/integrations/xiaomi-mimo/think-stream.ts +0 -229
- /package/dist/dashboard/assets/brand/{og_bansos.webp → og_bansos_initial.webp} +0 -0
- /package/dist/dashboard/assets/brand/{og_image.webp → og_image_initial.webp} +0 -0
|
@@ -106,7 +106,7 @@ function factoryClientHeaders(orgId: string): Record<string, string> {
|
|
|
106
106
|
* (`packages/ai/src/providers/factory-droid.ts:271` — `"x-api-provider":
|
|
107
107
|
* input.upstream`), so a value outside it has no meaning on this wire.
|
|
108
108
|
*/
|
|
109
|
-
|
|
109
|
+
const FACTORY_DROID_UPSTREAMS = [
|
|
110
110
|
"fireworks",
|
|
111
111
|
"baseten",
|
|
112
112
|
"mistral",
|
|
@@ -123,7 +123,7 @@ export const FACTORY_DROID_UPSTREAMS = [
|
|
|
123
123
|
"databricks",
|
|
124
124
|
] as const;
|
|
125
125
|
|
|
126
|
-
|
|
126
|
+
type FactoryDroidUpstream = (typeof FACTORY_DROID_UPSTREAMS)[number];
|
|
127
127
|
|
|
128
128
|
const FACTORY_DROID_UPSTREAM_SET: ReadonlySet<string> = new Set(FACTORY_DROID_UPSTREAMS);
|
|
129
129
|
|
|
@@ -154,7 +154,7 @@ function logDroppedUpstream(reason: string, value: string): void {
|
|
|
154
154
|
* unmapped failure. Omitting falls back to Factory's default routing, which is
|
|
155
155
|
* the behavior a request that names no upstream already gets.
|
|
156
156
|
*/
|
|
157
|
-
|
|
157
|
+
function factoryApiProviderHeader(
|
|
158
158
|
options: Readonly<Record<string, unknown>> | undefined,
|
|
159
159
|
): Record<string, string> {
|
|
160
160
|
const value = options?.["factory_api_provider"];
|
|
@@ -179,7 +179,7 @@ export function factoryApiProviderHeader(
|
|
|
179
179
|
* `/api/cli/whoami` reported. An account with no recorded residency is the
|
|
180
180
|
* global host, which is Factory's default.
|
|
181
181
|
*/
|
|
182
|
-
|
|
182
|
+
function factoryBaseUrl(context: ProviderDispatchContext): string {
|
|
183
183
|
const region = context.credential.auth_state?.["region"];
|
|
184
184
|
return region === "eu" ? FACTORY_DROID_EU_BASE_URL : FACTORY_DROID_BASE_URL;
|
|
185
185
|
}
|
|
@@ -200,7 +200,7 @@ export function factoryBaseUrl(context: ProviderDispatchContext): string {
|
|
|
200
200
|
* operator wrote into `auth_state`. The two-axis read is kept because the donor
|
|
201
201
|
* needs it, not because we currently populate the second axis.
|
|
202
202
|
*/
|
|
203
|
-
|
|
203
|
+
function factoryInferenceRegion(
|
|
204
204
|
context: ProviderDispatchContext,
|
|
205
205
|
): "global" | "us" | "eu" {
|
|
206
206
|
const state = context.credential.auth_state;
|
|
@@ -266,7 +266,7 @@ const REGION_UPSTREAMS: Readonly<Record<"global" | "us" | "eu", readonly Factory
|
|
|
266
266
|
* is, which is a property of the route rather than of the request. The donor's
|
|
267
267
|
* own message text is preserved as the `reason` so the mapping is traceable.
|
|
268
268
|
*/
|
|
269
|
-
|
|
269
|
+
function factoryRegionUnavailable(modelId: string, region: string): GatewayError {
|
|
270
270
|
return new GatewayError(
|
|
271
271
|
"capability_unsupported",
|
|
272
272
|
400,
|
|
@@ -294,7 +294,7 @@ export function factoryRegionUnavailable(modelId: string, region: string): Gatew
|
|
|
294
294
|
* Sending `configured_order` without that discovery would claim a rotation we
|
|
295
295
|
* never resolved.
|
|
296
296
|
*/
|
|
297
|
-
|
|
297
|
+
function factoryRoutingSourceHeader(request: CanonicalRequest): Record<string, string> {
|
|
298
298
|
const continuing = request.messages.some((message) => message.role === "assistant");
|
|
299
299
|
return { "x-provider-routing-source": continuing ? "session_lock" : "registry_default" };
|
|
300
300
|
}
|
|
@@ -313,7 +313,7 @@ export function factoryRoutingSourceHeader(request: CanonicalRequest): Record<st
|
|
|
313
313
|
* donor's own region-unavailable error rather than an upstream 4xx this layer
|
|
314
314
|
* cannot explain.
|
|
315
315
|
*/
|
|
316
|
-
|
|
316
|
+
function factoryRegionCheckedApiProviderHeader(
|
|
317
317
|
request: CanonicalRequest,
|
|
318
318
|
context: ProviderDispatchContext,
|
|
319
319
|
): Record<string, string> {
|
|
@@ -330,7 +330,7 @@ export function factoryRegionCheckedApiProviderHeader(
|
|
|
330
330
|
}
|
|
331
331
|
|
|
332
332
|
/** The two routing headers together, as every Factory wire sends them. */
|
|
333
|
-
|
|
333
|
+
function factoryRoutingHeaders(
|
|
334
334
|
request: CanonicalRequest,
|
|
335
335
|
context: ProviderDispatchContext,
|
|
336
336
|
): Record<string, string> {
|
|
@@ -29,10 +29,12 @@ import {
|
|
|
29
29
|
GITHUB_PROVIDER_ID,
|
|
30
30
|
GITHUB_ENDPOINT_PATH,
|
|
31
31
|
GITHUB_RESPONSES_ENDPOINT_PATH,
|
|
32
|
+
GITHUB_MESSAGES_ENDPOINT_PATH,
|
|
32
33
|
GITHUB_AUTO_MODELS,
|
|
33
34
|
recordGithubModelRoutes,
|
|
34
35
|
} from "./github";
|
|
35
|
-
import { GITHUB_REQUEST_HEADERS, parseGithubCredential } from "./github-oauth";
|
|
36
|
+
import { GITHUB_REQUEST_HEADERS, githubCopilotClientIds, parseGithubCredential } from "./github-oauth";
|
|
37
|
+
import { createHash } from "node:crypto";
|
|
36
38
|
|
|
37
39
|
/** A context window above this is unsatisfiable headroom, not a real limit. */
|
|
38
40
|
const MAX_CONTEXT = 1_000_000_000;
|
|
@@ -47,12 +49,11 @@ function positiveLimit(value: unknown): number | undefined {
|
|
|
47
49
|
* The surfaces a row answers on, normalized to the canonical paths this
|
|
48
50
|
* gateway registers.
|
|
49
51
|
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
* row as chat would publish a route that fails on first dispatch.
|
|
52
|
+
* Copilot serves three OpenAI/Anthropic surfaces: `/chat/completions`,
|
|
53
|
+
* `/responses`, and `/v1/messages`. A row that supports none of them is
|
|
54
|
+
* skipped rather than defaulted.
|
|
54
55
|
*/
|
|
55
|
-
function wireFamilyFor(entry: Record<string, unknown>): "chat" | "responses" | undefined {
|
|
56
|
+
function wireFamilyFor(entry: Record<string, unknown>): "chat" | "responses" | "messages" | undefined {
|
|
56
57
|
const declared = boundedUpstreamArray(entry.supported_endpoints);
|
|
57
58
|
if (!declared) return "chat";
|
|
58
59
|
const endpoints = new Set<string>();
|
|
@@ -60,11 +61,12 @@ function wireFamilyFor(entry: Record<string, unknown>): "chat" | "responses" | u
|
|
|
60
61
|
const label = sanitizeUpstreamLabel(value);
|
|
61
62
|
if (label !== undefined) endpoints.add(label.replace(/^\/v1(?=\/)/, ""));
|
|
62
63
|
}
|
|
63
|
-
// Chat is preferred when a row supports
|
|
64
|
-
// OpenAI-compatible client already speaks
|
|
65
|
-
//
|
|
64
|
+
// Chat is preferred when a row supports it: it is the surface every
|
|
65
|
+
// OpenAI-compatible client already speaks. Responses and Messages are used
|
|
66
|
+
// only when chat is absent, in that order.
|
|
66
67
|
if (endpoints.has("/chat/completions")) return "chat";
|
|
67
68
|
if (endpoints.has("/responses")) return "responses";
|
|
69
|
+
if (endpoints.has("/messages")) return "messages";
|
|
68
70
|
return undefined;
|
|
69
71
|
}
|
|
70
72
|
|
|
@@ -75,6 +77,34 @@ function supports(entry: Record<string, unknown>, key: string): boolean {
|
|
|
75
77
|
return declared?.[key] === true;
|
|
76
78
|
}
|
|
77
79
|
|
|
80
|
+
/**
|
|
81
|
+
* Price deltas from the account's `/models` row when it publishes `token_prices`
|
|
82
|
+
* (dollars per 1M tokens). This is the real per-account price Copilot serves,
|
|
83
|
+
* which the shared models.dev catalog cannot know; when absent the base-catalog
|
|
84
|
+
* estimate is used instead.
|
|
85
|
+
*/
|
|
86
|
+
function copilotCost(entry: Record<string, unknown>): ModelDefinition["cost"] {
|
|
87
|
+
const billing = isRecord(entry.billing) ? entry.billing : undefined;
|
|
88
|
+
const prices = isRecord(billing?.token_prices) ? billing.token_prices : undefined;
|
|
89
|
+
const defaultTier = isRecord(prices?.default) ? prices.default : undefined;
|
|
90
|
+
const input = positiveLimit(defaultTier?.input_price);
|
|
91
|
+
const output = positiveLimit(defaultTier?.output_price);
|
|
92
|
+
if (input === undefined && output === undefined) {
|
|
93
|
+
return modelsDevCatalog.costFor(GITHUB_PROVIDER_ID, String(entry.id ?? ""));
|
|
94
|
+
}
|
|
95
|
+
return {
|
|
96
|
+
input: input ?? null,
|
|
97
|
+
output: output ?? null,
|
|
98
|
+
...(typeof (defaultTier as Record<string, unknown> | undefined)?.cache_read_price === "number"
|
|
99
|
+
? { cache_read: (defaultTier as { cache_read_price?: number }).cache_read_price }
|
|
100
|
+
: {}),
|
|
101
|
+
...(typeof (defaultTier as Record<string, unknown> | undefined)?.cache_write_price === "number"
|
|
102
|
+
? { cache_write: (defaultTier as { cache_write_price?: number }).cache_write_price }
|
|
103
|
+
: {}),
|
|
104
|
+
pricing_model: "pay-per-use",
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
78
108
|
function toModelDefinition(entry: Record<string, unknown>): ModelDefinition | undefined {
|
|
79
109
|
const modelId = sanitizeUpstreamLabel(entry.id);
|
|
80
110
|
if (modelId === undefined) return undefined;
|
|
@@ -89,16 +119,32 @@ function toModelDefinition(entry: Record<string, unknown>): ModelDefinition | un
|
|
|
89
119
|
// clamped the same way every other catalog row is.
|
|
90
120
|
const outputLimit = Math.min(declaredOutput, contextLimit);
|
|
91
121
|
const vision = supports(entry, "vision");
|
|
122
|
+
// `reasoning_effort` is a list (e.g. ["low","medium","high","xhigh","max"])
|
|
123
|
+
// when the model supports effort levels; absence means the model does not
|
|
124
|
+
// expose an effort knob. Carrying it lets routing clamp to the model's real
|
|
125
|
+
// ladder instead of the provider-wide `max→high` cap.
|
|
126
|
+
const supportsObj = isRecord(capabilities?.supports) ? capabilities.supports : undefined;
|
|
127
|
+
const reasoningEfforts = Array.isArray(supportsObj?.reasoning_effort)
|
|
128
|
+
? (supportsObj.reasoning_effort as unknown[])
|
|
129
|
+
.filter((level): level is string => typeof level === "string" && level.length > 0)
|
|
130
|
+
: undefined;
|
|
131
|
+
const cost = copilotCost(entry);
|
|
92
132
|
return {
|
|
93
133
|
modelId,
|
|
94
134
|
wireFamily,
|
|
95
|
-
endpointPath:
|
|
135
|
+
endpointPath:
|
|
136
|
+
wireFamily === "responses"
|
|
137
|
+
? GITHUB_RESPONSES_ENDPOINT_PATH
|
|
138
|
+
: wireFamily === "messages"
|
|
139
|
+
? GITHUB_MESSAGES_ENDPOINT_PATH
|
|
140
|
+
: GITHUB_ENDPOINT_PATH,
|
|
96
141
|
contextLimit,
|
|
97
142
|
outputLimit,
|
|
98
143
|
modalities: { input: vision ? ["text", "image"] : ["text"], output: ["text"] },
|
|
99
144
|
reasoning: true,
|
|
145
|
+
...(reasoningEfforts === undefined ? {} : { reasoningEfforts }),
|
|
100
146
|
toolCall: true,
|
|
101
|
-
cost
|
|
147
|
+
cost,
|
|
102
148
|
};
|
|
103
149
|
}
|
|
104
150
|
|
|
@@ -125,8 +171,20 @@ export async function discoverGithubModels(
|
|
|
125
171
|
const signal =
|
|
126
172
|
input.signal === undefined ? timeoutSignal : AbortSignal.any([input.signal, timeoutSignal]);
|
|
127
173
|
try {
|
|
174
|
+
// The reference client stamps its Copilot identity on `/models` too
|
|
175
|
+
// (`_mixinHeaders` covers the Models request type): session + machine ids.
|
|
176
|
+
// Machine id is per-account — see `githubCopilotClientIds`.
|
|
177
|
+
const accountKey = createHash("sha256").update(`${apiHost}:${access}`).digest("hex");
|
|
178
|
+
const { sessionId, machineId } = await githubCopilotClientIds(accountKey);
|
|
128
179
|
const response = await fetcher(`https://${apiHost}/models`, {
|
|
129
|
-
headers: {
|
|
180
|
+
headers: {
|
|
181
|
+
accept: "application/json",
|
|
182
|
+
authorization: `Bearer ${access}`,
|
|
183
|
+
...GITHUB_REQUEST_HEADERS,
|
|
184
|
+
"VScode-SessionId": sessionId,
|
|
185
|
+
"VScode-MachineId": machineId,
|
|
186
|
+
"editor-device-id": machineId,
|
|
187
|
+
},
|
|
130
188
|
signal,
|
|
131
189
|
});
|
|
132
190
|
if (!response.ok) return null;
|
|
@@ -29,13 +29,31 @@ import type { OAuthTokenRefreshResult } from "../../authentication/oauth-refresh
|
|
|
29
29
|
import { OAuthDeviceFlow } from "../../authentication/oauth-device-flow";
|
|
30
30
|
import type { FetchLike } from "../../authentication/oauth-client";
|
|
31
31
|
import { resolveGithubEnterpriseDomain } from "../../../config/providers";
|
|
32
|
+
import { randomUUID, createHash } from "node:crypto";
|
|
33
|
+
import { getOrCreateInstallId } from "../shared/install-id";
|
|
32
34
|
|
|
33
35
|
|
|
34
36
|
/** Public GitHub OAuth app id the Copilot clients use; not a secret. */
|
|
35
37
|
export const GITHUB_CLIENT_ID = "Iv1.b507a08c87ecfe98" as const;
|
|
38
|
+
/** The OpenCode desktop client's own GitHub OAuth app id (used by its Copilot integration). */
|
|
39
|
+
export const GITHUB_OPENCODE_CLIENT_ID = "Ov23li8tweQw6odWQebz" as const;
|
|
36
40
|
export const GITHUB_ACCESS_TOKEN_URL = "https://github.com/login/oauth/access_token" as const;
|
|
37
41
|
/** Default API host when a token carries no `proxy-ep` claim. */
|
|
38
42
|
export const GITHUB_DEFAULT_API_HOST = "api.individual.githubcopilot.com" as const;
|
|
43
|
+
/**
|
|
44
|
+
* The Copilot API host OpenCode's integration uses. The reference plugin always
|
|
45
|
+
* dials `api.githubcopilot.com` for GitHub.com (never the per-claim
|
|
46
|
+
* `api.individual.*` host the VS Code minted tokens carry), so an OpenCode
|
|
47
|
+
* login pins this host rather than falling back to the VS Code default.
|
|
48
|
+
*/
|
|
49
|
+
export const GITHUB_OPENCODE_API_HOST = "api.githubcopilot.com" as const;
|
|
50
|
+
/**
|
|
51
|
+
* How long an OpenCode login's credential is treated as valid before the
|
|
52
|
+
* refresh sweep revisits it. The raw GitHub OAuth token has no Copilot token
|
|
53
|
+
* expiry (there is no minted token), so this is a far-future safeguard, not a
|
|
54
|
+
* real lifetime: a re-login or upstream expiry is what actually rotates it.
|
|
55
|
+
*/
|
|
56
|
+
const GITHUB_OPENCODE_TOKEN_LIFETIME_MS = 365 * 24 * 60 * 60 * 1000;
|
|
39
57
|
|
|
40
58
|
/**
|
|
41
59
|
* Headers every Copilot inference and policy request must carry.
|
|
@@ -43,17 +61,64 @@ export const GITHUB_DEFAULT_API_HOST = "api.individual.githubcopilot.com" as con
|
|
|
43
61
|
* The API rejects a request that does not identify a Copilot client, so these
|
|
44
62
|
* are a protocol requirement rather than cosmetic fingerprinting — unlike the
|
|
45
63
|
* Codex/Claude CLI identities elsewhere in this repository, which exist to look
|
|
46
|
-
* like a first-party client. The version pair is the Copilot Chat extension's
|
|
47
|
-
* which is what the upstream expects for this
|
|
64
|
+
* like a first-party client. The version pair is the Copilot Chat extension's
|
|
65
|
+
* (0.72.0 on VS Code 1.144), which is what the upstream expects for this
|
|
66
|
+
* client id.
|
|
48
67
|
*/
|
|
49
68
|
export const GITHUB_REQUEST_HEADERS: Readonly<Record<string, string>> = Object.freeze({
|
|
50
|
-
"user-agent": "GitHubCopilotChat/0.
|
|
51
|
-
"editor-version": "vscode/1.
|
|
52
|
-
"editor-plugin-version": "copilot-chat/0.
|
|
69
|
+
"user-agent": "GitHubCopilotChat/0.72.0",
|
|
70
|
+
"editor-version": "vscode/1.144.0",
|
|
71
|
+
"editor-plugin-version": "copilot-chat/0.72.0",
|
|
53
72
|
"copilot-integration-id": "vscode-chat",
|
|
54
73
|
"x-github-api-version": "2026-08-01",
|
|
55
74
|
});
|
|
56
75
|
|
|
76
|
+
/**
|
|
77
|
+
* Stable per-process Copilot session id.
|
|
78
|
+
*
|
|
79
|
+
* The reference extension (`_mixinHeaders`) stamps `VScode-SessionId` with the
|
|
80
|
+
* VS Code window session and `VScode-MachineId` with the machine id. There is
|
|
81
|
+
* no VS Code window here, so the closest 1:1 shapes are: a session id minted
|
|
82
|
+
* once per gateway process (a new window = a new process in the reference), and
|
|
83
|
+
* a stable per-installation identity for the machine id.
|
|
84
|
+
*/
|
|
85
|
+
export const GITHUB_COPILOT_SESSION_ID = randomUUID();
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Resolves the Copilot client identity headers shared by chat/auto/discovery.
|
|
89
|
+
*
|
|
90
|
+
* The machine id is derived deterministically from the persisted install id and
|
|
91
|
+
* an account fingerprint when one is provided. The reference extension uses one
|
|
92
|
+
* machine id per VS Code installation for every account, but this gateway pools
|
|
93
|
+
* many accounts on one host — sharing one id across all of them makes every
|
|
94
|
+
* account look like a single device (an easy clustering/abuse signal). A
|
|
95
|
+
* per-account machine id keeps each account looking like its own device while
|
|
96
|
+
* staying stable across restarts for the same account. Without an account key
|
|
97
|
+
* (e.g. credential could not be parsed) the raw install id is used.
|
|
98
|
+
*/
|
|
99
|
+
export async function githubCopilotClientIds(
|
|
100
|
+
accountKey?: string,
|
|
101
|
+
): Promise<{
|
|
102
|
+
readonly sessionId: string;
|
|
103
|
+
readonly machineId: string;
|
|
104
|
+
}> {
|
|
105
|
+
const installId = await getOrCreateInstallId("github");
|
|
106
|
+
const machineId =
|
|
107
|
+
accountKey === undefined || accountKey.length === 0
|
|
108
|
+
? installId
|
|
109
|
+
: deterministicMachineId(installId, accountKey);
|
|
110
|
+
return {
|
|
111
|
+
sessionId: GITHUB_COPILOT_SESSION_ID,
|
|
112
|
+
machineId,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Stable UUID-shaped machine id derived from the install id and an account key. */
|
|
117
|
+
export function deterministicMachineId(installId: string, accountKey: string): string {
|
|
118
|
+
const digest = createHash("sha256").update(`${installId}:${accountKey}`).digest("hex");
|
|
119
|
+
return `${digest.slice(0, 8)}-${digest.slice(8, 12)}-${digest.slice(12, 16)}-${digest.slice(16, 20)}-${digest.slice(20, 32)}`;
|
|
120
|
+
}
|
|
121
|
+
|
|
57
122
|
const DEVICE_INTERVAL_SECONDS = 5;
|
|
58
123
|
const DEVICE_EXPIRY_SECONDS = 900;
|
|
59
124
|
/** Copilot tokens are short-lived; a missing `expires_at` falls back to 25 minutes. */
|
|
@@ -195,17 +260,80 @@ async function fetchGitHubLogin(
|
|
|
195
260
|
}
|
|
196
261
|
}
|
|
197
262
|
|
|
263
|
+
interface CopilotUserProfile {
|
|
264
|
+
readonly chat_enabled?: boolean;
|
|
265
|
+
readonly can_signup_for_limited?: boolean;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Reads the Copilot entitlement profile for a freshly approved GitHub token.
|
|
270
|
+
*
|
|
271
|
+
* OAuth itself succeeds for any GitHub account; entitlement is the only signal
|
|
272
|
+
* that the account can chat. Mirrors the reference OpenCode provider: an
|
|
273
|
+
* explicit `chat_enabled: false` is a denial (the account is not signed up or
|
|
274
|
+
* has no seat), and a failed/malformed lookup must not turn a GitHub hiccup
|
|
275
|
+
* into a denial — only an explicit answer blocks login.
|
|
276
|
+
*/
|
|
277
|
+
async function fetchGitHubCopilotEntitlement(
|
|
278
|
+
githubAccessToken: string,
|
|
279
|
+
domain: string,
|
|
280
|
+
fetchFn: FetchLike,
|
|
281
|
+
signal: AbortSignal,
|
|
282
|
+
): Promise<{ denied: string } | { denied?: undefined }> {
|
|
283
|
+
try {
|
|
284
|
+
const endpoint =
|
|
285
|
+
domain === "github.com"
|
|
286
|
+
? "https://api.github.com/copilot_internal/user"
|
|
287
|
+
: `https://api.${domain}/copilot_internal/user`;
|
|
288
|
+
const response = await fetchFn(endpoint, {
|
|
289
|
+
headers: {
|
|
290
|
+
accept: "application/json",
|
|
291
|
+
authorization: `Bearer ${githubAccessToken}`,
|
|
292
|
+
"user-agent": GITHUB_REQUEST_HEADERS["user-agent"]!,
|
|
293
|
+
"x-github-api-version": "2025-04-01",
|
|
294
|
+
},
|
|
295
|
+
signal,
|
|
296
|
+
});
|
|
297
|
+
if (!response.ok) return { denied: undefined };
|
|
298
|
+
const profile = record(await response.json()) as CopilotUserProfile | undefined;
|
|
299
|
+
if (profile?.chat_enabled === false) {
|
|
300
|
+
if (profile.can_signup_for_limited) {
|
|
301
|
+
return {
|
|
302
|
+
denied:
|
|
303
|
+
"This GitHub account is not signed up for GitHub Copilot. Sign up for Copilot Free at https://github.com/features/copilot/plans and connect again.",
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
return {
|
|
307
|
+
denied:
|
|
308
|
+
"This GitHub account does not have GitHub Copilot access. It needs an active Copilot subscription or a seat assigned by an organization.",
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
return { denied: undefined };
|
|
312
|
+
} catch {
|
|
313
|
+
// Entitlement is a gate, not a reliability dependency: a network glitch
|
|
314
|
+
// here must not lock out an account that did approve the login.
|
|
315
|
+
return { denied: undefined };
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
198
319
|
export class GithubOAuthClient extends OAuthDeviceFlow {
|
|
199
320
|
override readonly supportsDeviceCode = true;
|
|
200
321
|
override readonly supportsBrowserCode = false;
|
|
201
322
|
|
|
202
323
|
protected override readonly providerLabel = "GitHub Copilot";
|
|
203
|
-
protected override readonly clientId = GITHUB_CLIENT_ID;
|
|
204
324
|
protected override readonly tokenUrl = GITHUB_ACCESS_TOKEN_URL;
|
|
205
325
|
protected override readonly scopes = "read:user";
|
|
206
326
|
|
|
207
|
-
|
|
327
|
+
/** Which GitHub OAuth app this flow presents. Defaults to the Copilot Chat
|
|
328
|
+
* extension's app; OpenCode's own app differs (`GITHUB_OPENCODE_CLIENT_ID`). */
|
|
329
|
+
protected override readonly clientId: string;
|
|
330
|
+
|
|
331
|
+
constructor(
|
|
332
|
+
fetchFn: FetchLike = globalThis.fetch,
|
|
333
|
+
clientId: string = GITHUB_CLIENT_ID,
|
|
334
|
+
) {
|
|
208
335
|
super(fetchFn);
|
|
336
|
+
this.clientId = clientId;
|
|
209
337
|
}
|
|
210
338
|
|
|
211
339
|
override async startDeviceAuth(_context?: OAuthDeviceFlowContext): Promise<OAuthDeviceStartResult> {
|
|
@@ -217,7 +345,7 @@ export class GithubOAuthClient extends OAuthDeviceFlow {
|
|
|
217
345
|
"content-type": "application/x-www-form-urlencoded",
|
|
218
346
|
"user-agent": GITHUB_REQUEST_HEADERS["user-agent"]!,
|
|
219
347
|
},
|
|
220
|
-
body: new URLSearchParams({ client_id:
|
|
348
|
+
body: new URLSearchParams({ client_id: this.clientId, scope: "read:user" }),
|
|
221
349
|
});
|
|
222
350
|
const body = record(await readJsonResponse(response, "GitHub device authorization"));
|
|
223
351
|
const deviceCode = nonEmptyTrimmedString(body?.device_code);
|
|
@@ -271,7 +399,7 @@ export class GithubOAuthClient extends OAuthDeviceFlow {
|
|
|
271
399
|
"user-agent": GITHUB_REQUEST_HEADERS["user-agent"]!,
|
|
272
400
|
},
|
|
273
401
|
body: new URLSearchParams({
|
|
274
|
-
client_id:
|
|
402
|
+
client_id: this.clientId,
|
|
275
403
|
device_code: deviceAuthId,
|
|
276
404
|
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
277
405
|
}),
|
|
@@ -314,21 +442,48 @@ export class GithubOAuthClient extends OAuthDeviceFlow {
|
|
|
314
442
|
: `GitHub device polling failed (${response.status})`;
|
|
315
443
|
return { status: "failed", reason };
|
|
316
444
|
}
|
|
317
|
-
let minted;
|
|
445
|
+
let minted: Awaited<ReturnType<typeof mintGithubToken>> | undefined;
|
|
318
446
|
try {
|
|
319
|
-
|
|
447
|
+
// OAuth succeeds for any GitHub account; the entitlement profile is the
|
|
448
|
+
// only signal that the account can actually chat. An explicit denial is
|
|
449
|
+
// surfaced as a failed verdict before any token is minted or stored,
|
|
450
|
+
// mirroring the reference OpenCode provider.
|
|
451
|
+
const entitlement = await fetchGitHubCopilotEntitlement(
|
|
452
|
+
githubToken,
|
|
453
|
+
domain,
|
|
454
|
+
this.fetchFn,
|
|
455
|
+
AbortSignal.timeout(10_000),
|
|
456
|
+
);
|
|
457
|
+
if (entitlement.denied !== undefined) {
|
|
458
|
+
return { status: "failed", reason: entitlement.denied };
|
|
459
|
+
}
|
|
460
|
+
// OpenCode's OAuth app is the client the `opencode-copilot` wire mode
|
|
461
|
+
// presents upstream, and that mode uses the raw GitHub token directly —
|
|
462
|
+
// no minted Copilot token. The VS Code mint endpoint
|
|
463
|
+
// (`/copilot_internal/v2/token`) rejects non-VS-Code client ids with 403
|
|
464
|
+
// (GitHub's anti-scraping gate), so an OpenCode login must not mint.
|
|
465
|
+
if (this.clientId !== GITHUB_OPENCODE_CLIENT_ID) {
|
|
466
|
+
minted = await mintGithubToken(githubToken, domain, this.fetchFn);
|
|
467
|
+
}
|
|
320
468
|
} catch (error) {
|
|
321
469
|
// The mint runs after approval: surface its reason as a failed verdict,
|
|
322
470
|
// not an exception that escapes the dashboard's poll request.
|
|
323
471
|
return { status: "failed", reason: error instanceof Error ? error.message : "GitHub Copilot token mint failed" };
|
|
324
472
|
}
|
|
325
473
|
const label = await fetchGitHubLogin(githubToken, domain, this.fetchFn, AbortSignal.timeout(10_000));
|
|
474
|
+
const access = minted === undefined ? githubToken : minted.access;
|
|
475
|
+
const apiHost = minted === undefined ? GITHUB_OPENCODE_API_HOST : minted.apiHost;
|
|
476
|
+
const expiresAt =
|
|
477
|
+
minted === undefined
|
|
478
|
+
? new Date(Date.now() + GITHUB_OPENCODE_TOKEN_LIFETIME_MS)
|
|
479
|
+
: minted.expiresAt;
|
|
326
480
|
const result: OAuthExchangeResult = {
|
|
327
|
-
access: encodeGithubCredential(
|
|
481
|
+
access: encodeGithubCredential(access, apiHost, githubToken),
|
|
328
482
|
// The GitHub token is the refresh value: it is what re-mints the Copilot
|
|
329
|
-
// token
|
|
483
|
+
// token (VS Code app) or is the raw credential itself (OpenCode app),
|
|
484
|
+
// depending on which app the flow presented.
|
|
330
485
|
refresh: githubToken,
|
|
331
|
-
expiresAt
|
|
486
|
+
expiresAt,
|
|
332
487
|
...(label === undefined ? {} : { accountLabel: label }),
|
|
333
488
|
};
|
|
334
489
|
return { status: "complete", result };
|
|
@@ -336,6 +491,21 @@ export class GithubOAuthClient extends OAuthDeviceFlow {
|
|
|
336
491
|
|
|
337
492
|
|
|
338
493
|
override async refresh(refreshToken: string, signal?: AbortSignal): Promise<OAuthTokenRefreshResult> {
|
|
494
|
+
// OpenCode's app uses the raw GitHub token directly — there is no minted
|
|
495
|
+
// Copilot token to re-mint, and the VS Code mint endpoint rejects the
|
|
496
|
+
// OpenCode client id. Refresh is then a no-op that keeps the credential
|
|
497
|
+
// valid on its far-future lifetime.
|
|
498
|
+
if (this.clientId === GITHUB_OPENCODE_CLIENT_ID) {
|
|
499
|
+
return {
|
|
500
|
+
access: encodeGithubCredential(
|
|
501
|
+
refreshToken,
|
|
502
|
+
GITHUB_OPENCODE_API_HOST,
|
|
503
|
+
refreshToken,
|
|
504
|
+
),
|
|
505
|
+
refresh: refreshToken,
|
|
506
|
+
expiresAt: new Date(Date.now() + GITHUB_OPENCODE_TOKEN_LIFETIME_MS),
|
|
507
|
+
};
|
|
508
|
+
}
|
|
339
509
|
const minted = await mintGithubToken(
|
|
340
510
|
refreshToken,
|
|
341
511
|
copilotGitHubDomain(),
|