cartethyia 2.7.3 → 2.7.5
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 +2 -0
- package/README.md +17 -5
- package/dist/dashboard/assets/{BannedPage-2TJD37Hb.js → BannedPage-Cj15GsMp.js} +1 -1
- package/dist/dashboard/assets/BannedPage-Cj15GsMp.js.gz +0 -0
- package/dist/dashboard/assets/ClaimGiftsButton-D83pBJ6J.js +1 -0
- package/dist/dashboard/assets/ClaimGiftsButton-D83pBJ6J.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolDetailPage-xefJNRmJ.js → CliToolDetailPage-DZKmVuJN.js} +1 -1
- package/dist/dashboard/assets/CliToolDetailPage-DZKmVuJN.js.gz +0 -0
- package/dist/dashboard/assets/{CliToolsPage-Rj9iHQ3F.js → CliToolsPage-BIccZmeL.js} +1 -1
- package/dist/dashboard/assets/CliToolsPage-BIccZmeL.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-DEEKkYD-.js +1 -0
- package/dist/dashboard/assets/CombosPage-DEEKkYD-.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-D5rol2j2.js +1 -0
- package/dist/dashboard/assets/ConsoleLogPage-D5rol2j2.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-BKj_r0rp.js +1 -0
- package/dist/dashboard/assets/CustomizationPage-BKj_r0rp.js.gz +0 -0
- package/dist/dashboard/assets/InjectSystemPromptPanel-B5nZvmf0.js +656 -0
- package/dist/dashboard/assets/InjectSystemPromptPanel-B5nZvmf0.js.gz +0 -0
- package/dist/dashboard/assets/{LoginPage-C4w7vea-.js → LoginPage-DkXb6dx-.js} +1 -1
- package/dist/dashboard/assets/LoginPage-DkXb6dx-.js.gz +0 -0
- package/dist/dashboard/assets/{ModelPicker-BtPcAfot.js → ModelPicker-DnQPSIA8.js} +1 -1
- package/dist/dashboard/assets/ModelPicker-DnQPSIA8.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-Ci2wQKUz.js +1 -0
- package/dist/dashboard/assets/OverviewPage-Ci2wQKUz.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-odlgYrOg.js +5 -0
- package/dist/dashboard/assets/ProviderDetailPage-odlgYrOg.js.gz +0 -0
- package/dist/dashboard/assets/ProviderIcon-EfeloBn4.js +1 -0
- package/dist/dashboard/assets/ProviderIcon-EfeloBn4.js.gz +0 -0
- package/dist/dashboard/assets/ProvidersPage-CqFBQEB8.js +1 -0
- package/dist/dashboard/assets/ProvidersPage-CqFBQEB8.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-5SnUSy3j.js +9 -0
- package/dist/dashboard/assets/ProxyPage-5SnUSy3j.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-CmnMDzhP.js +1 -0
- package/dist/dashboard/assets/QuotaPage-CmnMDzhP.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-BJtRZ4qD.js +1 -0
- package/dist/dashboard/assets/SettingsPage-BJtRZ4qD.js.gz +0 -0
- package/dist/dashboard/assets/{SetupPage-BQS0Rx_u.js → SetupPage-WTa0sktC.js} +1 -1
- package/dist/dashboard/assets/SetupPage-WTa0sktC.js.gz +0 -0
- package/dist/dashboard/assets/{StudioPage-TP7voAQ0.js → StudioPage-BfAb5-o_.js} +3 -3
- package/dist/dashboard/assets/StudioPage-BfAb5-o_.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-BNzeZ2Vk.js +56 -0
- package/dist/dashboard/assets/UsagePage-BNzeZ2Vk.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-l29JROPv.js +1 -0
- package/dist/dashboard/assets/api-keys-l29JROPv.js.gz +0 -0
- package/dist/dashboard/assets/app--PJWq8_C.css +2 -0
- package/dist/dashboard/assets/app--PJWq8_C.css.gz +0 -0
- package/dist/dashboard/assets/{assert-C0YYuZJ4.js → assert-C0K-c4eo.js} +1 -1
- package/dist/dashboard/assets/assert-C0K-c4eo.js.gz +0 -0
- package/dist/dashboard/assets/{badge-B_DMpMJN.js → badge-DfbhQU4k.js} +1 -1
- package/dist/dashboard/assets/badge-DfbhQU4k.js.gz +0 -0
- package/dist/dashboard/assets/{banner-DMrMPmth.js → banner-BllT4LcF.js} +1 -1
- package/dist/dashboard/assets/banner-BllT4LcF.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-Be5Uzamx.js → card-CtkX66Wd.js} +1 -1
- package/dist/dashboard/assets/card-CtkX66Wd.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/{cli-tools-muFRphDu.js → cli-tools-CQXVMn4P.js} +1 -1
- package/dist/dashboard/assets/cli-tools-CQXVMn4P.js.gz +0 -0
- package/dist/dashboard/assets/{clipboard-button-DOfrVmsi.js → clipboard-button-BQqBQd2n.js} +1 -1
- package/dist/dashboard/assets/clipboard-button-BQqBQd2n.js.gz +0 -0
- package/dist/dashboard/assets/{dialog-C9omdjQt.js → dialog-BEK7KP0H.js} +1 -1
- package/dist/dashboard/assets/dialog-BEK7KP0H.js.gz +0 -0
- package/dist/dashboard/assets/{drawer-Bm8WKaGY.js → drawer-BrTEZ8zu.js} +1 -1
- package/dist/dashboard/assets/drawer-BrTEZ8zu.js.gz +0 -0
- package/dist/dashboard/assets/entry-CHWxbS56.js +1 -0
- package/dist/dashboard/assets/entry-CHWxbS56.js.gz +0 -0
- package/dist/dashboard/assets/{entry-q5HwZ8nN.js → entry-EBHJ3DrE.js} +1 -1
- package/dist/dashboard/assets/entry-EBHJ3DrE.js.gz +0 -0
- package/dist/dashboard/assets/entry-LCS85hiq.js +4 -0
- package/dist/dashboard/assets/entry-LCS85hiq.js.gz +0 -0
- package/dist/dashboard/assets/{formatters-BTScrQpe.js → formatters-BAACA727.js} +1 -1
- package/dist/dashboard/assets/formatters-BAACA727.js.gz +0 -0
- package/dist/dashboard/assets/{github-badge-Orjuzemr.js → github-badge-YSCE4UC2.js} +1 -1
- package/dist/dashboard/assets/github-badge-YSCE4UC2.js.gz +0 -0
- package/dist/dashboard/assets/{index-cPrX5vn6.js → index-FQlj0HUT.js} +2 -2
- package/dist/dashboard/assets/index-FQlj0HUT.js.gz +0 -0
- package/dist/dashboard/assets/{inline-Dvux8w-0.js → inline-C4gU5iOL.js} +1 -1
- package/dist/dashboard/assets/inline-C4gU5iOL.js.gz +0 -0
- package/dist/dashboard/assets/{input-CU1Rgf3p.js → input-BHHX_Tmm.js} +1 -1
- package/dist/dashboard/assets/input-BHHX_Tmm.js.gz +0 -0
- package/dist/dashboard/assets/{layout-C_7A6H55.js → layout-CajrYUnD.js} +1 -1
- package/dist/dashboard/assets/{layout-C_7A6H55.js.gz → layout-CajrYUnD.js.gz} +0 -0
- package/dist/dashboard/assets/{list-row-CzFvogbj.js → list-row-Bx2I7XUe.js} +1 -1
- package/dist/dashboard/assets/list-row-Bx2I7XUe.js.gz +0 -0
- package/dist/dashboard/assets/live-DNNqRbj2.js +1 -0
- package/dist/dashboard/assets/live-DNNqRbj2.js.gz +0 -0
- package/dist/dashboard/assets/minimize-2-BTmMTK4o.js +1 -0
- package/dist/dashboard/assets/minimize-2-BTmMTK4o.js.gz +0 -0
- package/dist/dashboard/assets/{mutation-JJGQm8TD.js → mutation-Rr0TIvcj.js} +1 -1
- package/dist/dashboard/assets/mutation-Rr0TIvcj.js.gz +0 -0
- package/dist/dashboard/assets/network-C5UBACrJ.js +1 -0
- package/dist/dashboard/assets/network-C5UBACrJ.js.gz +0 -0
- package/dist/dashboard/assets/numbers-CUWAZK4J.js +2 -0
- package/dist/dashboard/assets/numbers-CUWAZK4J.js.gz +0 -0
- package/dist/dashboard/assets/pagination-Xsi9Qx04.js +1 -0
- package/dist/dashboard/assets/pagination-Xsi9Qx04.js.gz +0 -0
- package/dist/dashboard/assets/{query-keys-CLaNz8Xz.js → query-keys-BloKtaXJ.js} +1 -1
- package/dist/dashboard/assets/query-keys-BloKtaXJ.js.gz +0 -0
- package/dist/dashboard/assets/quota-DvQbD_sr.js +1 -0
- package/dist/dashboard/assets/quota-DvQbD_sr.js.gz +0 -0
- package/dist/dashboard/assets/{routing-BEH32QOC.js → routing-in0f_IfK.js} +1 -1
- package/dist/dashboard/assets/routing-in0f_IfK.js.gz +0 -0
- package/dist/dashboard/assets/{select-B3iTetHN.js → select-BLULNqTp.js} +2 -2
- package/dist/dashboard/assets/select-BLULNqTp.js.gz +0 -0
- package/dist/dashboard/assets/settings-CcGczj6I.js +1 -0
- package/dist/dashboard/assets/settings-CcGczj6I.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-KrOrm7JZ.js → stack-D2lIR0tg.js} +1 -1
- package/dist/dashboard/assets/stack-D2lIR0tg.js.gz +0 -0
- package/dist/dashboard/assets/{state-DLS7IDMy.js → state-DTARjfiW.js} +1 -1
- package/dist/dashboard/assets/state-DTARjfiW.js.gz +0 -0
- package/dist/dashboard/assets/{switch-42ItvbAs.js → switch-c0oPSnqW.js} +1 -1
- package/dist/dashboard/assets/switch-c0oPSnqW.js.gz +0 -0
- package/dist/dashboard/assets/{useMutation-Dmnfh1Ue.js → useMutation-m8WGNPrz.js} +1 -1
- package/dist/dashboard/assets/useMutation-m8WGNPrz.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 +59 -57
- package/migrations/full-postgres/0027_qoder_identity_genuine_seed.sql +9 -10
- package/migrations/full-postgres/0032_add_provider_pool_accounts.sql +53 -0
- package/migrations/full-postgres/0033_add_api_key_ban.sql +9 -0
- package/migrations/full-postgres/0034_add_api_key_ban_until.sql +7 -0
- package/migrations/full-postgres/0035_align_pool_account_status_enum.sql +21 -0
- 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/lite-sqlite3/0027_qoder_identity_genuine_seed.sql +9 -10
- package/migrations/lite-sqlite3/0032_add_provider_pool_accounts.sql +44 -0
- package/migrations/lite-sqlite3/0033_add_api_key_ban.sql +8 -0
- package/migrations/lite-sqlite3/0034_add_api_key_ban_until.sql +5 -0
- package/migrations/lite-sqlite3/0035_align_pool_account_status_enum.sql +6 -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/package.json +4 -5
- package/scripts/cli/update.ts +15 -41
- package/src/boot/app.ts +67 -10
- package/src/boot/dependencies.ts +786 -732
- package/src/boot/lifecycle.ts +11 -12
- package/src/config/cooldowns.ts +34 -55
- package/src/config/network.ts +8 -4
- package/src/config/package-paths.ts +1 -1
- package/src/config/providers.ts +13 -14
- package/src/config/resources.ts +0 -5
- package/src/config/runtime.ts +2 -5
- package/src/config/spec.ts +26 -6
- package/src/console/api-keys/ban-constants.ts +38 -0
- package/src/console/api-keys/contracts.ts +92 -31
- package/src/console/api-keys/routes.ts +131 -6
- package/src/console/audit/routes.ts +3 -3
- package/src/console/auth/access.ts +4 -4
- package/src/console/auth/account-reset.ts +5 -5
- 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 +40 -34
- 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 +11 -9
- package/src/console/backup/export-endpoint.ts +11 -12
- package/src/console/backup/nine-router.ts +1 -7
- package/src/console/backup/routes.ts +32 -36
- 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 +7 -7
- package/src/console/cli-tools/injectors/cline.ts +110 -110
- package/src/console/cli-tools/injectors/codex.ts +181 -181
- package/src/console/cli-tools/injectors/copilot.ts +108 -108
- package/src/console/cli-tools/injectors/driver.ts +247 -247
- package/src/console/cli-tools/injectors/jcode.ts +115 -115
- package/src/console/cli-tools/injectors/openclaw.ts +161 -161
- package/src/console/cli-tools/injectors/opencode.ts +153 -153
- package/src/console/cli-tools/routes.ts +4 -4
- package/src/console/cli-tools/service.ts +1 -1
- package/src/console/cli-tools/store.ts +2 -2
- package/src/console/console-router-guards.ts +2 -2
- package/src/console/console-router.ts +36 -4
- package/src/console/dashboard-assets.ts +9 -9
- package/src/console/data-management/routes.ts +3 -3
- package/src/console/domain-registration.ts +18 -4
- 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 +5 -1
- package/src/console/observability/live.ts +34 -21
- package/src/console/observability/logs.ts +4 -4
- package/src/console/observability/performance.ts +3 -3
- package/src/console/observability/routes.ts +3 -3
- package/src/console/observability/sse.ts +62 -8
- package/src/console/observability/store.ts +36 -20
- package/src/console/providers/catalog/catalog-projections.ts +1 -1
- package/src/console/providers/catalog/contracts.ts +82 -22
- package/src/console/providers/catalog/model-operations.ts +1 -1
- package/src/console/providers/catalog/provider-operations.ts +110 -17
- package/src/console/providers/catalog/public-model-store.ts +0 -0
- package/src/console/providers/catalog/routes.ts +30 -6
- package/src/console/providers/catalog/store-helpers.ts +349 -326
- package/src/console/providers/catalog/store.ts +1662 -1292
- package/src/console/providers/detail/routes.ts +4 -4
- package/src/console/providers/oauth/callback-listener.ts +100 -18
- package/src/console/providers/oauth/routes.ts +95 -16
- 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 +285 -67
- package/src/console/quota/account-test-egress.ts +4 -4
- 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/model/routes.ts +3 -3
- package/src/console/routing/pools/contracts.ts +13 -10
- package/src/console/routing/pools/import-parse.ts +3 -3
- package/src/console/routing/pools/operations.ts +14 -7
- package/src/console/routing/pools/probe-verdict.ts +8 -3
- package/src/console/routing/pools/relay-deploy.ts +12 -12
- package/src/console/routing/pools/routes.ts +3 -3
- package/src/console/routing/pools/store.ts +21 -1
- package/src/console/settings/contracts.ts +29 -2
- package/src/console/settings/routes.ts +3 -3
- package/src/console/settings/store.ts +20 -0
- package/src/console/share/share-router.ts +112 -44
- package/src/console/share/share-stats.ts +109 -39
- 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 +42 -6
- 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 +256 -98
- 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 +76 -5
- package/src/network/pool/agent.ts +7 -6
- package/src/network/pool/byte-accounting.ts +1 -1
- package/src/network/pool/resolver.ts +55 -51
- package/src/network/pool/selector.ts +150 -136
- package/src/network/pool-cooldown-store.ts +71 -16
- package/src/network/pool-health-machine.ts +22 -2
- package/src/network/rate-limit-verdict.ts +68 -0
- package/src/network/retry.ts +1 -1
- package/src/network/ssrf.ts +24 -75
- package/src/observability/event-log.ts +2 -2
- package/src/observability/event-query.ts +286 -214
- 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 +12 -3
- package/src/observability/telemetry-reconciliation.ts +3 -3
- package/src/observability/telemetry-status.ts +1 -1
- 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 +10 -5
- package/src/persistence/dialect.ts +5 -1
- package/src/persistence/readiness.ts +19 -7
- package/src/persistence/redis.ts +1 -1
- package/src/persistence/schema.ts +177 -45
- package/src/persistence/shared-cache.ts +2 -2
- package/src/persistence/sort-order.ts +1 -1
- package/src/persistence/stores/api-key-store.ts +109 -4
- package/src/persistence/stores/share-store.ts +20 -8
- package/src/persistence/stores/telemetry-store.ts +1 -1
- 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 +12 -16
- package/src/protocol/dsml.ts +3 -49
- package/src/protocol/gateway-error.ts +6 -3
- package/src/protocol/image-clamp.ts +2 -2
- package/src/protocol/image-generation.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 +27 -12
- package/src/protocol/response/codex.ts +2 -2
- package/src/protocol/response/gemini.ts +1 -1
- package/src/protocol/response/messages.ts +6 -6
- package/src/protocol/response/omp-tool-call-markup.ts +1 -1
- package/src/protocol/response/responses.ts +3 -3
- package/src/protocol/sse-decoder.ts +64 -4
- 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 +8 -8
- 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/protocol/upstream/send-claude-messages.ts +3 -3
- package/src/providers/adapters/base-adapter.ts +1 -1
- package/src/providers/adapters/compatible-adapter.ts +44 -5
- 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/authentication/oauth-refresh-service.ts +2 -2
- package/src/providers/bundled-discovery-providers.ts +6 -30
- package/src/providers/configured-openai-providers.ts +82 -97
- package/src/providers/configured-provider.ts +1 -1
- package/src/providers/default-registry.ts +23 -35
- 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 +50 -15
- package/src/providers/discovery/probing-service.ts +18 -20
- package/src/providers/integrations/agentrouter/agentrouter.ts +14 -5
- package/src/providers/integrations/alibabacp/alibabacp.ts +2 -2
- package/src/providers/integrations/anthropic.ts +4 -11
- package/src/providers/integrations/antigravity/antigravity-oauth.ts +0 -1
- package/src/providers/integrations/antigravity/antigravity-protocol.ts +0 -14
- package/src/providers/integrations/antigravity/antigravity.ts +15 -26
- package/src/providers/integrations/buddy/buddy-catalog-shared.ts +2 -2
- 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-discovery-shared.ts +37 -6
- package/src/providers/integrations/buddy/buddy-identity.ts +108 -1
- package/src/providers/integrations/buddy/buddy-media.ts +16 -16
- 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 +5 -16
- package/src/providers/integrations/buddy/codebuddy/shared.ts +30 -9
- package/src/providers/integrations/buddy/workbuddy/provider.ts +6 -10
- package/src/providers/integrations/buddy/workbuddy/shared.ts +21 -3
- package/src/providers/integrations/claude/claude-betas.ts +1 -1
- package/src/providers/integrations/claude/claude-cch.ts +148 -148
- package/src/providers/integrations/claude/claude-credentials.ts +1 -1
- package/src/providers/integrations/claude/claude-slow-lane-retry.ts +1 -1
- package/src/providers/integrations/claude/claude.ts +3 -10
- package/src/providers/integrations/claude/fallback-credit.ts +3 -7
- package/src/providers/integrations/cline/cline.ts +2 -2
- package/src/providers/integrations/cloudflareai/cloudflareai.ts +4 -7
- package/src/providers/integrations/codex/codex-device-code.ts +101 -101
- package/src/providers/integrations/codex/codex-errors.ts +1 -1
- package/src/providers/integrations/codex/codex-oauth.ts +0 -1
- package/src/providers/integrations/codex/codex.ts +14 -15
- package/src/providers/integrations/commandcode/commandcode.ts +76 -116
- package/src/providers/integrations/commandcode/quota.ts +132 -48
- package/src/providers/integrations/cursor/cursor-http2.ts +11 -14
- 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 +53 -20
- 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 +13 -13
- package/src/providers/integrations/gemini.ts +11 -11
- package/src/providers/integrations/github/github-discovery.ts +55 -10
- package/src/providers/integrations/github/github-oauth.ts +6 -5
- package/src/providers/integrations/github/github.ts +47 -20
- 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 +88 -42
- package/src/providers/integrations/inferhub.ts +4 -11
- package/src/providers/integrations/kilo/kilo-oauth.ts +1 -1
- package/src/providers/integrations/kimi/kimi.ts +4 -11
- 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 +91 -78
- package/src/providers/integrations/muse/muse-oauth.ts +0 -1
- package/src/providers/integrations/muse/muse-quota.ts +0 -4
- package/src/providers/integrations/muse/muse.ts +6 -9
- package/src/providers/integrations/opencode/fingerprint.ts +7 -5
- package/src/providers/integrations/opencode/provider.ts +17 -24
- package/src/providers/integrations/perplexity.ts +2 -2
- package/src/providers/integrations/qoder/qoder-catalog.ts +132 -17
- package/src/providers/integrations/qoder/qoder-checkin.ts +26 -8
- package/src/providers/integrations/qoder/qoder-native-identity-store.ts +9 -8
- 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 +22 -28
- package/src/providers/integrations/qoder/qoder-sse.ts +159 -0
- package/src/providers/integrations/qoder/qoder-umid.ts +33 -38
- package/src/providers/integrations/qoder/qoder-virtual.ts +14 -2
- package/src/providers/integrations/qoder/qoder.ts +521 -63
- 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/tokenharbor.ts +2 -2
- 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 +59 -19
- package/src/providers/integrations/xai/xai-oauth.ts +5 -5
- package/src/providers/integrations/zcode/oauth.ts +1 -1
- 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 +131 -116
- package/src/providers/operations/account-health-service.ts +31 -39
- package/src/providers/operations/account-reset-service.ts +58 -1
- 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 +22 -6
- package/src/providers/operations/provider-credential-service.ts +35 -6
- package/src/providers/operations/upstream-deadline.ts +3 -3
- package/src/providers/provider-contracts.ts +65 -10
- package/src/providers/provider-credential-resolver.ts +1 -1
- 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-registry.ts +4 -4
- package/src/providers/provider-usage.ts +81 -16
- package/src/providers/quota/cache.ts +29 -0
- package/src/providers/quota/quota-contracts.ts +28 -10
- package/src/providers/quota/refresh.ts +29 -6
- package/src/providers/quota/snapshot.ts +94 -0
- package/src/providers/search/search-provider.ts +6 -8
- package/src/providers/search/search-providers.ts +2 -1
- package/src/security/abuse.ts +8 -8
- package/src/security/admission/reasons.ts +17 -17
- package/src/security/admission/service.ts +2 -2
- package/src/security/admission/ttl.ts +61 -61
- package/src/security/api-key-auth.ts +20 -8
- 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/coerce.ts +19 -0
- package/src/shared/invoke-guard.ts +47 -0
- package/src/transport/contracts.ts +13 -4
- package/src/transport/dispatch/attempt-finalize.ts +5 -4
- package/src/transport/dispatch/attempt-loop.ts +10 -7
- package/src/transport/dispatch/capture-policy.ts +34 -37
- package/src/transport/dispatch/empty-stream.ts +28 -25
- package/src/transport/dispatch/generation.ts +33 -53
- package/src/transport/dispatch/leases.ts +8 -8
- package/src/transport/dispatch/native-shared.ts +127 -0
- package/src/transport/dispatch/proxy-request.ts +10 -8
- package/src/transport/dispatch/responses-compact.ts +35 -56
- package/src/transport/dispatch/retry-policy.ts +4 -67
- package/src/transport/dispatch/stream-bridge.ts +1 -1
- package/src/transport/dispatch/streaming/encoder.ts +18 -72
- package/src/transport/dispatch/streaming/finalize.ts +11 -10
- 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 +27 -55
- package/src/transport/dispatch/token-count.ts +49 -46
- package/src/transport/dispatch/websearch.ts +24 -50
- package/src/transport/drain-endpoint.ts +3 -3
- package/src/transport/failure-policy.ts +19 -6
- package/src/transport/guards/auth-guard.ts +6 -11
- package/src/transport/guards/body-policy.ts +7 -7
- package/src/transport/guards/error-lifecycle.ts +18 -3
- package/src/transport/guards/guard-errors.ts +7 -3
- package/src/transport/guards/headroom-guard.ts +2 -2
- package/src/transport/guards/pipeline.ts +7 -7
- package/src/transport/guards/readiness-guard.ts +16 -2
- package/src/transport/guards/request-context.ts +15 -20
- package/src/transport/request/preparer.ts +306 -27
- 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 +5 -20
- 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 +113 -19
- package/src/transport/routing/route-model.ts +131 -38
- package/src/transport/routing/router.ts +154 -39
- package/src/transport/shutdown-notice.ts +21 -9
- 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 +3 -3
- package/src/transport/surface/content-parts.ts +1 -1
- package/src/transport/surface/dialects.ts +2 -2
- package/src/transport/surface/messages/encode.ts +1 -1
- package/src/transport/surface/messages/parse.ts +3 -3
- 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 +376 -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-2TJD37Hb.js.gz +0 -0
- package/dist/dashboard/assets/CliToolDetailPage-xefJNRmJ.js.gz +0 -0
- package/dist/dashboard/assets/CliToolsPage-Rj9iHQ3F.js.gz +0 -0
- package/dist/dashboard/assets/CombosPage-JEbYJFxw.js +0 -1
- package/dist/dashboard/assets/CombosPage-JEbYJFxw.js.gz +0 -0
- package/dist/dashboard/assets/ConsoleLogPage-C3xx_inM.js +0 -1
- package/dist/dashboard/assets/ConsoleLogPage-C3xx_inM.js.gz +0 -0
- package/dist/dashboard/assets/CustomizationPage-Dw306gJU.js +0 -1
- package/dist/dashboard/assets/CustomizationPage-Dw306gJU.js.gz +0 -0
- package/dist/dashboard/assets/HealthEventsModal-jaBo1TWV.js +0 -1
- package/dist/dashboard/assets/HealthEventsModal-jaBo1TWV.js.gz +0 -0
- package/dist/dashboard/assets/LoginPage-C4w7vea-.js.gz +0 -0
- package/dist/dashboard/assets/ModelPicker-BtPcAfot.js.gz +0 -0
- package/dist/dashboard/assets/OverviewPage-CGC5gNs1.js +0 -1
- package/dist/dashboard/assets/OverviewPage-CGC5gNs1.js.gz +0 -0
- package/dist/dashboard/assets/ProviderDetailPage-DQcthNGt.js +0 -4
- package/dist/dashboard/assets/ProviderDetailPage-DQcthNGt.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-0OsXRGXe.js +0 -1
- package/dist/dashboard/assets/ProvidersPage-0OsXRGXe.js.gz +0 -0
- package/dist/dashboard/assets/ProxyPage-DdPc8slb.js +0 -9
- package/dist/dashboard/assets/ProxyPage-DdPc8slb.js.gz +0 -0
- package/dist/dashboard/assets/QuotaPage-Ci5O4BnH.js +0 -1
- package/dist/dashboard/assets/QuotaPage-Ci5O4BnH.js.gz +0 -0
- package/dist/dashboard/assets/SettingsPage-CDZjTt87.js +0 -1
- package/dist/dashboard/assets/SettingsPage-CDZjTt87.js.gz +0 -0
- package/dist/dashboard/assets/SetupPage-BQS0Rx_u.js.gz +0 -0
- package/dist/dashboard/assets/SortableList-BAL5npBC.js +0 -1
- package/dist/dashboard/assets/SortableList-BAL5npBC.js.gz +0 -0
- package/dist/dashboard/assets/StudioPage-TP7voAQ0.js.gz +0 -0
- package/dist/dashboard/assets/UsagePage-Cj_fEzhg.js +0 -56
- package/dist/dashboard/assets/UsagePage-Cj_fEzhg.js.gz +0 -0
- package/dist/dashboard/assets/api-keys-MbI7e7rt.js +0 -1
- package/dist/dashboard/assets/api-keys-MbI7e7rt.js.gz +0 -0
- package/dist/dashboard/assets/app-B2vyPoII.css +0 -2
- package/dist/dashboard/assets/app-B2vyPoII.css.gz +0 -0
- package/dist/dashboard/assets/assert-C0YYuZJ4.js.gz +0 -0
- package/dist/dashboard/assets/badge-B_DMpMJN.js.gz +0 -0
- package/dist/dashboard/assets/banner-DMrMPmth.js.gz +0 -0
- package/dist/dashboard/assets/card-Be5Uzamx.js.gz +0 -0
- package/dist/dashboard/assets/cli-tools-muFRphDu.js.gz +0 -0
- package/dist/dashboard/assets/clipboard-button-DOfrVmsi.js.gz +0 -0
- package/dist/dashboard/assets/dialog-C9omdjQt.js.gz +0 -0
- package/dist/dashboard/assets/drawer-Bm8WKaGY.js.gz +0 -0
- package/dist/dashboard/assets/entry-DS90gUks.js +0 -1
- package/dist/dashboard/assets/entry-DS90gUks.js.gz +0 -0
- package/dist/dashboard/assets/entry-j40LJotO.js +0 -4
- package/dist/dashboard/assets/entry-j40LJotO.js.gz +0 -0
- package/dist/dashboard/assets/entry-q5HwZ8nN.js.gz +0 -0
- package/dist/dashboard/assets/formatters-BTScrQpe.js.gz +0 -0
- package/dist/dashboard/assets/github-badge-Orjuzemr.js.gz +0 -0
- package/dist/dashboard/assets/index-cPrX5vn6.js.gz +0 -0
- package/dist/dashboard/assets/inline-Dvux8w-0.js.gz +0 -0
- package/dist/dashboard/assets/input-CU1Rgf3p.js.gz +0 -0
- package/dist/dashboard/assets/list-row-CzFvogbj.js.gz +0 -0
- package/dist/dashboard/assets/live-KaG15HLQ.js +0 -1
- package/dist/dashboard/assets/live-KaG15HLQ.js.gz +0 -0
- package/dist/dashboard/assets/minimize-2-D-MN8W8a.js +0 -1
- package/dist/dashboard/assets/minimize-2-D-MN8W8a.js.gz +0 -0
- package/dist/dashboard/assets/mutation-JJGQm8TD.js.gz +0 -0
- package/dist/dashboard/assets/numbers-BNDZnsUE.js +0 -2
- package/dist/dashboard/assets/numbers-BNDZnsUE.js.gz +0 -0
- package/dist/dashboard/assets/query-keys-CLaNz8Xz.js.gz +0 -0
- package/dist/dashboard/assets/quota-DZGyp0N5.js +0 -1
- package/dist/dashboard/assets/quota-DZGyp0N5.js.gz +0 -0
- package/dist/dashboard/assets/routing-BEH32QOC.js.gz +0 -0
- package/dist/dashboard/assets/select-B3iTetHN.js.gz +0 -0
- package/dist/dashboard/assets/settings-Dt3Eqbnq.js +0 -1
- package/dist/dashboard/assets/settings-Dt3Eqbnq.js.gz +0 -0
- package/dist/dashboard/assets/stack-KrOrm7JZ.js.gz +0 -0
- package/dist/dashboard/assets/state-DLS7IDMy.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-42ItvbAs.js.gz +0 -0
- package/dist/dashboard/assets/useMutation-Dmnfh1Ue.js.gz +0 -0
- 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/{app-wvwCi4Eh.js → app-Bgd3zANQ.js} +0 -0
- /package/dist/dashboard/assets/{app-wvwCi4Eh.js.gz → app-Bgd3zANQ.js.gz} +0 -0
- /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
|
@@ -25,8 +25,11 @@
|
|
|
25
25
|
import {
|
|
26
26
|
decodeJwtPayload,
|
|
27
27
|
devicePollBackoff,
|
|
28
|
+
generateOAuthState,
|
|
29
|
+
generatePkcePair,
|
|
28
30
|
nonEmptyString,
|
|
29
31
|
nonEmptyTrimmedString,
|
|
32
|
+
parseDeviceAuthStart,
|
|
30
33
|
readJsonResponse,
|
|
31
34
|
record,
|
|
32
35
|
} from "../../authentication/oauth-flow-store";
|
|
@@ -53,6 +56,8 @@ import {
|
|
|
53
56
|
getKiroVersion,
|
|
54
57
|
} from "../../operations/client-versions";
|
|
55
58
|
import { deriveApiKeyMachineId, deriveOAuthMachineId, normalizeMachineId } from "./kiro-machine-id";
|
|
59
|
+
import { KIRO_MANAGEMENT_HOST_TEMPLATE, AWS_REGION_PATTERN, KIRO_DEFAULT_REGION } from "./kiro-catalog";
|
|
60
|
+
import { fetchKiroQuota } from "./kiro-quota";
|
|
56
61
|
|
|
57
62
|
/** Auth methods this integration supports, as stored on the account. */
|
|
58
63
|
export type KiroAuthMethod =
|
|
@@ -64,35 +69,40 @@ export type KiroAuthMethod =
|
|
|
64
69
|
| "external_idp"
|
|
65
70
|
| "api_key";
|
|
66
71
|
|
|
67
|
-
|
|
68
|
-
const AWS_REGION_PATTERN = /^[a-z]{2}-[a-z]+-\d{1,2}$/;
|
|
69
|
-
|
|
70
|
-
/** Default region when an account carries none. */
|
|
71
|
-
const DEFAULT_REGION = "us-east-1";
|
|
72
|
+
const DEFAULT_REGION: string = KIRO_DEFAULT_REGION;
|
|
72
73
|
|
|
73
74
|
/** The vendor's own sign-in and refresh service for the social families. */
|
|
74
75
|
export const KIRO_AUTH_SERVICE = "https://prod.us-east-1.auth.desktop.kiro.dev" as const;
|
|
75
76
|
|
|
77
|
+
/** The web portal that offers the merged social/OIDC sign-in. */
|
|
78
|
+
export const KIRO_SOCIAL_PORTAL = "https://app.kiro.dev" as const;
|
|
79
|
+
|
|
76
80
|
/**
|
|
77
81
|
* Redirect URI the vendor's identity provider allowlists. It is a custom
|
|
78
82
|
* scheme, not a loopback address: the authorization code is shown to the
|
|
79
|
-
* operator rather than delivered to a local listener.
|
|
83
|
+
* operator rather than delivered to a local listener. The 1.4.62 IDE uses
|
|
84
|
+
* `kiro://kiro.oauth/callback` (observed in the agent bundle).
|
|
80
85
|
*/
|
|
81
|
-
export const KIRO_SOCIAL_REDIRECT_URI = "kiro://kiro.
|
|
86
|
+
export const KIRO_SOCIAL_REDIRECT_URI = "kiro://kiro.oauth/callback" as const;
|
|
82
87
|
|
|
83
|
-
/** Scopes the OIDC client registration requests. */
|
|
88
|
+
/** Scopes the OIDC client registration requests (observed in the 1.4.62 IDE). */
|
|
84
89
|
const KIRO_SCOPES = [
|
|
85
90
|
"codewhisperer:completions",
|
|
86
91
|
"codewhisperer:analysis",
|
|
87
92
|
"codewhisperer:conversations",
|
|
93
|
+
"codewhisperer:transformations",
|
|
94
|
+
"codewhisperer:taskassist",
|
|
88
95
|
] as const;
|
|
89
96
|
|
|
90
97
|
/** Grant types the OIDC client registration declares. */
|
|
91
98
|
const KIRO_GRANT_TYPES = [
|
|
92
|
-
"
|
|
99
|
+
"authorization_code",
|
|
93
100
|
"refresh_token",
|
|
94
101
|
] as const;
|
|
95
102
|
|
|
103
|
+
/** Loopback redirect the 1.4.62 IDE registers for the IdC/Builder-ID flow. */
|
|
104
|
+
const KIRO_LOOPBACK_REDIRECT_URI = "http://127.0.0.1/oauth/callback" as const;
|
|
105
|
+
|
|
96
106
|
/**
|
|
97
107
|
* Client name the OIDC registration presents.
|
|
98
108
|
*
|
|
@@ -101,7 +111,7 @@ const KIRO_GRANT_TYPES = [
|
|
|
101
111
|
*/
|
|
102
112
|
const KIRO_OIDC_CLIENT_NAME = "Kiro IDE" as const;
|
|
103
113
|
|
|
104
|
-
/** Organization entry point the
|
|
114
|
+
/** Organization entry point the IdC/Builder-ID flow starts from by default. */
|
|
105
115
|
export const KIRO_DEFAULT_START_URL = "https://view.awsapps.com/start" as const;
|
|
106
116
|
|
|
107
117
|
/** Microsoft login hosts an enterprise token endpoint may point at. */
|
|
@@ -125,7 +135,7 @@ export interface KiroAuthState {
|
|
|
125
135
|
readonly profileArn?: string;
|
|
126
136
|
/** Identity Center organization URL, when the account came from one. */
|
|
127
137
|
readonly startUrl?: string;
|
|
128
|
-
/** OIDC client id from the
|
|
138
|
+
/** OIDC client id from the IdC/Builder-ID client registration. */
|
|
129
139
|
readonly clientId?: string;
|
|
130
140
|
/** Enterprise identity-provider token endpoint. */
|
|
131
141
|
readonly tokenEndpoint?: string;
|
|
@@ -266,90 +276,111 @@ export function kiroAccountLabel(accessToken: string | undefined): string | unde
|
|
|
266
276
|
return undefined;
|
|
267
277
|
}
|
|
268
278
|
|
|
269
|
-
/**
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
279
|
+
/**
|
|
280
|
+
* Falls back to an informative per-family label when the token carries no
|
|
281
|
+
* readable identity (the social access token is opaque, not a JWT). The
|
|
282
|
+
* console otherwise labels every Kiro account "kiro".
|
|
283
|
+
*/
|
|
284
|
+
export function kiroDefaultLabel(authMethod: KiroAuthMethod): string {
|
|
285
|
+
switch (authMethod) {
|
|
286
|
+
case "google":
|
|
287
|
+
return "Google account";
|
|
288
|
+
case "github":
|
|
289
|
+
return "GitHub account";
|
|
290
|
+
case "builder-id":
|
|
291
|
+
return "AWS Builder ID";
|
|
292
|
+
case "idc":
|
|
293
|
+
return "IAM Identity Center";
|
|
294
|
+
case "external_idp":
|
|
295
|
+
return "Enterprise IdP";
|
|
296
|
+
case "imported":
|
|
297
|
+
return "Imported Kiro session";
|
|
298
|
+
case "api_key":
|
|
299
|
+
return "Kiro API key";
|
|
300
|
+
}
|
|
278
301
|
}
|
|
279
302
|
|
|
280
|
-
/**
|
|
303
|
+
/**
|
|
304
|
+
* Every stock fallback label `kiroDefaultLabel` can produce. The quota refresh
|
|
305
|
+
* sweep may adopt an upstream-reported identity (an email from `GetUsageLimits`)
|
|
306
|
+
* over any label in this set — those are generated defaults, never an operator's
|
|
307
|
+
* rename — but leaves anything else alone.
|
|
308
|
+
*/
|
|
309
|
+
export const KIRO_DEFAULT_LABELS: ReadonlySet<string> = new Set(
|
|
310
|
+
(
|
|
311
|
+
["google", "github", "builder-id", "idc", "external_idp", "imported", "api_key"] as const
|
|
312
|
+
).map(kiroDefaultLabel),
|
|
313
|
+
);
|
|
314
|
+
|
|
315
|
+
/** Credentials the client registration minted and refresh must replay. */
|
|
281
316
|
interface DeviceFlowRegistration {
|
|
282
317
|
readonly clientId: string;
|
|
283
318
|
readonly clientSecret: string;
|
|
284
319
|
readonly region: string;
|
|
285
320
|
readonly startUrl: string;
|
|
286
321
|
readonly authMethod: "builder-id" | "idc";
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
const clientSecret = nonEmptyString(raw["clientSecret"]);
|
|
294
|
-
const region = normalizeRegion(raw["region"]);
|
|
295
|
-
const startUrl = nonEmptyTrimmedString(raw["startUrl"]) ?? KIRO_DEFAULT_START_URL;
|
|
296
|
-
const authMethod = raw["authMethod"] === "idc" ? "idc" : "builder-id";
|
|
297
|
-
if (clientId === undefined || clientSecret === undefined) return undefined;
|
|
298
|
-
return { clientId, clientSecret, region, startUrl, authMethod };
|
|
322
|
+
/**
|
|
323
|
+
* Redirect URI sent on authorize + CreateToken. The registration body itself
|
|
324
|
+
* always declares {@link KIRO_LOOPBACK_REDIRECT_URI} (no port); AWS SSO OIDC
|
|
325
|
+
* accepts a loopback authorize URI that adds a port (RFC 8252).
|
|
326
|
+
*/
|
|
327
|
+
readonly redirectUri: string;
|
|
299
328
|
}
|
|
300
329
|
|
|
301
330
|
/**
|
|
302
331
|
* Kiro's authentication client.
|
|
303
332
|
*
|
|
304
|
-
*
|
|
305
|
-
*
|
|
306
|
-
*
|
|
307
|
-
|
|
333
|
+
* The 1.4.62 IDE migrated every AWS family to the OAuth 2.0 authorization-code
|
|
334
|
+
* flow with PKCE: it registers a public client against AWS SSO OIDC, opens a
|
|
335
|
+
* loopback listener on `http://127.0.0.1/oauth/callback`, and exchanges the code
|
|
336
|
+
* with `CreateToken`. The social families (Google/GitHub) use the vendor's own
|
|
337
|
+
* sign-in portal with a loopback redirect. The device-code flow (RFC 8628) is
|
|
338
|
+
* offered against the same AWS SSO OIDC service for Builder-ID / Identity
|
|
339
|
+
* Center. The import, enterprise, and API-key paths are unchanged.
|
|
308
340
|
*/
|
|
309
341
|
export class KiroOAuthClient extends OAuthDeviceFlow {
|
|
310
342
|
override readonly supportsDeviceCode = true;
|
|
343
|
+
override readonly supportsBrowserCode = true;
|
|
344
|
+
|
|
311
345
|
/**
|
|
312
|
-
*
|
|
346
|
+
* Loopback callback the portal and the IdC flow land on.
|
|
313
347
|
*
|
|
314
|
-
*
|
|
315
|
-
*
|
|
316
|
-
*
|
|
317
|
-
*
|
|
318
|
-
*
|
|
319
|
-
*
|
|
348
|
+
* Host must be `127.0.0.1`, not `localhost`: OIDC client registration uses
|
|
349
|
+
* the portless `http://127.0.0.1/oauth/callback`, and AWS SSO OIDC allows
|
|
350
|
+
* RFC 8252 port variation on the *same* host only — swapping in `localhost`
|
|
351
|
+
* at authorize yields `Invalid redirectUri provided`. The 1.4.62 IDE binds
|
|
352
|
+
* and authorizes on `127.0.0.1:<port>/oauth/callback` the same way. The
|
|
353
|
+
* portal gets the origin only (`http://127.0.0.1:3128`); the callback lands
|
|
354
|
+
* at `/oauth/callback?login_option=…` or `/signin/callback?…`.
|
|
320
355
|
*/
|
|
321
|
-
override readonly
|
|
356
|
+
override readonly browserRedirectUri = "http://127.0.0.1:3128/oauth/callback";
|
|
322
357
|
|
|
323
358
|
protected override readonly providerLabel = "kiro";
|
|
324
359
|
protected override readonly clientId = "";
|
|
325
360
|
protected override readonly tokenUrl = `${KIRO_AUTH_SERVICE}/refreshToken`;
|
|
326
361
|
protected override readonly scopes = "";
|
|
362
|
+
|
|
363
|
+
/** IdC/Builder-ID registrations keyed by flow state, for the code exchange. */
|
|
364
|
+
readonly #registrationByState = new Map<string, DeviceFlowRegistration>();
|
|
365
|
+
|
|
327
366
|
/**
|
|
328
|
-
*
|
|
329
|
-
*
|
|
330
|
-
*
|
|
331
|
-
*
|
|
367
|
+
* Fields the browser flow needs before it can start.
|
|
368
|
+
*
|
|
369
|
+
* None: the vendor's sign-in portal (`app.kiro.dev/signin`) is the single
|
|
370
|
+
* entry point for every family — Google, GitHub, AWS Builder ID, "Your
|
|
371
|
+
* organization" (IdC), and enterprise — and the callback's `login_option`
|
|
372
|
+
* names which one approved. Asking for a family/region/start URL up front
|
|
373
|
+
* both duplicated the portal's own choice and forced non-social families
|
|
374
|
+
* onto a different (OIDC-direct) path the 1.4.62 client does not use.
|
|
332
375
|
*/
|
|
333
|
-
readonly browserLoginFields: readonly OAuthLoginField[] = [
|
|
334
|
-
{
|
|
335
|
-
key: "idp",
|
|
336
|
-
label: "Identity provider",
|
|
337
|
-
required: true,
|
|
338
|
-
defaultValue: "google",
|
|
339
|
-
options: [
|
|
340
|
-
{ value: "google", label: "Google" },
|
|
341
|
-
{ value: "github", label: "GitHub" },
|
|
342
|
-
],
|
|
343
|
-
},
|
|
344
|
-
];
|
|
376
|
+
readonly browserLoginFields: readonly OAuthLoginField[] = [];
|
|
345
377
|
|
|
346
378
|
/**
|
|
347
|
-
* Fields the device
|
|
379
|
+
* Fields the device-flow (RFC 8628) needs before it can start.
|
|
348
380
|
*
|
|
349
|
-
* The flow
|
|
350
|
-
*
|
|
351
|
-
*
|
|
352
|
-
* operator approves in.
|
|
381
|
+
* The device flow targets the AWS SSO OIDC service (Builder-ID / Identity
|
|
382
|
+
* Center), so it asks the same region/organization questions the browser
|
|
383
|
+
* flow does and defaults to the same families.
|
|
353
384
|
*/
|
|
354
385
|
readonly deviceLoginFields: readonly OAuthLoginField[] = [
|
|
355
386
|
{
|
|
@@ -405,181 +436,337 @@ export class KiroOAuthClient extends OAuthDeviceFlow {
|
|
|
405
436
|
];
|
|
406
437
|
|
|
407
438
|
/**
|
|
408
|
-
* Registers
|
|
439
|
+
* Registers an OIDC client for the IdC/Builder-ID flow.
|
|
440
|
+
*
|
|
441
|
+
* The 1.4.62 IDE registers a public client per region/issuer, then opens a
|
|
442
|
+
* browser authorization-code flow with PKCE. The registration is retained for
|
|
443
|
+
* the code exchange and later refreshes via the flow `state`, which both the
|
|
444
|
+
* authorize URL and the callback carry.
|
|
445
|
+
*/
|
|
446
|
+
/**
|
|
447
|
+
* Starts the AWS SSO OIDC device authorization.
|
|
409
448
|
*
|
|
410
|
-
*
|
|
411
|
-
*
|
|
412
|
-
*
|
|
413
|
-
*
|
|
449
|
+
* The same client registration the browser flow uses is reused: register a
|
|
450
|
+
* public client for the chosen family/region, then ask the regional OIDC
|
|
451
|
+
* service for a device code. The registration is carried in `providerState`
|
|
452
|
+
* so `pollDeviceAuth` can replay it at the token endpoint.
|
|
414
453
|
*/
|
|
415
454
|
override async startDeviceAuth(context?: OAuthDeviceFlowContext): Promise<OAuthDeviceStartResult> {
|
|
416
455
|
const parameters = context?.parameters ?? {};
|
|
456
|
+
const authMethod = parameters["authMethod"] === "idc" ? "idc" : "builder-id";
|
|
417
457
|
const region = normalizeRegion(parameters["region"]);
|
|
418
458
|
const startUrl = nonEmptyTrimmedString(parameters["startUrl"]) ?? KIRO_DEFAULT_START_URL;
|
|
419
|
-
const authMethod = parameters["authMethod"] === "idc" ? "idc" : "builder-id";
|
|
420
459
|
assertAwsRegion(region);
|
|
460
|
+
const registration = await this.#registerClient(region, startUrl, authMethod);
|
|
421
461
|
|
|
422
|
-
const
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
const
|
|
436
|
-
const
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
throw new Error("Kiro device authorization response omitted required fields");
|
|
462
|
+
const response = await this.fetchFn(
|
|
463
|
+
`https://oidc.${region}.amazonaws.com/device_authorization`,
|
|
464
|
+
{
|
|
465
|
+
method: "POST",
|
|
466
|
+
headers: ssoOidcHeaders(),
|
|
467
|
+
body: JSON.stringify({
|
|
468
|
+
clientId: registration.clientId,
|
|
469
|
+
clientSecret: registration.clientSecret,
|
|
470
|
+
startUrl,
|
|
471
|
+
}),
|
|
472
|
+
signal: AbortSignal.timeout(15_000),
|
|
473
|
+
},
|
|
474
|
+
);
|
|
475
|
+
const payload = record(await readJsonResponse(response, "kiro device authorization")) ?? {};
|
|
476
|
+
const start = parseDeviceAuthStart(payload, { intervalSeconds: 5, expiresInSeconds: 600 });
|
|
477
|
+
if (start === undefined) {
|
|
478
|
+
throw new Error("Kiro device authorization returned an invalid response");
|
|
440
479
|
}
|
|
441
|
-
const interval = Number(raw["interval"]);
|
|
442
|
-
const expiresIn = Number(raw["expiresIn"]);
|
|
443
480
|
return {
|
|
444
|
-
verificationUri,
|
|
445
|
-
userCode,
|
|
446
|
-
deviceAuthId: deviceCode,
|
|
447
|
-
intervalSeconds:
|
|
448
|
-
expiresInSeconds:
|
|
449
|
-
|
|
450
|
-
// the client secret the refresh will need.
|
|
451
|
-
providerState: JSON.stringify({
|
|
452
|
-
clientId: registration.clientId,
|
|
453
|
-
clientSecret: registration.clientSecret,
|
|
454
|
-
region,
|
|
455
|
-
startUrl,
|
|
456
|
-
authMethod,
|
|
457
|
-
}),
|
|
481
|
+
verificationUri: start.verificationUriComplete ?? start.verificationUri,
|
|
482
|
+
userCode: start.userCode,
|
|
483
|
+
deviceAuthId: start.deviceCode,
|
|
484
|
+
intervalSeconds: start.intervalSeconds,
|
|
485
|
+
expiresInSeconds: start.expiresInSeconds,
|
|
486
|
+
providerState: JSON.stringify(registration),
|
|
458
487
|
};
|
|
459
488
|
}
|
|
460
489
|
|
|
461
|
-
/** Polls the AWS SSO OIDC token endpoint for the device
|
|
490
|
+
/** Polls the AWS SSO OIDC token endpoint for the device grant result. */
|
|
462
491
|
override async pollDeviceAuth(
|
|
463
492
|
deviceAuthId: string,
|
|
464
493
|
context?: OAuthDeviceFlowContext,
|
|
465
494
|
): Promise<OAuthDevicePollResult> {
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
return { status: "failed", reason: "
|
|
495
|
+
let registration: DeviceFlowRegistration;
|
|
496
|
+
try {
|
|
497
|
+
registration = JSON.parse(context?.providerState ?? "") as DeviceFlowRegistration;
|
|
498
|
+
} catch {
|
|
499
|
+
return { status: "failed", reason: "unknown device authorization" };
|
|
471
500
|
}
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
headers: ssoOidcHeaders(),
|
|
475
|
-
body: JSON.stringify({
|
|
476
|
-
clientId: registration.clientId,
|
|
477
|
-
clientSecret: registration.clientSecret,
|
|
478
|
-
deviceCode: deviceAuthId,
|
|
479
|
-
grantType: "urn:ietf:params:oauth:grant-type:device_code",
|
|
480
|
-
}),
|
|
481
|
-
});
|
|
482
|
-
// The pending verdicts arrive as a non-2xx body, so the payload is read
|
|
483
|
-
// without the helper that throws on a non-ok status — otherwise
|
|
484
|
-
// `authorization_pending` would be lost and the flow reported as failed,
|
|
485
|
-
// stopping the dashboard's poll while the operator is still authorizing.
|
|
486
|
-
const payload = (await readJsonBody(response)) as OidcTokenResponse;
|
|
487
|
-
const error = nonEmptyString(payload.error);
|
|
488
|
-
// AWS reports its pending states in snake_case, matching RFC 8628.
|
|
489
|
-
const verdict = devicePollBackoff(error, payload.interval);
|
|
490
|
-
if (verdict !== undefined) return verdict;
|
|
491
|
-
if (!response.ok) {
|
|
492
|
-
return {
|
|
493
|
-
status: "failed",
|
|
494
|
-
reason: nonEmptyString(payload.error_description) ?? error ?? `token polling failed (${response.status})`,
|
|
495
|
-
};
|
|
501
|
+
if (!registration.clientId || !registration.clientSecret || !registration.region) {
|
|
502
|
+
return { status: "failed", reason: "unknown device authorization" };
|
|
496
503
|
}
|
|
497
|
-
const
|
|
498
|
-
|
|
504
|
+
const region = assertAwsRegion(registration.region);
|
|
505
|
+
let response: Response;
|
|
506
|
+
try {
|
|
507
|
+
response = await this.fetchFn(`https://oidc.${region}.amazonaws.com/token`, {
|
|
508
|
+
method: "POST",
|
|
509
|
+
headers: ssoOidcHeaders(),
|
|
510
|
+
body: JSON.stringify({
|
|
511
|
+
clientId: registration.clientId,
|
|
512
|
+
clientSecret: registration.clientSecret,
|
|
513
|
+
deviceCode: deviceAuthId,
|
|
514
|
+
grantType: "urn:ietf:params:oauth:grant-type:device_code",
|
|
515
|
+
}),
|
|
516
|
+
signal: AbortSignal.timeout(15_000),
|
|
517
|
+
});
|
|
518
|
+
} catch (error) {
|
|
519
|
+
return { status: "failed", reason: error instanceof Error ? error.message : "Kiro device polling failed" };
|
|
520
|
+
}
|
|
521
|
+
const payload = record(await response.json().catch(() => undefined)) ?? {};
|
|
522
|
+
if (response.status === 400 || response.status === 428) {
|
|
523
|
+
const err = typeof payload.error === "string" ? payload.error : "";
|
|
524
|
+
const verdict = devicePollBackoff(err, payload.interval);
|
|
525
|
+
if (verdict !== undefined) return verdict;
|
|
526
|
+
return { status: "failed", reason: err || `device polling failed (${response.status})` };
|
|
527
|
+
}
|
|
528
|
+
const access = nonEmptyString(payload["accessToken"]);
|
|
529
|
+
if (!response.ok || access === undefined) {
|
|
530
|
+
const detail = nonEmptyString(payload["error_description"]) ?? nonEmptyString(payload["error"]);
|
|
499
531
|
return {
|
|
500
532
|
status: "failed",
|
|
501
|
-
reason:
|
|
533
|
+
reason: detail ?? `Kiro device polling failed (${response.status})`,
|
|
502
534
|
};
|
|
503
535
|
}
|
|
504
|
-
const refresh = nonEmptyString(payload
|
|
505
|
-
|
|
506
|
-
// profile-scoped surface refuses a request without the field, so the account
|
|
507
|
-
// resolves its own before the credential is persisted.
|
|
508
|
-
const reported = nonEmptyString(payload.profileArn);
|
|
509
|
-
const profileArn =
|
|
510
|
-
reported ?? (await this.resolveProfileArn(access, registration.region, registration.authMethod));
|
|
536
|
+
const refresh = nonEmptyString(payload["refreshToken"]) ?? "";
|
|
537
|
+
const profileArn = nonEmptyString(payload["profileArn"]);
|
|
511
538
|
const authState: KiroAuthState = {
|
|
512
539
|
authMethod: registration.authMethod,
|
|
513
|
-
region
|
|
540
|
+
region,
|
|
514
541
|
...(profileArn === undefined ? {} : { profileArn }),
|
|
515
542
|
startUrl: registration.startUrl,
|
|
516
543
|
clientId: registration.clientId,
|
|
517
544
|
machineId: deriveOAuthMachineId(refresh),
|
|
518
545
|
};
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
546
|
+
let label = kiroAccountLabel(access) ?? kiroDefaultLabel(registration.authMethod);
|
|
547
|
+
// Same best-effort identity enrichment as the browser flows: the device
|
|
548
|
+
// token is opaque, so ask the usage surface for the account email.
|
|
549
|
+
if (label === kiroDefaultLabel(registration.authMethod)) {
|
|
550
|
+
try {
|
|
551
|
+
const quota = await fetchKiroQuota(access, this.fetchFn, { auth_state: authStateOf(authState) });
|
|
552
|
+
if (quota.accountLabel !== undefined && quota.accountLabel.trim().length > 0) {
|
|
553
|
+
label = quota.accountLabel;
|
|
554
|
+
}
|
|
555
|
+
} catch {
|
|
556
|
+
// keep the family fallback
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
return {
|
|
560
|
+
status: "complete",
|
|
561
|
+
result: {
|
|
562
|
+
access,
|
|
563
|
+
refresh,
|
|
564
|
+
expiresAt: this.#expiry(payload["expiresIn"]),
|
|
565
|
+
accountLabel: label,
|
|
566
|
+
auth_state: authStateOf(authState),
|
|
567
|
+
client_secret: registration.clientSecret,
|
|
568
|
+
},
|
|
527
569
|
};
|
|
528
|
-
return { status: "complete", result };
|
|
529
570
|
}
|
|
530
571
|
|
|
531
|
-
/**
|
|
532
|
-
* No-op: the social flows need no preparation. The authorize URL carries the
|
|
533
|
-
* identity provider rather than a client id and allowlists a custom-scheme
|
|
534
|
-
* redirect, so the operator completes consent in a browser and pastes the
|
|
535
|
-
* resulting URL back — there is no loopback listener to register.
|
|
536
|
-
*/
|
|
537
572
|
async prepareAuthorize(request: OAuthAuthorizeRequest): Promise<OAuthAuthorizeRequest> {
|
|
573
|
+
// The portal is the single entry point for every family; it chooses the
|
|
574
|
+
// identity provider and reports it back in the callback's `login_option`.
|
|
575
|
+
// No registration happens here.
|
|
538
576
|
return request;
|
|
539
577
|
}
|
|
540
578
|
|
|
541
579
|
override buildAuthorizeUrl(request: OAuthAuthorizeRequest): string {
|
|
580
|
+
// Every family goes through the portal. The 1.4.62 IDE has no per-family
|
|
581
|
+
// OIDC-direct URL: its `buildPortalUrl` sends `state`, `code_challenge`,
|
|
582
|
+
// `redirect_uri`, and `redirect_from` to `app.kiro.dev/signin`, and the
|
|
583
|
+
// portal picks the identity provider. The callback's `login_option` then
|
|
584
|
+
// tells the exchange which family approved.
|
|
542
585
|
return this.#buildSocialAuthorizeUrl(request);
|
|
543
586
|
}
|
|
544
587
|
|
|
545
588
|
#buildSocialAuthorizeUrl(request: OAuthAuthorizeRequest): string {
|
|
546
|
-
|
|
589
|
+
// The portal chooses the identity provider (Google/GitHub/OIDC) itself, so
|
|
590
|
+
// the authorize URL carries no `idp`. The redirect URI is the origin only —
|
|
591
|
+
// exactly what the bundle's `PortalAuthProvider.buildPortalUrl` sends; a
|
|
592
|
+
// path-carrying redirect is rejected by Cognito with `redirect_mismatch`.
|
|
547
593
|
const params = new URLSearchParams({
|
|
548
|
-
|
|
549
|
-
redirect_uri: KIRO_SOCIAL_REDIRECT_URI,
|
|
594
|
+
state: request.state,
|
|
550
595
|
code_challenge: request.codeChallenge,
|
|
551
596
|
code_challenge_method: "S256",
|
|
552
|
-
|
|
553
|
-
|
|
597
|
+
redirect_uri: "",
|
|
598
|
+
redirect_from: "KiroIDE",
|
|
554
599
|
});
|
|
555
|
-
|
|
600
|
+
const redirectOrigin = new URL(this.browserRedirectUri);
|
|
601
|
+
params.set("redirect_uri", redirectOrigin.origin);
|
|
602
|
+
return `${KIRO_SOCIAL_PORTAL}/signin?${params.toString()}`;
|
|
556
603
|
}
|
|
557
604
|
|
|
558
605
|
/**
|
|
559
|
-
* Exchanges the
|
|
606
|
+
* Exchanges the authorization code the operator pastes back.
|
|
560
607
|
*
|
|
561
|
-
*
|
|
562
|
-
*
|
|
608
|
+
* Social families exchange against the vendor's own sign-in service. The
|
|
609
|
+
* OIDC families (Builder-ID / IdC) use a two-phase flow mirrored from the
|
|
610
|
+
* 1.4.62 IDE: the portal's first callback carries no `code`, only
|
|
611
|
+
* `login_option` + `issuer_url` + `idc_region`; this exchange then registers
|
|
612
|
+
* an OIDC client, builds the real authorize URL, and starts the *second*
|
|
613
|
+
* browser leg by throwing {@link KiroOidcPendingError}. When that second
|
|
614
|
+
* callback arrives (with a `code`), `exchangeCode` runs again with the
|
|
615
|
+
* registration now on file and exchanges it via AWS SSO OIDC `CreateToken`.
|
|
563
616
|
*/
|
|
564
617
|
override async exchangeCode(
|
|
565
618
|
code: string,
|
|
566
619
|
codeVerifier: string,
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
620
|
+
redirectUri: string,
|
|
621
|
+
state?: string,
|
|
622
|
+
context?: OAuthCodeExchangeContext,
|
|
623
|
+
): Promise<OAuthExchangeResult> {
|
|
624
|
+
// Phase 2 (or a resumed device/browser OIDC exchange): registration may be
|
|
625
|
+
// in the process Map or restored from the pending flow's providerState.
|
|
626
|
+
const registration =
|
|
627
|
+
(state === undefined ? undefined : this.#registrationByState.get(state)) ??
|
|
628
|
+
parseRegistration(context?.providerState);
|
|
629
|
+
if (registration !== undefined) {
|
|
630
|
+
if (state !== undefined) this.#registrationByState.delete(state);
|
|
631
|
+
return this.#exchangeIdcCode(code, codeVerifier, registration.redirectUri, registration);
|
|
632
|
+
}
|
|
633
|
+
const params = context?.parameters ?? {};
|
|
634
|
+
const loginOption = params["login_option"];
|
|
635
|
+
if (loginOption === "builderid" || loginOption === "awsidc") {
|
|
636
|
+
// Phase 1: the portal callback for an OIDC family. `code` is absent and
|
|
637
|
+
// the issuer/region tell us which regional OIDC service to use. Mirror the
|
|
638
|
+
// 1.4.62 IDE's IdC-provider.authenticate: register with the portless
|
|
639
|
+
// loopback URI, mint a *fresh* PKCE pair, authorize against the full
|
|
640
|
+
// `127.0.0.1:<port>/oauth/callback` (port allowed under RFC 8252; host
|
|
641
|
+
// must stay `127.0.0.1` — `localhost` is rejected as Invalid redirectUri),
|
|
642
|
+
// scopes comma-joined.
|
|
643
|
+
const issuerUrl = params["issuer_url"];
|
|
644
|
+
const idcRegion = params["idc_region"] ?? params["idcRegion"];
|
|
645
|
+
if (issuerUrl === undefined || idcRegion === undefined) {
|
|
646
|
+
throw new Error(`Kiro ${loginOption} callback missing issuer_url or idc_region`);
|
|
647
|
+
}
|
|
648
|
+
const region = normalizeRegion(idcRegion);
|
|
649
|
+
assertAwsRegion(region);
|
|
650
|
+
const authMethod = loginOption === "awsidc" ? "idc" : "builder-id";
|
|
651
|
+
const registered = await this.#registerClient(region, issuerUrl, authMethod);
|
|
652
|
+
const authorizeRedirect = oidcAuthorizeRedirectUri(redirectUri);
|
|
653
|
+
const phase2: DeviceFlowRegistration = { ...registered, redirectUri: authorizeRedirect };
|
|
654
|
+
const pkce = generatePkcePair();
|
|
655
|
+
// The second browser leg uses a *fresh* state, mirroring the 1.4.62
|
|
656
|
+
// IDE (IdCAuthProvider mints a new UUID for the IdC authorize step). The
|
|
657
|
+
// code exchange keys off that new state, not the portal's.
|
|
658
|
+
const continueState = generateOAuthState();
|
|
659
|
+
this.#registrationByState.set(continueState, phase2);
|
|
660
|
+
const authorizeParams = new URLSearchParams({
|
|
661
|
+
response_type: "code",
|
|
662
|
+
client_id: phase2.clientId,
|
|
663
|
+
redirect_uri: authorizeRedirect,
|
|
664
|
+
scopes: KIRO_SCOPES.join(","),
|
|
665
|
+
state: continueState,
|
|
666
|
+
code_challenge: pkce.codeChallenge,
|
|
667
|
+
code_challenge_method: "S256",
|
|
668
|
+
});
|
|
669
|
+
throw new KiroOidcPendingError(
|
|
670
|
+
`https://oidc.${region}.amazonaws.com/authorize?${authorizeParams.toString()}`,
|
|
671
|
+
pkce.codeVerifier,
|
|
672
|
+
JSON.stringify(phase2),
|
|
673
|
+
continueState,
|
|
674
|
+
);
|
|
675
|
+
}
|
|
676
|
+
if (loginOption === "external_idp") {
|
|
677
|
+
// Enterprise IdP is only reachable by import; the portal cannot complete
|
|
678
|
+
// it for us because the refresh needs the operator's pasted JSON.
|
|
679
|
+
throw new Error("Kiro enterprise sign-in must be imported — use Import credential");
|
|
680
|
+
}
|
|
681
|
+
return this.exchangeSocialCode(code, codeVerifier, redirectUri, loginOption);
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
/** Exchanges an IdC/Builder-ID authorization code via AWS SSO OIDC CreateToken. */
|
|
685
|
+
async #exchangeIdcCode(
|
|
686
|
+
code: string,
|
|
687
|
+
codeVerifier: string,
|
|
688
|
+
redirectUri: string,
|
|
689
|
+
registration: DeviceFlowRegistration,
|
|
570
690
|
): Promise<OAuthExchangeResult> {
|
|
571
|
-
|
|
691
|
+
const response = await this.fetchFn(`https://oidc.${registration.region}.amazonaws.com/token`, {
|
|
692
|
+
method: "POST",
|
|
693
|
+
headers: ssoOidcHeaders(),
|
|
694
|
+
body: JSON.stringify({
|
|
695
|
+
clientId: registration.clientId,
|
|
696
|
+
clientSecret: registration.clientSecret,
|
|
697
|
+
grantType: "authorization_code",
|
|
698
|
+
code,
|
|
699
|
+
redirectUri,
|
|
700
|
+
codeVerifier,
|
|
701
|
+
}),
|
|
702
|
+
});
|
|
703
|
+
const payload = record(await readJsonResponse(response, "kiro IdC token exchange")) ?? {};
|
|
704
|
+
const access = nonEmptyString(payload["accessToken"]);
|
|
705
|
+
if (access === undefined) {
|
|
706
|
+
const detail = nonEmptyString(payload["error_description"]) ?? nonEmptyString(payload["error"]);
|
|
707
|
+
throw new Error(`Kiro IdC token exchange omitted accessToken${detail === undefined ? "" : `: ${detail}`}`);
|
|
708
|
+
}
|
|
709
|
+
const refresh = nonEmptyString(payload["refreshToken"]) ?? "";
|
|
710
|
+
const reported = nonEmptyString(payload["profileArn"]);
|
|
711
|
+
const profileArn =
|
|
712
|
+
reported ?? (await this.resolveProfileArn(access, registration.region, registration.authMethod));
|
|
713
|
+
const authState: KiroAuthState = {
|
|
714
|
+
authMethod: registration.authMethod,
|
|
715
|
+
region: registration.region,
|
|
716
|
+
...(profileArn === undefined ? {} : { profileArn }),
|
|
717
|
+
startUrl: registration.startUrl,
|
|
718
|
+
clientId: registration.clientId,
|
|
719
|
+
machineId: deriveOAuthMachineId(refresh),
|
|
720
|
+
};
|
|
721
|
+
let label = kiroAccountLabel(access) ?? kiroDefaultLabel(registration.authMethod);
|
|
722
|
+
// Builder-ID / IdC access tokens are opaque (not JWTs), so the JWT
|
|
723
|
+
// extraction above misses the identity; ask the usage surface for the
|
|
724
|
+
// account email instead — the same best-effort enrichment social uses.
|
|
725
|
+
if (label === kiroDefaultLabel(registration.authMethod)) {
|
|
726
|
+
try {
|
|
727
|
+
const quota = await fetchKiroQuota(access, this.fetchFn, { auth_state: authStateOf(authState) });
|
|
728
|
+
if (quota.accountLabel !== undefined && quota.accountLabel.trim().length > 0) {
|
|
729
|
+
label = quota.accountLabel;
|
|
730
|
+
}
|
|
731
|
+
} catch {
|
|
732
|
+
// keep the family fallback
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
return {
|
|
736
|
+
access,
|
|
737
|
+
refresh,
|
|
738
|
+
expiresAt: this.#expiry(payload["expiresIn"]),
|
|
739
|
+
accountLabel: label,
|
|
740
|
+
auth_state: authStateOf(authState),
|
|
741
|
+
client_secret: registration.clientSecret,
|
|
742
|
+
};
|
|
572
743
|
}
|
|
573
744
|
|
|
574
745
|
/** Exchanges a social authorization code for tokens. */
|
|
575
|
-
async exchangeSocialCode(
|
|
746
|
+
async exchangeSocialCode(
|
|
747
|
+
code: string,
|
|
748
|
+
codeVerifier: string,
|
|
749
|
+
redirectUri?: string,
|
|
750
|
+
loginOption?: string,
|
|
751
|
+
): Promise<OAuthExchangeResult> {
|
|
752
|
+
// The portal callback lands as `?login_option=google&code=…`. The exchange
|
|
753
|
+
// must replay the exact redirect URI the portal saw — the callback origin +
|
|
754
|
+
// path + `?login_option=…` — otherwise the vendor service rejects it (the
|
|
755
|
+
// bundle rebuilds it the same way). We do not have the full callback URL at
|
|
756
|
+
// exchange time, so the caller passes the pieces: `redirectUri` is the
|
|
757
|
+
// loopback callback URL the flow registered and `loginOption` is the query
|
|
758
|
+
// value. Default to the original scheme when the pieces are absent.
|
|
759
|
+
const redirectUriFinal =
|
|
760
|
+
redirectUri !== undefined && loginOption !== undefined
|
|
761
|
+
? `${redirectUri}?login_option=${loginOption}`
|
|
762
|
+
: redirectUri ?? KIRO_SOCIAL_REDIRECT_URI;
|
|
576
763
|
const response = await this.fetchFn(`${KIRO_AUTH_SERVICE}/oauth/token`, {
|
|
577
764
|
method: "POST",
|
|
578
765
|
headers: socialServiceHeaders(),
|
|
579
766
|
body: JSON.stringify({
|
|
580
767
|
code,
|
|
581
768
|
code_verifier: codeVerifier,
|
|
582
|
-
redirect_uri:
|
|
769
|
+
redirect_uri: redirectUriFinal,
|
|
583
770
|
}),
|
|
584
771
|
});
|
|
585
772
|
const payload = await readJsonResponse(response, "kiro social token exchange");
|
|
@@ -591,18 +778,32 @@ export class KiroOAuthClient extends OAuthDeviceFlow {
|
|
|
591
778
|
}
|
|
592
779
|
const refresh = nonEmptyString(raw["refreshToken"]) ?? "";
|
|
593
780
|
const profileArn = nonEmptyString(raw["profileArn"]);
|
|
781
|
+
const authMethod: KiroAuthMethod = loginOption === "github" ? "github" : "google";
|
|
594
782
|
const authState: KiroAuthState = {
|
|
595
|
-
authMethod
|
|
783
|
+
authMethod,
|
|
596
784
|
region: DEFAULT_REGION,
|
|
597
785
|
...(profileArn === undefined ? {} : { profileArn }),
|
|
598
786
|
machineId: deriveOAuthMachineId(refresh),
|
|
599
787
|
};
|
|
600
|
-
|
|
788
|
+
let label = kiroAccountLabel(access) ?? kiroDefaultLabel(authMethod);
|
|
789
|
+
// Social access tokens are opaque (not JWTs), so the JWT extraction above
|
|
790
|
+
// misses them; ask the usage surface for the account's email instead. The
|
|
791
|
+
// call is best-effort and guarded — a failure keeps the family fallback.
|
|
792
|
+
if (label === kiroDefaultLabel(authMethod)) {
|
|
793
|
+
try {
|
|
794
|
+
const quota = await fetchKiroQuota(access, this.fetchFn, { auth_state: authStateOf(authState) });
|
|
795
|
+
if (quota.accountLabel !== undefined && quota.accountLabel.trim().length > 0) {
|
|
796
|
+
label = quota.accountLabel;
|
|
797
|
+
}
|
|
798
|
+
} catch {
|
|
799
|
+
// keep the family fallback
|
|
800
|
+
}
|
|
801
|
+
}
|
|
601
802
|
return {
|
|
602
803
|
access,
|
|
603
804
|
refresh,
|
|
604
805
|
expiresAt: this.#expiry(raw["expiresIn"]),
|
|
605
|
-
|
|
806
|
+
accountLabel: label,
|
|
606
807
|
auth_state: authStateOf(authState),
|
|
607
808
|
};
|
|
608
809
|
}
|
|
@@ -611,7 +812,7 @@ export class KiroOAuthClient extends OAuthDeviceFlow {
|
|
|
611
812
|
* Completes a login from pasted credential material.
|
|
612
813
|
*
|
|
613
814
|
* One entry point for the four families that cannot be reached by a redirect
|
|
614
|
-
* or a
|
|
815
|
+
* or a browser flow, because the operator already holds the material:
|
|
615
816
|
*
|
|
616
817
|
* * a Kiro social refresh token;
|
|
617
818
|
* * an Identity Center refresh token, which needs the client registration
|
|
@@ -695,7 +896,20 @@ export class KiroOAuthClient extends OAuthDeviceFlow {
|
|
|
695
896
|
...(profileArn === undefined ? {} : { profileArn }),
|
|
696
897
|
machineId: deriveOAuthMachineId(refreshed.refresh),
|
|
697
898
|
};
|
|
698
|
-
|
|
899
|
+
let label = kiroAccountLabel(refreshed.access) ?? kiroDefaultLabel(isIdc ? "idc" : "imported");
|
|
900
|
+
// An imported refresh token's access token is opaque, so extract the identity
|
|
901
|
+
// from the usage surface instead — the same call the quota sweep makes, and
|
|
902
|
+
// the same best-effort guard.
|
|
903
|
+
if (label === kiroDefaultLabel(isIdc ? "idc" : "imported")) {
|
|
904
|
+
try {
|
|
905
|
+
const quota = await fetchKiroQuota(refreshed.access, this.fetchFn, { auth_state: authStateOf(authState) });
|
|
906
|
+
if (quota.accountLabel !== undefined && quota.accountLabel.trim().length > 0) {
|
|
907
|
+
label = quota.accountLabel;
|
|
908
|
+
}
|
|
909
|
+
} catch {
|
|
910
|
+
// keep the family fallback
|
|
911
|
+
}
|
|
912
|
+
}
|
|
699
913
|
return {
|
|
700
914
|
access: refreshed.access,
|
|
701
915
|
refresh: refreshed.refresh,
|
|
@@ -748,12 +962,12 @@ export class KiroOAuthClient extends OAuthDeviceFlow {
|
|
|
748
962
|
machineId: deriveOAuthMachineId(refresh),
|
|
749
963
|
};
|
|
750
964
|
const expiresAt = readExpiry(raw) ?? this.#expiry(undefined);
|
|
751
|
-
const label = kiroAccountLabel(access);
|
|
965
|
+
const label = kiroAccountLabel(access) ?? kiroDefaultLabel("external_idp");
|
|
752
966
|
return {
|
|
753
967
|
access,
|
|
754
968
|
refresh,
|
|
755
969
|
expiresAt,
|
|
756
|
-
|
|
970
|
+
accountLabel: label,
|
|
757
971
|
auth_state: authStateOf(authState),
|
|
758
972
|
};
|
|
759
973
|
}
|
|
@@ -771,24 +985,22 @@ export class KiroOAuthClient extends OAuthDeviceFlow {
|
|
|
771
985
|
const safeRegion = assertAwsRegion(normalizeRegion(region));
|
|
772
986
|
const version = getKiroVersion();
|
|
773
987
|
const machineId = deriveApiKeyMachineId(trimmed);
|
|
774
|
-
const
|
|
775
|
-
|
|
776
|
-
{
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
"x-amz-user-agent": buildKiroAmzUserAgent(version, machineId),
|
|
788
|
-
},
|
|
789
|
-
signal: AbortSignal.timeout(15_000),
|
|
988
|
+
const host = KIRO_MANAGEMENT_HOST_TEMPLATE.replace("{region}", safeRegion);
|
|
989
|
+
const response = await this.fetchFn(`${host}/List-Available-Models?origin=AI_EDITOR`, {
|
|
990
|
+
headers: {
|
|
991
|
+
authorization: `Bearer ${trimmed}`,
|
|
992
|
+
TokenType: "API_KEY",
|
|
993
|
+
accept: "application/json",
|
|
994
|
+
connection: "close",
|
|
995
|
+
"x-amz-target": "KiroControlPlaneBearerService.ListAvailableModels",
|
|
996
|
+
"amz-sdk-request": "attempt=1; max=1",
|
|
997
|
+
"amz-sdk-invocation-id": crypto.randomUUID(),
|
|
998
|
+
"user-agent": buildKiroUserAgent(version, machineId),
|
|
999
|
+
"x-amz-user-agent": buildKiroAmzUserAgent(version, machineId),
|
|
1000
|
+
"x-kiro-machineid": machineId,
|
|
790
1001
|
},
|
|
791
|
-
|
|
1002
|
+
signal: AbortSignal.timeout(15_000),
|
|
1003
|
+
});
|
|
792
1004
|
if (!response.ok) throw new Error(`API key validation failed (${response.status})`);
|
|
793
1005
|
const payload = record(await response.json().catch(() => undefined));
|
|
794
1006
|
const models = payload?.["models"];
|
|
@@ -816,10 +1028,8 @@ export class KiroOAuthClient extends OAuthDeviceFlow {
|
|
|
816
1028
|
* Profile-scoped surfaces refuse a request that omits the field (`400
|
|
817
1029
|
* profileArn is required for this request.`), and an Identity Center account
|
|
818
1030
|
* whose token response carried no profile needs its own resolved before it is
|
|
819
|
-
* persisted. `
|
|
820
|
-
*
|
|
821
|
-
* header. The generation host carries no equivalent, and an unqualified
|
|
822
|
-
* request to it is answered `UnknownOperationException`.
|
|
1031
|
+
* persisted. `GetProfile` on the Kiro control-plane service is that lookup: a
|
|
1032
|
+
* POST with an `x-amz-target` header on `https://management.{region}.kiro.dev`.
|
|
823
1033
|
*
|
|
824
1034
|
* A Builder ID account is skipped rather than asked: the operation refuses
|
|
825
1035
|
* that family outright (`403 AWS Builder ID is not supported for this
|
|
@@ -839,36 +1049,39 @@ export class KiroOAuthClient extends OAuthDeviceFlow {
|
|
|
839
1049
|
if (authMethod === "builder-id") return undefined;
|
|
840
1050
|
const safeRegion = assertAwsRegion(normalizeRegion(region));
|
|
841
1051
|
try {
|
|
842
|
-
const
|
|
1052
|
+
const host = KIRO_MANAGEMENT_HOST_TEMPLATE.replace("{region}", safeRegion);
|
|
1053
|
+
const response = await this.fetchFn(`${host}/Get-Profile`, {
|
|
843
1054
|
method: "POST",
|
|
844
1055
|
headers: {
|
|
845
1056
|
authorization: `Bearer ${accessToken}`,
|
|
846
1057
|
"content-type": "application/x-amz-json-1.0",
|
|
847
|
-
"x-amz-target": "
|
|
1058
|
+
"x-amz-target": "KiroControlPlaneBearerService.GetProfile",
|
|
848
1059
|
accept: "application/json",
|
|
849
1060
|
connection: "close",
|
|
850
1061
|
"amz-sdk-request": "attempt=1; max=1",
|
|
851
1062
|
"amz-sdk-invocation-id": crypto.randomUUID(),
|
|
852
1063
|
},
|
|
853
|
-
body: JSON.stringify({
|
|
1064
|
+
body: JSON.stringify({}),
|
|
854
1065
|
signal: AbortSignal.timeout(15_000),
|
|
855
1066
|
});
|
|
856
1067
|
if (!response.ok) return undefined;
|
|
857
1068
|
const payload = record(await response.json().catch(() => undefined));
|
|
1069
|
+
const profile = record(payload?.["profile"]);
|
|
1070
|
+
const arn = nonEmptyTrimmedString(profile?.["arn"] ?? profile?.["profileArn"]);
|
|
1071
|
+
if (arn !== undefined) return arn;
|
|
858
1072
|
const profiles = payload?.["profiles"];
|
|
859
|
-
if (
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
fallback ??= arn;
|
|
1073
|
+
if (Array.isArray(profiles)) {
|
|
1074
|
+
let fallback: string | undefined;
|
|
1075
|
+
for (const entry of profiles) {
|
|
1076
|
+
const p = record(entry);
|
|
1077
|
+
const value = nonEmptyTrimmedString(p?.["arn"] ?? p?.["profileArn"]);
|
|
1078
|
+
if (value === undefined) continue;
|
|
1079
|
+
if (value.includes(`:${safeRegion}:`)) return value;
|
|
1080
|
+
fallback ??= value;
|
|
1081
|
+
}
|
|
1082
|
+
return fallback;
|
|
870
1083
|
}
|
|
871
|
-
return
|
|
1084
|
+
return undefined;
|
|
872
1085
|
} catch {
|
|
873
1086
|
return undefined;
|
|
874
1087
|
}
|
|
@@ -923,7 +1136,7 @@ export class KiroOAuthClient extends OAuthDeviceFlow {
|
|
|
923
1136
|
const clientId = authState.clientId;
|
|
924
1137
|
const clientSecret = context?.client_secret;
|
|
925
1138
|
if (clientId === undefined || clientSecret === undefined) {
|
|
926
|
-
throw new Error("Kiro
|
|
1139
|
+
throw new Error("Kiro IdC refresh is missing its client registration");
|
|
927
1140
|
}
|
|
928
1141
|
const refreshed = await this.#refreshOidc(
|
|
929
1142
|
refreshToken,
|
|
@@ -966,19 +1179,23 @@ export class KiroOAuthClient extends OAuthDeviceFlow {
|
|
|
966
1179
|
}
|
|
967
1180
|
|
|
968
1181
|
/**
|
|
969
|
-
* Registers an OIDC client for the
|
|
1182
|
+
* Registers an OIDC client for the IdC/Builder-ID authorization-code flow.
|
|
970
1183
|
*
|
|
971
1184
|
* `clientName` and `User-Agent` identify this as the vendor's own IDE client.
|
|
972
1185
|
* The registration is the first request a brand-new account ever makes, so it
|
|
973
1186
|
* is the one most likely to be looked at: a client name no shipped build uses,
|
|
974
1187
|
* or a missing User-Agent, is a shape the real client never produces.
|
|
975
1188
|
*
|
|
976
|
-
*
|
|
977
|
-
*
|
|
978
|
-
*
|
|
979
|
-
* declares an organization the account is not part of.
|
|
1189
|
+
* The 1.4.62 IDE registers a public client with `authorization_code` grant and
|
|
1190
|
+
* the loopback redirect, and declares the issuer (the start URL) so the
|
|
1191
|
+
* authorization server knows which organization/portal this client belongs to.
|
|
980
1192
|
*/
|
|
981
|
-
async #registerClient(
|
|
1193
|
+
async #registerClient(
|
|
1194
|
+
region: string,
|
|
1195
|
+
startUrl: string,
|
|
1196
|
+
authMethod: "builder-id" | "idc",
|
|
1197
|
+
redirectUri = KIRO_LOOPBACK_REDIRECT_URI,
|
|
1198
|
+
): Promise<DeviceFlowRegistration> {
|
|
982
1199
|
const response = await this.fetchFn(`https://oidc.${region}.amazonaws.com/client/register`, {
|
|
983
1200
|
method: "POST",
|
|
984
1201
|
headers: {
|
|
@@ -994,6 +1211,8 @@ export class KiroOAuthClient extends OAuthDeviceFlow {
|
|
|
994
1211
|
clientType: "public",
|
|
995
1212
|
scopes: [...KIRO_SCOPES],
|
|
996
1213
|
grantTypes: [...KIRO_GRANT_TYPES],
|
|
1214
|
+
redirectUris: [redirectUri],
|
|
1215
|
+
issuerUrl: startUrl,
|
|
997
1216
|
}),
|
|
998
1217
|
});
|
|
999
1218
|
const payload = record(await readJsonResponse(response, "kiro client registration")) ?? {};
|
|
@@ -1002,13 +1221,13 @@ export class KiroOAuthClient extends OAuthDeviceFlow {
|
|
|
1002
1221
|
if (clientId === undefined || clientSecret === undefined) {
|
|
1003
1222
|
throw new Error("Kiro client registration omitted credentials");
|
|
1004
1223
|
}
|
|
1005
|
-
return { clientId, clientSecret };
|
|
1224
|
+
return { clientId, clientSecret, region, startUrl, authMethod, redirectUri };
|
|
1006
1225
|
}
|
|
1007
1226
|
|
|
1008
1227
|
/** Refreshes through the regional AWS SSO OIDC token endpoint. */
|
|
1009
1228
|
async #refreshOidc(
|
|
1010
1229
|
refreshToken: string,
|
|
1011
|
-
registration: DeviceFlowRegistration,
|
|
1230
|
+
registration: Omit<DeviceFlowRegistration, "redirectUri">,
|
|
1012
1231
|
signal?: AbortSignal,
|
|
1013
1232
|
): Promise<{ readonly access: string; readonly refresh: string; readonly expiresAt: Date; readonly profileArn?: string }> {
|
|
1014
1233
|
const region = assertAwsRegion(registration.region);
|
|
@@ -1071,6 +1290,81 @@ export class KiroOAuthClient extends OAuthDeviceFlow {
|
|
|
1071
1290
|
}
|
|
1072
1291
|
}
|
|
1073
1292
|
|
|
1293
|
+
/**
|
|
1294
|
+
* Signals that an OIDC portal callback needs a second browser leg: the portal
|
|
1295
|
+
* returns `login_option=builderid|awsidc` without a code, and the real
|
|
1296
|
+
* authorize URL for the regional AWS SSO OIDC service is attached so the route
|
|
1297
|
+
* can redirect the operator to it. Carries the fresh PKCE verifier and the
|
|
1298
|
+
* serialized registration so `completeLogin` can rewrite the pending flow
|
|
1299
|
+
* before sending the browser on — memory alone is not enough across gateways.
|
|
1300
|
+
*/
|
|
1301
|
+
export class KiroOidcPendingError extends Error {
|
|
1302
|
+
readonly authorizeUrl: string;
|
|
1303
|
+
readonly codeVerifier: string;
|
|
1304
|
+
readonly providerState: string;
|
|
1305
|
+
readonly continueState: string;
|
|
1306
|
+
constructor(
|
|
1307
|
+
authorizeUrl: string,
|
|
1308
|
+
codeVerifier: string,
|
|
1309
|
+
providerState: string,
|
|
1310
|
+
continueState: string,
|
|
1311
|
+
) {
|
|
1312
|
+
super(`Kiro OIDC requires a second authorization step: ${authorizeUrl}`);
|
|
1313
|
+
this.name = "KiroOidcPendingError";
|
|
1314
|
+
this.authorizeUrl = authorizeUrl;
|
|
1315
|
+
this.codeVerifier = codeVerifier;
|
|
1316
|
+
this.providerState = providerState;
|
|
1317
|
+
this.continueState = continueState;
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
/**
|
|
1322
|
+
* Authorize/token redirect for AWS SSO OIDC after a portless registration.
|
|
1323
|
+
*
|
|
1324
|
+
* Registration always declares `http://127.0.0.1/oauth/callback`. RFC 8252 lets
|
|
1325
|
+
* the authorize step add a port on that same host; swapping the host to
|
|
1326
|
+
* `localhost` (or any other name) is answered `Invalid redirectUri provided`.
|
|
1327
|
+
* Port is taken from the console's loopback callback when present.
|
|
1328
|
+
*/
|
|
1329
|
+
function oidcAuthorizeRedirectUri(loopbackCallback: string): string {
|
|
1330
|
+
let port = "3128";
|
|
1331
|
+
if (loopbackCallback.length > 0) {
|
|
1332
|
+
try {
|
|
1333
|
+
const parsed = new URL(loopbackCallback);
|
|
1334
|
+
if (parsed.port.length > 0) port = parsed.port;
|
|
1335
|
+
} catch {
|
|
1336
|
+
// keep the Kiro browser default port
|
|
1337
|
+
}
|
|
1338
|
+
}
|
|
1339
|
+
return `http://127.0.0.1:${port}/oauth/callback`;
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
/** Restores a {@link DeviceFlowRegistration} from pending-flow providerState. */
|
|
1343
|
+
function parseRegistration(providerState: string | undefined): DeviceFlowRegistration | undefined {
|
|
1344
|
+
if (providerState === undefined || providerState.length === 0) return undefined;
|
|
1345
|
+
const parsed = safeJson(providerState);
|
|
1346
|
+
const root = record(parsed);
|
|
1347
|
+
if (root === undefined) return undefined;
|
|
1348
|
+
const clientId = nonEmptyString(root["clientId"]);
|
|
1349
|
+
const clientSecret = nonEmptyString(root["clientSecret"]);
|
|
1350
|
+
const region = nonEmptyString(root["region"]);
|
|
1351
|
+
const startUrl = nonEmptyString(root["startUrl"]);
|
|
1352
|
+
const authMethod = root["authMethod"] === "idc" || root["authMethod"] === "builder-id"
|
|
1353
|
+
? root["authMethod"]
|
|
1354
|
+
: undefined;
|
|
1355
|
+
const redirectUri = nonEmptyString(root["redirectUri"]) ?? KIRO_LOOPBACK_REDIRECT_URI;
|
|
1356
|
+
if (
|
|
1357
|
+
clientId === undefined ||
|
|
1358
|
+
clientSecret === undefined ||
|
|
1359
|
+
region === undefined ||
|
|
1360
|
+
startUrl === undefined ||
|
|
1361
|
+
authMethod === undefined
|
|
1362
|
+
) {
|
|
1363
|
+
return undefined;
|
|
1364
|
+
}
|
|
1365
|
+
return { clientId, clientSecret, region, startUrl, authMethod, redirectUri };
|
|
1366
|
+
}
|
|
1367
|
+
|
|
1074
1368
|
/** Parses JSON without throwing. */
|
|
1075
1369
|
function safeJson(value: string): unknown {
|
|
1076
1370
|
try {
|
|
@@ -1080,21 +1374,6 @@ function safeJson(value: string): unknown {
|
|
|
1080
1374
|
}
|
|
1081
1375
|
}
|
|
1082
1376
|
|
|
1083
|
-
/**
|
|
1084
|
-
* Reads a JSON body regardless of status.
|
|
1085
|
-
*
|
|
1086
|
-
* The device-token endpoint reports "still waiting" and "slow down" as non-2xx
|
|
1087
|
-
* bodies, so a helper that throws on a non-ok status would discard the verdict
|
|
1088
|
-
* the poll needs. An unparseable body yields `{}` rather than throwing, because
|
|
1089
|
-
* the caller decides what a missing field means.
|
|
1090
|
-
*/
|
|
1091
|
-
async function readJsonBody(response: Response): Promise<Record<string, unknown>> {
|
|
1092
|
-
const text = await response.text().catch(() => "");
|
|
1093
|
-
if (text.trim().length === 0) return {};
|
|
1094
|
-
const parsed = safeJson(text);
|
|
1095
|
-
return record(parsed) ?? {};
|
|
1096
|
-
}
|
|
1097
|
-
|
|
1098
1377
|
/** Normalizes a scope list or space-delimited string. */
|
|
1099
1378
|
function normalizeScope(value: unknown): string {
|
|
1100
1379
|
if (Array.isArray(value)) {
|
|
@@ -1118,15 +1397,4 @@ function readExpiry(raw: Record<string, unknown>): Date | undefined {
|
|
|
1118
1397
|
return undefined;
|
|
1119
1398
|
}
|
|
1120
1399
|
|
|
1121
|
-
/**
|
|
1122
|
-
* Chooses the identity provider a social sign-in starts with.
|
|
1123
|
-
*
|
|
1124
|
-
* The vendor selects the provider by name rather than by client id, and the
|
|
1125
|
-
* console passes the operator's choice through the start parameters.
|
|
1126
|
-
*/
|
|
1127
|
-
function readAuthorizeIdp(request: OAuthAuthorizeRequest): "google" | "github" {
|
|
1128
|
-
const idp = request.parameters?.["idp"] ?? request.parameters?.["provider"];
|
|
1129
|
-
return idp === "github" ? "github" : "google";
|
|
1130
|
-
}
|
|
1131
|
-
|
|
1132
1400
|
export const kiroOAuthClient = new KiroOAuthClient();
|