feat: upgrade multi-flow account contributions

This commit is contained in:
QLHazycoder
2026-05-19 13:20:06 +00:00
parent 68c1f387e1
commit 4687d07e2d
36 changed files with 2960 additions and 442 deletions
+3 -3
View File
@@ -431,7 +431,7 @@
source,
autoRunContext: source === 'auto' ? autoRunContext : null,
plusModeEnabled: Boolean(record.plusModeEnabled),
contributionMode: Boolean(record.contributionMode),
accountContributionEnabled: Boolean(record.accountContributionEnabled),
};
}
@@ -526,7 +526,7 @@
source,
autoRunContext,
plusModeEnabled: Boolean(state.plusModeEnabled),
contributionMode: Boolean(state.contributionMode),
accountContributionEnabled: Boolean(state.accountContributionEnabled),
};
}
@@ -637,7 +637,7 @@
}
function shouldSyncAccountRunHistorySnapshot(state = {}) {
if (Boolean(state.contributionMode)) {
if (Boolean(state.accountContributionEnabled)) {
return false;
}
+12 -10
View File
@@ -8,8 +8,10 @@
const CALLBACK_WAITING_STATUSES = new Set(['idle', 'waiting', 'captured', 'failed', 'submitting']);
const RUNTIME_DEFAULTS = {
contributionMode: false,
contributionModeExpected: false,
accountContributionEnabled: false,
accountContributionExpected: false,
contributionAdapterId: '',
flowContributionRuntime: {},
contributionSource: 'sub2api',
contributionTargetGroupName: 'codex号池',
contributionNickname: '',
@@ -265,14 +267,14 @@
return Boolean(state?.plusModeEnabled);
}
function normalizeContributionModeSource(value = '') {
function normalizeOpenAiContributionSource(value = '') {
const normalized = normalizeString(value).toLowerCase();
return normalized === 'sub2api' ? 'sub2api' : 'cpa';
}
function resolveContributionModeRoutingState(state = {}) {
function resolveOpenAiContributionRoutingState(state = {}) {
const currentStatus = normalizeString(state?.contributionStatus).toLowerCase();
const currentSource = normalizeContributionModeSource(state?.contributionSource);
const currentSource = normalizeOpenAiContributionSource(state?.contributionSource);
const hasActiveSession = Boolean(
normalizeString(state?.contributionSessionId)
&& currentStatus
@@ -533,7 +535,7 @@
async function handleCapturedCallback(rawUrl, metadata = {}) {
const currentState = await getState();
if (!normalizeString(currentState.contributionSessionId) || !currentState.contributionMode) {
if (!normalizeString(currentState.contributionSessionId) || !currentState.accountContributionEnabled) {
return currentState;
}
if (!isContributionCallbackUrl(rawUrl, currentState)) {
@@ -642,10 +644,10 @@
return nextState;
}
async function startContributionFlow(options = {}) {
async function startFlowContribution(options = {}) {
const currentState = options.stateOverride || await getState();
const shouldOpenAuthTab = options.openAuthTab !== false;
if (!currentState.contributionMode) {
if (!currentState.accountContributionEnabled) {
throw new Error('请先进入贡献模式。');
}
@@ -662,7 +664,7 @@
return pollContributionStatus({ reason: 'resume_existing' });
}
const routingState = resolveContributionModeRoutingState(currentState);
const routingState = resolveOpenAiContributionRoutingState(currentState);
const payload = await fetchContributionJson('/start', {
method: 'POST',
body: {
@@ -744,7 +746,7 @@
isContributionCallbackUrl,
isContributionFinalStatus,
pollContributionStatus,
startContributionFlow,
startFlowContribution,
submitContributionCallback,
};
}
@@ -0,0 +1,245 @@
(function attachBackgroundKiroBuilderIdContributionAdapter(root, factory) {
root.MultiPageBackgroundKiroBuilderIdContributionAdapter = factory(root);
})(typeof self !== 'undefined' ? self : globalThis, function createBackgroundKiroBuilderIdContributionAdapterModule(root) {
const artifactApi = root?.MultiPageBackgroundKiroCredentialArtifact || {};
const FLOW_ID = artifactApi.FLOW_ID || 'kiro';
const ADAPTER_ID = artifactApi.ADAPTER_ID || 'kiro-builder-id';
const ARTIFACT_KIND = artifactApi.ARTIFACT_KIND || 'kiro-builder-id';
const DEFAULT_CONTRIBUTION_API_URL = 'https://flowpilot.qlhazycoder.top/api/contributions';
function isPlainObject(value) {
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
}
function cleanString(value = '') {
return String(value ?? '').trim();
}
function getErrorMessage(error) {
return error instanceof Error ? error.message : String(error ?? '未知错误');
}
function normalizeFlowId(state = {}) {
return cleanString(state.activeFlowId || state.flowId).toLowerCase() || 'openai';
}
function normalizeAdapterId(state = {}) {
return cleanString(state.contributionAdapterId || ADAPTER_ID).toLowerCase() || ADAPTER_ID;
}
function shouldSubmitKiroBuilderIdContribution(state = {}) {
return Boolean(state?.accountContributionEnabled)
&& normalizeFlowId(state) === FLOW_ID
&& normalizeAdapterId(state) === ADAPTER_ID;
}
function normalizeContributionApiUrl(value = '') {
const normalized = cleanString(value).replace(/\/+$/, '');
if (!normalized) {
return DEFAULT_CONTRIBUTION_API_URL;
}
if (/\/api\/contributions$/i.test(normalized)) {
return normalized;
}
return `${normalized}/api/contributions`;
}
function mergeFlowContributionRuntime(currentRuntime = {}, flowId = FLOW_ID, patch = {}) {
const runtime = isPlainObject(currentRuntime) ? currentRuntime : {};
const currentFlowRuntime = isPlainObject(runtime[flowId]) ? runtime[flowId] : {};
return {
...runtime,
[flowId]: {
...currentFlowRuntime,
...patch,
},
};
}
function redactKnownArtifactSecrets(message = '', artifact = {}) {
let result = cleanString(message);
const secrets = [
artifact?.credentials?.refreshToken,
artifact?.credentials?.clientSecret,
]
.map((value) => cleanString(value))
.filter(Boolean);
secrets.forEach((secret) => {
result = result.split(secret).join(artifactApi.redactSecret?.(secret) || '[redacted]');
});
return result;
}
async function readJsonResponse(response) {
const text = await response.text();
try {
return text ? JSON.parse(text) : {};
} catch (_error) {
return { message: text };
}
}
async function submitContributionArtifact(artifact = {}, options = {}) {
const fetchImpl = options.fetchImpl;
if (typeof fetchImpl !== 'function') {
throw new Error('账号贡献提交能力缺少 fetch。');
}
const endpoint = normalizeContributionApiUrl(options.apiUrl);
const payload = {
flow: FLOW_ID,
adapter_id: ADAPTER_ID,
artifact_kind: ARTIFACT_KIND,
source: 'flowpilot-extension',
contributor: {
nickname: cleanString(options.nickname),
qq: cleanString(options.qq),
},
artifact,
};
const response = await fetchImpl(endpoint, {
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify(payload),
});
const body = await readJsonResponse(response);
if (!response.ok || body?.ok === false) {
throw new Error(cleanString(body?.message || body?.detail || body?.error) || `贡献服务请求失败(HTTP ${response.status})。`);
}
return {
ok: true,
status: response.status,
contributionId: cleanString(body?.contribution_id || body?.contributionId || body?.id),
message: cleanString(body?.message) || '贡献提交成功',
raw: body,
};
}
function createKiroBuilderIdContributionAdapter(deps = {}) {
const {
addLog = async () => {},
contributionApiUrl = DEFAULT_CONTRIBUTION_API_URL,
fetchImpl = typeof fetch === 'function' ? fetch.bind(globalThis) : null,
getState = async () => ({}),
setState = async () => {},
} = deps;
async function updateFlowContributionRuntime(currentState = {}, patch = {}) {
const runtime = mergeFlowContributionRuntime(currentState.flowContributionRuntime, FLOW_ID, {
adapterId: ADAPTER_ID,
...patch,
});
await setState({
flowContributionRuntime: runtime,
});
return runtime;
}
async function log(message, level = 'info', nodeId = '') {
await addLog(message, level, nodeId ? { nodeId } : {});
}
async function maybeSubmitFlowContribution(state = {}, options = {}) {
const currentState = Object.keys(state || {}).length ? state : await getState();
const nodeId = cleanString(options.nodeId || 'kiro-complete-desktop-authorize');
if (!shouldSubmitKiroBuilderIdContribution(currentState)) {
return {
ok: true,
skipped: true,
reason: 'not_enabled_for_kiro_builder_id',
};
}
let artifact = null;
try {
artifact = artifactApi.buildKiroBuilderIdArtifact(currentState);
} catch (error) {
const message = redactKnownArtifactSecrets(getErrorMessage(error), artifact);
await updateFlowContributionRuntime(currentState, {
enabled: true,
status: 'skipped',
error: message,
lastMessage: message,
updatedAt: Date.now(),
});
await log(`Kiro 账号贡献已跳过:${message}`, 'warn', nodeId);
return {
ok: false,
skipped: true,
reason: error?.code || 'artifact_invalid',
message,
};
}
await updateFlowContributionRuntime(currentState, {
enabled: true,
status: 'submitting',
error: '',
lastMessage: '正在提交 Kiro Builder ID 贡献',
updatedAt: Date.now(),
});
const safeSummary = artifactApi.buildSafeArtifactSummary?.(artifact) || {};
await log(`Kiro 账号贡献:正在提交 Builder ID,邮箱 ${safeSummary.email || '未知'}`, 'info', nodeId);
try {
const result = await submitContributionArtifact(artifact, {
apiUrl: options.contributionApiUrl || contributionApiUrl,
fetchImpl,
nickname: currentState.contributionNickname,
qq: currentState.contributionQq,
});
await updateFlowContributionRuntime(currentState, {
enabled: true,
status: 'submitted',
error: '',
contributionId: result.contributionId,
lastMessage: result.message,
submittedAt: Date.now(),
updatedAt: Date.now(),
});
await log(`Kiro 账号贡献:提交完成,${result.message}`, 'ok', nodeId);
return result;
} catch (error) {
const message = redactKnownArtifactSecrets(getErrorMessage(error), artifact);
await updateFlowContributionRuntime(currentState, {
enabled: true,
status: 'error',
error: message,
lastMessage: message,
updatedAt: Date.now(),
});
await log(`Kiro 账号贡献提交失败:${message}`, 'warn', nodeId);
return {
ok: false,
skipped: false,
reason: 'submit_failed',
message,
};
}
}
return {
maybeSubmitFlowContribution,
shouldSubmitKiroBuilderIdContribution,
submitContributionArtifact: (artifact, options = {}) => submitContributionArtifact(artifact, {
...options,
apiUrl: options.apiUrl || contributionApiUrl,
fetchImpl: options.fetchImpl || fetchImpl,
}),
};
}
return {
ADAPTER_ID,
ARTIFACT_KIND,
DEFAULT_CONTRIBUTION_API_URL,
FLOW_ID,
createKiroBuilderIdContributionAdapter,
normalizeContributionApiUrl,
shouldSubmitKiroBuilderIdContribution,
submitContributionArtifact,
};
});
+138
View File
@@ -0,0 +1,138 @@
(function attachBackgroundKiroCredentialArtifact(root, factory) {
root.MultiPageBackgroundKiroCredentialArtifact = factory(root);
})(typeof self !== 'undefined' ? self : globalThis, function createBackgroundKiroCredentialArtifactModule(root) {
const kiroStateApi = root?.MultiPageBackgroundKiroState || null;
const FLOW_ID = 'kiro';
const ADAPTER_ID = 'kiro-builder-id';
const ARTIFACT_KIND = 'kiro-builder-id';
const DEFAULT_REGION = kiroStateApi?.DEFAULT_REGION || 'us-east-1';
const DEFAULT_TARGET_ID = kiroStateApi?.DEFAULT_TARGET_ID || 'kiro-rs';
const BUILDER_ID_PROFILE_ARN = 'arn:aws:codewhisperer:us-east-1:638616132270:profile/AAAACCCCXXXX';
function isPlainObject(value) {
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
}
function cleanString(value = '') {
return String(value ?? '').trim();
}
function readKiroRuntime(state = {}) {
return kiroStateApi?.ensureRuntimeState
? kiroStateApi.ensureRuntimeState(state)
: (isPlainObject(state?.kiroRuntime) ? state.kiroRuntime : {});
}
function resolveKiroTargetId(state = {}, runtimeState = readKiroRuntime(state)) {
return cleanString(
state?.settingsState?.flows?.kiro?.targetId
|| state?.flows?.kiro?.targetId
|| state?.kiroTargetId
|| runtimeState?.upload?.targetId
|| DEFAULT_TARGET_ID
) || DEFAULT_TARGET_ID;
}
function resolveEmail(state = {}, runtimeState = readKiroRuntime(state)) {
return cleanString(
runtimeState?.register?.email
|| state?.email
|| state?.registrationEmailState?.current
|| state?.accountIdentifier
);
}
function resolveRegion(state = {}, runtimeState = readKiroRuntime(state), targetId = DEFAULT_TARGET_ID) {
return cleanString(
runtimeState?.desktopAuth?.region
|| state?.settingsState?.flows?.kiro?.targets?.[targetId]?.region
|| state?.flows?.kiro?.targets?.[targetId]?.region
|| DEFAULT_REGION
) || DEFAULT_REGION;
}
function assertRequiredField(name, value, message) {
if (!cleanString(value)) {
const error = new Error(message);
error.code = `missing_${name}`;
throw error;
}
}
function redactSecret(value = '') {
const normalized = cleanString(value);
if (!normalized) {
return '';
}
if (normalized.length <= 10) {
return `${normalized.slice(0, 2)}***`;
}
return `${normalized.slice(0, 6)}...${normalized.slice(-4)}`;
}
function buildKiroBuilderIdArtifact(state = {}, options = {}) {
const runtimeState = readKiroRuntime(state);
const desktopAuth = runtimeState?.desktopAuth || {};
const targetId = resolveKiroTargetId(state, runtimeState);
const region = resolveRegion(state, runtimeState, targetId);
const email = resolveEmail(state, runtimeState);
const refreshToken = String(desktopAuth.refreshToken || '');
const clientId = cleanString(desktopAuth.clientId);
const clientSecret = String(desktopAuth.clientSecret || '');
assertRequiredField('refreshToken', refreshToken, '缺少桌面授权 refreshToken,无法提交 Kiro Builder ID 贡献。');
assertRequiredField('clientId', clientId, '缺少桌面授权 clientId,无法提交 Kiro Builder ID 贡献。');
assertRequiredField('clientSecret', clientSecret, '缺少桌面授权 clientSecret,无法提交 Kiro Builder ID 贡献。');
assertRequiredField('email', email, '缺少注册邮箱,无法提交 Kiro Builder ID 贡献。');
return {
schemaVersion: 1,
flow: FLOW_ID,
adapter: ADAPTER_ID,
artifact: ARTIFACT_KIND,
account: {
email,
},
credentials: {
refreshToken,
clientId,
clientSecret,
profileArn: cleanString(options.profileArn) || BUILDER_ID_PROFILE_ARN,
authMethod: 'idc',
region,
authRegion: region,
apiRegion: region,
tokenSource: cleanString(desktopAuth.tokenSource) || 'desktop_authorization_code_pkce',
},
metadata: {
targetId,
authorizedAt: Math.max(0, Number(desktopAuth.authorizedAt) || 0),
generatedAt: new Date().toISOString(),
source: 'flowpilot-extension',
},
};
}
function buildSafeArtifactSummary(artifact = {}) {
return {
flow: cleanString(artifact.flow),
adapter: cleanString(artifact.adapter),
artifact: cleanString(artifact.artifact),
email: cleanString(artifact.account?.email),
clientId: cleanString(artifact.credentials?.clientId),
refreshToken: redactSecret(artifact.credentials?.refreshToken),
clientSecret: redactSecret(artifact.credentials?.clientSecret),
region: cleanString(artifact.credentials?.region),
};
}
return {
ADAPTER_ID,
ARTIFACT_KIND,
BUILDER_ID_PROFILE_ARN,
FLOW_ID,
buildKiroBuilderIdArtifact,
buildSafeArtifactSummary,
redactSecret,
};
});
@@ -365,6 +365,7 @@
MAIL_2925_VERIFICATION_INTERVAL_MS = 15000,
MAIL_2925_VERIFICATION_MAX_ATTEMPTS = 15,
isTabAlive = async () => false,
maybeSubmitFlowContribution = async () => null,
KIRO_REGISTER_INJECT_FILES = null,
KIRO_DESKTOP_AUTHORIZE_INJECT_FILES = null,
pollCloudflareTempEmailVerificationCode = null,
@@ -477,6 +478,15 @@
},
});
await log('步骤 8:桌面授权回调已捕获,Token 换取成功。', 'ok', nodeId);
await maybeSubmitFlowContribution({
...currentState,
...payload,
}, {
nodeId,
trigger: 'kiro-step-8',
}).catch(async (error) => {
await log(`步骤 8:Kiro 公共贡献提交异常,已保留桌面授权结果:${getErrorMessage(error)}`, 'warn', nodeId);
});
await completeNodeFromBackground(nodeId, payload);
return payload;
}
+44 -32
View File
@@ -67,7 +67,7 @@
}
return Boolean(state?.phoneVerificationEnabled)
&& !Boolean(state?.plusModeEnabled)
&& !Boolean(state?.contributionMode);
&& !Boolean(state?.accountContributionEnabled);
},
resolveSignupMethod = (state = {}) => {
const method = normalizeSignupMethod(state?.signupMethod);
@@ -149,6 +149,7 @@
patchMail2925Account,
patchHotmailAccount,
pollContributionStatus,
submitFlowContribution,
registerTab,
requestStop,
probeIpProxyExit,
@@ -162,7 +163,7 @@
setCurrentPayPalAccount,
setCurrentMail2925Account,
setCurrentHotmailAccount,
setContributionMode,
setAccountContributionMode,
setEmailState,
setEmailStateSilently,
persistRegistrationEmailState,
@@ -179,7 +180,7 @@
setNodeStatus,
skipAutoRunCountdown,
skipNode,
startContributionFlow,
startFlowContribution,
startAutoRunLoop,
deleteMail2925Account,
deleteMail2925Accounts,
@@ -1154,59 +1155,61 @@
return await setFreeReusablePhoneActivation(message.payload || {});
}
case 'SET_CONTRIBUTION_MODE': {
case 'SET_ACCOUNT_CONTRIBUTION_MODE': {
const enabled = Boolean(message.payload?.enabled);
const state = await ensureManualInteractionAllowed(enabled ? '进入贡献模式' : '退出贡献模式');
const state = await ensureManualInteractionAllowed(enabled ? '进入账号贡献' : '退出账号贡献');
if (Object.values(state.nodeStatuses || {}).some((status) => status === 'running')) {
throw new Error(enabled ? '当前有步骤正在执行,无法进入贡献模式。' : '当前有步骤正在执行,无法退出贡献模式。');
throw new Error(enabled ? '当前有步骤正在执行,无法进入账号贡献。' : '当前有步骤正在执行,无法退出账号贡献。');
}
if (typeof setContributionMode !== 'function') {
throw new Error('贡献模式切换能力未接入。');
if (typeof setAccountContributionMode !== 'function') {
throw new Error('账号贡献切换能力未接入。');
}
return {
ok: true,
state: await setContributionMode(enabled),
state: await setAccountContributionMode(enabled, {
adapterId: message.payload?.adapterId,
flowId: message.payload?.flowId || state?.activeFlowId || state?.flowId,
}),
};
}
case 'START_CONTRIBUTION_FLOW': {
case 'START_FLOW_CONTRIBUTION': {
const state = await ensureManualInteractionAllowed('开始贡献');
if (Object.values(state.nodeStatuses || {}).some((status) => status === 'running')) {
throw new Error('当前有步骤正在执行,无法开始贡献流程。');
}
if (typeof startContributionFlow !== 'function') {
if (!state?.accountContributionEnabled) {
throw new Error('请先进入账号贡献。');
}
if (typeof startFlowContribution !== 'function') {
throw new Error('贡献 OAuth 流程尚未接入。');
}
return {
ok: true,
state: await startContributionFlow({
state: await startFlowContribution({
nickname: message.payload?.nickname,
qq: message.payload?.qq,
}),
};
}
case 'SET_CONTRIBUTION_PROFILE': {
case 'SUBMIT_FLOW_CONTRIBUTION': {
const state = await getState();
if (!state?.contributionMode) {
throw new Error('请先进入贡献模式。');
if (!state?.accountContributionEnabled) {
throw new Error('请先进入账号贡献。');
}
const nickname = String(message.payload?.nickname || '').trim();
const qq = String(message.payload?.qq || '').trim();
if (qq && !/^\d{1,20}$/.test(qq)) {
throw new Error('QQ 只能填写数字,且长度不能超过 20 位。');
if (typeof submitFlowContribution !== 'function') {
throw new Error('贡献提交能力尚未接入。');
}
await setState({
contributionNickname: nickname,
contributionQq: qq,
});
return {
ok: true,
state: await getState(),
state: await submitFlowContribution(message.payload?.callbackUrl, {
reason: message.payload?.reason || 'sidepanel_submit',
}),
};
}
case 'POLL_CONTRIBUTION_STATUS': {
case 'POLL_FLOW_CONTRIBUTION_STATUS': {
if (typeof pollContributionStatus !== 'function') {
throw new Error('贡献状态轮询能力尚未接入。');
}
@@ -1285,8 +1288,11 @@
if (message.source === 'sidepanel') {
await lockAutomationWindowFromMessage(message, sender);
}
if (Boolean(message.payload?.contributionMode) && typeof setContributionMode === 'function') {
await setContributionMode(true);
if (Boolean(message.payload?.accountContributionEnabled) && typeof setAccountContributionMode === 'function') {
await setAccountContributionMode(true, {
adapterId: message.payload?.contributionAdapterId,
flowId: message.payload?.activeFlowId || message.payload?.flowId,
});
if (typeof setState === 'function') {
const contributionNickname = String(message.payload?.contributionNickname || '').trim();
const contributionQq = String(message.payload?.contributionQq || '').trim();
@@ -1325,8 +1331,11 @@
if (message.source === 'sidepanel') {
await lockAutomationWindowFromMessage(message, sender);
}
if (Boolean(message.payload?.contributionMode) && typeof setContributionMode === 'function') {
await setContributionMode(true);
if (Boolean(message.payload?.accountContributionEnabled) && typeof setAccountContributionMode === 'function') {
await setAccountContributionMode(true, {
adapterId: message.payload?.contributionAdapterId,
flowId: message.payload?.activeFlowId || message.payload?.flowId,
});
if (typeof setState === 'function') {
const contributionNickname = String(message.payload?.contributionNickname || '').trim();
const contributionQq = String(message.payload?.contributionQq || '').trim();
@@ -1444,7 +1453,7 @@
|| Object.prototype.hasOwnProperty.call(updates, 'signupMethod')
|| Object.prototype.hasOwnProperty.call(updates, 'panelMode')
|| Object.prototype.hasOwnProperty.call(updates, 'activeFlowId')
|| Object.prototype.hasOwnProperty.call(updates, 'contributionMode')
|| Object.prototype.hasOwnProperty.call(updates, 'accountContributionEnabled')
) {
updates.signupMethod = resolveSignupMethod(nextSignupState);
}
@@ -1576,8 +1585,11 @@
error: error?.message || String(error || '代理应用失败'),
}));
}
if (Boolean(currentState?.contributionMode) && typeof setContributionMode === 'function') {
await setContributionMode(true);
if (Boolean(currentState?.accountContributionEnabled) && typeof setAccountContributionMode === 'function') {
await setAccountContributionMode(true, {
adapterId: currentState?.contributionAdapterId,
flowId: currentState?.activeFlowId || currentState?.flowId,
});
}
if (Object.keys(stateUpdates).length > 0 && typeof broadcastDataUpdate === 'function') {
broadcastDataUpdate(stateUpdates);
+1 -1
View File
@@ -59,7 +59,7 @@
return normalizeStep7SignupMethod(state?.signupMethod) === 'phone'
&& Boolean(state?.phoneVerificationEnabled)
&& !Boolean(state?.plusModeEnabled)
&& !Boolean(state?.contributionMode);
&& !Boolean(state?.accountContributionEnabled);
}
function hasStep7PhoneSignupIdentity(state = {}) {