Localize Kiro flow copy

This commit is contained in:
QLHazyCoder
2026-05-18 03:47:03 +08:00
parent feba51da65
commit 9d04877954
6 changed files with 89 additions and 39 deletions
+6 -2
View File
@@ -1,4 +1,4 @@
// background.js — Service Worker: orchestration, state, tab management, message routing
// background.js — Service Worker: orchestration, state, tab management, message routing
importScripts(
'shared/flow-registry.js',
@@ -12397,6 +12397,10 @@ async function runAutoSequenceFromNodeGraph(startNodeId, context = {}) {
};
const initialFlowState = await getState();
const activeFlowId = String(initialFlowState?.activeFlowId || initialFlowState?.flowId || DEFAULT_ACTIVE_FLOW_ID).trim().toLowerCase() || DEFAULT_ACTIVE_FLOW_ID;
const activeFlowLabel = String(
self.MultiPageFlowRegistry?.getFlowLabel?.(activeFlowId)
|| activeFlowId
).trim() || activeFlowId;
if (activeFlowId !== DEFAULT_ACTIVE_FLOW_ID) {
await broadcastAutoRunStatus('running', {
@@ -12409,7 +12413,7 @@ async function runAutoSequenceFromNodeGraph(startNodeId, context = {}) {
if (continueCurrentAttempt) {
await addLog(`=== 目标 ${targetRun}/${totalRuns} 轮:继续当前进度,从节点 ${currentStartNodeId} 开始(第 ${attemptRuns} 次尝试)===`, 'info');
} else {
await addLog(`=== 目标 ${targetRun}/${totalRuns} 轮:第 ${attemptRuns} 次尝试,开始执行 ${activeFlowId} flow ===`, 'info');
await addLog(`=== 目标 ${targetRun}/${totalRuns} 轮:第 ${attemptRuns} 次尝试,开始执行 ${activeFlowLabel} 流程 ===`, 'info');
}
let latestState = await getState();
+43 -30
View File
@@ -26,7 +26,7 @@
function normalizeKiroRsBaseUrl(value = '') {
const normalized = cleanString(value).replace(/\/+$/, '');
if (!normalized) {
throw new Error('Missing kiro.rs admin URL.');
throw new Error('缺少 kiro.rs 管理后台地址。');
}
return normalized.endsWith('/admin')
? normalized.slice(0, -'/admin'.length)
@@ -45,7 +45,20 @@
}
function getErrorMessage(error) {
return error instanceof Error ? error.message : String(error ?? 'Unknown error');
return error instanceof Error ? error.message : String(error ?? '未知错误');
}
function normalizeKiroUploadMessage(value = '') {
const rawValue = cleanString(value);
if (!rawValue) {
return '上传成功';
}
const normalizedValue = rawValue.toLowerCase();
if (normalizedValue === 'uploaded' || normalizedValue === 'credential uploaded.') {
return '上传成功';
}
return rawValue;
}
function normalizePositiveInteger(value, fallback) {
@@ -121,13 +134,13 @@
});
const registerBody = await readResponse(registerResponse);
if (!registerResponse.ok) {
throw new Error(`Builder ID client registration failed: ${cleanString(registerBody.text || registerResponse.statusText) || registerResponse.status}`);
throw new Error(`Builder ID 客户端注册失败:${cleanString(registerBody.text || registerResponse.statusText) || registerResponse.status}`);
}
const clientId = cleanString(registerBody.json?.clientId);
const clientSecret = String(registerBody.json?.clientSecret || '');
if (!clientId || !clientSecret) {
throw new Error('Builder ID client registration response is missing client credentials.');
throw new Error('Builder ID 客户端注册响应缺少凭据。');
}
const authorizationResponse = await fetchImpl(`${oidcBaseUrl}/device_authorization`, {
@@ -143,7 +156,7 @@
});
const authorizationBody = await readResponse(authorizationResponse);
if (!authorizationResponse.ok) {
throw new Error(`Builder ID device authorization failed: ${cleanString(authorizationBody.text || authorizationResponse.statusText) || authorizationResponse.status}`);
throw new Error(`Builder ID 设备授权失败:${cleanString(authorizationBody.text || authorizationResponse.statusText) || authorizationResponse.status}`);
}
const deviceCode = String(authorizationBody.json?.deviceCode || '');
@@ -155,7 +168,7 @@
const interval = normalizePositiveInteger(authorizationBody.json?.interval, 5);
const expiresIn = normalizePositiveInteger(authorizationBody.json?.expiresIn, 600);
if (!deviceCode || !userCode || !verificationUriComplete) {
throw new Error('Builder ID device authorization response is missing required fields.');
throw new Error('Builder ID 设备授权响应缺少必要字段。');
}
return {
@@ -205,14 +218,14 @@
return { completed: false, status: 'slow_down' };
}
if (errorCode === 'expired_token') {
throw new Error('Kiro device login expired.');
throw new Error('Kiro 设备登录已过期。');
}
if (errorCode === 'access_denied') {
throw new Error('User denied the Builder ID device login request.');
throw new Error('用户拒绝了 Builder ID 设备登录授权请求。');
}
throw new Error(`Builder ID authorization failed: ${errorCode || cleanString(body.text || response.statusText) || response.status}`);
throw new Error(`Builder ID 授权失败:${errorCode || cleanString(body.text || response.statusText) || response.status}`);
}
throw new Error(`Builder ID token request failed: HTTP ${response.status}`);
throw new Error(`Builder ID 令牌请求失败:HTTP ${response.status}`);
}
async function checkKiroRsConnection(baseUrl, apiKey, fetchImpl) {
@@ -228,31 +241,31 @@
if (response.ok) {
return {
ok: true,
message: `kiro.rs connection ok (HTTP ${response.status})`,
message: `kiro.rs 连接正常(HTTP ${response.status}`,
};
}
if (response.status === 405) {
return {
ok: true,
message: 'kiro.rs upload endpoint is reachable.',
message: 'kiro.rs 上传接口可访问。',
};
}
if (response.status === 401 || response.status === 403) {
return {
ok: false,
message: `kiro.rs API key rejected (HTTP ${response.status})`,
message: `kiro.rs API Key 被拒绝(HTTP ${response.status}`,
};
}
if (response.status === 404) {
return {
ok: false,
message: 'kiro.rs admin endpoint not found.',
message: '未找到 kiro.rs 管理接口。',
};
}
return {
ok: false,
message: cleanString(body.json?.error?.message || body.json?.message || body.text || response.statusText)
|| `kiro.rs connection failed (HTTP ${response.status})`,
|| `kiro.rs 连接失败(HTTP ${response.status}`,
};
}
@@ -271,13 +284,13 @@
if (!response.ok) {
const message = cleanString(body.json?.error?.message || body.json?.message || body.text || response.statusText)
|| `HTTP ${response.status}`;
throw new Error(`kiro.rs credential upload failed: ${message}`);
throw new Error(`kiro.rs 凭据上传失败:${message}`);
}
return {
credentialId: Number(body.json?.credentialId || body.json?.credential_id || 0) || null,
email: cleanString(body.json?.email),
message: cleanString(body.json?.message) || 'Credential uploaded.',
message: normalizeKiroUploadMessage(body.json?.message),
raw: body.json,
};
}
@@ -360,7 +373,7 @@
};
await setState(updates);
await log(`Kiro device login started. Open ${loginUrl} and approve with code ${auth.userCode}.`, 'info', nodeId);
await log(`Kiro 设备登录已启动。请打开 ${loginUrl},并输入授权码 ${auth.userCode} 完成确认。`, 'info', nodeId);
await completeNodeFromBackground(nodeId, updates);
} catch (error) {
const message = getErrorMessage(error);
@@ -382,10 +395,10 @@
const region = normalizeRegion(latestState.kiroAuthRegion, DEFAULT_REGION);
const expiresAt = Math.max(0, Number(latestState.kiroAuthExpiresAt) || 0);
if (!clientId || !clientSecret || !deviceCode) {
throw new Error('Kiro device login has not been started yet.');
throw new Error('尚未启动 Kiro 设备登录,请先执行步骤 1。');
}
if (!expiresAt || expiresAt <= Date.now()) {
throw new Error('Kiro device login expired. Restart step 1.');
throw new Error('Kiro 设备登录已过期,请重新执行步骤 1');
}
await setState({
@@ -393,7 +406,7 @@
kiroAuthStatus: 'waiting_user',
kiroUploadStatus: 'waiting_login',
});
await log('Waiting for Kiro device login approval...', 'info', nodeId);
await log('正在等待 Kiro 设备登录授权确认...', 'info', nodeId);
let intervalSeconds = normalizePositiveInteger(latestState.kiroAuthIntervalSeconds, 5);
while (Date.now() < expiresAt) {
@@ -414,7 +427,7 @@
kiroUploadStatus: 'ready_to_upload',
};
await setState(updates);
await log('Kiro device login approved. Refresh token captured.', 'ok', nodeId);
await log('Kiro 设备登录已确认,已获取 Refresh Token', 'ok', nodeId);
await completeNodeFromBackground(nodeId, updates);
return;
}
@@ -425,12 +438,12 @@
await sleepWithStop(intervalSeconds * 1000);
}
throw new Error('Kiro device login expired. Restart step 1.');
throw new Error('Kiro 设备登录已过期,请重新执行步骤 1');
} catch (error) {
const message = getErrorMessage(error);
await persistFailure({
kiroAuthError: message,
kiroAuthStatus: /expired/i.test(message) ? 'expired' : 'error',
kiroAuthStatus: /(expired|已过期)/i.test(message) ? 'expired' : 'error',
});
throw error;
}
@@ -447,20 +460,20 @@
const kiroRsUrl = String(latestState.kiroRsUrl || '');
const kiroRsKey = String(latestState.kiroRsKey || '');
if (!refreshToken || !clientId || !clientSecret) {
throw new Error('Kiro refresh token is missing. Complete step 2 first.');
throw new Error('缺少 Kiro Refresh Token,请先完成步骤 2。');
}
if (!cleanString(kiroRsUrl)) {
throw new Error('Missing kiro.rs admin URL.');
throw new Error('缺少 kiro.rs 管理后台地址。');
}
if (!cleanString(kiroRsKey)) {
throw new Error('Missing kiro.rs API key.');
throw new Error('缺少 kiro.rs API Key');
}
await setState({
kiroUploadError: '',
kiroUploadStatus: 'uploading',
});
await log('Uploading Builder ID credential to kiro.rs...', 'info', nodeId);
await log('正在上传 Builder ID 凭据到 kiro.rs...', 'info', nodeId);
const connection = await checkKiroRsConnection(kiroRsUrl, kiroRsKey, fetchImpl);
await setState({
@@ -492,11 +505,11 @@
kiroCredentialId: uploadResult.credentialId,
kiroLastUploadAt: Date.now(),
kiroUploadError: '',
kiroUploadStatus: uploadResult.message || 'uploaded',
kiroUploadStatus: normalizeKiroUploadMessage(uploadResult.message),
};
await setState(updates);
await log(`kiro.rs upload completed: ${updates.kiroUploadStatus}`, 'ok', nodeId);
await log(`kiro.rs 上传完成:${updates.kiroUploadStatus}`, 'ok', nodeId);
await completeNodeFromBackground(nodeId, updates);
} catch (error) {
const message = getErrorMessage(error);
+3 -3
View File
@@ -116,7 +116,7 @@
id: 1,
order: 10,
key: 'kiro-start-device-login',
title: 'Start device login',
title: '启动设备登录',
sourceId: 'kiro-device-auth',
driverId: 'background/kiro-device-auth',
command: 'kiro-start-device-login',
@@ -125,7 +125,7 @@
id: 2,
order: 20,
key: 'kiro-await-device-login',
title: 'Wait for device login',
title: '等待设备登录确认',
sourceId: 'kiro-device-auth',
driverId: 'background/kiro-device-auth',
command: 'kiro-await-device-login',
@@ -134,7 +134,7 @@
id: 3,
order: 30,
key: 'kiro-upload-credential',
title: 'Upload credential to kiro.rs',
title: '上传凭据到 kiro.rs',
sourceId: 'kiro-rs-admin',
driverId: 'background/kiro-device-auth',
command: 'kiro-upload-credential',
+31 -1
View File
@@ -2373,6 +2373,36 @@ function normalizeStepExecutionRangeEntry(value = {}) {
};
}
function getKiroUploadStatusLabel(value = '') {
const rawValue = String(value || '').trim();
if (!rawValue) {
return '未开始';
}
const normalizedValue = rawValue.toLowerCase();
switch (normalizedValue) {
case 'waiting_login':
return '等待登录授权';
case 'ready_to_upload':
return '等待上传';
case 'uploading':
return '上传中';
case 'uploaded':
case 'credential uploaded.':
return '上传成功';
case 'error':
return '上传失败';
case 'waiting_user':
return '等待用户确认';
case 'authorized':
return '已授权';
case 'expired':
return '已过期';
default:
return rawValue;
}
}
function normalizeStepExecutionRangeByFlow(value = {}) {
const source = isPlainObjectValue(value) ? value : {};
const next = {};
@@ -9954,7 +9984,7 @@ function applySettingsState(state) {
|| state?.kiroUploadStatus
|| ''
).trim();
displayKiroUploadStatus.textContent = kiroUploadStatus || '未开始';
displayKiroUploadStatus.textContent = getKiroUploadStatusLabel(kiroUploadStatus);
}
const normalizedIpProxyService = resolveIpProxyService(state?.ipProxyService);
const normalizedIpProxyServiceProfiles = typeof normalizeIpProxyServiceProfiles === 'function'
@@ -312,15 +312,15 @@ test('kiro upload credential checks connection and uploads builder id credential
});
const finalState = mergeUpdates(stateUpdates);
assert.equal(finalState.kiroLastConnectionMessage, 'kiro.rs connection ok (HTTP 200)');
assert.equal(finalState.kiroLastConnectionMessage, 'kiro.rs 连接正常(HTTP 200');
assert.equal(finalState.kiroAuthorizedEmail, 'aws-user@example.com');
assert.equal(finalState.kiroCredentialId, 321);
assert.equal(finalState.kiroUploadStatus, 'uploaded');
assert.equal(finalState.kiroUploadStatus, '上传成功');
assert.equal(typeof finalState.kiroLastUploadAt, 'number');
assert.equal(finalState.kiroLastUploadAt > 0, true);
assert.equal(completeCalls.length, 1);
assert.equal(completeCalls[0].nodeId, 'kiro-upload-credential');
assert.equal(completeCalls[0].payload.kiroCredentialId, 321);
assert.equal(completeCalls[0].payload.kiroUploadStatus, 'uploaded');
assert.equal(completeCalls[0].payload.kiroUploadStatus, '上传成功');
});
+3
View File
@@ -175,6 +175,9 @@ test('step definitions module exposes ordered normal and Plus step metadata', ()
assert.equal(kiroSteps.every((step) => step.flowId === 'kiro'), true);
assert.equal(kiroSteps[0].driverId, 'background/kiro-device-auth');
assert.equal(kiroSteps[2].sourceId, 'kiro-rs-admin');
assert.equal(kiroSteps[0].title, '启动设备登录');
assert.equal(kiroSteps[1].title, '等待设备登录确认');
assert.equal(kiroSteps[2].title, '上传凭据到 kiro.rs');
assert.deepStrictEqual(api.getStepIds({ activeFlowId: 'kiro' }), [1, 2, 3]);
assert.equal(api.getLastStepId({ activeFlowId: 'kiro' }), 3);
assert.deepStrictEqual(