diff --git a/background.js b/background.js index 903e078..f35fbe3 100644 --- a/background.js +++ b/background.js @@ -798,9 +798,8 @@ function getNodeDefinitionsForState(state = {}) { } return getStepDefinitionsForState(state) .map((definition) => ({ - legacyStepId: Number(definition?.id), nodeId: String(definition?.key || '').trim(), - displayOrder: Number.isFinite(Number(definition?.order)) ? Number(definition.order) : Number(definition?.id), + displayOrder: Number.isFinite(Number(definition?.id)) ? Number(definition.id) : Number(definition?.order), title: String(definition?.title || '').trim(), executeKey: String(definition?.key || '').trim(), })) @@ -829,17 +828,21 @@ function getLastNodeIdForState(state = {}) { } function getNodeIdByStepForState(step, state = {}) { - const definition = getStepDefinitionForState(step, state); - return String(definition?.key || '').trim(); + const numericStep = Number(step); + if (!Number.isInteger(numericStep) || numericStep <= 0) { + return ''; + } + const node = getNodeDefinitionsForState(state).find((definition) => Number(definition?.displayOrder) === numericStep); + return String(node?.nodeId || '').trim(); } function getStepIdByNodeIdForState(nodeId, state = {}) { const normalizedNodeId = String(nodeId || '').trim(); if (!normalizedNodeId) return null; const node = getNodeDefinitionForState(normalizedNodeId, state); - const legacyStepId = Number(node?.legacyStepId); - if (Number.isInteger(legacyStepId) && legacyStepId > 0) { - return legacyStepId; + const displayOrder = Number(node?.displayOrder); + if (Number.isInteger(displayOrder) && displayOrder > 0) { + return displayOrder; } return getStepIdByKeyForState(normalizedNodeId, state); } @@ -1074,6 +1077,39 @@ const PERSISTED_SETTING_DEFAULTS = { const PERSISTED_SETTING_KEYS = Object.keys(PERSISTED_SETTING_DEFAULTS); const PERSISTED_SETTINGS_SCHEMA_KEYS = ['settingsSchemaVersion', 'settingsState']; +const SETTINGS_SCHEMA_VIEW_KEYS = Object.freeze([ + 'activeFlowId', + 'openaiIntegrationTargetId', + 'kiroIntegrationTargetId', + 'panelMode', + 'kiroSourceId', + 'vpsUrl', + 'vpsPassword', + 'localCpaStep9Mode', + 'sub2apiUrl', + 'sub2apiEmail', + 'sub2apiPassword', + 'sub2apiGroupName', + 'sub2apiGroupNames', + 'sub2apiAccountPriority', + 'sub2apiDefaultProxyName', + 'codex2apiUrl', + 'codex2apiAdminKey', + 'customPassword', + 'signupMethod', + 'phoneVerificationEnabled', + 'phoneSignupReloginAfterBindEmailEnabled', + 'plusModeEnabled', + 'plusPaymentMethod', + 'mailProvider', + 'ipProxyEnabled', + 'ipProxyService', + 'ipProxyMode', + 'kiroRsUrl', + 'kiroRsKey', + 'stepExecutionRangeByFlow', +]); +const SETTINGS_SCHEMA_VIEW_KEY_SET = new Set(SETTINGS_SCHEMA_VIEW_KEYS); const SETTINGS_EXPORT_SCHEMA_VERSION = 1; const SETTINGS_EXPORT_FILENAME_PREFIX = 'multipage-settings'; const STEP6_REGISTRATION_SUCCESS_WAIT_MS = 20000; @@ -1087,91 +1123,14 @@ const DEFAULT_STATE = { nodeStatuses: { ...DEFAULT_NODE_STATUSES }, runtimeState: runtimeStateHelpers?.buildDefaultRuntimeState?.() || null, ...CONTRIBUTION_RUNTIME_DEFAULTS, - oauthUrl: null, // 运行时抓取到的 OAuth 地址,不要手动预填。 - resolvedSignupMethod: null, // 当前自动轮次冻结后的实际注册方式。 - accountIdentifierType: null, - accountIdentifier: '', - registrationEmailState: { ...DEFAULT_REGISTRATION_EMAIL_STATE }, - email: null, // 运行时邮箱,由程序自动获取并写入,不能手动预填。 - password: null, // 运行时实际密码,由 customPassword 或程序自动生成后写入。 accounts: [], // 已生成账号记录:{ email, password, createdAt }。 accountRunHistory: [], // 账号运行历史快照,实际持久化在 chrome.storage.local。 manualAliasUsage: {}, preservedAliases: {}, icloudAliasCache: [], icloudAliasCacheAt: 0, - lastEmailTimestamp: null, // 最近一次获取到邮箱数据的运行时时间戳。 - lastSignupCode: null, // 注册验证码,运行时由程序自动读取并写入。 - lastLoginCode: null, // 登录验证码,运行时由程序自动读取并写入。 - localhostUrl: null, // 运行时捕获到的 localhost 回调地址,不要手动预填。 - cpaOAuthState: null, // CPA OAuth state。 - cpaManagementOrigin: null, // CPA 管理接口 origin。 - sub2apiSessionId: null, // SUB2API OpenAI Auth 会话 ID。 - sub2apiOAuthState: null, // SUB2API OpenAI Auth state。 - sub2apiGroupId: null, // SUB2API 目标分组 ID。 - sub2apiGroupIds: [], // SUB2API 多目标分组 ID。 - sub2apiDraftName: null, // SUB2API 本轮预生成的账号名称。 - sub2apiProxyId: null, // SUB2API 本轮使用的代理 ID。 - codex2apiSessionId: null, // Codex2API OAuth 会话 ID。 - codex2apiOAuthState: null, // Codex2API OAuth state。 - plusCheckoutTabId: null, // Plus checkout / PayPal 标签页 ID。 - automationWindowId: null, // 当前任务锁定的浏览器窗口 ID,避免新标签页跑到其它窗口。 - plusCheckoutUrl: null, // Plus checkout 运行时短链,不写入持久配置。 - plusCheckoutCountry: 'DE', - plusCheckoutCurrency: 'EUR', - plusCheckoutSource: '', - plusBillingCountryText: '', - plusBillingAddress: null, - plusPaypalApprovedAt: null, - plusGoPayApprovedAt: null, - plusReturnUrl: '', - plusManualConfirmationPending: false, - plusManualConfirmationRequestId: '', - plusManualConfirmationStep: 0, - plusManualConfirmationMethod: '', - plusManualConfirmationTitle: '', - plusManualConfirmationMessage: '', - gopayHelperReferenceId: '', - gopayHelperGoPayGuid: '', - gopayHelperRedirectUrl: '', - gopayHelperNextAction: '', - gopayHelperFlowId: '', - gopayHelperChallengeId: '', - gopayHelperStartPayload: null, - gopayHelperTaskId: '', - gopayHelperTaskStatus: '', - gopayHelperStatusText: '', - gopayHelperRemoteStage: '', - gopayHelperApiWaitingFor: '', - gopayHelperApiInputDeadlineAt: '', - gopayHelperApiInputWaitSeconds: 0, - gopayHelperLastInputError: '', - gopayHelperOtpInvalidCount: 0, - gopayHelperFailureStage: '', - gopayHelperFailureDetail: '', - gopayHelperTaskPayload: null, - gopayHelperOrderCreatedAt: 0, - gopayHelperTaskProgressSignature: '', - gopayHelperTaskProgressAt: 0, - gopayHelperTaskProgressTaskId: '', - gopayHelperPinPayload: null, - gopayHelperResolvedOtp: '', - gopayHelperOtpRequestId: '', - gopayHelperOtpReferenceId: '', - flowStartTime: null, // 当前流程开始时间。 - tabRegistry: {}, // 程序维护的标签页注册表。 - sourceLastUrls: {}, // 各来源页面最近一次打开的地址记录。 logs: [], // 侧边栏展示的运行日志。 ...PERSISTED_SETTING_DEFAULTS, // 合并 chrome.storage.local 中持久化保存的用户配置。 - ipProxyApiPool: [], - ipProxyApiCurrentIndex: 0, - ipProxyApiCurrent: null, - ipProxyAccountPool: [], - ipProxyAccountCurrentIndex: 0, - ipProxyAccountCurrent: null, - ipProxyPool: [], - ipProxyCurrentIndex: 0, - ipProxyCurrent: null, luckmailApiKey: '', luckmailBaseUrl: DEFAULT_LUCKMAIL_BASE_URL, luckmailEmailType: DEFAULT_LUCKMAIL_EMAIL_TYPE, @@ -1179,23 +1138,6 @@ const DEFAULT_STATE = { luckmailUsedPurchases: {}, luckmailPreserveTagId: 0, luckmailPreserveTagName: DEFAULT_LUCKMAIL_PRESERVE_TAG_NAME, - currentLuckmailPurchase: null, - currentLuckmailMailCursor: null, - currentYydsMailInbox: null, - currentPhoneActivation: null, - phoneNumber: '', - currentPhoneVerificationCode: '', - currentPhoneVerificationCountdownEndsAt: 0, - currentPhoneVerificationCountdownWindowIndex: 0, - currentPhoneVerificationCountdownWindowTotal: 0, - reusablePhoneActivation: null, - freeReusablePhoneActivation: null, - phoneReusableActivationPool: [], - signupPhoneNumber: '', - signupPhoneActivation: null, - signupPhoneCompletedActivation: null, - signupPhoneVerificationRequestedAt: null, - signupPhoneVerificationPurpose: '', heroSmsLastPriceTiers: [], heroSmsLastPriceCountryId: 0, heroSmsLastPriceCountryLabel: '', @@ -3353,14 +3295,14 @@ function buildPersistentSettingsPayload(input = {}, options = {}) { const settingsSchemaApi = typeof getSettingsSchemaApi === 'function' ? getSettingsSchemaApi() : null; - if (settingsSchemaApi?.normalizeSettingsState && settingsSchemaApi?.buildLegacySettingsPayload) { + if (settingsSchemaApi?.normalizeSettingsState && settingsSchemaApi?.buildSettingsView) { const settingsSchemaInput = {}; for (const key of persistedSettingKeys) { if (normalizedInput[key] !== undefined) { settingsSchemaInput[key] = payload[key]; } } - const normalizedSettingsState = settingsSchemaApi.normalizeSettingsState({ + Object.assign(payload, projectSettingsSchemaView(settingsSchemaApi, { ...settingsSchemaInput, ...(isPlainObjectForSettingsSchema(normalizedInput.settingsState) ? { settingsState: normalizedInput.settingsState } @@ -3368,13 +3310,7 @@ function buildPersistentSettingsPayload(input = {}, options = {}) { ...(Object.prototype.hasOwnProperty.call(normalizedInput, 'settingsSchemaVersion') ? { settingsSchemaVersion: normalizedInput.settingsSchemaVersion } : {}), - }, { - activeFlowId: settingsSchemaInput.activeFlowId || normalizedInput.activeFlowId || DEFAULT_ACTIVE_FLOW_ID, - }); - Object.assign( - payload, - settingsSchemaApi.buildLegacySettingsPayload(normalizedSettingsState, payload) - ); + }, payload)); } } @@ -3391,6 +3327,32 @@ function getSettingsSchemaApi() { }); } +function projectSettingsSchemaView(settingsSchemaApi, normalizedInput = {}, payload = {}) { + if ( + !settingsSchemaApi?.normalizeSettingsState + || !settingsSchemaApi?.buildSettingsView + ) { + return payload; + } + const normalizedSettingsState = settingsSchemaApi.normalizeSettingsState(normalizedInput, { + activeFlowId: normalizedInput?.activeFlowId || DEFAULT_ACTIVE_FLOW_ID, + }); + return settingsSchemaApi.buildSettingsView(normalizedSettingsState, payload); +} + +function buildPersistedSettingsStoragePayload(payload = {}) { + const storagePayload = {}; + Object.entries(payload || {}).forEach(([key, value]) => { + if (SETTINGS_SCHEMA_VIEW_KEY_SET.has(key)) { + return; + } + storagePayload[key] = value; + }); + storagePayload.settingsSchemaVersion = Number(payload?.settingsSchemaVersion) || 0; + storagePayload.settingsState = payload?.settingsState; + return storagePayload; +} + async function getPersistedSettings() { const stored = await chrome.storage.local.get([ ...PERSISTED_SETTING_KEYS, @@ -3481,6 +3443,9 @@ function buildAutoRunFreshResetSettingsState(prevState = {}, activeFlowId = DEFA const nextSettingsStatePatch = { activeFlowId, services: { + account: { + customPassword: prevState?.customPassword, + }, email: { provider: prevState?.mailProvider, }, @@ -3492,9 +3457,7 @@ function buildAutoRunFreshResetSettingsState(prevState = {}, activeFlowId = DEFA }, flows: { openai: { - source: { - selected: prevState?.panelMode, - }, + integrationTargetId: prevState?.openaiIntegrationTargetId || prevState?.panelMode, autoRun: normalizedStepExecutionRangeByFlow.openai ? { stepExecutionRange: normalizedStepExecutionRangeByFlow.openai, @@ -3502,9 +3465,7 @@ function buildAutoRunFreshResetSettingsState(prevState = {}, activeFlowId = DEFA : undefined, }, kiro: { - source: { - selected: prevState?.kiroSourceId, - }, + integrationTargetId: prevState?.kiroIntegrationTargetId || prevState?.kiroSourceId, autoRun: normalizedStepExecutionRangeByFlow.kiro ? { stepExecutionRange: normalizedStepExecutionRangeByFlow.kiro, @@ -3651,78 +3612,58 @@ async function setPersistentSettings(updates) { const settingsSchemaApi = typeof getSettingsSchemaApi === 'function' ? getSettingsSchemaApi() : null; + const hasSchemaApi = Boolean( + settingsSchemaApi?.normalizeSettingsState + && settingsSchemaApi?.buildSettingsView + ); + const explicitFlatUpdates = { + ...nextUpdates, + }; + delete explicitFlatUpdates.settingsSchemaVersion; + delete explicitFlatUpdates.settingsState; - let persistedUpdates; - if (settingsSchemaApi?.normalizeSettingsState && settingsSchemaApi?.buildLegacySettingsPayload) { - const isPlainObjectForSettingsSchema = typeof isPlainObjectValue === 'function' - ? isPlainObjectValue - : ((value) => Boolean(value) && typeof value === 'object' && !Array.isArray(value)); - const cloneSettingsValue = (value) => { - if (Array.isArray(value)) { - return value.map((entry) => cloneSettingsValue(entry)); - } - if (isPlainObjectForSettingsSchema(value)) { - return Object.fromEntries( - Object.entries(value).map(([key, entryValue]) => [key, cloneSettingsValue(entryValue)]) - ); - } - return value; - }; - const mergeSettingsState = (baseValue, patchValue) => { - if (Array.isArray(patchValue)) { - return patchValue.map((entry) => cloneSettingsValue(entry)); - } - if (!isPlainObjectForSettingsSchema(patchValue)) { - return patchValue === undefined ? cloneSettingsValue(baseValue) : patchValue; - } - - const baseObject = isPlainObjectForSettingsSchema(baseValue) ? baseValue : {}; - const nextObject = { - ...cloneSettingsValue(baseObject), - }; - for (const [key, entryValue] of Object.entries(patchValue)) { - nextObject[key] = mergeSettingsState(baseObject[key], entryValue); - } - return nextObject; - }; - + let mergedSettingsState = nextUpdates.settingsState ?? currentSettings.settingsState; + if (hasSchemaApi) { const currentSettingsState = settingsSchemaApi.normalizeSettingsState( - isPlainObjectForSettingsSchema(currentSettings?.settingsState) + isPlainObjectValue(currentSettings?.settingsState) ? { settingsState: currentSettings.settingsState } : currentSettings, { activeFlowId: currentSettings?.activeFlowId || DEFAULT_ACTIVE_FLOW_ID, } ); - const mergedSettingsState = isPlainObjectForSettingsSchema(nextUpdates.settingsState) - ? mergeSettingsState(currentSettingsState, nextUpdates.settingsState) + mergedSettingsState = isPlainObjectValue(nextUpdates.settingsState) + ? (typeof settingsSchemaApi.mergeSettingsState === 'function' + ? settingsSchemaApi.mergeSettingsState(currentSettingsState, nextUpdates.settingsState) + : nextUpdates.settingsState) : currentSettingsState; - const explicitFlatUpdates = { - ...nextUpdates, - }; - delete explicitFlatUpdates.settingsSchemaVersion; - delete explicitFlatUpdates.settingsState; - - persistedUpdates = buildPersistentSettingsPayload({ - ...explicitFlatUpdates, - settingsSchemaVersion: nextUpdates.settingsSchemaVersion ?? currentSettings.settingsSchemaVersion, - settingsState: mergedSettingsState, - }, { - fillDefaults: true, - }); - } else { - persistedUpdates = buildPersistentSettingsPayload({ - ...currentSettings, - ...nextUpdates, - settingsSchemaVersion: nextUpdates.settingsSchemaVersion ?? currentSettings.settingsSchemaVersion, - settingsState: nextUpdates.settingsState ?? currentSettings.settingsState, - }, { - fillDefaults: true, - }); } + const nextPayloadInput = { + ...currentSettings, + ...explicitFlatUpdates, + settingsSchemaVersion: nextUpdates.settingsSchemaVersion ?? currentSettings.settingsSchemaVersion, + settingsState: mergedSettingsState, + }; + if (hasSchemaApi && isPlainObjectValue(nextUpdates.settingsState)) { + for (const key of SETTINGS_SCHEMA_VIEW_KEYS) { + delete nextPayloadInput[key]; + } + Object.assign(nextPayloadInput, explicitFlatUpdates); + } + + const persistedUpdates = buildPersistentSettingsPayload(nextPayloadInput, { + fillDefaults: true, + }); + if (Object.keys(persistedUpdates).length > 0) { - await chrome.storage.local.set(persistedUpdates); + const storagePayload = hasSchemaApi + ? buildPersistedSettingsStoragePayload(persistedUpdates) + : persistedUpdates; + if (hasSchemaApi && chrome.storage?.local?.remove) { + await chrome.storage.local.remove(SETTINGS_SCHEMA_VIEW_KEYS); + } + await chrome.storage.local.set(storagePayload); } return persistedUpdates; } @@ -13563,9 +13504,8 @@ function buildNodeRegistry(definitions = []) { return self.MultiPageBackgroundStepRegistry?.createNodeRegistry( definitions.map((definition) => ({ ...definition, - legacyStepId: definition.legacyStepId || definition.id, nodeId: definition.nodeId || definition.key, - displayOrder: definition.displayOrder || definition.order, + displayOrder: definition.displayOrder || definition.id || definition.order, executeKey: definition.executeKey || definition.key, execute: stepExecutorsByKey[definition.executeKey || definition.key || definition.nodeId], })) @@ -13580,15 +13520,15 @@ function buildStepRegistry(definitions = []) { const normalizedDefinitions = (Array.isArray(definitions) ? definitions : []) .map((definition) => ({ ...definition, - legacyStepId: Number(definition?.legacyStepId ?? definition?.id) || 0, + displayOrder: Number(definition?.displayOrder ?? definition?.id ?? definition?.order) || 0, nodeId: String(definition?.nodeId || definition?.key || '').trim(), })) .filter((definition) => definition.nodeId); const nodeRegistry = buildNodeRegistry(normalizedDefinitions); const stepToNodeDefinition = new Map( normalizedDefinitions - .filter((definition) => Number.isInteger(definition.legacyStepId) && definition.legacyStepId > 0) - .map((definition) => [definition.legacyStepId, definition]) + .filter((definition) => Number.isInteger(definition.displayOrder) && definition.displayOrder > 0) + .map((definition) => [definition.displayOrder, definition]) ); return { @@ -13617,10 +13557,10 @@ function getStepRegistryForState(state = {}) { const activeFlowId = String(state?.activeFlowId || state?.flowId || DEFAULT_ACTIVE_FLOW_ID).trim().toLowerCase() || DEFAULT_ACTIVE_FLOW_ID; const cacheKey = `${activeFlowId}:${(Array.isArray(definitions) ? definitions : []) .map((definition) => [ - Number(definition?.legacyStepId ?? definition?.id) || 0, + Number(definition?.displayOrder ?? definition?.id ?? definition?.order) || 0, String(definition?.nodeId || definition?.key || '').trim(), String(definition?.executeKey || definition?.key || '').trim(), - Number(definition?.displayOrder ?? definition?.order) || 0, + Number(definition?.displayOrder ?? definition?.id ?? definition?.order) || 0, ].join(':')) .join('|')}`; diff --git a/background/runtime-state.js b/background/runtime-state.js index babc891..cdb420d 100644 --- a/background/runtime-state.js +++ b/background/runtime-state.js @@ -244,21 +244,6 @@ }; } - function flattenFlowStateById(flowState = {}, flowId = 'openai') { - const fieldGroups = FLOW_FIELD_GROUPS[flowId] || {}; - const scopedState = normalizePlainObject(flowState[flowId]); - const next = {}; - for (const [groupKey, fields] of Object.entries(fieldGroups)) { - const group = normalizePlainObject(scopedState[groupKey]); - for (const field of fields) { - if (Object.prototype.hasOwnProperty.call(group, field)) { - next[field] = cloneValue(group[field]); - } - } - } - return next; - } - function buildScopedFlowState(baseFlowState = {}, state = {}, flowId = 'openai') { const fieldGroups = FLOW_FIELD_GROUPS[flowId] || {}; const baseScopedState = cloneValue(normalizePlainObject(baseFlowState[flowId])); @@ -276,7 +261,7 @@ return scopedState; } - function buildOpenAiFlowState(baseValue = {}, state = {}) { + function buildFlowState(baseValue = {}, state = {}) { const baseFlowState = cloneValue(normalizePlainObject(baseValue)); return { ...baseFlowState, @@ -285,6 +270,126 @@ }; } + function listFlowFieldNames() { + const fields = []; + for (const flowGroups of Object.values(FLOW_FIELD_GROUPS)) { + for (const groupFields of Object.values(normalizePlainObject(flowGroups))) { + for (const field of Array.isArray(groupFields) ? groupFields : []) { + const normalizedField = String(field || '').trim(); + if (normalizedField) { + fields.push(normalizedField); + } + } + } + } + return Array.from(new Set(fields)); + } + + const FLOW_RUNTIME_FIELDS = Object.freeze(listFlowFieldNames()); + const RUNTIME_TOP_LEVEL_FIELDS = Object.freeze([ + ...RUNTIME_SHARED_FIELDS, + ...RUNTIME_PROXY_FIELDS, + ...FLOW_RUNTIME_FIELDS, + ]); + const RUNTIME_TOP_LEVEL_FIELD_SET = new Set(RUNTIME_TOP_LEVEL_FIELDS); + const RUNTIME_PATCH_IGNORED_KEYS = new Set([ + 'runtimeState', + 'flowState', + 'sharedState', + 'serviceState', + 'flows', + 'shared', + 'services', + 'currentStep', + 'stepStatuses', + 'legacyStepCompat', + 'flowId', + 'runId', + 'activeFlowId', + 'activeRunId', + 'currentNodeId', + 'nodeStatuses', + ...RUNTIME_TOP_LEVEL_FIELDS, + ]); + + function projectScopedFlowFields(flowState = {}) { + const next = {}; + for (const [flowId, fieldGroups] of Object.entries(FLOW_FIELD_GROUPS)) { + const scopedState = normalizePlainObject(normalizePlainObject(flowState)[flowId]); + for (const [groupKey, fields] of Object.entries(fieldGroups)) { + const group = normalizePlainObject(scopedState[groupKey]); + Object.assign(next, pickDefinedFields(group, fields)); + } + } + return next; + } + + function projectRuntimeViewFields(runtimeState = {}) { + const normalizedRuntimeState = normalizePlainObject(runtimeState); + return { + ...pickDefinedFields( + normalizePlainObject(normalizedRuntimeState.sharedState), + RUNTIME_SHARED_FIELDS + ), + ...pickDefinedFields( + normalizePlainObject(normalizePlainObject(normalizedRuntimeState.serviceState).proxy), + RUNTIME_PROXY_FIELDS + ), + ...projectScopedFlowFields(normalizedRuntimeState.flowState), + }; + } + + function buildRuntimeInputFromPatch(updates = {}) { + const normalizedUpdates = normalizePlainObject(updates); + const runtimeStatePatch = normalizePlainObject(normalizedUpdates.runtimeState); + const next = { + ...pickDefinedFields(normalizedUpdates, [ + 'flowId', + 'runId', + 'activeFlowId', + 'activeRunId', + 'currentNodeId', + 'nodeStatuses', + ]), + ...pickDefinedFields(runtimeStatePatch, [ + 'flowId', + 'runId', + 'activeFlowId', + 'activeRunId', + 'currentNodeId', + 'nodeStatuses', + ]), + ...projectRuntimeViewFields(runtimeStatePatch), + ...pickDefinedFields(normalizedUpdates, RUNTIME_TOP_LEVEL_FIELDS), + }; + + if (Object.prototype.hasOwnProperty.call(normalizedUpdates, 'sharedState')) { + Object.assign( + next, + pickDefinedFields(normalizePlainObject(normalizedUpdates.sharedState), RUNTIME_SHARED_FIELDS) + ); + } + if (Object.prototype.hasOwnProperty.call(normalizedUpdates, 'serviceState')) { + Object.assign( + next, + pickDefinedFields( + normalizePlainObject(normalizePlainObject(normalizedUpdates.serviceState).proxy), + RUNTIME_PROXY_FIELDS + ) + ); + } + if (Object.prototype.hasOwnProperty.call(normalizedUpdates, 'flowState')) { + Object.assign(next, projectScopedFlowFields(normalizedUpdates.flowState)); + } + if (!Object.prototype.hasOwnProperty.call(next, 'flowId') && Object.prototype.hasOwnProperty.call(next, 'activeFlowId')) { + next.flowId = next.activeFlowId; + } + if (!Object.prototype.hasOwnProperty.call(next, 'runId') && Object.prototype.hasOwnProperty.call(next, 'activeRunId')) { + next.runId = next.activeRunId; + } + return next; + } + function buildRuntimeStateDefault() { return { flowId: DEFAULT_ACTIVE_FLOW_ID, @@ -366,69 +471,18 @@ nodeStatuses, sharedState: buildSharedState(baseRuntimeState.sharedState, state), serviceState: buildServiceState(baseRuntimeState.serviceState, state), - flowState: buildOpenAiFlowState(baseRuntimeState.flowState, state), + flowState: buildFlowState(baseRuntimeState.flowState, state), }; } - function buildFlattenedUpdates(updates = {}) { - const ignoredKeys = new Set(['current' + 'Step', 'step' + 'Statuses', 'legacy' + 'StepCompat']); + function buildPersistentPatchPayload(updates = {}) { const next = {}; - for (const [key, value] of Object.entries(updates || {})) { - if (!ignoredKeys.has(key)) { - next[key] = value; + for (const [key, value] of Object.entries(normalizePlainObject(updates))) { + if (RUNTIME_PATCH_IGNORED_KEYS.has(key)) { + continue; } + next[key] = cloneValue(value); } - const runtimeState = normalizePlainObject(updates.runtimeState); - const sharedState = normalizePlainObject(updates.sharedState); - const serviceState = normalizePlainObject(updates.serviceState); - const flowState = normalizePlainObject(updates.flowState); - - if (Object.prototype.hasOwnProperty.call(runtimeState, 'activeFlowId')) { - next.activeFlowId = runtimeState.activeFlowId; - } - if (Object.prototype.hasOwnProperty.call(runtimeState, 'flowId')) { - next.flowId = runtimeState.flowId; - next.activeFlowId = runtimeState.flowId; - } - if (Object.prototype.hasOwnProperty.call(runtimeState, 'activeRunId')) { - next.activeRunId = runtimeState.activeRunId; - } - if (Object.prototype.hasOwnProperty.call(runtimeState, 'runId')) { - next.runId = runtimeState.runId; - next.activeRunId = runtimeState.runId; - } - if (Object.prototype.hasOwnProperty.call(runtimeState, 'currentNodeId')) { - next.currentNodeId = runtimeState.currentNodeId; - } - if (Object.prototype.hasOwnProperty.call(runtimeState, 'nodeStatuses')) { - next.nodeStatuses = cloneValue(runtimeState.nodeStatuses); - } - Object.assign(next, pickDefinedFields(sharedState, RUNTIME_SHARED_FIELDS)); - if (Object.prototype.hasOwnProperty.call(runtimeState, 'sharedState')) { - Object.assign( - next, - pickDefinedFields(normalizePlainObject(runtimeState.sharedState), RUNTIME_SHARED_FIELDS) - ); - } - - const serviceProxy = normalizePlainObject(serviceState.proxy); - Object.assign(next, pickDefinedFields(serviceProxy, RUNTIME_PROXY_FIELDS)); - if (Object.prototype.hasOwnProperty.call(runtimeState, 'serviceState')) { - const runtimeServiceState = normalizePlainObject(runtimeState.serviceState); - Object.assign( - next, - pickDefinedFields(normalizePlainObject(runtimeServiceState.proxy), RUNTIME_PROXY_FIELDS) - ); - } - - Object.assign(next, flattenFlowStateById(flowState, 'openai')); - Object.assign(next, flattenFlowStateById(flowState, 'kiro')); - if (Object.prototype.hasOwnProperty.call(runtimeState, 'flowState')) { - const runtimeFlowState = normalizePlainObject(runtimeState.flowState); - Object.assign(next, flattenFlowStateById(runtimeFlowState, 'openai')); - Object.assign(next, flattenFlowStateById(runtimeFlowState, 'kiro')); - } - return next; } @@ -436,6 +490,7 @@ const runtimeState = ensureRuntimeState(state); return { ...state, + ...projectRuntimeViewFields(runtimeState), flowId: runtimeState.flowId, runId: runtimeState.runId, activeFlowId: runtimeState.activeFlowId, @@ -453,15 +508,15 @@ } function buildSessionStatePatch(currentState = {}, updates = {}) { - const flattenedUpdates = buildFlattenedUpdates(updates); - const nextState = { - ...currentState, - ...flattenedUpdates, - }; - const runtimeState = ensureRuntimeState(nextState); + const currentRuntimeState = ensureRuntimeState(currentState); + const runtimeState = ensureRuntimeState({ + runtimeState: currentRuntimeState, + ...projectRuntimeViewFields(currentRuntimeState), + ...buildRuntimeInputFromPatch(updates), + }); return { - ...flattenedUpdates, + ...buildPersistentPatchPayload(updates), flowId: runtimeState.flowId, runId: runtimeState.runId, activeFlowId: runtimeState.activeFlowId, diff --git a/background/steps/registry.js b/background/steps/registry.js index 072c513..780d394 100644 --- a/background/steps/registry.js +++ b/background/steps/registry.js @@ -4,10 +4,9 @@ function createNodeRegistry(definitions = []) { const ordered = (Array.isArray(definitions) ? definitions : []) .map((definition) => ({ - legacyStepId: Number(definition?.legacyStepId ?? definition?.id) || 0, flowId: String(definition?.flowId || '').trim(), nodeId: String(definition?.nodeId || definition?.key || '').trim(), - displayOrder: Number(definition?.displayOrder ?? definition?.order), + displayOrder: Number(definition?.displayOrder ?? definition?.order ?? definition?.id), executeKey: String(definition?.executeKey || definition?.key || definition?.nodeId || '').trim(), title: String(definition?.title || '').trim(), execute: definition?.execute, diff --git a/data/step-definitions.js b/data/step-definitions.js index 5548bb5..6bdc756 100644 --- a/data/step-definitions.js +++ b/data/step-definitions.js @@ -348,11 +348,10 @@ function cloneNodes(steps = [], options = {}, flowId = DEFAULT_ACTIVE_FLOW_ID) { const { builder } = getFlowDefinitionBuilder({ activeFlowId: flowId }); return steps.map((step) => ({ - legacyStepId: Number(step.id), nodeId: String(step.key || '').trim(), flowId, title: builder?.resolveStepTitle ? builder.resolveStepTitle(step, options) : step.title, - displayOrder: Number.isFinite(Number(step.order)) ? Number(step.order) : Number(step.id), + displayOrder: Number.isFinite(Number(step.id)) ? Number(step.id) : Number(step.order), nodeType: 'task', sourceId: step.sourceId || '', driverId: step.driverId || '', diff --git a/shared/flow-capabilities.js b/shared/flow-capabilities.js index 4ea7b1c..8d6af25 100644 --- a/shared/flow-capabilities.js +++ b/shared/flow-capabilities.js @@ -5,12 +5,11 @@ const flowRegistryApi = rootScope.MultiPageFlowRegistry || {}; const settingsSchemaApi = rootScope.MultiPageSettingsSchema || {}; const DEFAULT_FLOW_ID = flowRegistryApi.DEFAULT_FLOW_ID || 'openai'; - const DEFAULT_PANEL_MODE = flowRegistryApi.DEFAULT_OPENAI_SOURCE_ID || 'cpa'; - const LEGACY_OPENAI_FLOW_ALIAS = String(flowRegistryApi.LEGACY_OPENAI_FLOW_ALIAS || 'codex').trim().toLowerCase(); + const DEFAULT_OPENAI_INTEGRATION_TARGET_ID = flowRegistryApi.DEFAULT_OPENAI_INTEGRATION_TARGET_ID || 'cpa'; const SIGNUP_METHOD_EMAIL = 'email'; const SIGNUP_METHOD_PHONE = 'phone'; - const VALID_PANEL_MODES = Array.isArray(flowRegistryApi.OPENAI_SOURCE_IDS) - ? flowRegistryApi.OPENAI_SOURCE_IDS.slice() + const VALID_OPENAI_INTEGRATION_TARGET_IDS = Array.isArray(flowRegistryApi.OPENAI_INTEGRATION_TARGET_IDS) + ? flowRegistryApi.OPENAI_INTEGRATION_TARGET_IDS.slice() : ['cpa', 'sub2api', 'codex2api']; const REGISTERED_FLOW_IDS = Array.isArray(flowRegistryApi.getRegisteredFlowIds?.()) ? flowRegistryApi.getRegisteredFlowIds().map((flowId) => String(flowId || '').trim().toLowerCase()).filter(Boolean) @@ -23,7 +22,7 @@ supportsPhoneVerificationSettings: false, supportsPlusMode: false, supportsContributionMode: false, - supportsPlatformBinding: [], + supportedIntegrationTargets: [], supportsLuckmail: false, supportsOauthTimeoutBudget: false, canSwitchFlow: true, @@ -48,7 +47,7 @@ ) ); - const DEFAULT_PANEL_CAPABILITIES = Object.freeze({ + const DEFAULT_INTEGRATION_TARGET_CAPABILITIES = Object.freeze({ supportsPhoneSignup: true, requiresPhoneSignupWarning: false, }); @@ -61,9 +60,11 @@ 'plusModeEnabled', 'signupMethod', 'kiroSourceId', + 'openaiIntegrationTargetId', + 'kiroIntegrationTargetId', ]); - const PANEL_CAPABILITIES = Object.freeze({ + const OPENAI_INTEGRATION_TARGET_CAPABILITIES = Object.freeze({ cpa: Object.freeze({ supportsPhoneSignup: true, requiresPhoneSignupWarning: true, @@ -88,30 +89,26 @@ function normalizeCapabilityFlowId(value = '', fallback = DEFAULT_FLOW_ID) { const normalized = String(value || '').trim().toLowerCase(); - if (normalized === LEGACY_OPENAI_FLOW_ALIAS) { - return DEFAULT_FLOW_ID; - } if (normalized) { return normalized; } - const normalizedFallback = String(fallback || '').trim().toLowerCase(); - if (normalizedFallback === LEGACY_OPENAI_FLOW_ALIAS) { - return DEFAULT_FLOW_ID; - } - return normalizedFallback || DEFAULT_FLOW_ID; + return normalizeFlowId(fallback, DEFAULT_FLOW_ID); } function isRegisteredFlowId(flowId = '') { - return REGISTERED_FLOW_ID_SET.has(normalizeCapabilityFlowId(flowId, '')); + const normalized = String(flowId || '').trim().toLowerCase(); + return Boolean(normalized) && REGISTERED_FLOW_ID_SET.has(normalized); } - function normalizePanelMode(value = '', fallback = DEFAULT_PANEL_MODE) { + function normalizeOpenAiIntegrationTargetId(value = '', fallback = DEFAULT_OPENAI_INTEGRATION_TARGET_ID) { const normalized = String(value || '').trim().toLowerCase(); - if (VALID_PANEL_MODES.includes(normalized)) { + if (VALID_OPENAI_INTEGRATION_TARGET_IDS.includes(normalized)) { return normalized; } const fallbackValue = String(fallback || '').trim().toLowerCase(); - return VALID_PANEL_MODES.includes(fallbackValue) ? fallbackValue : DEFAULT_PANEL_MODE; + return VALID_OPENAI_INTEGRATION_TARGET_IDS.includes(fallbackValue) + ? fallbackValue + : DEFAULT_OPENAI_INTEGRATION_TARGET_ID; } function normalizeSignupMethod(value = '') { @@ -120,41 +117,50 @@ : SIGNUP_METHOD_EMAIL; } - function normalizePanelModeList(values = []) { + function normalizeOpenAiIntegrationTargetList(values = []) { if (!Array.isArray(values)) { return []; } const seen = new Set(); const normalized = []; values.forEach((value) => { - const mode = normalizePanelMode(value, ''); - if (!mode || seen.has(mode)) { + const integrationTargetId = normalizeOpenAiIntegrationTargetId(value, ''); + if (!integrationTargetId || seen.has(integrationTargetId)) { return; } - seen.add(mode); - normalized.push(mode); + seen.add(integrationTargetId); + normalized.push(integrationTargetId); }); return normalized; } - function getPanelModeLabel(panelMode = '') { - const normalized = normalizePanelMode(panelMode); + function getIntegrationTargetLabel(flowId = DEFAULT_FLOW_ID, integrationTargetId = '') { + if ( + isRegisteredFlowId(flowId) + && typeof flowRegistryApi.getIntegrationTargetLabel === 'function' + ) { + return flowRegistryApi.getIntegrationTargetLabel(flowId, integrationTargetId); + } + const normalized = String(integrationTargetId || '').trim().toLowerCase(); if (normalized === 'sub2api') { return 'SUB2API'; } if (normalized === 'codex2api') { return 'Codex2API'; } - return 'CPA'; + if (normalized === 'cpa') { + return 'CPA'; + } + return normalized || String(integrationTargetId || '').trim(); } function createFlowCapabilityRegistry(deps = {}) { const { defaultFlowCapabilities = DEFAULT_FLOW_CAPABILITIES, defaultFlowId = DEFAULT_FLOW_ID, - defaultPanelCapabilities = DEFAULT_PANEL_CAPABILITIES, + defaultIntegrationTargetCapabilities = DEFAULT_INTEGRATION_TARGET_CAPABILITIES, flowCapabilities = FLOW_CAPABILITIES, - panelCapabilities = PANEL_CAPABILITIES, + integrationTargetCapabilities = OPENAI_INTEGRATION_TARGET_CAPABILITIES, } = deps; const settingsSchema = settingsSchemaApi.createSettingsSchema ? settingsSchemaApi.createSettingsSchema({ @@ -165,21 +171,72 @@ function getFlowCapabilities(flowId) { const normalizedFlowId = normalizeCapabilityFlowId(flowId, defaultFlowId); const entry = flowCapabilities[normalizedFlowId] || null; + const supportedIntegrationTargets = normalizedFlowId === 'openai' + ? normalizeOpenAiIntegrationTargetList( + entry?.supportedIntegrationTargets || defaultFlowCapabilities.supportedIntegrationTargets + ) + : (Array.isArray(entry?.supportedIntegrationTargets) + ? entry.supportedIntegrationTargets.map((value) => String(value || '').trim().toLowerCase()).filter(Boolean) + : []); return { ...defaultFlowCapabilities, ...(entry || {}), - supportsPlatformBinding: normalizePanelModeList(entry?.supportsPlatformBinding || defaultFlowCapabilities.supportsPlatformBinding), + supportedIntegrationTargets, }; } - function getPanelCapabilities(panelMode) { - const normalizedPanelMode = normalizePanelMode(panelMode); + function getOpenAiIntegrationTargetCapabilities(integrationTargetId) { + const normalizedIntegrationTargetId = normalizeOpenAiIntegrationTargetId(integrationTargetId); return { - ...defaultPanelCapabilities, - ...(panelCapabilities[normalizedPanelMode] || {}), + ...defaultIntegrationTargetCapabilities, + ...(integrationTargetCapabilities[normalizedIntegrationTargetId] || {}), }; } + function normalizeRequestedIntegrationTargetId(activeFlowId, state = {}, options = {}) { + if (activeFlowId === 'openai') { + return normalizeOpenAiIntegrationTargetId( + options?.integrationTargetId + ?? options?.panelMode + ?? state?.openaiIntegrationTargetId + ?? state?.panelMode, + DEFAULT_OPENAI_INTEGRATION_TARGET_ID + ); + } + + const rawIntegrationTargetId = activeFlowId === 'kiro' + ? ( + options?.integrationTargetId + ?? state?.kiroIntegrationTargetId + ?? state?.kiroSourceId + ?? flowRegistryApi.getDefaultIntegrationTargetId?.(activeFlowId) + ?? '' + ) + : ( + options?.integrationTargetId + ?? state?.integrationTargetId + ?? state?.openaiIntegrationTargetId + ?? state?.panelMode + ?? state?.kiroIntegrationTargetId + ?? state?.kiroSourceId + ?? flowRegistryApi.getDefaultIntegrationTargetId?.(activeFlowId) + ?? '' + ); + + if ( + isRegisteredFlowId(activeFlowId) + && typeof flowRegistryApi.normalizeIntegrationTargetId === 'function' + ) { + return flowRegistryApi.normalizeIntegrationTargetId( + activeFlowId, + rawIntegrationTargetId, + flowRegistryApi.getDefaultIntegrationTargetId?.(activeFlowId) + ); + } + + return String(rawIntegrationTargetId || '').trim().toLowerCase(); + } + function normalizeChangedKeys(values = []) { const list = Array.isArray(values) ? values : []; const seen = new Set(); @@ -195,28 +252,33 @@ return normalized; } - function resolveEffectiveSourceId(activeFlowId, state = {}, requestedPanelMode = DEFAULT_PANEL_MODE) { + function resolveEffectiveIntegrationTargetId(activeFlowId, state = {}, requestedIntegrationTargetId = DEFAULT_OPENAI_INTEGRATION_TARGET_ID) { if (!isRegisteredFlowId(activeFlowId)) { - return normalizePanelMode(state?.panelMode || requestedPanelMode, requestedPanelMode || DEFAULT_PANEL_MODE); + return normalizeRequestedIntegrationTargetId(activeFlowId, state, { + integrationTargetId: requestedIntegrationTargetId, + }); } - if (settingsSchema?.getSelectedSourceId) { - const sourceFromSchema = settingsSchema.getSelectedSourceId({ + if (settingsSchema?.getSelectedIntegrationTargetId) { + const integrationTargetId = settingsSchema.getSelectedIntegrationTargetId({ ...state, activeFlowId, }, activeFlowId); - if (sourceFromSchema) { - return sourceFromSchema; + if (integrationTargetId) { + return integrationTargetId; } } - if (typeof flowRegistryApi.normalizeSourceId === 'function') { - if (activeFlowId === 'openai') { - return flowRegistryApi.normalizeSourceId('openai', state?.panelMode || requestedPanelMode, DEFAULT_PANEL_MODE); - } - return flowRegistryApi.normalizeSourceId(activeFlowId, state?.kiroSourceId || '', flowRegistryApi.getDefaultSourceId?.(activeFlowId)); + if (typeof flowRegistryApi.normalizeIntegrationTargetId === 'function') { + return flowRegistryApi.normalizeIntegrationTargetId( + activeFlowId, + activeFlowId === 'openai' + ? (state?.openaiIntegrationTargetId || state?.panelMode || requestedIntegrationTargetId) + : (state?.kiroIntegrationTargetId || state?.kiroSourceId || requestedIntegrationTargetId), + flowRegistryApi.getDefaultIntegrationTargetId?.(activeFlowId) + ); } return activeFlowId === 'openai' - ? normalizePanelMode(state?.panelMode || requestedPanelMode) - : ''; + ? normalizeOpenAiIntegrationTargetId(requestedIntegrationTargetId) + : String(requestedIntegrationTargetId || '').trim().toLowerCase(); } function resolveSidepanelCapabilities(options = {}) { @@ -226,19 +288,25 @@ defaultFlowId ); const flowState = getFlowCapabilities(activeFlowId); - const requestedPanelMode = normalizePanelMode( - options?.panelMode ?? state?.panelMode, - DEFAULT_PANEL_MODE + const requestedIntegrationTargetId = normalizeRequestedIntegrationTargetId( + activeFlowId, + state, + options ); - const supportedPanelModes = normalizePanelModeList(flowState.supportsPlatformBinding); - const panelModeSupported = supportedPanelModes.length === 0 + const supportedIntegrationTargets = activeFlowId === 'openai' + ? normalizeOpenAiIntegrationTargetList(flowState.supportedIntegrationTargets) + : (Array.isArray(flowState.supportedIntegrationTargets) + ? flowState.supportedIntegrationTargets.slice() + : []); + const integrationTargetSupported = supportedIntegrationTargets.length === 0 ? true - : supportedPanelModes.includes(requestedPanelMode); - const effectivePanelMode = panelModeSupported - ? requestedPanelMode - : (supportedPanelModes[0] || requestedPanelMode); - const panelState = getPanelCapabilities(effectivePanelMode); - const effectiveSourceId = resolveEffectiveSourceId(activeFlowId, state, effectivePanelMode); + : supportedIntegrationTargets.includes(requestedIntegrationTargetId); + const effectiveIntegrationTargetId = integrationTargetSupported + ? requestedIntegrationTargetId + : (supportedIntegrationTargets[0] || requestedIntegrationTargetId); + const integrationTargetState = activeFlowId === 'openai' + ? getOpenAiIntegrationTargetCapabilities(effectiveIntegrationTargetId) + : defaultIntegrationTargetCapabilities; const runtimeLocks = { autoRunLocked: Boolean(options?.autoRunLocked ?? state?.autoRunLocked), contributionMode: activeFlowId === 'openai' && flowState.supportsContributionMode && Boolean(state?.contributionMode), @@ -252,7 +320,7 @@ } const canSelectPhoneSignup = activeFlowId === 'openai' && Boolean(flowState.supportsPhoneSignup) - && Boolean(panelState.supportsPhoneSignup) + && Boolean(integrationTargetState.supportsPhoneSignup) && runtimeLocks.phoneVerificationEnabled && !runtimeLocks.plusModeEnabled && !runtimeLocks.contributionMode; @@ -272,7 +340,7 @@ : effectiveSignupMethods[0]); const visibleGroupIds = typeof flowRegistryApi.getVisibleGroupIds === 'function' && isRegisteredFlowId(activeFlowId) - ? flowRegistryApi.getVisibleGroupIds(activeFlowId, effectiveSourceId) + ? flowRegistryApi.getVisibleGroupIds(activeFlowId, effectiveIntegrationTargetId) : []; return { @@ -283,33 +351,38 @@ canShowPlusSettings: activeFlowId === 'openai' && Boolean(flowState.supportsPlusMode), canSwitchFlow: Boolean(flowState.canSwitchFlow), canUsePhoneSignup: canSelectPhoneSignup, - canUseSelectedPanelMode: panelModeSupported, - effectivePanelMode, + canUseSelectedPanelMode: integrationTargetSupported, + effectiveIntegrationTargetId, + effectivePanelMode: effectiveIntegrationTargetId, effectiveSignupMethod, effectiveSignupMethods, - effectiveSourceId, + effectiveSourceId: effectiveIntegrationTargetId, flowCapabilities: flowState, - panelCapabilities: panelState, - panelMode: effectivePanelMode, - requestedPanelMode, + integrationTargetCapabilities: integrationTargetState, + panelCapabilities: integrationTargetState, + panelMode: effectiveIntegrationTargetId, + requestedIntegrationTargetId, + requestedPanelMode: requestedIntegrationTargetId, requestedSignupMethod, runtimeLocks, shouldWarnCpaPhoneSignup: effectiveSignupMethod === SIGNUP_METHOD_PHONE - && Boolean(panelState.requiresPhoneSignupWarning), + && Boolean(integrationTargetState.requiresPhoneSignupWarning), stepDefinitionOptions: { activeFlowId, - panelMode: effectivePanelMode, + integrationTargetId: effectiveIntegrationTargetId, + panelMode: effectiveIntegrationTargetId, plusModeEnabled: runtimeLocks.plusModeEnabled, signupMethod: effectiveSignupMethod, }, - supportedPanelModes, + supportedIntegrationTargets, + supportedPanelModes: supportedIntegrationTargets, visibleGroupIds, }; } function buildPhoneSignupValidationError(capabilityState = {}) { const flowState = capabilityState.flowCapabilities || {}; - const panelState = capabilityState.panelCapabilities || {}; + const integrationTargetState = capabilityState.integrationTargetCapabilities || {}; const runtimeLocks = capabilityState.runtimeLocks || {}; if (!flowState.supportsPhoneSignup) { @@ -318,10 +391,10 @@ message: '当前 flow 不支持手机号注册。', }; } - if (!panelState.supportsPhoneSignup) { + if (!integrationTargetState.supportsPhoneSignup) { return { code: 'phone_signup_panel_unsupported', - message: `当前来源 ${getPanelModeLabel(capabilityState.requestedPanelMode)} 不支持手机号注册。`, + message: `当前来源 ${getIntegrationTargetLabel(capabilityState.activeFlowId, capabilityState.requestedIntegrationTargetId)} 不支持手机号注册。`, }; } if (!runtimeLocks.phoneVerificationEnabled) { @@ -354,13 +427,13 @@ const errors = []; if ( - Array.isArray(capabilityState.supportedPanelModes) - && capabilityState.supportedPanelModes.length > 0 + Array.isArray(capabilityState.supportedIntegrationTargets) + && capabilityState.supportedIntegrationTargets.length > 0 && capabilityState.canUseSelectedPanelMode === false ) { errors.push({ code: 'panel_mode_unsupported', - message: `当前 flow 不支持 ${getPanelModeLabel(capabilityState.requestedPanelMode)} 来源。`, + message: `当前 flow 不支持 ${getIntegrationTargetLabel(capabilityState.activeFlowId, capabilityState.requestedIntegrationTargetId)} 来源。`, }); } @@ -408,15 +481,18 @@ const shouldReconcileSignupMethod = MODE_SWITCH_RELEVANT_KEYS.some((key) => changedKeySet.has(key)); if ( - changedKeySet.has('panelMode') - && Array.isArray(capabilityState.supportedPanelModes) - && capabilityState.supportedPanelModes.length > 0 + (changedKeySet.has('panelMode') || changedKeySet.has('openaiIntegrationTargetId') || changedKeySet.has('kiroIntegrationTargetId')) + && Array.isArray(capabilityState.supportedIntegrationTargets) + && capabilityState.supportedIntegrationTargets.length > 0 && capabilityState.canUseSelectedPanelMode === false ) { - normalizedUpdates.panelMode = capabilityState.effectivePanelMode; + normalizedUpdates.panelMode = capabilityState.effectiveIntegrationTargetId; + normalizedUpdates.openaiIntegrationTargetId = capabilityState.effectiveIntegrationTargetId; + normalizedUpdates.kiroIntegrationTargetId = capabilityState.effectiveIntegrationTargetId; + normalizedUpdates.kiroSourceId = capabilityState.effectiveIntegrationTargetId; errors.push({ code: 'panel_mode_unsupported', - message: `当前 flow 不支持 ${getPanelModeLabel(capabilityState.requestedPanelMode)} 来源。`, + message: `当前 flow 不支持 ${getIntegrationTargetLabel(capabilityState.activeFlowId, capabilityState.requestedIntegrationTargetId)} 来源。`, }); } @@ -480,9 +556,9 @@ return { canUsePhoneSignup, getFlowCapabilities, - getPanelCapabilities, + getOpenAiIntegrationTargetCapabilities, normalizeFlowId, - normalizePanelMode, + normalizeOpenAiIntegrationTargetId, normalizeSignupMethod, resolveSidepanelCapabilities, resolveSignupMethod, @@ -495,15 +571,15 @@ createFlowCapabilityRegistry, DEFAULT_FLOW_CAPABILITIES, DEFAULT_FLOW_ID, - DEFAULT_PANEL_CAPABILITIES, - DEFAULT_PANEL_MODE, + DEFAULT_INTEGRATION_TARGET_CAPABILITIES, + DEFAULT_OPENAI_INTEGRATION_TARGET_ID, FLOW_CAPABILITIES, - PANEL_CAPABILITIES, + OPENAI_INTEGRATION_TARGET_CAPABILITIES, SIGNUP_METHOD_EMAIL, SIGNUP_METHOD_PHONE, - VALID_PANEL_MODES, + VALID_OPENAI_INTEGRATION_TARGET_IDS, normalizeFlowId, - normalizePanelMode, + normalizeOpenAiIntegrationTargetId, normalizeSignupMethod, }; }); diff --git a/shared/flow-registry.js b/shared/flow-registry.js index acdc2bb..3d1d9ed 100644 --- a/shared/flow-registry.js +++ b/shared/flow-registry.js @@ -2,11 +2,11 @@ root.MultiPageFlowRegistry = factory(); })(typeof self !== 'undefined' ? self : globalThis, function createFlowRegistryModule() { const DEFAULT_FLOW_ID = 'openai'; - const LEGACY_OPENAI_FLOW_ALIAS = 'codex'; - const DEFAULT_OPENAI_SOURCE_ID = 'cpa'; - const DEFAULT_KIRO_SOURCE_ID = 'kiro-rs'; + const DEFAULT_OPENAI_INTEGRATION_TARGET_ID = 'cpa'; + const DEFAULT_KIRO_INTEGRATION_TARGET_ID = 'kiro-rs'; + const DEFAULT_KIRO_PUBLICATION_TARGET_ID = 'kiro-rs'; const DEFAULT_KIRO_RS_URL = 'https://kiro.leftcode.xyz/admin'; - const OPENAI_SOURCE_IDS = Object.freeze(['cpa', 'sub2api', 'codex2api']); + const OPENAI_INTEGRATION_TARGET_IDS = Object.freeze(['cpa', 'sub2api', 'codex2api']); const SHARED_SERVICE_IDS = Object.freeze(['account', 'email', 'proxy']); const DEFAULT_FLOW_CAPABILITIES = Object.freeze({ @@ -15,7 +15,7 @@ supportsPhoneVerificationSettings: false, supportsPlusMode: false, supportsContributionMode: false, - supportsPlatformBinding: [], + supportedIntegrationTargets: [], supportsLuckmail: false, supportsOauthTimeoutBudget: false, canSwitchFlow: true, @@ -44,7 +44,7 @@ supportsPhoneVerificationSettings: true, supportsPlusMode: true, supportsContributionMode: true, - supportsPlatformBinding: [...OPENAI_SOURCE_IDS], + supportedIntegrationTargets: [...OPENAI_INTEGRATION_TARGET_IDS], supportsLuckmail: true, supportsOauthTimeoutBudget: true, stepDefinitionMode: 'openai-dynamic', @@ -55,24 +55,21 @@ 'openai-oauth', 'openai-step6', ], - sources: { + integrationTargets: { cpa: { id: 'cpa', label: 'CPA 面板', - legacyPanelMode: 'cpa', - groups: ['openai-source-cpa'], + groups: ['openai-target-cpa'], }, sub2api: { id: 'sub2api', label: 'SUB2API', - legacyPanelMode: 'sub2api', - groups: ['openai-source-sub2api'], + groups: ['openai-target-sub2api'], }, codex2api: { id: 'codex2api', label: 'Codex2API', - legacyPanelMode: 'codex2api', - groups: ['openai-source-codex2api'], + groups: ['openai-target-codex2api'], }, }, runtimeSources: { @@ -206,16 +203,23 @@ services: ['account', 'email', 'proxy'], capabilities: { ...DEFAULT_FLOW_CAPABILITIES, + supportedIntegrationTargets: [DEFAULT_KIRO_INTEGRATION_TARGET_ID], stepDefinitionMode: 'kiro-device-auth', }, baseGroups: [ 'kiro-runtime-status', ], - sources: { + integrationTargets: { + 'kiro-rs': { + id: 'kiro-rs', + label: 'kiro.rs', + groups: ['kiro-target-kiro-rs'], + }, + }, + publicationTargets: { 'kiro-rs': { id: 'kiro-rs', label: 'kiro.rs', - groups: ['kiro-source-kiro-rs'], }, }, runtimeSources: { @@ -280,13 +284,13 @@ label: 'IP 代理', sectionIds: ['ip-proxy-section'], }, - 'openai-source-cpa': { - id: 'openai-source-cpa', + 'openai-target-cpa': { + id: 'openai-target-cpa', label: 'CPA 来源', rowIds: ['row-vps-url', 'row-vps-password', 'row-local-cpa-step9-mode'], }, - 'openai-source-sub2api': { - id: 'openai-source-sub2api', + 'openai-target-sub2api': { + id: 'openai-target-sub2api', label: 'SUB2API 来源', rowIds: [ 'row-sub2api-url', @@ -297,17 +301,15 @@ 'row-sub2api-default-proxy', ], }, - 'openai-source-codex2api': { - id: 'openai-source-codex2api', + 'openai-target-codex2api': { + id: 'openai-target-codex2api', label: 'Codex2API 来源', rowIds: ['row-codex2api-url', 'row-codex2api-admin-key'], }, 'openai-plus': { id: 'openai-plus', label: 'Plus', - rowIds: [ - 'row-plus-mode', - ], + rowIds: ['row-plus-mode'], }, 'openai-phone': { id: 'openai-phone', @@ -325,8 +327,8 @@ label: '第六步', rowIds: ['row-step6-cookie-settings'], }, - 'kiro-source-kiro-rs': { - id: 'kiro-source-kiro-rs', + 'kiro-target-kiro-rs': { + id: 'kiro-target-kiro-rs', label: 'kiro.rs 配置', rowIds: ['row-kiro-rs-url', 'row-kiro-rs-key'], }, @@ -339,16 +341,10 @@ function normalizeFlowId(value = '', fallback = DEFAULT_FLOW_ID) { const normalized = String(value || '').trim().toLowerCase(); - if (normalized === LEGACY_OPENAI_FLOW_ALIAS) { - return DEFAULT_FLOW_ID; - } if (normalized && Object.prototype.hasOwnProperty.call(FLOW_DEFINITIONS, normalized)) { return normalized; } const fallbackValue = String(fallback || '').trim().toLowerCase(); - if (fallbackValue === LEGACY_OPENAI_FLOW_ALIAS) { - return DEFAULT_FLOW_ID; - } return Object.prototype.hasOwnProperty.call(FLOW_DEFINITIONS, fallbackValue) ? fallbackValue : DEFAULT_FLOW_ID; @@ -359,73 +355,92 @@ } function getFlowDefinition(flowId) { - const normalizedFlowId = normalizeFlowId(flowId); - return FLOW_DEFINITIONS[normalizedFlowId] || FLOW_DEFINITIONS[DEFAULT_FLOW_ID]; + return FLOW_DEFINITIONS[normalizeFlowId(flowId)] || FLOW_DEFINITIONS[DEFAULT_FLOW_ID]; } function getFlowLabel(flowId) { return getFlowDefinition(flowId)?.label || normalizeFlowId(flowId); } - function getDefaultSourceId(flowId) { + function getDefaultIntegrationTargetId(flowId) { return normalizeFlowId(flowId) === 'kiro' - ? DEFAULT_KIRO_SOURCE_ID - : DEFAULT_OPENAI_SOURCE_ID; + ? DEFAULT_KIRO_INTEGRATION_TARGET_ID + : DEFAULT_OPENAI_INTEGRATION_TARGET_ID; } - function normalizeOpenAiSourceId(value = '', fallback = DEFAULT_OPENAI_SOURCE_ID) { + function normalizeOpenAiIntegrationTargetId(value = '', fallback = DEFAULT_OPENAI_INTEGRATION_TARGET_ID) { const normalized = String(value || '').trim().toLowerCase(); - if (OPENAI_SOURCE_IDS.includes(normalized)) { + if (OPENAI_INTEGRATION_TARGET_IDS.includes(normalized)) { return normalized; } const fallbackValue = String(fallback || '').trim().toLowerCase(); - return OPENAI_SOURCE_IDS.includes(fallbackValue) ? fallbackValue : DEFAULT_OPENAI_SOURCE_ID; + return OPENAI_INTEGRATION_TARGET_IDS.includes(fallbackValue) + ? fallbackValue + : DEFAULT_OPENAI_INTEGRATION_TARGET_ID; } - function normalizeKiroSourceId(value = '', fallback = DEFAULT_KIRO_SOURCE_ID) { + function normalizeKiroIntegrationTargetId(value = '', fallback = DEFAULT_KIRO_INTEGRATION_TARGET_ID) { const normalized = String(value || '').trim().toLowerCase(); - if (normalized === DEFAULT_KIRO_SOURCE_ID) { + if (normalized === DEFAULT_KIRO_INTEGRATION_TARGET_ID) { return normalized; } const fallbackValue = String(fallback || '').trim().toLowerCase(); - return fallbackValue === DEFAULT_KIRO_SOURCE_ID ? fallbackValue : DEFAULT_KIRO_SOURCE_ID; + return fallbackValue === DEFAULT_KIRO_INTEGRATION_TARGET_ID + ? fallbackValue + : DEFAULT_KIRO_INTEGRATION_TARGET_ID; } - function normalizeSourceId(flowId, sourceId = '', fallback = undefined) { + function normalizeIntegrationTargetId(flowId, integrationTargetId = '', fallback = undefined) { const normalizedFlowId = normalizeFlowId(flowId); if (normalizedFlowId === 'kiro') { - return normalizeKiroSourceId(sourceId, fallback || DEFAULT_KIRO_SOURCE_ID); + return normalizeKiroIntegrationTargetId( + integrationTargetId, + fallback || DEFAULT_KIRO_INTEGRATION_TARGET_ID + ); } - return normalizeOpenAiSourceId(sourceId, fallback || DEFAULT_OPENAI_SOURCE_ID); + return normalizeOpenAiIntegrationTargetId( + integrationTargetId, + fallback || DEFAULT_OPENAI_INTEGRATION_TARGET_ID + ); } - function getSourceDefinitions(flowId) { - return getFlowDefinition(flowId)?.sources || {}; + function getIntegrationTargetDefinitions(flowId) { + return getFlowDefinition(flowId)?.integrationTargets || {}; } - function getSourceDefinition(flowId, sourceId) { + function getIntegrationTargetDefinition(flowId, integrationTargetId) { const normalizedFlowId = normalizeFlowId(flowId); - const normalizedSourceId = normalizeSourceId(normalizedFlowId, sourceId, getDefaultSourceId(normalizedFlowId)); - return getSourceDefinitions(normalizedFlowId)[normalizedSourceId] || null; + const normalizedIntegrationTargetId = normalizeIntegrationTargetId( + normalizedFlowId, + integrationTargetId, + getDefaultIntegrationTargetId(normalizedFlowId) + ); + return getIntegrationTargetDefinitions(normalizedFlowId)[normalizedIntegrationTargetId] || null; } - function getSourceOptions(flowId) { - return Object.values(getSourceDefinitions(flowId)); + function getIntegrationTargetOptions(flowId) { + return Object.values(getIntegrationTargetDefinitions(flowId)); } - function getSourceLabel(flowId, sourceId) { - return getSourceDefinition(flowId, sourceId)?.label || normalizeSourceId(flowId, sourceId); + function getIntegrationTargetLabel(flowId, integrationTargetId) { + return getIntegrationTargetDefinition(flowId, integrationTargetId)?.label + || normalizeIntegrationTargetId(flowId, integrationTargetId); } - function mapPanelModeToSourceId(panelMode = '', fallback = DEFAULT_OPENAI_SOURCE_ID) { - return normalizeOpenAiSourceId(panelMode, fallback); + function getPublicationTargetDefinitions(flowId) { + return getFlowDefinition(flowId)?.publicationTargets || {}; } - function mapSourceIdToPanelMode(flowId, sourceId = '', fallback = DEFAULT_OPENAI_SOURCE_ID) { - if (normalizeFlowId(flowId) !== DEFAULT_FLOW_ID) { - return normalizeOpenAiSourceId(fallback, DEFAULT_OPENAI_SOURCE_ID); - } - return normalizeOpenAiSourceId(sourceId, fallback || DEFAULT_OPENAI_SOURCE_ID); + function getPublicationTargetDefinition(flowId, publicationTargetId) { + const normalizedFlowId = normalizeFlowId(flowId); + const normalizedPublicationTargetId = String( + publicationTargetId || ( + normalizedFlowId === 'kiro' + ? DEFAULT_KIRO_PUBLICATION_TARGET_ID + : '' + ) + ).trim().toLowerCase(); + return getPublicationTargetDefinitions(normalizedFlowId)[normalizedPublicationTargetId] || null; } function getFlowCapabilities(flowId) { @@ -435,18 +450,27 @@ }; } - function getVisibleGroupIds(flowId, sourceId, options = {}) { + function getVisibleGroupIds(flowId, integrationTargetId, options = {}) { const normalizedFlowId = normalizeFlowId(flowId); const flowDefinition = getFlowDefinition(normalizedFlowId); - const normalizedSourceId = normalizeSourceId(normalizedFlowId, sourceId, getDefaultSourceId(normalizedFlowId)); - const sourceDefinition = getSourceDefinition(normalizedFlowId, normalizedSourceId); + const normalizedIntegrationTargetId = normalizeIntegrationTargetId( + normalizedFlowId, + integrationTargetId, + getDefaultIntegrationTargetId(normalizedFlowId) + ); + const integrationTargetDefinition = getIntegrationTargetDefinition( + normalizedFlowId, + normalizedIntegrationTargetId + ); const includeSharedServices = options?.includeSharedServices !== false; const serviceGroups = includeSharedServices - ? (Array.isArray(flowDefinition?.services) ? flowDefinition.services.map((serviceId) => `service-${serviceId}`) : []) + ? (Array.isArray(flowDefinition?.services) + ? flowDefinition.services.map((serviceId) => `service-${serviceId}`) + : []) : []; return Array.from(new Set([ ...(Array.isArray(flowDefinition?.baseGroups) ? flowDefinition.baseGroups : []), - ...(Array.isArray(sourceDefinition?.groups) ? sourceDefinition.groups : []), + ...(Array.isArray(integrationTargetDefinition?.groups) ? integrationTargetDefinition.groups : []), ...serviceGroups, ])); } @@ -479,33 +503,33 @@ return { DEFAULT_FLOW_CAPABILITIES, DEFAULT_FLOW_ID, + DEFAULT_KIRO_INTEGRATION_TARGET_ID, + DEFAULT_KIRO_PUBLICATION_TARGET_ID, DEFAULT_KIRO_RS_URL, - DEFAULT_KIRO_SOURCE_ID, - DEFAULT_OPENAI_SOURCE_ID, + DEFAULT_OPENAI_INTEGRATION_TARGET_ID, FLOW_DEFINITIONS, - LEGACY_OPENAI_FLOW_ALIAS, - OPENAI_SOURCE_IDS, + OPENAI_INTEGRATION_TARGET_IDS, SETTINGS_GROUP_DEFINITIONS, SHARED_SERVICE_IDS, - getDefaultSourceId, + getDefaultIntegrationTargetId, getDriverDefinitions, getFlowCapabilities, getFlowDefinition, getFlowLabel, + getIntegrationTargetDefinition, + getIntegrationTargetDefinitions, + getIntegrationTargetLabel, + getIntegrationTargetOptions, + getPublicationTargetDefinition, + getPublicationTargetDefinitions, getRegisteredFlowIds, getRuntimeSourceDefinitions, getSettingsGroupDefinition, getSettingsGroupDefinitions, - getSourceDefinition, - getSourceDefinitions, - getSourceLabel, - getSourceOptions, getVisibleGroupIds, - mapPanelModeToSourceId, - mapSourceIdToPanelMode, normalizeFlowId, - normalizeKiroSourceId, - normalizeOpenAiSourceId, - normalizeSourceId, + normalizeIntegrationTargetId, + normalizeKiroIntegrationTargetId, + normalizeOpenAiIntegrationTargetId, }; }); diff --git a/shared/settings-schema.js b/shared/settings-schema.js index 6cc441c..cc42150 100644 --- a/shared/settings-schema.js +++ b/shared/settings-schema.js @@ -32,9 +32,11 @@ function createSettingsSchema(deps = {}) { const rootScope = typeof self !== 'undefined' ? self : globalThis; const flowRegistry = deps.flowRegistry || rootScope.MultiPageFlowRegistry || {}; - const defaultFlowId = String(deps.defaultFlowId || flowRegistry.DEFAULT_FLOW_ID || 'openai').trim().toLowerCase() || 'openai'; - const defaultOpenAiSourceId = flowRegistry.DEFAULT_OPENAI_SOURCE_ID || 'cpa'; - const defaultKiroSourceId = flowRegistry.DEFAULT_KIRO_SOURCE_ID || 'kiro-rs'; + const defaultFlowId = String( + deps.defaultFlowId || flowRegistry.DEFAULT_FLOW_ID || 'openai' + ).trim().toLowerCase() || 'openai'; + const defaultOpenAiIntegrationTargetId = flowRegistry.DEFAULT_OPENAI_INTEGRATION_TARGET_ID || 'cpa'; + const defaultKiroIntegrationTargetId = flowRegistry.DEFAULT_KIRO_INTEGRATION_TARGET_ID || 'kiro-rs'; const defaultKiroRsUrl = flowRegistry.DEFAULT_KIRO_RS_URL || 'https://kiro.leftcode.xyz/admin'; const normalizeFlowId = typeof flowRegistry.normalizeFlowId === 'function' ? flowRegistry.normalizeFlowId @@ -42,19 +44,13 @@ const normalized = String(value || '').trim().toLowerCase(); return normalized || String(fallback || '').trim().toLowerCase() || defaultFlowId; }); - const normalizeSourceId = typeof flowRegistry.normalizeSourceId === 'function' - ? flowRegistry.normalizeSourceId - : ((flowId, value = '', fallback = '') => String(value || fallback || '').trim().toLowerCase()); - const mapSourceIdToPanelMode = typeof flowRegistry.mapSourceIdToPanelMode === 'function' - ? flowRegistry.mapSourceIdToPanelMode - : ((_flowId, sourceId = '', fallback = defaultOpenAiSourceId) => String(sourceId || fallback || defaultOpenAiSourceId).trim().toLowerCase()); - const mapPanelModeToSourceId = typeof flowRegistry.mapPanelModeToSourceId === 'function' - ? flowRegistry.mapPanelModeToSourceId - : ((panelMode = '', fallback = defaultOpenAiSourceId) => String(panelMode || fallback || defaultOpenAiSourceId).trim().toLowerCase()); + const normalizeIntegrationTargetId = typeof flowRegistry.normalizeIntegrationTargetId === 'function' + ? flowRegistry.normalizeIntegrationTargetId + : ((_flowId, value = '', fallback = '') => String(value || fallback || '').trim().toLowerCase()); function buildDefaultSettingsState() { return { - schemaVersion: 3, + schemaVersion: 4, activeFlowId: defaultFlowId, services: { account: { @@ -71,27 +67,25 @@ }, flows: { openai: { - source: { - selected: defaultOpenAiSourceId, - entries: { - cpa: { - vpsUrl: '', - vpsPassword: '', - localCpaStep9Mode: 'submit', - }, - sub2api: { - sub2apiUrl: '', - sub2apiEmail: '', - sub2apiPassword: '', - sub2apiGroupName: 'codex', - sub2apiGroupNames: ['codex', 'openai-plus'], - sub2apiAccountPriority: 1, - sub2apiDefaultProxyName: '', - }, - codex2api: { - codex2apiUrl: '', - codex2apiAdminKey: '', - }, + integrationTargetId: defaultOpenAiIntegrationTargetId, + integrationTargets: { + cpa: { + vpsUrl: '', + vpsPassword: '', + localCpaStep9Mode: 'submit', + }, + sub2api: { + sub2apiUrl: '', + sub2apiEmail: '', + sub2apiPassword: '', + sub2apiGroupName: 'codex', + sub2apiGroupNames: ['codex', 'openai-plus'], + sub2apiAccountPriority: 1, + sub2apiDefaultProxyName: '', + }, + codex2api: { + codex2apiUrl: '', + codex2apiAdminKey: '', }, }, signup: { @@ -112,21 +106,13 @@ }, }, kiro: { - source: { - selected: defaultKiroSourceId, - entries: { - 'kiro-rs': { - kiroRsUrl: defaultKiroRsUrl, - kiroRsKey: '', - }, + integrationTargetId: defaultKiroIntegrationTargetId, + integrationTargets: { + 'kiro-rs': { + baseUrl: defaultKiroRsUrl, + apiKey: '', }, }, - options: { - kiroRsPriority: 0, - kiroRsEndpoint: '', - kiroRsAuthRegion: '', - kiroRsApiRegion: '', - }, autoRun: { stepExecutionRange: { enabled: false, @@ -139,7 +125,7 @@ }; } - function getSourceValue(settingsState, pathGetter, fallback = {}) { + function getIntegrationTargetValue(settingsState, pathGetter, fallback = {}) { return cloneValue(pathGetter(isPlainObject(settingsState) ? settingsState : {}) || fallback); } @@ -155,20 +141,21 @@ ?? defaults.activeFlowId, defaults.activeFlowId ); - const openaiSelectedSource = normalizeSourceId( + const openaiIntegrationTargetId = normalizeIntegrationTargetId( 'openai', - nested?.flows?.openai?.source?.selected + nested?.flows?.openai?.integrationTargetId + ?? input?.openaiIntegrationTargetId ?? input?.panelMode - ?? input?.openaiSourceId - ?? defaults.flows.openai.source.selected, - defaults.flows.openai.source.selected + ?? defaults.flows.openai.integrationTargetId, + defaults.flows.openai.integrationTargetId ); - const kiroSelectedSource = normalizeSourceId( + const kiroIntegrationTargetId = normalizeIntegrationTargetId( 'kiro', - nested?.flows?.kiro?.source?.selected + nested?.flows?.kiro?.integrationTargetId + ?? input?.kiroIntegrationTargetId ?? input?.kiroSourceId - ?? defaults.flows.kiro.source.selected, - defaults.flows.kiro.source.selected + ?? defaults.flows.kiro.integrationTargetId, + defaults.flows.kiro.integrationTargetId ); const stepExecutionRangeByFlow = isPlainObject(input?.stepExecutionRangeByFlow) ? input.stepExecutionRangeByFlow @@ -206,58 +193,115 @@ customPassword: String( input?.customPassword ?? nested?.services?.account?.customPassword - ?? nested?.flows?.openai?.account?.customPassword ?? defaults.services.account.customPassword ).trim(), }, }, flows: { openai: { - source: { - selected: openaiSelectedSource, - entries: { - cpa: { - ...defaults.flows.openai.source.entries.cpa, - ...getSourceValue(nested, (state) => state.flows?.openai?.source?.entries?.cpa), - vpsUrl: String(input?.vpsUrl ?? nested?.flows?.openai?.source?.entries?.cpa?.vpsUrl ?? '').trim(), - vpsPassword: String(input?.vpsPassword ?? nested?.flows?.openai?.source?.entries?.cpa?.vpsPassword ?? ''), - localCpaStep9Mode: String( - input?.localCpaStep9Mode - ?? nested?.flows?.openai?.source?.entries?.cpa?.localCpaStep9Mode - ?? defaults.flows.openai.source.entries.cpa.localCpaStep9Mode - ).trim() || defaults.flows.openai.source.entries.cpa.localCpaStep9Mode, - }, - sub2api: { - ...defaults.flows.openai.source.entries.sub2api, - ...getSourceValue(nested, (state) => state.flows?.openai?.source?.entries?.sub2api), - sub2apiUrl: String(input?.sub2apiUrl ?? nested?.flows?.openai?.source?.entries?.sub2api?.sub2apiUrl ?? '').trim(), - sub2apiEmail: String(input?.sub2apiEmail ?? nested?.flows?.openai?.source?.entries?.sub2api?.sub2apiEmail ?? '').trim(), - sub2apiPassword: String(input?.sub2apiPassword ?? nested?.flows?.openai?.source?.entries?.sub2api?.sub2apiPassword ?? ''), - sub2apiGroupName: String(input?.sub2apiGroupName ?? nested?.flows?.openai?.source?.entries?.sub2api?.sub2apiGroupName ?? defaults.flows.openai.source.entries.sub2api.sub2apiGroupName).trim() || defaults.flows.openai.source.entries.sub2api.sub2apiGroupName, - sub2apiGroupNames: Array.isArray(input?.sub2apiGroupNames) - ? input.sub2apiGroupNames.map((entry) => String(entry || '').trim()).filter(Boolean) - : (Array.isArray(nested?.flows?.openai?.source?.entries?.sub2api?.sub2apiGroupNames) - ? nested.flows.openai.source.entries.sub2api.sub2apiGroupNames.map((entry) => String(entry || '').trim()).filter(Boolean) - : [...defaults.flows.openai.source.entries.sub2api.sub2apiGroupNames]), - sub2apiAccountPriority: Math.max(1, Number(input?.sub2apiAccountPriority ?? nested?.flows?.openai?.source?.entries?.sub2api?.sub2apiAccountPriority ?? defaults.flows.openai.source.entries.sub2api.sub2apiAccountPriority) || defaults.flows.openai.source.entries.sub2api.sub2apiAccountPriority), - sub2apiDefaultProxyName: String(input?.sub2apiDefaultProxyName ?? nested?.flows?.openai?.source?.entries?.sub2api?.sub2apiDefaultProxyName ?? '').trim(), - }, - codex2api: { - ...defaults.flows.openai.source.entries.codex2api, - ...getSourceValue(nested, (state) => state.flows?.openai?.source?.entries?.codex2api), - codex2apiUrl: String(input?.codex2apiUrl ?? nested?.flows?.openai?.source?.entries?.codex2api?.codex2apiUrl ?? '').trim(), - codex2apiAdminKey: String(input?.codex2apiAdminKey ?? nested?.flows?.openai?.source?.entries?.codex2api?.codex2apiAdminKey ?? '').trim(), - }, + integrationTargetId: openaiIntegrationTargetId, + integrationTargets: { + cpa: { + ...defaults.flows.openai.integrationTargets.cpa, + ...getIntegrationTargetValue(nested, (state) => state.flows?.openai?.integrationTargets?.cpa), + vpsUrl: String( + input?.vpsUrl + ?? nested?.flows?.openai?.integrationTargets?.cpa?.vpsUrl + ?? '' + ).trim(), + vpsPassword: String( + input?.vpsPassword + ?? nested?.flows?.openai?.integrationTargets?.cpa?.vpsPassword + ?? '' + ), + localCpaStep9Mode: String( + input?.localCpaStep9Mode + ?? nested?.flows?.openai?.integrationTargets?.cpa?.localCpaStep9Mode + ?? defaults.flows.openai.integrationTargets.cpa.localCpaStep9Mode + ).trim() || defaults.flows.openai.integrationTargets.cpa.localCpaStep9Mode, + }, + sub2api: { + ...defaults.flows.openai.integrationTargets.sub2api, + ...getIntegrationTargetValue(nested, (state) => state.flows?.openai?.integrationTargets?.sub2api), + sub2apiUrl: String( + input?.sub2apiUrl + ?? nested?.flows?.openai?.integrationTargets?.sub2api?.sub2apiUrl + ?? '' + ).trim(), + sub2apiEmail: String( + input?.sub2apiEmail + ?? nested?.flows?.openai?.integrationTargets?.sub2api?.sub2apiEmail + ?? '' + ).trim(), + sub2apiPassword: String( + input?.sub2apiPassword + ?? nested?.flows?.openai?.integrationTargets?.sub2api?.sub2apiPassword + ?? '' + ), + sub2apiGroupName: String( + input?.sub2apiGroupName + ?? nested?.flows?.openai?.integrationTargets?.sub2api?.sub2apiGroupName + ?? defaults.flows.openai.integrationTargets.sub2api.sub2apiGroupName + ).trim() || defaults.flows.openai.integrationTargets.sub2api.sub2apiGroupName, + sub2apiGroupNames: Array.isArray(input?.sub2apiGroupNames) + ? input.sub2apiGroupNames.map((entry) => String(entry || '').trim()).filter(Boolean) + : (Array.isArray(nested?.flows?.openai?.integrationTargets?.sub2api?.sub2apiGroupNames) + ? nested.flows.openai.integrationTargets.sub2api.sub2apiGroupNames.map((entry) => String(entry || '').trim()).filter(Boolean) + : [...defaults.flows.openai.integrationTargets.sub2api.sub2apiGroupNames]), + sub2apiAccountPriority: Math.max(1, Number( + input?.sub2apiAccountPriority + ?? nested?.flows?.openai?.integrationTargets?.sub2api?.sub2apiAccountPriority + ?? defaults.flows.openai.integrationTargets.sub2api.sub2apiAccountPriority + ) || defaults.flows.openai.integrationTargets.sub2api.sub2apiAccountPriority), + sub2apiDefaultProxyName: String( + input?.sub2apiDefaultProxyName + ?? nested?.flows?.openai?.integrationTargets?.sub2api?.sub2apiDefaultProxyName + ?? '' + ).trim(), + }, + codex2api: { + ...defaults.flows.openai.integrationTargets.codex2api, + ...getIntegrationTargetValue(nested, (state) => state.flows?.openai?.integrationTargets?.codex2api), + codex2apiUrl: String( + input?.codex2apiUrl + ?? nested?.flows?.openai?.integrationTargets?.codex2api?.codex2apiUrl + ?? '' + ).trim(), + codex2apiAdminKey: String( + input?.codex2apiAdminKey + ?? nested?.flows?.openai?.integrationTargets?.codex2api?.codex2apiAdminKey + ?? '' + ).trim(), }, }, signup: { - signupMethod: String(input?.signupMethod ?? nested?.flows?.openai?.signup?.signupMethod ?? defaults.flows.openai.signup.signupMethod).trim().toLowerCase() === 'phone' ? 'phone' : 'email', - phoneVerificationEnabled: Boolean(input?.phoneVerificationEnabled ?? nested?.flows?.openai?.signup?.phoneVerificationEnabled ?? defaults.flows.openai.signup.phoneVerificationEnabled), - phoneSignupReloginAfterBindEmailEnabled: Boolean(input?.phoneSignupReloginAfterBindEmailEnabled ?? nested?.flows?.openai?.signup?.phoneSignupReloginAfterBindEmailEnabled ?? defaults.flows.openai.signup.phoneSignupReloginAfterBindEmailEnabled), + signupMethod: String( + input?.signupMethod + ?? nested?.flows?.openai?.signup?.signupMethod + ?? defaults.flows.openai.signup.signupMethod + ).trim().toLowerCase() === 'phone' ? 'phone' : 'email', + phoneVerificationEnabled: Boolean( + input?.phoneVerificationEnabled + ?? nested?.flows?.openai?.signup?.phoneVerificationEnabled + ?? defaults.flows.openai.signup.phoneVerificationEnabled + ), + phoneSignupReloginAfterBindEmailEnabled: Boolean( + input?.phoneSignupReloginAfterBindEmailEnabled + ?? nested?.flows?.openai?.signup?.phoneSignupReloginAfterBindEmailEnabled + ?? defaults.flows.openai.signup.phoneSignupReloginAfterBindEmailEnabled + ), }, plus: { - plusModeEnabled: Boolean(input?.plusModeEnabled ?? nested?.flows?.openai?.plus?.plusModeEnabled ?? defaults.flows.openai.plus.plusModeEnabled), - plusPaymentMethod: String(input?.plusPaymentMethod ?? nested?.flows?.openai?.plus?.plusPaymentMethod ?? defaults.flows.openai.plus.plusPaymentMethod).trim() || defaults.flows.openai.plus.plusPaymentMethod, + plusModeEnabled: Boolean( + input?.plusModeEnabled + ?? nested?.flows?.openai?.plus?.plusModeEnabled + ?? defaults.flows.openai.plus.plusModeEnabled + ), + plusPaymentMethod: String( + input?.plusPaymentMethod + ?? nested?.flows?.openai?.plus?.plusPaymentMethod + ?? defaults.flows.openai.plus.plusPaymentMethod + ).trim() || defaults.flows.openai.plus.plusPaymentMethod, }, autoRun: { stepExecutionRange: normalizeStepExecutionRangeEntry( @@ -269,47 +313,25 @@ }, }, kiro: { - source: { - selected: kiroSelectedSource, - entries: { - 'kiro-rs': { - ...defaults.flows.kiro.source.entries['kiro-rs'], - ...getSourceValue(nested, (state) => state.flows?.kiro?.source?.entries?.['kiro-rs']), - kiroRsUrl: String( - input?.kiroRsUrl - ?? nested?.flows?.kiro?.source?.entries?.['kiro-rs']?.kiroRsUrl - ?? defaults.flows.kiro.source.entries['kiro-rs'].kiroRsUrl - ).trim() || defaults.flows.kiro.source.entries['kiro-rs'].kiroRsUrl, - kiroRsKey: String( - input?.kiroRsKey - ?? nested?.flows?.kiro?.source?.entries?.['kiro-rs']?.kiroRsKey - ?? defaults.flows.kiro.source.entries['kiro-rs'].kiroRsKey - ), - }, + integrationTargetId: kiroIntegrationTargetId, + integrationTargets: { + 'kiro-rs': { + ...defaults.flows.kiro.integrationTargets['kiro-rs'], + ...getIntegrationTargetValue(nested, (state) => state.flows?.kiro?.integrationTargets?.['kiro-rs']), + baseUrl: String( + input?.kiroRsUrl + ?? input?.kiroRsBaseUrl + ?? nested?.flows?.kiro?.integrationTargets?.['kiro-rs']?.baseUrl + ?? defaults.flows.kiro.integrationTargets['kiro-rs'].baseUrl + ).trim() || defaults.flows.kiro.integrationTargets['kiro-rs'].baseUrl, + apiKey: String( + input?.kiroRsKey + ?? input?.kiroRsApiKey + ?? nested?.flows?.kiro?.integrationTargets?.['kiro-rs']?.apiKey + ?? defaults.flows.kiro.integrationTargets['kiro-rs'].apiKey + ), }, }, - options: { - kiroRsPriority: Number( - input?.kiroRsPriority - ?? nested?.flows?.kiro?.options?.kiroRsPriority - ?? defaults.flows.kiro.options.kiroRsPriority - ) || 0, - kiroRsEndpoint: String( - input?.kiroRsEndpoint - ?? nested?.flows?.kiro?.options?.kiroRsEndpoint - ?? defaults.flows.kiro.options.kiroRsEndpoint - ).trim(), - kiroRsAuthRegion: String( - input?.kiroRsAuthRegion - ?? nested?.flows?.kiro?.options?.kiroRsAuthRegion - ?? defaults.flows.kiro.options.kiroRsAuthRegion - ).trim(), - kiroRsApiRegion: String( - input?.kiroRsApiRegion - ?? nested?.flows?.kiro?.options?.kiroRsApiRegion - ?? defaults.flows.kiro.options.kiroRsApiRegion - ).trim(), - }, autoRun: { stepExecutionRange: normalizeStepExecutionRangeEntry( nested?.flows?.kiro?.autoRun?.stepExecutionRange @@ -323,6 +345,53 @@ }; } + function mergeSettingsState(baseValue = {}, patchValue = {}) { + const baseSettingsState = normalizeSettingsState(baseValue); + const patchSettingsState = normalizeSettingsState({ + settingsState: patchValue, + activeFlowId: patchValue?.activeFlowId ?? baseSettingsState.activeFlowId, + }); + + function mergeRecursive(baseNode, patchNode) { + if (Array.isArray(patchNode)) { + return patchNode.map((entry) => cloneValue(entry)); + } + if (!isPlainObject(patchNode)) { + return patchNode === undefined ? cloneValue(baseNode) : patchNode; + } + const next = { + ...cloneValue(isPlainObject(baseNode) ? baseNode : {}), + }; + Object.entries(patchNode).forEach(([key, value]) => { + next[key] = mergeRecursive(baseNode?.[key], value); + }); + return next; + } + + return normalizeSettingsState({ + settingsState: mergeRecursive(baseSettingsState, patchSettingsState), + }); + } + + function getFlowSettings(settingsState = {}, flowId) { + const normalizedState = normalizeSettingsState(settingsState); + const normalizedFlowId = normalizeFlowId(flowId, normalizedState.activeFlowId); + return cloneValue(normalizedState?.flows?.[normalizedFlowId] || {}); + } + + function getSelectedIntegrationTargetId(settingsState = {}, flowId) { + const normalizedState = normalizeSettingsState(settingsState); + const normalizedFlowId = normalizeFlowId(flowId, normalizedState.activeFlowId); + const flowSettings = normalizedState?.flows?.[normalizedFlowId] || {}; + return normalizeIntegrationTargetId( + normalizedFlowId, + flowSettings?.integrationTargetId, + normalizedFlowId === 'kiro' + ? defaultKiroIntegrationTargetId + : defaultOpenAiIntegrationTargetId + ); + } + function buildStepExecutionRangeByFlow(settingsState = {}) { const normalizedState = normalizeSettingsState(settingsState); return { @@ -337,23 +406,7 @@ }; } - function getFlowSettings(settingsState = {}, flowId) { - const normalizedState = normalizeSettingsState(settingsState); - const normalizedFlowId = normalizeFlowId(flowId, normalizedState.activeFlowId); - return cloneValue(normalizedState?.flows?.[normalizedFlowId] || {}); - } - - function getSelectedSourceId(settingsState = {}, flowId) { - const flowSettings = getFlowSettings(settingsState, flowId); - const normalizedFlowId = normalizeFlowId(flowId, normalizeSettingsState(settingsState).activeFlowId); - return normalizeSourceId( - normalizedFlowId, - flowSettings?.source?.selected, - normalizedFlowId === 'kiro' ? defaultKiroSourceId : defaultOpenAiSourceId - ); - } - - function buildLegacySettingsPayload(settingsState = {}, baseInput = {}) { + function buildSettingsView(settingsState = {}, baseInput = {}) { const normalizedState = normalizeSettingsState(settingsState); const next = { ...(isPlainObject(baseInput) ? cloneValue(baseInput) : {}), @@ -361,20 +414,22 @@ const openaiState = normalizedState.flows.openai; const kiroState = normalizedState.flows.kiro; next.activeFlowId = normalizedState.activeFlowId; - next.panelMode = mapSourceIdToPanelMode('openai', openaiState.source.selected, defaultOpenAiSourceId); - next.kiroSourceId = getSelectedSourceId(normalizedState, 'kiro'); - next.vpsUrl = openaiState.source.entries.cpa.vpsUrl; - next.vpsPassword = openaiState.source.entries.cpa.vpsPassword; - next.localCpaStep9Mode = openaiState.source.entries.cpa.localCpaStep9Mode; - next.sub2apiUrl = openaiState.source.entries.sub2api.sub2apiUrl; - next.sub2apiEmail = openaiState.source.entries.sub2api.sub2apiEmail; - next.sub2apiPassword = openaiState.source.entries.sub2api.sub2apiPassword; - next.sub2apiGroupName = openaiState.source.entries.sub2api.sub2apiGroupName; - next.sub2apiGroupNames = cloneValue(openaiState.source.entries.sub2api.sub2apiGroupNames); - next.sub2apiAccountPriority = openaiState.source.entries.sub2api.sub2apiAccountPriority; - next.sub2apiDefaultProxyName = openaiState.source.entries.sub2api.sub2apiDefaultProxyName; - next.codex2apiUrl = openaiState.source.entries.codex2api.codex2apiUrl; - next.codex2apiAdminKey = openaiState.source.entries.codex2api.codex2apiAdminKey; + next.openaiIntegrationTargetId = getSelectedIntegrationTargetId(normalizedState, 'openai'); + next.kiroIntegrationTargetId = getSelectedIntegrationTargetId(normalizedState, 'kiro'); + next.panelMode = next.openaiIntegrationTargetId; + next.kiroSourceId = next.kiroIntegrationTargetId; + next.vpsUrl = openaiState.integrationTargets.cpa.vpsUrl; + next.vpsPassword = openaiState.integrationTargets.cpa.vpsPassword; + next.localCpaStep9Mode = openaiState.integrationTargets.cpa.localCpaStep9Mode; + next.sub2apiUrl = openaiState.integrationTargets.sub2api.sub2apiUrl; + next.sub2apiEmail = openaiState.integrationTargets.sub2api.sub2apiEmail; + next.sub2apiPassword = openaiState.integrationTargets.sub2api.sub2apiPassword; + next.sub2apiGroupName = openaiState.integrationTargets.sub2api.sub2apiGroupName; + next.sub2apiGroupNames = cloneValue(openaiState.integrationTargets.sub2api.sub2apiGroupNames); + next.sub2apiAccountPriority = openaiState.integrationTargets.sub2api.sub2apiAccountPriority; + next.sub2apiDefaultProxyName = openaiState.integrationTargets.sub2api.sub2apiDefaultProxyName; + next.codex2apiUrl = openaiState.integrationTargets.codex2api.codex2apiUrl; + next.codex2apiAdminKey = openaiState.integrationTargets.codex2api.codex2apiAdminKey; next.customPassword = normalizedState.services.account.customPassword; next.signupMethod = openaiState.signup.signupMethod; next.phoneVerificationEnabled = openaiState.signup.phoneVerificationEnabled; @@ -385,13 +440,8 @@ next.ipProxyEnabled = normalizedState.services.proxy.enabled; next.ipProxyService = normalizedState.services.proxy.provider; next.ipProxyMode = normalizedState.services.proxy.mode; - next.kiroRsUrl = kiroState.source.entries['kiro-rs'].kiroRsUrl; - next.kiroRsKey = kiroState.source.entries['kiro-rs'].kiroRsKey; - next.kiroRsPriority = kiroState.options.kiroRsPriority; - next.kiroRsEndpoint = kiroState.options.kiroRsEndpoint; - next.kiroRsAuthRegion = kiroState.options.kiroRsAuthRegion; - next.kiroRsApiRegion = kiroState.options.kiroRsApiRegion; - delete next.kiroRegion; + next.kiroRsUrl = kiroState.integrationTargets['kiro-rs'].baseUrl; + next.kiroRsKey = kiroState.integrationTargets['kiro-rs'].apiKey; next.stepExecutionRangeByFlow = buildStepExecutionRangeByFlow(normalizedState); next.settingsSchemaVersion = normalizedState.schemaVersion; next.settingsState = cloneValue(normalizedState); @@ -401,34 +451,29 @@ function getFlowInputState(settingsState = {}, flowId) { const normalizedState = normalizeSettingsState(settingsState); const normalizedFlowId = normalizeFlowId(flowId, normalizedState.activeFlowId); + const integrationTargetId = getSelectedIntegrationTargetId(normalizedState, normalizedFlowId); if (normalizedFlowId === 'kiro') { return { activeFlowId: normalizedFlowId, - sourceId: getSelectedSourceId(normalizedState, 'kiro'), - kiroRsUrl: normalizedState.flows.kiro.source.entries['kiro-rs'].kiroRsUrl, - kiroRsKey: normalizedState.flows.kiro.source.entries['kiro-rs'].kiroRsKey, + integrationTargetId, + kiroRsUrl: normalizedState.flows.kiro.integrationTargets['kiro-rs'].baseUrl, + kiroRsKey: normalizedState.flows.kiro.integrationTargets['kiro-rs'].apiKey, }; } return { activeFlowId: normalizedFlowId, - sourceId: getSelectedSourceId(normalizedState, 'openai'), - panelMode: mapSourceIdToPanelMode('openai', normalizedState.flows.openai.source.selected, defaultOpenAiSourceId), + integrationTargetId, }; } - function normalizeFlatInput(input = {}) { - const state = normalizeSettingsState(input); - return buildLegacySettingsPayload(state, input); - } - return { buildDefaultSettingsState, - buildLegacySettingsPayload, + buildSettingsView, buildStepExecutionRangeByFlow, getFlowInputState, getFlowSettings, - getSelectedSourceId, - normalizeFlatInput, + getSelectedIntegrationTargetId, + mergeSettingsState, normalizeSettingsState, }; } diff --git a/sidepanel/sidepanel.html b/sidepanel/sidepanel.html index 73cb5d9..54e9d8c 100644 --- a/sidepanel/sidepanel.html +++ b/sidepanel/sidepanel.html @@ -683,10 +683,10 @@