feat(claude-kiro): 支持AWS企业用户idcRegion字段并优化模型配置

- 在AWS凭据导入说明中增加idcRegion字段要求
- 移除不再使用的Amazon Q相关URL和旧模型映射
- 为Builder ID认证流程添加region选择和idcRegion支持
- 优化凭据加载逻辑,支持独立配置idcRegion
- 在UI中添加region输入框和刷新按钮
This commit is contained in:
hex2077 2026-01-21 15:56:01 +08:00
parent 37dab55d49
commit 785ac7890d
4 changed files with 56 additions and 32 deletions

View file

@ -16,7 +16,7 @@ const KIRO_OAUTH_CONFIG = {
authServiceEndpoint: 'https://prod.us-east-1.auth.desktop.kiro.dev',
// AWS SSO OIDC 端点 (用于 Builder ID)
ssoOIDCEndpoint: 'https://oidc.us-east-1.amazonaws.com',
ssoOIDCEndpoint: 'https://oidc.{{region}}.amazonaws.com',
// AWS Builder ID 起始 URL
builderIDStartURL: 'https://view.awsapps.com/start',
@ -34,8 +34,8 @@ const KIRO_OAUTH_CONFIG = {
'codewhisperer:completions',
'codewhisperer:analysis',
'codewhisperer:conversations',
'codewhisperer:transformations',
'codewhisperer:taskassist'
// 'codewhisperer:transformations',
// 'codewhisperer:taskassist'
],
// 凭据存储(符合现有规范)
@ -252,7 +252,10 @@ async function handleKiroBuilderIDDeviceCode(currentConfig, options = {}) {
console.log(`${KIRO_OAUTH_CONFIG.logPrefix} Using Builder ID Start URL: ${builderIDStartURL}`);
// 1. 注册 OIDC 客户端
const regResponse = await fetchWithProxy(`${KIRO_OAUTH_CONFIG.ssoOIDCEndpoint}/client/register`, {
const region = options.region || 'us-east-1';
const ssoOIDCEndpoint = KIRO_OAUTH_CONFIG.ssoOIDCEndpoint.replace('{{region}}', region);
const regResponse = await fetchWithProxy(`${ssoOIDCEndpoint}/client/register`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
@ -262,7 +265,7 @@ async function handleKiroBuilderIDDeviceCode(currentConfig, options = {}) {
clientName: 'Kiro IDE',
clientType: 'public',
scopes: KIRO_OAUTH_CONFIG.scopes,
grantTypes: ['urn:ietf:params:oauth:grant-type:device_code', 'refresh_token']
// grantTypes: ['urn:ietf:params:oauth:grant-type:device_code', 'refresh_token']
})
}, 'claude-kiro-oauth');
@ -273,11 +276,10 @@ async function handleKiroBuilderIDDeviceCode(currentConfig, options = {}) {
const regData = await regResponse.json();
// 2. 启动设备授权
const authResponse = await fetchWithProxy(`${KIRO_OAUTH_CONFIG.ssoOIDCEndpoint}/device_authorization`, {
const authResponse = await fetchWithProxy(`${ssoOIDCEndpoint}/device_authorization`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'User-Agent': 'KiroIDE'
'Content-Type': 'application/json'
},
body: JSON.stringify({
clientId: regData.clientId,
@ -304,7 +306,7 @@ async function handleKiroBuilderIDDeviceCode(currentConfig, options = {}) {
5,
300,
taskId,
options
{ ...options, region }
).catch(error => {
console.error(`${KIRO_OAUTH_CONFIG.logPrefix} 轮询失败 [${taskId}]:`, error);
broadcastEvent('oauth_error', {
@ -356,7 +358,9 @@ async function pollKiroBuilderIDToken(clientId, clientSecret, deviceCode, interv
attempts++;
try {
const response = await fetchWithProxy(`${KIRO_OAUTH_CONFIG.ssoOIDCEndpoint}/token`, {
const region = options.region || 'us-east-1';
const ssoOIDCEndpoint = KIRO_OAUTH_CONFIG.ssoOIDCEndpoint.replace('{{region}}', region);
const response = await fetchWithProxy(`${ssoOIDCEndpoint}/token`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
@ -391,7 +395,7 @@ async function pollKiroBuilderIDToken(clientId, clientSecret, deviceCode, interv
authMethod: 'builder-id',
clientId,
clientSecret,
region: 'us-east-1'
idcRegion: options.region || 'us-east-1'
};
await fs.promises.mkdir(path.dirname(credPath), { recursive: true });
@ -634,7 +638,8 @@ const KIRO_REFRESH_CONSTANTS = {
AUTH_METHOD_SOCIAL: 'social',
DEFAULT_PROVIDER: 'Google',
REQUEST_TIMEOUT: 30000,
DEFAULT_REGION: 'us-east-1'
DEFAULT_REGION: 'us-east-1',
IDC_REGION: 'us-east-1' // 用于 REFRESH_IDC_URL 的区域配置
};
/**
@ -1024,7 +1029,8 @@ export async function importAwsCredentials(credentials, skipDuplicateCheck = fal
accessToken: credentials.accessToken,
refreshToken: credentials.refreshToken,
authMethod: credentials.authMethod || 'builder-id',
region: credentials.region || KIRO_REFRESH_CONSTANTS.DEFAULT_REGION
// region: credentials.region || KIRO_REFRESH_CONSTANTS.DEFAULT_REGION,
idcRegion: credentials.idcRegion || KIRO_REFRESH_CONSTANTS.IDC_REGION
};
// 可选字段
@ -1042,8 +1048,8 @@ export async function importAwsCredentials(credentials, skipDuplicateCheck = fal
try {
console.log(`${KIRO_OAUTH_CONFIG.logPrefix} Attempting to refresh token with provided credentials...`);
const region = credentials.region || KIRO_REFRESH_CONSTANTS.DEFAULT_REGION;
const refreshUrl = KIRO_REFRESH_CONSTANTS.REFRESH_IDC_URL.replace('{{region}}', region);
const refreshRegion = credentials.idcRegion || KIRO_REFRESH_CONSTANTS.IDC_REGION;
const refreshUrl = KIRO_REFRESH_CONSTANTS.REFRESH_IDC_URL.replace('{{region}}', refreshRegion);
const refreshResponse = await fetchWithProxy(refreshUrl, {
method: 'POST',

View file

@ -25,9 +25,7 @@ const KIRO_CONSTANTS = {
REFRESH_URL: 'https://prod.{{region}}.auth.desktop.kiro.dev/refreshToken',
REFRESH_IDC_URL: 'https://oidc.{{region}}.amazonaws.com/token',
BASE_URL: 'https://q.{{region}}.amazonaws.com/generateAssistantResponse',
AMAZON_Q_URL: 'https://codewhisperer.{{region}}.amazonaws.com/SendMessageStreaming',
USAGE_LIMITS_URL: 'https://q.{{region}}.amazonaws.com/getUsageLimits',
DEFAULT_MODEL_NAME: 'claude-opus-4-5',
DEFAULT_MODEL_NAME: 'claude-sonnet-4-5',
AXIOS_TIMEOUT: 120000, // 2 minutes timeout for normal requests
TOKEN_REFRESH_TIMEOUT: 15000, // 15 seconds timeout for token refresh (shorter to avoid blocking)
USER_AGENT: 'KiroIDE',
@ -49,9 +47,7 @@ const FULL_MODEL_MAPPING = {
"claude-opus-4-5":"claude-opus-4.5",
"claude-opus-4-5-20251101":"claude-opus-4.5",
"claude-sonnet-4-5": "CLAUDE_SONNET_4_5_20250929_V1_0",
"claude-sonnet-4-5-20250929": "CLAUDE_SONNET_4_5_20250929_V1_0",
"claude-sonnet-4-20250514": "CLAUDE_SONNET_4_20250514_V1_0",
"claude-3-7-sonnet-20250219": "CLAUDE_3_7_SONNET_20250219_V1_0"
"claude-sonnet-4-5-20250929": "CLAUDE_SONNET_4_5_20250929_V1_0"
};
// 只保留 KIRO_MODELS 中存在的模型映射
@ -362,7 +358,6 @@ export class KiroApiService {
// this.refreshUrl = KIRO_CONSTANTS.REFRESH_URL;
// this.refreshIDCUrl = KIRO_CONSTANTS.REFRESH_IDC_URL;
// this.baseUrl = KIRO_CONSTANTS.BASE_URL;
// this.amazonQUrl = KIRO_CONSTANTS.AMAZON_Q_URL;
// Add kiro-oauth-creds-base64 and kiro-oauth-creds-file to config
if (config.KIRO_OAUTH_CREDS_BASE64) {
@ -536,16 +531,21 @@ async loadCredentials() {
this.expiresAt = this.expiresAt || mergedCredentials.expiresAt;
this.profileArn = this.profileArn || mergedCredentials.profileArn;
this.region = this.region || mergedCredentials.region;
this.idcRegion = this.idcRegion || mergedCredentials.idcRegion;
if (!this.region) {
console.warn('[Kiro Auth] Region not found in credentials. Using default region us-east-1 for URLs.');
this.region = 'us-east-1';
}
// idcRegion 用于 REFRESH_IDC_URL如果未设置则使用 region
if (!this.idcRegion) {
this.idcRegion = this.region;
}
this.refreshUrl = (this.config.KIRO_REFRESH_URL || KIRO_CONSTANTS.REFRESH_URL).replace("{{region}}", this.region);
this.refreshIDCUrl = (this.config.KIRO_REFRESH_IDC_URL || KIRO_CONSTANTS.REFRESH_IDC_URL).replace("{{region}}", this.region);
this.refreshIDCUrl = (this.config.KIRO_REFRESH_IDC_URL || KIRO_CONSTANTS.REFRESH_IDC_URL).replace("{{region}}", this.idcRegion);
this.baseUrl = (this.config.KIRO_BASE_URL || KIRO_CONSTANTS.BASE_URL).replace("{{region}}", this.region);
this.amazonQUrl = (KIRO_CONSTANTS.AMAZON_Q_URL).replace("{{region}}", this.region);
} catch (error) {
console.warn(`[Kiro Auth] Error during credential loading: ${error.message}`);
}
@ -2724,7 +2724,8 @@ async saveCredentialsToFile(filePath, newData) {
const resourceType = 'AGENTIC_REQUEST';
// 构建请求 URL
const usageLimitsUrl = KIRO_CONSTANTS.USAGE_LIMITS_URL.replace('{{region}}', this.region);
let usageLimitsUrl = this.baseUrl;
usageLimitsUrl = usageLimitsUrl.replace('generateAssistantResponse', 'getUsageLimits');
const params = new URLSearchParams({
isEmailRequired: 'true',
origin: KIRO_CONSTANTS.ORIGIN_AI_EDITOR,

View file

@ -143,7 +143,7 @@ const translations = {
'oauth.kiro.batchImportInstructions': '请输入 refreshToken每行一个。系统将自动刷新并生成凭据文件。',
'oauth.kiro.awsImport': '导入 AWS 账号',
'oauth.kiro.awsImportDesc': '从 AWS SSO cache 目录导入凭据文件,适用于 AWS Builder ID 模式。',
'oauth.kiro.awsImportInstructions': '请上传 AWS SSO cache 目录中的 JSON 文件,需包含 clientId、clientSecret、accessToken、refreshToken 四个字段。',
'oauth.kiro.awsImportInstructions': '请上传 AWS SSO cache 目录中的 JSON 文件,需包含 clientId、clientSecret、accessToken、refreshToken 四个字段。如果是AWS企业用户需增加 idcRegion 字段。',
'oauth.kiro.awsModeFile': '文件上传',
'oauth.kiro.awsModeJson': 'JSON 粘贴',
'oauth.kiro.awsUploadFiles': '上传凭据文件',
@ -856,7 +856,7 @@ const translations = {
'oauth.kiro.batchImportInstructions': 'Enter refreshTokens, one per line. The system will automatically refresh and generate credential files.',
'oauth.kiro.awsImport': 'Import AWS Account',
'oauth.kiro.awsImportDesc': 'Import credential files from AWS SSO cache directory. For AWS Builder ID mode.',
'oauth.kiro.awsImportInstructions': 'Upload JSON files from AWS SSO cache directory. Must contain clientId, clientSecret, accessToken, and refreshToken.',
'oauth.kiro.awsImportInstructions': 'Upload JSON files from AWS SSO cache directory. Must contain clientId, clientSecret, accessToken, and refreshToken. For AWS enterprise users, add the idcRegion field.',
'oauth.kiro.awsModeFile': 'File Upload',
'oauth.kiro.awsModeJson': 'Paste JSON',
'oauth.kiro.awsUploadFiles': 'Upload Credential Files',

View file

@ -1723,7 +1723,12 @@ function showAuthModal(authUrl, authInfo) {
<div class="modal-body">
<div class="auth-info">
<p><strong data-i18n="oauth.modal.provider">${t('oauth.modal.provider')}</strong> ${authInfo.provider}</p>
<div class="port-info-section" style="margin: 12px 0; padding: 12px; background: #fef3c7; border: 1px solid #fcd34d; border-radius: 8px;">
<div class="port-info-section" style="margin: 12px 0; padding: 12px; background: #fef3c7; border: 1px solid #fcd34d; border-radius: 8px; position: relative;">
${(authInfo.provider === 'claude-kiro-oauth' && authInfo.authMethod === 'builder-id') ? `
<button class="regenerate-builder-id-btn" title="${t('common.generate')}" style="position: absolute; top: 12px; right: 12px; background: none; border: 1px solid #d97706; border-radius: 4px; cursor: pointer; color: #d97706; padding: 4px 8px;">
<i class="fas fa-sync-alt"></i>
</button>
` : ''}
<div style="margin: 0; display: flex; align-items: center; gap: 8px; flex-wrap: wrap;">
<i class="fas fa-network-wired" style="color: #d97706;"></i>
<strong data-i18n="oauth.modal.requiredPort">${t('oauth.modal.requiredPort')}</strong>
@ -1751,15 +1756,25 @@ function showAuthModal(authUrl, authInfo) {
placeholder="https://view.awsapps.com/start"
style="flex: 1; padding: 6px 10px; border: 1px solid #fcd34d; border-radius: 4px; font-size: 13px; color: #92400e; background: white;"
/>
<button class="regenerate-builder-id-btn" title="${t('common.generate')}" style="background: none; border: 1px solid #d97706; border-radius: 4px; cursor: pointer; color: #d97706; padding: 4px 8px;">
<i class="fas fa-sync-alt"></i>
</button>
</div>
<p style="margin: 6px 0 0 0; font-size: 0.75rem; color: #b45309;">
<i class="fas fa-info-circle"></i>
<span data-i18n="oauth.kiro.builderIDStartURLHint">${t('oauth.kiro.builderIDStartURLHint') || '如果您使用 AWS IAM Identity Center请输入您的 Start URL'}</span>
</p>
</div>
<div class="builder-id-region-section" style="margin-top: 12px; padding-top: 12px; border-top: 1px dashed #fcd34d;">
<label style="display: flex; align-items: center; gap: 6px; margin-bottom: 6px; font-size: 13px; font-weight: 600; color: #92400e;">
<i class="fas fa-globe"></i>
<span>AWS Region</span>
</label>
<div style="display: flex; align-items: center; gap: 4px;">
<input type="text" class="builder-id-region-input"
value="${authInfo.region || 'us-east-1'}"
placeholder="us-east-1"
style="flex: 1; padding: 6px 10px; border: 1px solid #fcd34d; border-radius: 4px; font-size: 13px; color: #92400e; background: white;"
/>
</div>
</div>
` : ''}
</div>
${instructionsHtml}
@ -1819,11 +1834,13 @@ function showAuthModal(authUrl, authInfo) {
if (regenerateBuilderIdBtn) {
regenerateBuilderIdBtn.onclick = async () => {
const builderIdStartUrl = modal.querySelector('.builder-id-start-url-input').value.trim();
const region = modal.querySelector('.builder-id-region-input').value.trim();
modal.remove();
// 构造重新请求的参数
const options = {
...authInfo,
builderIDStartURL: builderIdStartUrl || 'https://view.awsapps.com/start'
builderIDStartURL: builderIdStartUrl || 'https://view.awsapps.com/start',
region: region || 'us-east-1'
};
// 移除不需要传递回后端的字段
delete options.provider;