refactor: 重构 Kiro flow 配置与运行链路
This commit is contained in:
+123
-183
@@ -798,9 +798,8 @@ function getNodeDefinitionsForState(state = {}) {
|
|||||||
}
|
}
|
||||||
return getStepDefinitionsForState(state)
|
return getStepDefinitionsForState(state)
|
||||||
.map((definition) => ({
|
.map((definition) => ({
|
||||||
legacyStepId: Number(definition?.id),
|
|
||||||
nodeId: String(definition?.key || '').trim(),
|
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(),
|
title: String(definition?.title || '').trim(),
|
||||||
executeKey: String(definition?.key || '').trim(),
|
executeKey: String(definition?.key || '').trim(),
|
||||||
}))
|
}))
|
||||||
@@ -829,17 +828,21 @@ function getLastNodeIdForState(state = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getNodeIdByStepForState(step, state = {}) {
|
function getNodeIdByStepForState(step, state = {}) {
|
||||||
const definition = getStepDefinitionForState(step, state);
|
const numericStep = Number(step);
|
||||||
return String(definition?.key || '').trim();
|
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 = {}) {
|
function getStepIdByNodeIdForState(nodeId, state = {}) {
|
||||||
const normalizedNodeId = String(nodeId || '').trim();
|
const normalizedNodeId = String(nodeId || '').trim();
|
||||||
if (!normalizedNodeId) return null;
|
if (!normalizedNodeId) return null;
|
||||||
const node = getNodeDefinitionForState(normalizedNodeId, state);
|
const node = getNodeDefinitionForState(normalizedNodeId, state);
|
||||||
const legacyStepId = Number(node?.legacyStepId);
|
const displayOrder = Number(node?.displayOrder);
|
||||||
if (Number.isInteger(legacyStepId) && legacyStepId > 0) {
|
if (Number.isInteger(displayOrder) && displayOrder > 0) {
|
||||||
return legacyStepId;
|
return displayOrder;
|
||||||
}
|
}
|
||||||
return getStepIdByKeyForState(normalizedNodeId, state);
|
return getStepIdByKeyForState(normalizedNodeId, state);
|
||||||
}
|
}
|
||||||
@@ -1074,6 +1077,39 @@ const PERSISTED_SETTING_DEFAULTS = {
|
|||||||
|
|
||||||
const PERSISTED_SETTING_KEYS = Object.keys(PERSISTED_SETTING_DEFAULTS);
|
const PERSISTED_SETTING_KEYS = Object.keys(PERSISTED_SETTING_DEFAULTS);
|
||||||
const PERSISTED_SETTINGS_SCHEMA_KEYS = ['settingsSchemaVersion', 'settingsState'];
|
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_SCHEMA_VERSION = 1;
|
||||||
const SETTINGS_EXPORT_FILENAME_PREFIX = 'multipage-settings';
|
const SETTINGS_EXPORT_FILENAME_PREFIX = 'multipage-settings';
|
||||||
const STEP6_REGISTRATION_SUCCESS_WAIT_MS = 20000;
|
const STEP6_REGISTRATION_SUCCESS_WAIT_MS = 20000;
|
||||||
@@ -1087,91 +1123,14 @@ const DEFAULT_STATE = {
|
|||||||
nodeStatuses: { ...DEFAULT_NODE_STATUSES },
|
nodeStatuses: { ...DEFAULT_NODE_STATUSES },
|
||||||
runtimeState: runtimeStateHelpers?.buildDefaultRuntimeState?.() || null,
|
runtimeState: runtimeStateHelpers?.buildDefaultRuntimeState?.() || null,
|
||||||
...CONTRIBUTION_RUNTIME_DEFAULTS,
|
...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 }。
|
accounts: [], // 已生成账号记录:{ email, password, createdAt }。
|
||||||
accountRunHistory: [], // 账号运行历史快照,实际持久化在 chrome.storage.local。
|
accountRunHistory: [], // 账号运行历史快照,实际持久化在 chrome.storage.local。
|
||||||
manualAliasUsage: {},
|
manualAliasUsage: {},
|
||||||
preservedAliases: {},
|
preservedAliases: {},
|
||||||
icloudAliasCache: [],
|
icloudAliasCache: [],
|
||||||
icloudAliasCacheAt: 0,
|
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: [], // 侧边栏展示的运行日志。
|
logs: [], // 侧边栏展示的运行日志。
|
||||||
...PERSISTED_SETTING_DEFAULTS, // 合并 chrome.storage.local 中持久化保存的用户配置。
|
...PERSISTED_SETTING_DEFAULTS, // 合并 chrome.storage.local 中持久化保存的用户配置。
|
||||||
ipProxyApiPool: [],
|
|
||||||
ipProxyApiCurrentIndex: 0,
|
|
||||||
ipProxyApiCurrent: null,
|
|
||||||
ipProxyAccountPool: [],
|
|
||||||
ipProxyAccountCurrentIndex: 0,
|
|
||||||
ipProxyAccountCurrent: null,
|
|
||||||
ipProxyPool: [],
|
|
||||||
ipProxyCurrentIndex: 0,
|
|
||||||
ipProxyCurrent: null,
|
|
||||||
luckmailApiKey: '',
|
luckmailApiKey: '',
|
||||||
luckmailBaseUrl: DEFAULT_LUCKMAIL_BASE_URL,
|
luckmailBaseUrl: DEFAULT_LUCKMAIL_BASE_URL,
|
||||||
luckmailEmailType: DEFAULT_LUCKMAIL_EMAIL_TYPE,
|
luckmailEmailType: DEFAULT_LUCKMAIL_EMAIL_TYPE,
|
||||||
@@ -1179,23 +1138,6 @@ const DEFAULT_STATE = {
|
|||||||
luckmailUsedPurchases: {},
|
luckmailUsedPurchases: {},
|
||||||
luckmailPreserveTagId: 0,
|
luckmailPreserveTagId: 0,
|
||||||
luckmailPreserveTagName: DEFAULT_LUCKMAIL_PRESERVE_TAG_NAME,
|
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: [],
|
heroSmsLastPriceTiers: [],
|
||||||
heroSmsLastPriceCountryId: 0,
|
heroSmsLastPriceCountryId: 0,
|
||||||
heroSmsLastPriceCountryLabel: '',
|
heroSmsLastPriceCountryLabel: '',
|
||||||
@@ -3353,14 +3295,14 @@ function buildPersistentSettingsPayload(input = {}, options = {}) {
|
|||||||
const settingsSchemaApi = typeof getSettingsSchemaApi === 'function'
|
const settingsSchemaApi = typeof getSettingsSchemaApi === 'function'
|
||||||
? getSettingsSchemaApi()
|
? getSettingsSchemaApi()
|
||||||
: null;
|
: null;
|
||||||
if (settingsSchemaApi?.normalizeSettingsState && settingsSchemaApi?.buildLegacySettingsPayload) {
|
if (settingsSchemaApi?.normalizeSettingsState && settingsSchemaApi?.buildSettingsView) {
|
||||||
const settingsSchemaInput = {};
|
const settingsSchemaInput = {};
|
||||||
for (const key of persistedSettingKeys) {
|
for (const key of persistedSettingKeys) {
|
||||||
if (normalizedInput[key] !== undefined) {
|
if (normalizedInput[key] !== undefined) {
|
||||||
settingsSchemaInput[key] = payload[key];
|
settingsSchemaInput[key] = payload[key];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const normalizedSettingsState = settingsSchemaApi.normalizeSettingsState({
|
Object.assign(payload, projectSettingsSchemaView(settingsSchemaApi, {
|
||||||
...settingsSchemaInput,
|
...settingsSchemaInput,
|
||||||
...(isPlainObjectForSettingsSchema(normalizedInput.settingsState)
|
...(isPlainObjectForSettingsSchema(normalizedInput.settingsState)
|
||||||
? { settingsState: normalizedInput.settingsState }
|
? { settingsState: normalizedInput.settingsState }
|
||||||
@@ -3368,13 +3310,7 @@ function buildPersistentSettingsPayload(input = {}, options = {}) {
|
|||||||
...(Object.prototype.hasOwnProperty.call(normalizedInput, 'settingsSchemaVersion')
|
...(Object.prototype.hasOwnProperty.call(normalizedInput, 'settingsSchemaVersion')
|
||||||
? { settingsSchemaVersion: normalizedInput.settingsSchemaVersion }
|
? { settingsSchemaVersion: normalizedInput.settingsSchemaVersion }
|
||||||
: {}),
|
: {}),
|
||||||
}, {
|
}, payload));
|
||||||
activeFlowId: settingsSchemaInput.activeFlowId || normalizedInput.activeFlowId || DEFAULT_ACTIVE_FLOW_ID,
|
|
||||||
});
|
|
||||||
Object.assign(
|
|
||||||
payload,
|
|
||||||
settingsSchemaApi.buildLegacySettingsPayload(normalizedSettingsState, 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() {
|
async function getPersistedSettings() {
|
||||||
const stored = await chrome.storage.local.get([
|
const stored = await chrome.storage.local.get([
|
||||||
...PERSISTED_SETTING_KEYS,
|
...PERSISTED_SETTING_KEYS,
|
||||||
@@ -3481,6 +3443,9 @@ function buildAutoRunFreshResetSettingsState(prevState = {}, activeFlowId = DEFA
|
|||||||
const nextSettingsStatePatch = {
|
const nextSettingsStatePatch = {
|
||||||
activeFlowId,
|
activeFlowId,
|
||||||
services: {
|
services: {
|
||||||
|
account: {
|
||||||
|
customPassword: prevState?.customPassword,
|
||||||
|
},
|
||||||
email: {
|
email: {
|
||||||
provider: prevState?.mailProvider,
|
provider: prevState?.mailProvider,
|
||||||
},
|
},
|
||||||
@@ -3492,9 +3457,7 @@ function buildAutoRunFreshResetSettingsState(prevState = {}, activeFlowId = DEFA
|
|||||||
},
|
},
|
||||||
flows: {
|
flows: {
|
||||||
openai: {
|
openai: {
|
||||||
source: {
|
integrationTargetId: prevState?.openaiIntegrationTargetId || prevState?.panelMode,
|
||||||
selected: prevState?.panelMode,
|
|
||||||
},
|
|
||||||
autoRun: normalizedStepExecutionRangeByFlow.openai
|
autoRun: normalizedStepExecutionRangeByFlow.openai
|
||||||
? {
|
? {
|
||||||
stepExecutionRange: normalizedStepExecutionRangeByFlow.openai,
|
stepExecutionRange: normalizedStepExecutionRangeByFlow.openai,
|
||||||
@@ -3502,9 +3465,7 @@ function buildAutoRunFreshResetSettingsState(prevState = {}, activeFlowId = DEFA
|
|||||||
: undefined,
|
: undefined,
|
||||||
},
|
},
|
||||||
kiro: {
|
kiro: {
|
||||||
source: {
|
integrationTargetId: prevState?.kiroIntegrationTargetId || prevState?.kiroSourceId,
|
||||||
selected: prevState?.kiroSourceId,
|
|
||||||
},
|
|
||||||
autoRun: normalizedStepExecutionRangeByFlow.kiro
|
autoRun: normalizedStepExecutionRangeByFlow.kiro
|
||||||
? {
|
? {
|
||||||
stepExecutionRange: normalizedStepExecutionRangeByFlow.kiro,
|
stepExecutionRange: normalizedStepExecutionRangeByFlow.kiro,
|
||||||
@@ -3651,78 +3612,58 @@ async function setPersistentSettings(updates) {
|
|||||||
const settingsSchemaApi = typeof getSettingsSchemaApi === 'function'
|
const settingsSchemaApi = typeof getSettingsSchemaApi === 'function'
|
||||||
? getSettingsSchemaApi()
|
? getSettingsSchemaApi()
|
||||||
: null;
|
: null;
|
||||||
|
const hasSchemaApi = Boolean(
|
||||||
|
settingsSchemaApi?.normalizeSettingsState
|
||||||
|
&& settingsSchemaApi?.buildSettingsView
|
||||||
|
);
|
||||||
|
const explicitFlatUpdates = {
|
||||||
|
...nextUpdates,
|
||||||
|
};
|
||||||
|
delete explicitFlatUpdates.settingsSchemaVersion;
|
||||||
|
delete explicitFlatUpdates.settingsState;
|
||||||
|
|
||||||
let persistedUpdates;
|
let mergedSettingsState = nextUpdates.settingsState ?? currentSettings.settingsState;
|
||||||
if (settingsSchemaApi?.normalizeSettingsState && settingsSchemaApi?.buildLegacySettingsPayload) {
|
if (hasSchemaApi) {
|
||||||
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;
|
|
||||||
};
|
|
||||||
|
|
||||||
const currentSettingsState = settingsSchemaApi.normalizeSettingsState(
|
const currentSettingsState = settingsSchemaApi.normalizeSettingsState(
|
||||||
isPlainObjectForSettingsSchema(currentSettings?.settingsState)
|
isPlainObjectValue(currentSettings?.settingsState)
|
||||||
? { settingsState: currentSettings.settingsState }
|
? { settingsState: currentSettings.settingsState }
|
||||||
: currentSettings,
|
: currentSettings,
|
||||||
{
|
{
|
||||||
activeFlowId: currentSettings?.activeFlowId || DEFAULT_ACTIVE_FLOW_ID,
|
activeFlowId: currentSettings?.activeFlowId || DEFAULT_ACTIVE_FLOW_ID,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
const mergedSettingsState = isPlainObjectForSettingsSchema(nextUpdates.settingsState)
|
mergedSettingsState = isPlainObjectValue(nextUpdates.settingsState)
|
||||||
? mergeSettingsState(currentSettingsState, nextUpdates.settingsState)
|
? (typeof settingsSchemaApi.mergeSettingsState === 'function'
|
||||||
|
? settingsSchemaApi.mergeSettingsState(currentSettingsState, nextUpdates.settingsState)
|
||||||
|
: nextUpdates.settingsState)
|
||||||
: currentSettingsState;
|
: 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) {
|
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;
|
return persistedUpdates;
|
||||||
}
|
}
|
||||||
@@ -13563,9 +13504,8 @@ function buildNodeRegistry(definitions = []) {
|
|||||||
return self.MultiPageBackgroundStepRegistry?.createNodeRegistry(
|
return self.MultiPageBackgroundStepRegistry?.createNodeRegistry(
|
||||||
definitions.map((definition) => ({
|
definitions.map((definition) => ({
|
||||||
...definition,
|
...definition,
|
||||||
legacyStepId: definition.legacyStepId || definition.id,
|
|
||||||
nodeId: definition.nodeId || definition.key,
|
nodeId: definition.nodeId || definition.key,
|
||||||
displayOrder: definition.displayOrder || definition.order,
|
displayOrder: definition.displayOrder || definition.id || definition.order,
|
||||||
executeKey: definition.executeKey || definition.key,
|
executeKey: definition.executeKey || definition.key,
|
||||||
execute: stepExecutorsByKey[definition.executeKey || definition.key || definition.nodeId],
|
execute: stepExecutorsByKey[definition.executeKey || definition.key || definition.nodeId],
|
||||||
}))
|
}))
|
||||||
@@ -13580,15 +13520,15 @@ function buildStepRegistry(definitions = []) {
|
|||||||
const normalizedDefinitions = (Array.isArray(definitions) ? definitions : [])
|
const normalizedDefinitions = (Array.isArray(definitions) ? definitions : [])
|
||||||
.map((definition) => ({
|
.map((definition) => ({
|
||||||
...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(),
|
nodeId: String(definition?.nodeId || definition?.key || '').trim(),
|
||||||
}))
|
}))
|
||||||
.filter((definition) => definition.nodeId);
|
.filter((definition) => definition.nodeId);
|
||||||
const nodeRegistry = buildNodeRegistry(normalizedDefinitions);
|
const nodeRegistry = buildNodeRegistry(normalizedDefinitions);
|
||||||
const stepToNodeDefinition = new Map(
|
const stepToNodeDefinition = new Map(
|
||||||
normalizedDefinitions
|
normalizedDefinitions
|
||||||
.filter((definition) => Number.isInteger(definition.legacyStepId) && definition.legacyStepId > 0)
|
.filter((definition) => Number.isInteger(definition.displayOrder) && definition.displayOrder > 0)
|
||||||
.map((definition) => [definition.legacyStepId, definition])
|
.map((definition) => [definition.displayOrder, definition])
|
||||||
);
|
);
|
||||||
|
|
||||||
return {
|
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 activeFlowId = String(state?.activeFlowId || state?.flowId || DEFAULT_ACTIVE_FLOW_ID).trim().toLowerCase() || DEFAULT_ACTIVE_FLOW_ID;
|
||||||
const cacheKey = `${activeFlowId}:${(Array.isArray(definitions) ? definitions : [])
|
const cacheKey = `${activeFlowId}:${(Array.isArray(definitions) ? definitions : [])
|
||||||
.map((definition) => [
|
.map((definition) => [
|
||||||
Number(definition?.legacyStepId ?? definition?.id) || 0,
|
Number(definition?.displayOrder ?? definition?.id ?? definition?.order) || 0,
|
||||||
String(definition?.nodeId || definition?.key || '').trim(),
|
String(definition?.nodeId || definition?.key || '').trim(),
|
||||||
String(definition?.executeKey || 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(':'))
|
||||||
.join('|')}`;
|
.join('|')}`;
|
||||||
|
|
||||||
|
|||||||
+135
-80
@@ -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') {
|
function buildScopedFlowState(baseFlowState = {}, state = {}, flowId = 'openai') {
|
||||||
const fieldGroups = FLOW_FIELD_GROUPS[flowId] || {};
|
const fieldGroups = FLOW_FIELD_GROUPS[flowId] || {};
|
||||||
const baseScopedState = cloneValue(normalizePlainObject(baseFlowState[flowId]));
|
const baseScopedState = cloneValue(normalizePlainObject(baseFlowState[flowId]));
|
||||||
@@ -276,7 +261,7 @@
|
|||||||
return scopedState;
|
return scopedState;
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildOpenAiFlowState(baseValue = {}, state = {}) {
|
function buildFlowState(baseValue = {}, state = {}) {
|
||||||
const baseFlowState = cloneValue(normalizePlainObject(baseValue));
|
const baseFlowState = cloneValue(normalizePlainObject(baseValue));
|
||||||
return {
|
return {
|
||||||
...baseFlowState,
|
...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() {
|
function buildRuntimeStateDefault() {
|
||||||
return {
|
return {
|
||||||
flowId: DEFAULT_ACTIVE_FLOW_ID,
|
flowId: DEFAULT_ACTIVE_FLOW_ID,
|
||||||
@@ -366,69 +471,18 @@
|
|||||||
nodeStatuses,
|
nodeStatuses,
|
||||||
sharedState: buildSharedState(baseRuntimeState.sharedState, state),
|
sharedState: buildSharedState(baseRuntimeState.sharedState, state),
|
||||||
serviceState: buildServiceState(baseRuntimeState.serviceState, state),
|
serviceState: buildServiceState(baseRuntimeState.serviceState, state),
|
||||||
flowState: buildOpenAiFlowState(baseRuntimeState.flowState, state),
|
flowState: buildFlowState(baseRuntimeState.flowState, state),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildFlattenedUpdates(updates = {}) {
|
function buildPersistentPatchPayload(updates = {}) {
|
||||||
const ignoredKeys = new Set(['current' + 'Step', 'step' + 'Statuses', 'legacy' + 'StepCompat']);
|
|
||||||
const next = {};
|
const next = {};
|
||||||
for (const [key, value] of Object.entries(updates || {})) {
|
for (const [key, value] of Object.entries(normalizePlainObject(updates))) {
|
||||||
if (!ignoredKeys.has(key)) {
|
if (RUNTIME_PATCH_IGNORED_KEYS.has(key)) {
|
||||||
next[key] = value;
|
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;
|
return next;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -436,6 +490,7 @@
|
|||||||
const runtimeState = ensureRuntimeState(state);
|
const runtimeState = ensureRuntimeState(state);
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
|
...projectRuntimeViewFields(runtimeState),
|
||||||
flowId: runtimeState.flowId,
|
flowId: runtimeState.flowId,
|
||||||
runId: runtimeState.runId,
|
runId: runtimeState.runId,
|
||||||
activeFlowId: runtimeState.activeFlowId,
|
activeFlowId: runtimeState.activeFlowId,
|
||||||
@@ -453,15 +508,15 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function buildSessionStatePatch(currentState = {}, updates = {}) {
|
function buildSessionStatePatch(currentState = {}, updates = {}) {
|
||||||
const flattenedUpdates = buildFlattenedUpdates(updates);
|
const currentRuntimeState = ensureRuntimeState(currentState);
|
||||||
const nextState = {
|
const runtimeState = ensureRuntimeState({
|
||||||
...currentState,
|
runtimeState: currentRuntimeState,
|
||||||
...flattenedUpdates,
|
...projectRuntimeViewFields(currentRuntimeState),
|
||||||
};
|
...buildRuntimeInputFromPatch(updates),
|
||||||
const runtimeState = ensureRuntimeState(nextState);
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...flattenedUpdates,
|
...buildPersistentPatchPayload(updates),
|
||||||
flowId: runtimeState.flowId,
|
flowId: runtimeState.flowId,
|
||||||
runId: runtimeState.runId,
|
runId: runtimeState.runId,
|
||||||
activeFlowId: runtimeState.activeFlowId,
|
activeFlowId: runtimeState.activeFlowId,
|
||||||
|
|||||||
@@ -4,10 +4,9 @@
|
|||||||
function createNodeRegistry(definitions = []) {
|
function createNodeRegistry(definitions = []) {
|
||||||
const ordered = (Array.isArray(definitions) ? definitions : [])
|
const ordered = (Array.isArray(definitions) ? definitions : [])
|
||||||
.map((definition) => ({
|
.map((definition) => ({
|
||||||
legacyStepId: Number(definition?.legacyStepId ?? definition?.id) || 0,
|
|
||||||
flowId: String(definition?.flowId || '').trim(),
|
flowId: String(definition?.flowId || '').trim(),
|
||||||
nodeId: String(definition?.nodeId || definition?.key || '').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(),
|
executeKey: String(definition?.executeKey || definition?.key || definition?.nodeId || '').trim(),
|
||||||
title: String(definition?.title || '').trim(),
|
title: String(definition?.title || '').trim(),
|
||||||
execute: definition?.execute,
|
execute: definition?.execute,
|
||||||
|
|||||||
@@ -348,11 +348,10 @@
|
|||||||
function cloneNodes(steps = [], options = {}, flowId = DEFAULT_ACTIVE_FLOW_ID) {
|
function cloneNodes(steps = [], options = {}, flowId = DEFAULT_ACTIVE_FLOW_ID) {
|
||||||
const { builder } = getFlowDefinitionBuilder({ activeFlowId: flowId });
|
const { builder } = getFlowDefinitionBuilder({ activeFlowId: flowId });
|
||||||
return steps.map((step) => ({
|
return steps.map((step) => ({
|
||||||
legacyStepId: Number(step.id),
|
|
||||||
nodeId: String(step.key || '').trim(),
|
nodeId: String(step.key || '').trim(),
|
||||||
flowId,
|
flowId,
|
||||||
title: builder?.resolveStepTitle ? builder.resolveStepTitle(step, options) : step.title,
|
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',
|
nodeType: 'task',
|
||||||
sourceId: step.sourceId || '',
|
sourceId: step.sourceId || '',
|
||||||
driverId: step.driverId || '',
|
driverId: step.driverId || '',
|
||||||
|
|||||||
+163
-87
@@ -5,12 +5,11 @@
|
|||||||
const flowRegistryApi = rootScope.MultiPageFlowRegistry || {};
|
const flowRegistryApi = rootScope.MultiPageFlowRegistry || {};
|
||||||
const settingsSchemaApi = rootScope.MultiPageSettingsSchema || {};
|
const settingsSchemaApi = rootScope.MultiPageSettingsSchema || {};
|
||||||
const DEFAULT_FLOW_ID = flowRegistryApi.DEFAULT_FLOW_ID || 'openai';
|
const DEFAULT_FLOW_ID = flowRegistryApi.DEFAULT_FLOW_ID || 'openai';
|
||||||
const DEFAULT_PANEL_MODE = flowRegistryApi.DEFAULT_OPENAI_SOURCE_ID || 'cpa';
|
const DEFAULT_OPENAI_INTEGRATION_TARGET_ID = flowRegistryApi.DEFAULT_OPENAI_INTEGRATION_TARGET_ID || 'cpa';
|
||||||
const LEGACY_OPENAI_FLOW_ALIAS = String(flowRegistryApi.LEGACY_OPENAI_FLOW_ALIAS || 'codex').trim().toLowerCase();
|
|
||||||
const SIGNUP_METHOD_EMAIL = 'email';
|
const SIGNUP_METHOD_EMAIL = 'email';
|
||||||
const SIGNUP_METHOD_PHONE = 'phone';
|
const SIGNUP_METHOD_PHONE = 'phone';
|
||||||
const VALID_PANEL_MODES = Array.isArray(flowRegistryApi.OPENAI_SOURCE_IDS)
|
const VALID_OPENAI_INTEGRATION_TARGET_IDS = Array.isArray(flowRegistryApi.OPENAI_INTEGRATION_TARGET_IDS)
|
||||||
? flowRegistryApi.OPENAI_SOURCE_IDS.slice()
|
? flowRegistryApi.OPENAI_INTEGRATION_TARGET_IDS.slice()
|
||||||
: ['cpa', 'sub2api', 'codex2api'];
|
: ['cpa', 'sub2api', 'codex2api'];
|
||||||
const REGISTERED_FLOW_IDS = Array.isArray(flowRegistryApi.getRegisteredFlowIds?.())
|
const REGISTERED_FLOW_IDS = Array.isArray(flowRegistryApi.getRegisteredFlowIds?.())
|
||||||
? flowRegistryApi.getRegisteredFlowIds().map((flowId) => String(flowId || '').trim().toLowerCase()).filter(Boolean)
|
? flowRegistryApi.getRegisteredFlowIds().map((flowId) => String(flowId || '').trim().toLowerCase()).filter(Boolean)
|
||||||
@@ -23,7 +22,7 @@
|
|||||||
supportsPhoneVerificationSettings: false,
|
supportsPhoneVerificationSettings: false,
|
||||||
supportsPlusMode: false,
|
supportsPlusMode: false,
|
||||||
supportsContributionMode: false,
|
supportsContributionMode: false,
|
||||||
supportsPlatformBinding: [],
|
supportedIntegrationTargets: [],
|
||||||
supportsLuckmail: false,
|
supportsLuckmail: false,
|
||||||
supportsOauthTimeoutBudget: false,
|
supportsOauthTimeoutBudget: false,
|
||||||
canSwitchFlow: true,
|
canSwitchFlow: true,
|
||||||
@@ -48,7 +47,7 @@
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
const DEFAULT_PANEL_CAPABILITIES = Object.freeze({
|
const DEFAULT_INTEGRATION_TARGET_CAPABILITIES = Object.freeze({
|
||||||
supportsPhoneSignup: true,
|
supportsPhoneSignup: true,
|
||||||
requiresPhoneSignupWarning: false,
|
requiresPhoneSignupWarning: false,
|
||||||
});
|
});
|
||||||
@@ -61,9 +60,11 @@
|
|||||||
'plusModeEnabled',
|
'plusModeEnabled',
|
||||||
'signupMethod',
|
'signupMethod',
|
||||||
'kiroSourceId',
|
'kiroSourceId',
|
||||||
|
'openaiIntegrationTargetId',
|
||||||
|
'kiroIntegrationTargetId',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
const PANEL_CAPABILITIES = Object.freeze({
|
const OPENAI_INTEGRATION_TARGET_CAPABILITIES = Object.freeze({
|
||||||
cpa: Object.freeze({
|
cpa: Object.freeze({
|
||||||
supportsPhoneSignup: true,
|
supportsPhoneSignup: true,
|
||||||
requiresPhoneSignupWarning: true,
|
requiresPhoneSignupWarning: true,
|
||||||
@@ -88,30 +89,26 @@
|
|||||||
|
|
||||||
function normalizeCapabilityFlowId(value = '', fallback = DEFAULT_FLOW_ID) {
|
function normalizeCapabilityFlowId(value = '', fallback = DEFAULT_FLOW_ID) {
|
||||||
const normalized = String(value || '').trim().toLowerCase();
|
const normalized = String(value || '').trim().toLowerCase();
|
||||||
if (normalized === LEGACY_OPENAI_FLOW_ALIAS) {
|
|
||||||
return DEFAULT_FLOW_ID;
|
|
||||||
}
|
|
||||||
if (normalized) {
|
if (normalized) {
|
||||||
return normalized;
|
return normalized;
|
||||||
}
|
}
|
||||||
const normalizedFallback = String(fallback || '').trim().toLowerCase();
|
return normalizeFlowId(fallback, DEFAULT_FLOW_ID);
|
||||||
if (normalizedFallback === LEGACY_OPENAI_FLOW_ALIAS) {
|
|
||||||
return DEFAULT_FLOW_ID;
|
|
||||||
}
|
|
||||||
return normalizedFallback || DEFAULT_FLOW_ID;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function isRegisteredFlowId(flowId = '') {
|
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();
|
const normalized = String(value || '').trim().toLowerCase();
|
||||||
if (VALID_PANEL_MODES.includes(normalized)) {
|
if (VALID_OPENAI_INTEGRATION_TARGET_IDS.includes(normalized)) {
|
||||||
return normalized;
|
return normalized;
|
||||||
}
|
}
|
||||||
const fallbackValue = String(fallback || '').trim().toLowerCase();
|
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 = '') {
|
function normalizeSignupMethod(value = '') {
|
||||||
@@ -120,41 +117,50 @@
|
|||||||
: SIGNUP_METHOD_EMAIL;
|
: SIGNUP_METHOD_EMAIL;
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizePanelModeList(values = []) {
|
function normalizeOpenAiIntegrationTargetList(values = []) {
|
||||||
if (!Array.isArray(values)) {
|
if (!Array.isArray(values)) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
const seen = new Set();
|
const seen = new Set();
|
||||||
const normalized = [];
|
const normalized = [];
|
||||||
values.forEach((value) => {
|
values.forEach((value) => {
|
||||||
const mode = normalizePanelMode(value, '');
|
const integrationTargetId = normalizeOpenAiIntegrationTargetId(value, '');
|
||||||
if (!mode || seen.has(mode)) {
|
if (!integrationTargetId || seen.has(integrationTargetId)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
seen.add(mode);
|
seen.add(integrationTargetId);
|
||||||
normalized.push(mode);
|
normalized.push(integrationTargetId);
|
||||||
});
|
});
|
||||||
return normalized;
|
return normalized;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPanelModeLabel(panelMode = '') {
|
function getIntegrationTargetLabel(flowId = DEFAULT_FLOW_ID, integrationTargetId = '') {
|
||||||
const normalized = normalizePanelMode(panelMode);
|
if (
|
||||||
|
isRegisteredFlowId(flowId)
|
||||||
|
&& typeof flowRegistryApi.getIntegrationTargetLabel === 'function'
|
||||||
|
) {
|
||||||
|
return flowRegistryApi.getIntegrationTargetLabel(flowId, integrationTargetId);
|
||||||
|
}
|
||||||
|
const normalized = String(integrationTargetId || '').trim().toLowerCase();
|
||||||
if (normalized === 'sub2api') {
|
if (normalized === 'sub2api') {
|
||||||
return 'SUB2API';
|
return 'SUB2API';
|
||||||
}
|
}
|
||||||
if (normalized === 'codex2api') {
|
if (normalized === 'codex2api') {
|
||||||
return 'Codex2API';
|
return 'Codex2API';
|
||||||
}
|
}
|
||||||
return 'CPA';
|
if (normalized === 'cpa') {
|
||||||
|
return 'CPA';
|
||||||
|
}
|
||||||
|
return normalized || String(integrationTargetId || '').trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
function createFlowCapabilityRegistry(deps = {}) {
|
function createFlowCapabilityRegistry(deps = {}) {
|
||||||
const {
|
const {
|
||||||
defaultFlowCapabilities = DEFAULT_FLOW_CAPABILITIES,
|
defaultFlowCapabilities = DEFAULT_FLOW_CAPABILITIES,
|
||||||
defaultFlowId = DEFAULT_FLOW_ID,
|
defaultFlowId = DEFAULT_FLOW_ID,
|
||||||
defaultPanelCapabilities = DEFAULT_PANEL_CAPABILITIES,
|
defaultIntegrationTargetCapabilities = DEFAULT_INTEGRATION_TARGET_CAPABILITIES,
|
||||||
flowCapabilities = FLOW_CAPABILITIES,
|
flowCapabilities = FLOW_CAPABILITIES,
|
||||||
panelCapabilities = PANEL_CAPABILITIES,
|
integrationTargetCapabilities = OPENAI_INTEGRATION_TARGET_CAPABILITIES,
|
||||||
} = deps;
|
} = deps;
|
||||||
const settingsSchema = settingsSchemaApi.createSettingsSchema
|
const settingsSchema = settingsSchemaApi.createSettingsSchema
|
||||||
? settingsSchemaApi.createSettingsSchema({
|
? settingsSchemaApi.createSettingsSchema({
|
||||||
@@ -165,21 +171,72 @@
|
|||||||
function getFlowCapabilities(flowId) {
|
function getFlowCapabilities(flowId) {
|
||||||
const normalizedFlowId = normalizeCapabilityFlowId(flowId, defaultFlowId);
|
const normalizedFlowId = normalizeCapabilityFlowId(flowId, defaultFlowId);
|
||||||
const entry = flowCapabilities[normalizedFlowId] || null;
|
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 {
|
return {
|
||||||
...defaultFlowCapabilities,
|
...defaultFlowCapabilities,
|
||||||
...(entry || {}),
|
...(entry || {}),
|
||||||
supportsPlatformBinding: normalizePanelModeList(entry?.supportsPlatformBinding || defaultFlowCapabilities.supportsPlatformBinding),
|
supportedIntegrationTargets,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPanelCapabilities(panelMode) {
|
function getOpenAiIntegrationTargetCapabilities(integrationTargetId) {
|
||||||
const normalizedPanelMode = normalizePanelMode(panelMode);
|
const normalizedIntegrationTargetId = normalizeOpenAiIntegrationTargetId(integrationTargetId);
|
||||||
return {
|
return {
|
||||||
...defaultPanelCapabilities,
|
...defaultIntegrationTargetCapabilities,
|
||||||
...(panelCapabilities[normalizedPanelMode] || {}),
|
...(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 = []) {
|
function normalizeChangedKeys(values = []) {
|
||||||
const list = Array.isArray(values) ? values : [];
|
const list = Array.isArray(values) ? values : [];
|
||||||
const seen = new Set();
|
const seen = new Set();
|
||||||
@@ -195,28 +252,33 @@
|
|||||||
return normalized;
|
return normalized;
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveEffectiveSourceId(activeFlowId, state = {}, requestedPanelMode = DEFAULT_PANEL_MODE) {
|
function resolveEffectiveIntegrationTargetId(activeFlowId, state = {}, requestedIntegrationTargetId = DEFAULT_OPENAI_INTEGRATION_TARGET_ID) {
|
||||||
if (!isRegisteredFlowId(activeFlowId)) {
|
if (!isRegisteredFlowId(activeFlowId)) {
|
||||||
return normalizePanelMode(state?.panelMode || requestedPanelMode, requestedPanelMode || DEFAULT_PANEL_MODE);
|
return normalizeRequestedIntegrationTargetId(activeFlowId, state, {
|
||||||
|
integrationTargetId: requestedIntegrationTargetId,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if (settingsSchema?.getSelectedSourceId) {
|
if (settingsSchema?.getSelectedIntegrationTargetId) {
|
||||||
const sourceFromSchema = settingsSchema.getSelectedSourceId({
|
const integrationTargetId = settingsSchema.getSelectedIntegrationTargetId({
|
||||||
...state,
|
...state,
|
||||||
activeFlowId,
|
activeFlowId,
|
||||||
}, activeFlowId);
|
}, activeFlowId);
|
||||||
if (sourceFromSchema) {
|
if (integrationTargetId) {
|
||||||
return sourceFromSchema;
|
return integrationTargetId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (typeof flowRegistryApi.normalizeSourceId === 'function') {
|
if (typeof flowRegistryApi.normalizeIntegrationTargetId === 'function') {
|
||||||
if (activeFlowId === 'openai') {
|
return flowRegistryApi.normalizeIntegrationTargetId(
|
||||||
return flowRegistryApi.normalizeSourceId('openai', state?.panelMode || requestedPanelMode, DEFAULT_PANEL_MODE);
|
activeFlowId,
|
||||||
}
|
activeFlowId === 'openai'
|
||||||
return flowRegistryApi.normalizeSourceId(activeFlowId, state?.kiroSourceId || '', flowRegistryApi.getDefaultSourceId?.(activeFlowId));
|
? (state?.openaiIntegrationTargetId || state?.panelMode || requestedIntegrationTargetId)
|
||||||
|
: (state?.kiroIntegrationTargetId || state?.kiroSourceId || requestedIntegrationTargetId),
|
||||||
|
flowRegistryApi.getDefaultIntegrationTargetId?.(activeFlowId)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return activeFlowId === 'openai'
|
return activeFlowId === 'openai'
|
||||||
? normalizePanelMode(state?.panelMode || requestedPanelMode)
|
? normalizeOpenAiIntegrationTargetId(requestedIntegrationTargetId)
|
||||||
: '';
|
: String(requestedIntegrationTargetId || '').trim().toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
function resolveSidepanelCapabilities(options = {}) {
|
function resolveSidepanelCapabilities(options = {}) {
|
||||||
@@ -226,19 +288,25 @@
|
|||||||
defaultFlowId
|
defaultFlowId
|
||||||
);
|
);
|
||||||
const flowState = getFlowCapabilities(activeFlowId);
|
const flowState = getFlowCapabilities(activeFlowId);
|
||||||
const requestedPanelMode = normalizePanelMode(
|
const requestedIntegrationTargetId = normalizeRequestedIntegrationTargetId(
|
||||||
options?.panelMode ?? state?.panelMode,
|
activeFlowId,
|
||||||
DEFAULT_PANEL_MODE
|
state,
|
||||||
|
options
|
||||||
);
|
);
|
||||||
const supportedPanelModes = normalizePanelModeList(flowState.supportsPlatformBinding);
|
const supportedIntegrationTargets = activeFlowId === 'openai'
|
||||||
const panelModeSupported = supportedPanelModes.length === 0
|
? normalizeOpenAiIntegrationTargetList(flowState.supportedIntegrationTargets)
|
||||||
|
: (Array.isArray(flowState.supportedIntegrationTargets)
|
||||||
|
? flowState.supportedIntegrationTargets.slice()
|
||||||
|
: []);
|
||||||
|
const integrationTargetSupported = supportedIntegrationTargets.length === 0
|
||||||
? true
|
? true
|
||||||
: supportedPanelModes.includes(requestedPanelMode);
|
: supportedIntegrationTargets.includes(requestedIntegrationTargetId);
|
||||||
const effectivePanelMode = panelModeSupported
|
const effectiveIntegrationTargetId = integrationTargetSupported
|
||||||
? requestedPanelMode
|
? requestedIntegrationTargetId
|
||||||
: (supportedPanelModes[0] || requestedPanelMode);
|
: (supportedIntegrationTargets[0] || requestedIntegrationTargetId);
|
||||||
const panelState = getPanelCapabilities(effectivePanelMode);
|
const integrationTargetState = activeFlowId === 'openai'
|
||||||
const effectiveSourceId = resolveEffectiveSourceId(activeFlowId, state, effectivePanelMode);
|
? getOpenAiIntegrationTargetCapabilities(effectiveIntegrationTargetId)
|
||||||
|
: defaultIntegrationTargetCapabilities;
|
||||||
const runtimeLocks = {
|
const runtimeLocks = {
|
||||||
autoRunLocked: Boolean(options?.autoRunLocked ?? state?.autoRunLocked),
|
autoRunLocked: Boolean(options?.autoRunLocked ?? state?.autoRunLocked),
|
||||||
contributionMode: activeFlowId === 'openai' && flowState.supportsContributionMode && Boolean(state?.contributionMode),
|
contributionMode: activeFlowId === 'openai' && flowState.supportsContributionMode && Boolean(state?.contributionMode),
|
||||||
@@ -252,7 +320,7 @@
|
|||||||
}
|
}
|
||||||
const canSelectPhoneSignup = activeFlowId === 'openai'
|
const canSelectPhoneSignup = activeFlowId === 'openai'
|
||||||
&& Boolean(flowState.supportsPhoneSignup)
|
&& Boolean(flowState.supportsPhoneSignup)
|
||||||
&& Boolean(panelState.supportsPhoneSignup)
|
&& Boolean(integrationTargetState.supportsPhoneSignup)
|
||||||
&& runtimeLocks.phoneVerificationEnabled
|
&& runtimeLocks.phoneVerificationEnabled
|
||||||
&& !runtimeLocks.plusModeEnabled
|
&& !runtimeLocks.plusModeEnabled
|
||||||
&& !runtimeLocks.contributionMode;
|
&& !runtimeLocks.contributionMode;
|
||||||
@@ -272,7 +340,7 @@
|
|||||||
: effectiveSignupMethods[0]);
|
: effectiveSignupMethods[0]);
|
||||||
const visibleGroupIds = typeof flowRegistryApi.getVisibleGroupIds === 'function'
|
const visibleGroupIds = typeof flowRegistryApi.getVisibleGroupIds === 'function'
|
||||||
&& isRegisteredFlowId(activeFlowId)
|
&& isRegisteredFlowId(activeFlowId)
|
||||||
? flowRegistryApi.getVisibleGroupIds(activeFlowId, effectiveSourceId)
|
? flowRegistryApi.getVisibleGroupIds(activeFlowId, effectiveIntegrationTargetId)
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@@ -283,33 +351,38 @@
|
|||||||
canShowPlusSettings: activeFlowId === 'openai' && Boolean(flowState.supportsPlusMode),
|
canShowPlusSettings: activeFlowId === 'openai' && Boolean(flowState.supportsPlusMode),
|
||||||
canSwitchFlow: Boolean(flowState.canSwitchFlow),
|
canSwitchFlow: Boolean(flowState.canSwitchFlow),
|
||||||
canUsePhoneSignup: canSelectPhoneSignup,
|
canUsePhoneSignup: canSelectPhoneSignup,
|
||||||
canUseSelectedPanelMode: panelModeSupported,
|
canUseSelectedPanelMode: integrationTargetSupported,
|
||||||
effectivePanelMode,
|
effectiveIntegrationTargetId,
|
||||||
|
effectivePanelMode: effectiveIntegrationTargetId,
|
||||||
effectiveSignupMethod,
|
effectiveSignupMethod,
|
||||||
effectiveSignupMethods,
|
effectiveSignupMethods,
|
||||||
effectiveSourceId,
|
effectiveSourceId: effectiveIntegrationTargetId,
|
||||||
flowCapabilities: flowState,
|
flowCapabilities: flowState,
|
||||||
panelCapabilities: panelState,
|
integrationTargetCapabilities: integrationTargetState,
|
||||||
panelMode: effectivePanelMode,
|
panelCapabilities: integrationTargetState,
|
||||||
requestedPanelMode,
|
panelMode: effectiveIntegrationTargetId,
|
||||||
|
requestedIntegrationTargetId,
|
||||||
|
requestedPanelMode: requestedIntegrationTargetId,
|
||||||
requestedSignupMethod,
|
requestedSignupMethod,
|
||||||
runtimeLocks,
|
runtimeLocks,
|
||||||
shouldWarnCpaPhoneSignup: effectiveSignupMethod === SIGNUP_METHOD_PHONE
|
shouldWarnCpaPhoneSignup: effectiveSignupMethod === SIGNUP_METHOD_PHONE
|
||||||
&& Boolean(panelState.requiresPhoneSignupWarning),
|
&& Boolean(integrationTargetState.requiresPhoneSignupWarning),
|
||||||
stepDefinitionOptions: {
|
stepDefinitionOptions: {
|
||||||
activeFlowId,
|
activeFlowId,
|
||||||
panelMode: effectivePanelMode,
|
integrationTargetId: effectiveIntegrationTargetId,
|
||||||
|
panelMode: effectiveIntegrationTargetId,
|
||||||
plusModeEnabled: runtimeLocks.plusModeEnabled,
|
plusModeEnabled: runtimeLocks.plusModeEnabled,
|
||||||
signupMethod: effectiveSignupMethod,
|
signupMethod: effectiveSignupMethod,
|
||||||
},
|
},
|
||||||
supportedPanelModes,
|
supportedIntegrationTargets,
|
||||||
|
supportedPanelModes: supportedIntegrationTargets,
|
||||||
visibleGroupIds,
|
visibleGroupIds,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildPhoneSignupValidationError(capabilityState = {}) {
|
function buildPhoneSignupValidationError(capabilityState = {}) {
|
||||||
const flowState = capabilityState.flowCapabilities || {};
|
const flowState = capabilityState.flowCapabilities || {};
|
||||||
const panelState = capabilityState.panelCapabilities || {};
|
const integrationTargetState = capabilityState.integrationTargetCapabilities || {};
|
||||||
const runtimeLocks = capabilityState.runtimeLocks || {};
|
const runtimeLocks = capabilityState.runtimeLocks || {};
|
||||||
|
|
||||||
if (!flowState.supportsPhoneSignup) {
|
if (!flowState.supportsPhoneSignup) {
|
||||||
@@ -318,10 +391,10 @@
|
|||||||
message: '当前 flow 不支持手机号注册。',
|
message: '当前 flow 不支持手机号注册。',
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (!panelState.supportsPhoneSignup) {
|
if (!integrationTargetState.supportsPhoneSignup) {
|
||||||
return {
|
return {
|
||||||
code: 'phone_signup_panel_unsupported',
|
code: 'phone_signup_panel_unsupported',
|
||||||
message: `当前来源 ${getPanelModeLabel(capabilityState.requestedPanelMode)} 不支持手机号注册。`,
|
message: `当前来源 ${getIntegrationTargetLabel(capabilityState.activeFlowId, capabilityState.requestedIntegrationTargetId)} 不支持手机号注册。`,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if (!runtimeLocks.phoneVerificationEnabled) {
|
if (!runtimeLocks.phoneVerificationEnabled) {
|
||||||
@@ -354,13 +427,13 @@
|
|||||||
const errors = [];
|
const errors = [];
|
||||||
|
|
||||||
if (
|
if (
|
||||||
Array.isArray(capabilityState.supportedPanelModes)
|
Array.isArray(capabilityState.supportedIntegrationTargets)
|
||||||
&& capabilityState.supportedPanelModes.length > 0
|
&& capabilityState.supportedIntegrationTargets.length > 0
|
||||||
&& capabilityState.canUseSelectedPanelMode === false
|
&& capabilityState.canUseSelectedPanelMode === false
|
||||||
) {
|
) {
|
||||||
errors.push({
|
errors.push({
|
||||||
code: 'panel_mode_unsupported',
|
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));
|
const shouldReconcileSignupMethod = MODE_SWITCH_RELEVANT_KEYS.some((key) => changedKeySet.has(key));
|
||||||
|
|
||||||
if (
|
if (
|
||||||
changedKeySet.has('panelMode')
|
(changedKeySet.has('panelMode') || changedKeySet.has('openaiIntegrationTargetId') || changedKeySet.has('kiroIntegrationTargetId'))
|
||||||
&& Array.isArray(capabilityState.supportedPanelModes)
|
&& Array.isArray(capabilityState.supportedIntegrationTargets)
|
||||||
&& capabilityState.supportedPanelModes.length > 0
|
&& capabilityState.supportedIntegrationTargets.length > 0
|
||||||
&& capabilityState.canUseSelectedPanelMode === false
|
&& 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({
|
errors.push({
|
||||||
code: 'panel_mode_unsupported',
|
code: 'panel_mode_unsupported',
|
||||||
message: `当前 flow 不支持 ${getPanelModeLabel(capabilityState.requestedPanelMode)} 来源。`,
|
message: `当前 flow 不支持 ${getIntegrationTargetLabel(capabilityState.activeFlowId, capabilityState.requestedIntegrationTargetId)} 来源。`,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -480,9 +556,9 @@
|
|||||||
return {
|
return {
|
||||||
canUsePhoneSignup,
|
canUsePhoneSignup,
|
||||||
getFlowCapabilities,
|
getFlowCapabilities,
|
||||||
getPanelCapabilities,
|
getOpenAiIntegrationTargetCapabilities,
|
||||||
normalizeFlowId,
|
normalizeFlowId,
|
||||||
normalizePanelMode,
|
normalizeOpenAiIntegrationTargetId,
|
||||||
normalizeSignupMethod,
|
normalizeSignupMethod,
|
||||||
resolveSidepanelCapabilities,
|
resolveSidepanelCapabilities,
|
||||||
resolveSignupMethod,
|
resolveSignupMethod,
|
||||||
@@ -495,15 +571,15 @@
|
|||||||
createFlowCapabilityRegistry,
|
createFlowCapabilityRegistry,
|
||||||
DEFAULT_FLOW_CAPABILITIES,
|
DEFAULT_FLOW_CAPABILITIES,
|
||||||
DEFAULT_FLOW_ID,
|
DEFAULT_FLOW_ID,
|
||||||
DEFAULT_PANEL_CAPABILITIES,
|
DEFAULT_INTEGRATION_TARGET_CAPABILITIES,
|
||||||
DEFAULT_PANEL_MODE,
|
DEFAULT_OPENAI_INTEGRATION_TARGET_ID,
|
||||||
FLOW_CAPABILITIES,
|
FLOW_CAPABILITIES,
|
||||||
PANEL_CAPABILITIES,
|
OPENAI_INTEGRATION_TARGET_CAPABILITIES,
|
||||||
SIGNUP_METHOD_EMAIL,
|
SIGNUP_METHOD_EMAIL,
|
||||||
SIGNUP_METHOD_PHONE,
|
SIGNUP_METHOD_PHONE,
|
||||||
VALID_PANEL_MODES,
|
VALID_OPENAI_INTEGRATION_TARGET_IDS,
|
||||||
normalizeFlowId,
|
normalizeFlowId,
|
||||||
normalizePanelMode,
|
normalizeOpenAiIntegrationTargetId,
|
||||||
normalizeSignupMethod,
|
normalizeSignupMethod,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
+105
-81
@@ -2,11 +2,11 @@
|
|||||||
root.MultiPageFlowRegistry = factory();
|
root.MultiPageFlowRegistry = factory();
|
||||||
})(typeof self !== 'undefined' ? self : globalThis, function createFlowRegistryModule() {
|
})(typeof self !== 'undefined' ? self : globalThis, function createFlowRegistryModule() {
|
||||||
const DEFAULT_FLOW_ID = 'openai';
|
const DEFAULT_FLOW_ID = 'openai';
|
||||||
const LEGACY_OPENAI_FLOW_ALIAS = 'codex';
|
const DEFAULT_OPENAI_INTEGRATION_TARGET_ID = 'cpa';
|
||||||
const DEFAULT_OPENAI_SOURCE_ID = 'cpa';
|
const DEFAULT_KIRO_INTEGRATION_TARGET_ID = 'kiro-rs';
|
||||||
const DEFAULT_KIRO_SOURCE_ID = 'kiro-rs';
|
const DEFAULT_KIRO_PUBLICATION_TARGET_ID = 'kiro-rs';
|
||||||
const DEFAULT_KIRO_RS_URL = 'https://kiro.leftcode.xyz/admin';
|
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 SHARED_SERVICE_IDS = Object.freeze(['account', 'email', 'proxy']);
|
||||||
|
|
||||||
const DEFAULT_FLOW_CAPABILITIES = Object.freeze({
|
const DEFAULT_FLOW_CAPABILITIES = Object.freeze({
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
supportsPhoneVerificationSettings: false,
|
supportsPhoneVerificationSettings: false,
|
||||||
supportsPlusMode: false,
|
supportsPlusMode: false,
|
||||||
supportsContributionMode: false,
|
supportsContributionMode: false,
|
||||||
supportsPlatformBinding: [],
|
supportedIntegrationTargets: [],
|
||||||
supportsLuckmail: false,
|
supportsLuckmail: false,
|
||||||
supportsOauthTimeoutBudget: false,
|
supportsOauthTimeoutBudget: false,
|
||||||
canSwitchFlow: true,
|
canSwitchFlow: true,
|
||||||
@@ -44,7 +44,7 @@
|
|||||||
supportsPhoneVerificationSettings: true,
|
supportsPhoneVerificationSettings: true,
|
||||||
supportsPlusMode: true,
|
supportsPlusMode: true,
|
||||||
supportsContributionMode: true,
|
supportsContributionMode: true,
|
||||||
supportsPlatformBinding: [...OPENAI_SOURCE_IDS],
|
supportedIntegrationTargets: [...OPENAI_INTEGRATION_TARGET_IDS],
|
||||||
supportsLuckmail: true,
|
supportsLuckmail: true,
|
||||||
supportsOauthTimeoutBudget: true,
|
supportsOauthTimeoutBudget: true,
|
||||||
stepDefinitionMode: 'openai-dynamic',
|
stepDefinitionMode: 'openai-dynamic',
|
||||||
@@ -55,24 +55,21 @@
|
|||||||
'openai-oauth',
|
'openai-oauth',
|
||||||
'openai-step6',
|
'openai-step6',
|
||||||
],
|
],
|
||||||
sources: {
|
integrationTargets: {
|
||||||
cpa: {
|
cpa: {
|
||||||
id: 'cpa',
|
id: 'cpa',
|
||||||
label: 'CPA 面板',
|
label: 'CPA 面板',
|
||||||
legacyPanelMode: 'cpa',
|
groups: ['openai-target-cpa'],
|
||||||
groups: ['openai-source-cpa'],
|
|
||||||
},
|
},
|
||||||
sub2api: {
|
sub2api: {
|
||||||
id: 'sub2api',
|
id: 'sub2api',
|
||||||
label: 'SUB2API',
|
label: 'SUB2API',
|
||||||
legacyPanelMode: 'sub2api',
|
groups: ['openai-target-sub2api'],
|
||||||
groups: ['openai-source-sub2api'],
|
|
||||||
},
|
},
|
||||||
codex2api: {
|
codex2api: {
|
||||||
id: 'codex2api',
|
id: 'codex2api',
|
||||||
label: 'Codex2API',
|
label: 'Codex2API',
|
||||||
legacyPanelMode: 'codex2api',
|
groups: ['openai-target-codex2api'],
|
||||||
groups: ['openai-source-codex2api'],
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
runtimeSources: {
|
runtimeSources: {
|
||||||
@@ -206,16 +203,23 @@
|
|||||||
services: ['account', 'email', 'proxy'],
|
services: ['account', 'email', 'proxy'],
|
||||||
capabilities: {
|
capabilities: {
|
||||||
...DEFAULT_FLOW_CAPABILITIES,
|
...DEFAULT_FLOW_CAPABILITIES,
|
||||||
|
supportedIntegrationTargets: [DEFAULT_KIRO_INTEGRATION_TARGET_ID],
|
||||||
stepDefinitionMode: 'kiro-device-auth',
|
stepDefinitionMode: 'kiro-device-auth',
|
||||||
},
|
},
|
||||||
baseGroups: [
|
baseGroups: [
|
||||||
'kiro-runtime-status',
|
'kiro-runtime-status',
|
||||||
],
|
],
|
||||||
sources: {
|
integrationTargets: {
|
||||||
|
'kiro-rs': {
|
||||||
|
id: 'kiro-rs',
|
||||||
|
label: 'kiro.rs',
|
||||||
|
groups: ['kiro-target-kiro-rs'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
publicationTargets: {
|
||||||
'kiro-rs': {
|
'kiro-rs': {
|
||||||
id: 'kiro-rs',
|
id: 'kiro-rs',
|
||||||
label: 'kiro.rs',
|
label: 'kiro.rs',
|
||||||
groups: ['kiro-source-kiro-rs'],
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
runtimeSources: {
|
runtimeSources: {
|
||||||
@@ -280,13 +284,13 @@
|
|||||||
label: 'IP 代理',
|
label: 'IP 代理',
|
||||||
sectionIds: ['ip-proxy-section'],
|
sectionIds: ['ip-proxy-section'],
|
||||||
},
|
},
|
||||||
'openai-source-cpa': {
|
'openai-target-cpa': {
|
||||||
id: 'openai-source-cpa',
|
id: 'openai-target-cpa',
|
||||||
label: 'CPA 来源',
|
label: 'CPA 来源',
|
||||||
rowIds: ['row-vps-url', 'row-vps-password', 'row-local-cpa-step9-mode'],
|
rowIds: ['row-vps-url', 'row-vps-password', 'row-local-cpa-step9-mode'],
|
||||||
},
|
},
|
||||||
'openai-source-sub2api': {
|
'openai-target-sub2api': {
|
||||||
id: 'openai-source-sub2api',
|
id: 'openai-target-sub2api',
|
||||||
label: 'SUB2API 来源',
|
label: 'SUB2API 来源',
|
||||||
rowIds: [
|
rowIds: [
|
||||||
'row-sub2api-url',
|
'row-sub2api-url',
|
||||||
@@ -297,17 +301,15 @@
|
|||||||
'row-sub2api-default-proxy',
|
'row-sub2api-default-proxy',
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
'openai-source-codex2api': {
|
'openai-target-codex2api': {
|
||||||
id: 'openai-source-codex2api',
|
id: 'openai-target-codex2api',
|
||||||
label: 'Codex2API 来源',
|
label: 'Codex2API 来源',
|
||||||
rowIds: ['row-codex2api-url', 'row-codex2api-admin-key'],
|
rowIds: ['row-codex2api-url', 'row-codex2api-admin-key'],
|
||||||
},
|
},
|
||||||
'openai-plus': {
|
'openai-plus': {
|
||||||
id: 'openai-plus',
|
id: 'openai-plus',
|
||||||
label: 'Plus',
|
label: 'Plus',
|
||||||
rowIds: [
|
rowIds: ['row-plus-mode'],
|
||||||
'row-plus-mode',
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
'openai-phone': {
|
'openai-phone': {
|
||||||
id: 'openai-phone',
|
id: 'openai-phone',
|
||||||
@@ -325,8 +327,8 @@
|
|||||||
label: '第六步',
|
label: '第六步',
|
||||||
rowIds: ['row-step6-cookie-settings'],
|
rowIds: ['row-step6-cookie-settings'],
|
||||||
},
|
},
|
||||||
'kiro-source-kiro-rs': {
|
'kiro-target-kiro-rs': {
|
||||||
id: 'kiro-source-kiro-rs',
|
id: 'kiro-target-kiro-rs',
|
||||||
label: 'kiro.rs 配置',
|
label: 'kiro.rs 配置',
|
||||||
rowIds: ['row-kiro-rs-url', 'row-kiro-rs-key'],
|
rowIds: ['row-kiro-rs-url', 'row-kiro-rs-key'],
|
||||||
},
|
},
|
||||||
@@ -339,16 +341,10 @@
|
|||||||
|
|
||||||
function normalizeFlowId(value = '', fallback = DEFAULT_FLOW_ID) {
|
function normalizeFlowId(value = '', fallback = DEFAULT_FLOW_ID) {
|
||||||
const normalized = String(value || '').trim().toLowerCase();
|
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)) {
|
if (normalized && Object.prototype.hasOwnProperty.call(FLOW_DEFINITIONS, normalized)) {
|
||||||
return normalized;
|
return normalized;
|
||||||
}
|
}
|
||||||
const fallbackValue = String(fallback || '').trim().toLowerCase();
|
const fallbackValue = String(fallback || '').trim().toLowerCase();
|
||||||
if (fallbackValue === LEGACY_OPENAI_FLOW_ALIAS) {
|
|
||||||
return DEFAULT_FLOW_ID;
|
|
||||||
}
|
|
||||||
return Object.prototype.hasOwnProperty.call(FLOW_DEFINITIONS, fallbackValue)
|
return Object.prototype.hasOwnProperty.call(FLOW_DEFINITIONS, fallbackValue)
|
||||||
? fallbackValue
|
? fallbackValue
|
||||||
: DEFAULT_FLOW_ID;
|
: DEFAULT_FLOW_ID;
|
||||||
@@ -359,73 +355,92 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getFlowDefinition(flowId) {
|
function getFlowDefinition(flowId) {
|
||||||
const normalizedFlowId = normalizeFlowId(flowId);
|
return FLOW_DEFINITIONS[normalizeFlowId(flowId)] || FLOW_DEFINITIONS[DEFAULT_FLOW_ID];
|
||||||
return FLOW_DEFINITIONS[normalizedFlowId] || FLOW_DEFINITIONS[DEFAULT_FLOW_ID];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFlowLabel(flowId) {
|
function getFlowLabel(flowId) {
|
||||||
return getFlowDefinition(flowId)?.label || normalizeFlowId(flowId);
|
return getFlowDefinition(flowId)?.label || normalizeFlowId(flowId);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getDefaultSourceId(flowId) {
|
function getDefaultIntegrationTargetId(flowId) {
|
||||||
return normalizeFlowId(flowId) === 'kiro'
|
return normalizeFlowId(flowId) === 'kiro'
|
||||||
? DEFAULT_KIRO_SOURCE_ID
|
? DEFAULT_KIRO_INTEGRATION_TARGET_ID
|
||||||
: DEFAULT_OPENAI_SOURCE_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();
|
const normalized = String(value || '').trim().toLowerCase();
|
||||||
if (OPENAI_SOURCE_IDS.includes(normalized)) {
|
if (OPENAI_INTEGRATION_TARGET_IDS.includes(normalized)) {
|
||||||
return normalized;
|
return normalized;
|
||||||
}
|
}
|
||||||
const fallbackValue = String(fallback || '').trim().toLowerCase();
|
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();
|
const normalized = String(value || '').trim().toLowerCase();
|
||||||
if (normalized === DEFAULT_KIRO_SOURCE_ID) {
|
if (normalized === DEFAULT_KIRO_INTEGRATION_TARGET_ID) {
|
||||||
return normalized;
|
return normalized;
|
||||||
}
|
}
|
||||||
const fallbackValue = String(fallback || '').trim().toLowerCase();
|
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);
|
const normalizedFlowId = normalizeFlowId(flowId);
|
||||||
if (normalizedFlowId === 'kiro') {
|
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) {
|
function getIntegrationTargetDefinitions(flowId) {
|
||||||
return getFlowDefinition(flowId)?.sources || {};
|
return getFlowDefinition(flowId)?.integrationTargets || {};
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSourceDefinition(flowId, sourceId) {
|
function getIntegrationTargetDefinition(flowId, integrationTargetId) {
|
||||||
const normalizedFlowId = normalizeFlowId(flowId);
|
const normalizedFlowId = normalizeFlowId(flowId);
|
||||||
const normalizedSourceId = normalizeSourceId(normalizedFlowId, sourceId, getDefaultSourceId(normalizedFlowId));
|
const normalizedIntegrationTargetId = normalizeIntegrationTargetId(
|
||||||
return getSourceDefinitions(normalizedFlowId)[normalizedSourceId] || null;
|
normalizedFlowId,
|
||||||
|
integrationTargetId,
|
||||||
|
getDefaultIntegrationTargetId(normalizedFlowId)
|
||||||
|
);
|
||||||
|
return getIntegrationTargetDefinitions(normalizedFlowId)[normalizedIntegrationTargetId] || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSourceOptions(flowId) {
|
function getIntegrationTargetOptions(flowId) {
|
||||||
return Object.values(getSourceDefinitions(flowId));
|
return Object.values(getIntegrationTargetDefinitions(flowId));
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSourceLabel(flowId, sourceId) {
|
function getIntegrationTargetLabel(flowId, integrationTargetId) {
|
||||||
return getSourceDefinition(flowId, sourceId)?.label || normalizeSourceId(flowId, sourceId);
|
return getIntegrationTargetDefinition(flowId, integrationTargetId)?.label
|
||||||
|
|| normalizeIntegrationTargetId(flowId, integrationTargetId);
|
||||||
}
|
}
|
||||||
|
|
||||||
function mapPanelModeToSourceId(panelMode = '', fallback = DEFAULT_OPENAI_SOURCE_ID) {
|
function getPublicationTargetDefinitions(flowId) {
|
||||||
return normalizeOpenAiSourceId(panelMode, fallback);
|
return getFlowDefinition(flowId)?.publicationTargets || {};
|
||||||
}
|
}
|
||||||
|
|
||||||
function mapSourceIdToPanelMode(flowId, sourceId = '', fallback = DEFAULT_OPENAI_SOURCE_ID) {
|
function getPublicationTargetDefinition(flowId, publicationTargetId) {
|
||||||
if (normalizeFlowId(flowId) !== DEFAULT_FLOW_ID) {
|
const normalizedFlowId = normalizeFlowId(flowId);
|
||||||
return normalizeOpenAiSourceId(fallback, DEFAULT_OPENAI_SOURCE_ID);
|
const normalizedPublicationTargetId = String(
|
||||||
}
|
publicationTargetId || (
|
||||||
return normalizeOpenAiSourceId(sourceId, fallback || DEFAULT_OPENAI_SOURCE_ID);
|
normalizedFlowId === 'kiro'
|
||||||
|
? DEFAULT_KIRO_PUBLICATION_TARGET_ID
|
||||||
|
: ''
|
||||||
|
)
|
||||||
|
).trim().toLowerCase();
|
||||||
|
return getPublicationTargetDefinitions(normalizedFlowId)[normalizedPublicationTargetId] || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFlowCapabilities(flowId) {
|
function getFlowCapabilities(flowId) {
|
||||||
@@ -435,18 +450,27 @@
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function getVisibleGroupIds(flowId, sourceId, options = {}) {
|
function getVisibleGroupIds(flowId, integrationTargetId, options = {}) {
|
||||||
const normalizedFlowId = normalizeFlowId(flowId);
|
const normalizedFlowId = normalizeFlowId(flowId);
|
||||||
const flowDefinition = getFlowDefinition(normalizedFlowId);
|
const flowDefinition = getFlowDefinition(normalizedFlowId);
|
||||||
const normalizedSourceId = normalizeSourceId(normalizedFlowId, sourceId, getDefaultSourceId(normalizedFlowId));
|
const normalizedIntegrationTargetId = normalizeIntegrationTargetId(
|
||||||
const sourceDefinition = getSourceDefinition(normalizedFlowId, normalizedSourceId);
|
normalizedFlowId,
|
||||||
|
integrationTargetId,
|
||||||
|
getDefaultIntegrationTargetId(normalizedFlowId)
|
||||||
|
);
|
||||||
|
const integrationTargetDefinition = getIntegrationTargetDefinition(
|
||||||
|
normalizedFlowId,
|
||||||
|
normalizedIntegrationTargetId
|
||||||
|
);
|
||||||
const includeSharedServices = options?.includeSharedServices !== false;
|
const includeSharedServices = options?.includeSharedServices !== false;
|
||||||
const serviceGroups = includeSharedServices
|
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([
|
return Array.from(new Set([
|
||||||
...(Array.isArray(flowDefinition?.baseGroups) ? flowDefinition.baseGroups : []),
|
...(Array.isArray(flowDefinition?.baseGroups) ? flowDefinition.baseGroups : []),
|
||||||
...(Array.isArray(sourceDefinition?.groups) ? sourceDefinition.groups : []),
|
...(Array.isArray(integrationTargetDefinition?.groups) ? integrationTargetDefinition.groups : []),
|
||||||
...serviceGroups,
|
...serviceGroups,
|
||||||
]));
|
]));
|
||||||
}
|
}
|
||||||
@@ -479,33 +503,33 @@
|
|||||||
return {
|
return {
|
||||||
DEFAULT_FLOW_CAPABILITIES,
|
DEFAULT_FLOW_CAPABILITIES,
|
||||||
DEFAULT_FLOW_ID,
|
DEFAULT_FLOW_ID,
|
||||||
|
DEFAULT_KIRO_INTEGRATION_TARGET_ID,
|
||||||
|
DEFAULT_KIRO_PUBLICATION_TARGET_ID,
|
||||||
DEFAULT_KIRO_RS_URL,
|
DEFAULT_KIRO_RS_URL,
|
||||||
DEFAULT_KIRO_SOURCE_ID,
|
DEFAULT_OPENAI_INTEGRATION_TARGET_ID,
|
||||||
DEFAULT_OPENAI_SOURCE_ID,
|
|
||||||
FLOW_DEFINITIONS,
|
FLOW_DEFINITIONS,
|
||||||
LEGACY_OPENAI_FLOW_ALIAS,
|
OPENAI_INTEGRATION_TARGET_IDS,
|
||||||
OPENAI_SOURCE_IDS,
|
|
||||||
SETTINGS_GROUP_DEFINITIONS,
|
SETTINGS_GROUP_DEFINITIONS,
|
||||||
SHARED_SERVICE_IDS,
|
SHARED_SERVICE_IDS,
|
||||||
getDefaultSourceId,
|
getDefaultIntegrationTargetId,
|
||||||
getDriverDefinitions,
|
getDriverDefinitions,
|
||||||
getFlowCapabilities,
|
getFlowCapabilities,
|
||||||
getFlowDefinition,
|
getFlowDefinition,
|
||||||
getFlowLabel,
|
getFlowLabel,
|
||||||
|
getIntegrationTargetDefinition,
|
||||||
|
getIntegrationTargetDefinitions,
|
||||||
|
getIntegrationTargetLabel,
|
||||||
|
getIntegrationTargetOptions,
|
||||||
|
getPublicationTargetDefinition,
|
||||||
|
getPublicationTargetDefinitions,
|
||||||
getRegisteredFlowIds,
|
getRegisteredFlowIds,
|
||||||
getRuntimeSourceDefinitions,
|
getRuntimeSourceDefinitions,
|
||||||
getSettingsGroupDefinition,
|
getSettingsGroupDefinition,
|
||||||
getSettingsGroupDefinitions,
|
getSettingsGroupDefinitions,
|
||||||
getSourceDefinition,
|
|
||||||
getSourceDefinitions,
|
|
||||||
getSourceLabel,
|
|
||||||
getSourceOptions,
|
|
||||||
getVisibleGroupIds,
|
getVisibleGroupIds,
|
||||||
mapPanelModeToSourceId,
|
|
||||||
mapSourceIdToPanelMode,
|
|
||||||
normalizeFlowId,
|
normalizeFlowId,
|
||||||
normalizeKiroSourceId,
|
normalizeIntegrationTargetId,
|
||||||
normalizeOpenAiSourceId,
|
normalizeKiroIntegrationTargetId,
|
||||||
normalizeSourceId,
|
normalizeOpenAiIntegrationTargetId,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
+233
-188
@@ -32,9 +32,11 @@
|
|||||||
function createSettingsSchema(deps = {}) {
|
function createSettingsSchema(deps = {}) {
|
||||||
const rootScope = typeof self !== 'undefined' ? self : globalThis;
|
const rootScope = typeof self !== 'undefined' ? self : globalThis;
|
||||||
const flowRegistry = deps.flowRegistry || rootScope.MultiPageFlowRegistry || {};
|
const flowRegistry = deps.flowRegistry || rootScope.MultiPageFlowRegistry || {};
|
||||||
const defaultFlowId = String(deps.defaultFlowId || flowRegistry.DEFAULT_FLOW_ID || 'openai').trim().toLowerCase() || 'openai';
|
const defaultFlowId = String(
|
||||||
const defaultOpenAiSourceId = flowRegistry.DEFAULT_OPENAI_SOURCE_ID || 'cpa';
|
deps.defaultFlowId || flowRegistry.DEFAULT_FLOW_ID || 'openai'
|
||||||
const defaultKiroSourceId = flowRegistry.DEFAULT_KIRO_SOURCE_ID || 'kiro-rs';
|
).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 defaultKiroRsUrl = flowRegistry.DEFAULT_KIRO_RS_URL || 'https://kiro.leftcode.xyz/admin';
|
||||||
const normalizeFlowId = typeof flowRegistry.normalizeFlowId === 'function'
|
const normalizeFlowId = typeof flowRegistry.normalizeFlowId === 'function'
|
||||||
? flowRegistry.normalizeFlowId
|
? flowRegistry.normalizeFlowId
|
||||||
@@ -42,19 +44,13 @@
|
|||||||
const normalized = String(value || '').trim().toLowerCase();
|
const normalized = String(value || '').trim().toLowerCase();
|
||||||
return normalized || String(fallback || '').trim().toLowerCase() || defaultFlowId;
|
return normalized || String(fallback || '').trim().toLowerCase() || defaultFlowId;
|
||||||
});
|
});
|
||||||
const normalizeSourceId = typeof flowRegistry.normalizeSourceId === 'function'
|
const normalizeIntegrationTargetId = typeof flowRegistry.normalizeIntegrationTargetId === 'function'
|
||||||
? flowRegistry.normalizeSourceId
|
? flowRegistry.normalizeIntegrationTargetId
|
||||||
: ((flowId, value = '', fallback = '') => String(value || fallback || '').trim().toLowerCase());
|
: ((_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());
|
|
||||||
|
|
||||||
function buildDefaultSettingsState() {
|
function buildDefaultSettingsState() {
|
||||||
return {
|
return {
|
||||||
schemaVersion: 3,
|
schemaVersion: 4,
|
||||||
activeFlowId: defaultFlowId,
|
activeFlowId: defaultFlowId,
|
||||||
services: {
|
services: {
|
||||||
account: {
|
account: {
|
||||||
@@ -71,27 +67,25 @@
|
|||||||
},
|
},
|
||||||
flows: {
|
flows: {
|
||||||
openai: {
|
openai: {
|
||||||
source: {
|
integrationTargetId: defaultOpenAiIntegrationTargetId,
|
||||||
selected: defaultOpenAiSourceId,
|
integrationTargets: {
|
||||||
entries: {
|
cpa: {
|
||||||
cpa: {
|
vpsUrl: '',
|
||||||
vpsUrl: '',
|
vpsPassword: '',
|
||||||
vpsPassword: '',
|
localCpaStep9Mode: 'submit',
|
||||||
localCpaStep9Mode: 'submit',
|
},
|
||||||
},
|
sub2api: {
|
||||||
sub2api: {
|
sub2apiUrl: '',
|
||||||
sub2apiUrl: '',
|
sub2apiEmail: '',
|
||||||
sub2apiEmail: '',
|
sub2apiPassword: '',
|
||||||
sub2apiPassword: '',
|
sub2apiGroupName: 'codex',
|
||||||
sub2apiGroupName: 'codex',
|
sub2apiGroupNames: ['codex', 'openai-plus'],
|
||||||
sub2apiGroupNames: ['codex', 'openai-plus'],
|
sub2apiAccountPriority: 1,
|
||||||
sub2apiAccountPriority: 1,
|
sub2apiDefaultProxyName: '',
|
||||||
sub2apiDefaultProxyName: '',
|
},
|
||||||
},
|
codex2api: {
|
||||||
codex2api: {
|
codex2apiUrl: '',
|
||||||
codex2apiUrl: '',
|
codex2apiAdminKey: '',
|
||||||
codex2apiAdminKey: '',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
signup: {
|
signup: {
|
||||||
@@ -112,21 +106,13 @@
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
kiro: {
|
kiro: {
|
||||||
source: {
|
integrationTargetId: defaultKiroIntegrationTargetId,
|
||||||
selected: defaultKiroSourceId,
|
integrationTargets: {
|
||||||
entries: {
|
'kiro-rs': {
|
||||||
'kiro-rs': {
|
baseUrl: defaultKiroRsUrl,
|
||||||
kiroRsUrl: defaultKiroRsUrl,
|
apiKey: '',
|
||||||
kiroRsKey: '',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
options: {
|
|
||||||
kiroRsPriority: 0,
|
|
||||||
kiroRsEndpoint: '',
|
|
||||||
kiroRsAuthRegion: '',
|
|
||||||
kiroRsApiRegion: '',
|
|
||||||
},
|
|
||||||
autoRun: {
|
autoRun: {
|
||||||
stepExecutionRange: {
|
stepExecutionRange: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
@@ -139,7 +125,7 @@
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSourceValue(settingsState, pathGetter, fallback = {}) {
|
function getIntegrationTargetValue(settingsState, pathGetter, fallback = {}) {
|
||||||
return cloneValue(pathGetter(isPlainObject(settingsState) ? settingsState : {}) || fallback);
|
return cloneValue(pathGetter(isPlainObject(settingsState) ? settingsState : {}) || fallback);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -155,20 +141,21 @@
|
|||||||
?? defaults.activeFlowId,
|
?? defaults.activeFlowId,
|
||||||
defaults.activeFlowId
|
defaults.activeFlowId
|
||||||
);
|
);
|
||||||
const openaiSelectedSource = normalizeSourceId(
|
const openaiIntegrationTargetId = normalizeIntegrationTargetId(
|
||||||
'openai',
|
'openai',
|
||||||
nested?.flows?.openai?.source?.selected
|
nested?.flows?.openai?.integrationTargetId
|
||||||
|
?? input?.openaiIntegrationTargetId
|
||||||
?? input?.panelMode
|
?? input?.panelMode
|
||||||
?? input?.openaiSourceId
|
?? defaults.flows.openai.integrationTargetId,
|
||||||
?? defaults.flows.openai.source.selected,
|
defaults.flows.openai.integrationTargetId
|
||||||
defaults.flows.openai.source.selected
|
|
||||||
);
|
);
|
||||||
const kiroSelectedSource = normalizeSourceId(
|
const kiroIntegrationTargetId = normalizeIntegrationTargetId(
|
||||||
'kiro',
|
'kiro',
|
||||||
nested?.flows?.kiro?.source?.selected
|
nested?.flows?.kiro?.integrationTargetId
|
||||||
|
?? input?.kiroIntegrationTargetId
|
||||||
?? input?.kiroSourceId
|
?? input?.kiroSourceId
|
||||||
?? defaults.flows.kiro.source.selected,
|
?? defaults.flows.kiro.integrationTargetId,
|
||||||
defaults.flows.kiro.source.selected
|
defaults.flows.kiro.integrationTargetId
|
||||||
);
|
);
|
||||||
const stepExecutionRangeByFlow = isPlainObject(input?.stepExecutionRangeByFlow)
|
const stepExecutionRangeByFlow = isPlainObject(input?.stepExecutionRangeByFlow)
|
||||||
? input.stepExecutionRangeByFlow
|
? input.stepExecutionRangeByFlow
|
||||||
@@ -206,58 +193,115 @@
|
|||||||
customPassword: String(
|
customPassword: String(
|
||||||
input?.customPassword
|
input?.customPassword
|
||||||
?? nested?.services?.account?.customPassword
|
?? nested?.services?.account?.customPassword
|
||||||
?? nested?.flows?.openai?.account?.customPassword
|
|
||||||
?? defaults.services.account.customPassword
|
?? defaults.services.account.customPassword
|
||||||
).trim(),
|
).trim(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
flows: {
|
flows: {
|
||||||
openai: {
|
openai: {
|
||||||
source: {
|
integrationTargetId: openaiIntegrationTargetId,
|
||||||
selected: openaiSelectedSource,
|
integrationTargets: {
|
||||||
entries: {
|
cpa: {
|
||||||
cpa: {
|
...defaults.flows.openai.integrationTargets.cpa,
|
||||||
...defaults.flows.openai.source.entries.cpa,
|
...getIntegrationTargetValue(nested, (state) => state.flows?.openai?.integrationTargets?.cpa),
|
||||||
...getSourceValue(nested, (state) => state.flows?.openai?.source?.entries?.cpa),
|
vpsUrl: String(
|
||||||
vpsUrl: String(input?.vpsUrl ?? nested?.flows?.openai?.source?.entries?.cpa?.vpsUrl ?? '').trim(),
|
input?.vpsUrl
|
||||||
vpsPassword: String(input?.vpsPassword ?? nested?.flows?.openai?.source?.entries?.cpa?.vpsPassword ?? ''),
|
?? nested?.flows?.openai?.integrationTargets?.cpa?.vpsUrl
|
||||||
localCpaStep9Mode: String(
|
?? ''
|
||||||
input?.localCpaStep9Mode
|
).trim(),
|
||||||
?? nested?.flows?.openai?.source?.entries?.cpa?.localCpaStep9Mode
|
vpsPassword: String(
|
||||||
?? defaults.flows.openai.source.entries.cpa.localCpaStep9Mode
|
input?.vpsPassword
|
||||||
).trim() || defaults.flows.openai.source.entries.cpa.localCpaStep9Mode,
|
?? nested?.flows?.openai?.integrationTargets?.cpa?.vpsPassword
|
||||||
},
|
?? ''
|
||||||
sub2api: {
|
),
|
||||||
...defaults.flows.openai.source.entries.sub2api,
|
localCpaStep9Mode: String(
|
||||||
...getSourceValue(nested, (state) => state.flows?.openai?.source?.entries?.sub2api),
|
input?.localCpaStep9Mode
|
||||||
sub2apiUrl: String(input?.sub2apiUrl ?? nested?.flows?.openai?.source?.entries?.sub2api?.sub2apiUrl ?? '').trim(),
|
?? nested?.flows?.openai?.integrationTargets?.cpa?.localCpaStep9Mode
|
||||||
sub2apiEmail: String(input?.sub2apiEmail ?? nested?.flows?.openai?.source?.entries?.sub2api?.sub2apiEmail ?? '').trim(),
|
?? defaults.flows.openai.integrationTargets.cpa.localCpaStep9Mode
|
||||||
sub2apiPassword: String(input?.sub2apiPassword ?? nested?.flows?.openai?.source?.entries?.sub2api?.sub2apiPassword ?? ''),
|
).trim() || defaults.flows.openai.integrationTargets.cpa.localCpaStep9Mode,
|
||||||
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)
|
sub2api: {
|
||||||
? input.sub2apiGroupNames.map((entry) => String(entry || '').trim()).filter(Boolean)
|
...defaults.flows.openai.integrationTargets.sub2api,
|
||||||
: (Array.isArray(nested?.flows?.openai?.source?.entries?.sub2api?.sub2apiGroupNames)
|
...getIntegrationTargetValue(nested, (state) => state.flows?.openai?.integrationTargets?.sub2api),
|
||||||
? nested.flows.openai.source.entries.sub2api.sub2apiGroupNames.map((entry) => String(entry || '').trim()).filter(Boolean)
|
sub2apiUrl: String(
|
||||||
: [...defaults.flows.openai.source.entries.sub2api.sub2apiGroupNames]),
|
input?.sub2apiUrl
|
||||||
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),
|
?? nested?.flows?.openai?.integrationTargets?.sub2api?.sub2apiUrl
|
||||||
sub2apiDefaultProxyName: String(input?.sub2apiDefaultProxyName ?? nested?.flows?.openai?.source?.entries?.sub2api?.sub2apiDefaultProxyName ?? '').trim(),
|
?? ''
|
||||||
},
|
).trim(),
|
||||||
codex2api: {
|
sub2apiEmail: String(
|
||||||
...defaults.flows.openai.source.entries.codex2api,
|
input?.sub2apiEmail
|
||||||
...getSourceValue(nested, (state) => state.flows?.openai?.source?.entries?.codex2api),
|
?? nested?.flows?.openai?.integrationTargets?.sub2api?.sub2apiEmail
|
||||||
codex2apiUrl: String(input?.codex2apiUrl ?? nested?.flows?.openai?.source?.entries?.codex2api?.codex2apiUrl ?? '').trim(),
|
?? ''
|
||||||
codex2apiAdminKey: String(input?.codex2apiAdminKey ?? nested?.flows?.openai?.source?.entries?.codex2api?.codex2apiAdminKey ?? '').trim(),
|
).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: {
|
signup: {
|
||||||
signupMethod: String(input?.signupMethod ?? nested?.flows?.openai?.signup?.signupMethod ?? defaults.flows.openai.signup.signupMethod).trim().toLowerCase() === 'phone' ? 'phone' : 'email',
|
signupMethod: String(
|
||||||
phoneVerificationEnabled: Boolean(input?.phoneVerificationEnabled ?? nested?.flows?.openai?.signup?.phoneVerificationEnabled ?? defaults.flows.openai.signup.phoneVerificationEnabled),
|
input?.signupMethod
|
||||||
phoneSignupReloginAfterBindEmailEnabled: Boolean(input?.phoneSignupReloginAfterBindEmailEnabled ?? nested?.flows?.openai?.signup?.phoneSignupReloginAfterBindEmailEnabled ?? defaults.flows.openai.signup.phoneSignupReloginAfterBindEmailEnabled),
|
?? 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: {
|
plus: {
|
||||||
plusModeEnabled: Boolean(input?.plusModeEnabled ?? nested?.flows?.openai?.plus?.plusModeEnabled ?? defaults.flows.openai.plus.plusModeEnabled),
|
plusModeEnabled: Boolean(
|
||||||
plusPaymentMethod: String(input?.plusPaymentMethod ?? nested?.flows?.openai?.plus?.plusPaymentMethod ?? defaults.flows.openai.plus.plusPaymentMethod).trim() || defaults.flows.openai.plus.plusPaymentMethod,
|
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: {
|
autoRun: {
|
||||||
stepExecutionRange: normalizeStepExecutionRangeEntry(
|
stepExecutionRange: normalizeStepExecutionRangeEntry(
|
||||||
@@ -269,47 +313,25 @@
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
kiro: {
|
kiro: {
|
||||||
source: {
|
integrationTargetId: kiroIntegrationTargetId,
|
||||||
selected: kiroSelectedSource,
|
integrationTargets: {
|
||||||
entries: {
|
'kiro-rs': {
|
||||||
'kiro-rs': {
|
...defaults.flows.kiro.integrationTargets['kiro-rs'],
|
||||||
...defaults.flows.kiro.source.entries['kiro-rs'],
|
...getIntegrationTargetValue(nested, (state) => state.flows?.kiro?.integrationTargets?.['kiro-rs']),
|
||||||
...getSourceValue(nested, (state) => state.flows?.kiro?.source?.entries?.['kiro-rs']),
|
baseUrl: String(
|
||||||
kiroRsUrl: String(
|
input?.kiroRsUrl
|
||||||
input?.kiroRsUrl
|
?? input?.kiroRsBaseUrl
|
||||||
?? nested?.flows?.kiro?.source?.entries?.['kiro-rs']?.kiroRsUrl
|
?? nested?.flows?.kiro?.integrationTargets?.['kiro-rs']?.baseUrl
|
||||||
?? defaults.flows.kiro.source.entries['kiro-rs'].kiroRsUrl
|
?? defaults.flows.kiro.integrationTargets['kiro-rs'].baseUrl
|
||||||
).trim() || defaults.flows.kiro.source.entries['kiro-rs'].kiroRsUrl,
|
).trim() || defaults.flows.kiro.integrationTargets['kiro-rs'].baseUrl,
|
||||||
kiroRsKey: String(
|
apiKey: String(
|
||||||
input?.kiroRsKey
|
input?.kiroRsKey
|
||||||
?? nested?.flows?.kiro?.source?.entries?.['kiro-rs']?.kiroRsKey
|
?? input?.kiroRsApiKey
|
||||||
?? defaults.flows.kiro.source.entries['kiro-rs'].kiroRsKey
|
?? 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: {
|
autoRun: {
|
||||||
stepExecutionRange: normalizeStepExecutionRangeEntry(
|
stepExecutionRange: normalizeStepExecutionRangeEntry(
|
||||||
nested?.flows?.kiro?.autoRun?.stepExecutionRange
|
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 = {}) {
|
function buildStepExecutionRangeByFlow(settingsState = {}) {
|
||||||
const normalizedState = normalizeSettingsState(settingsState);
|
const normalizedState = normalizeSettingsState(settingsState);
|
||||||
return {
|
return {
|
||||||
@@ -337,23 +406,7 @@
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFlowSettings(settingsState = {}, flowId) {
|
function buildSettingsView(settingsState = {}, baseInput = {}) {
|
||||||
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 = {}) {
|
|
||||||
const normalizedState = normalizeSettingsState(settingsState);
|
const normalizedState = normalizeSettingsState(settingsState);
|
||||||
const next = {
|
const next = {
|
||||||
...(isPlainObject(baseInput) ? cloneValue(baseInput) : {}),
|
...(isPlainObject(baseInput) ? cloneValue(baseInput) : {}),
|
||||||
@@ -361,20 +414,22 @@
|
|||||||
const openaiState = normalizedState.flows.openai;
|
const openaiState = normalizedState.flows.openai;
|
||||||
const kiroState = normalizedState.flows.kiro;
|
const kiroState = normalizedState.flows.kiro;
|
||||||
next.activeFlowId = normalizedState.activeFlowId;
|
next.activeFlowId = normalizedState.activeFlowId;
|
||||||
next.panelMode = mapSourceIdToPanelMode('openai', openaiState.source.selected, defaultOpenAiSourceId);
|
next.openaiIntegrationTargetId = getSelectedIntegrationTargetId(normalizedState, 'openai');
|
||||||
next.kiroSourceId = getSelectedSourceId(normalizedState, 'kiro');
|
next.kiroIntegrationTargetId = getSelectedIntegrationTargetId(normalizedState, 'kiro');
|
||||||
next.vpsUrl = openaiState.source.entries.cpa.vpsUrl;
|
next.panelMode = next.openaiIntegrationTargetId;
|
||||||
next.vpsPassword = openaiState.source.entries.cpa.vpsPassword;
|
next.kiroSourceId = next.kiroIntegrationTargetId;
|
||||||
next.localCpaStep9Mode = openaiState.source.entries.cpa.localCpaStep9Mode;
|
next.vpsUrl = openaiState.integrationTargets.cpa.vpsUrl;
|
||||||
next.sub2apiUrl = openaiState.source.entries.sub2api.sub2apiUrl;
|
next.vpsPassword = openaiState.integrationTargets.cpa.vpsPassword;
|
||||||
next.sub2apiEmail = openaiState.source.entries.sub2api.sub2apiEmail;
|
next.localCpaStep9Mode = openaiState.integrationTargets.cpa.localCpaStep9Mode;
|
||||||
next.sub2apiPassword = openaiState.source.entries.sub2api.sub2apiPassword;
|
next.sub2apiUrl = openaiState.integrationTargets.sub2api.sub2apiUrl;
|
||||||
next.sub2apiGroupName = openaiState.source.entries.sub2api.sub2apiGroupName;
|
next.sub2apiEmail = openaiState.integrationTargets.sub2api.sub2apiEmail;
|
||||||
next.sub2apiGroupNames = cloneValue(openaiState.source.entries.sub2api.sub2apiGroupNames);
|
next.sub2apiPassword = openaiState.integrationTargets.sub2api.sub2apiPassword;
|
||||||
next.sub2apiAccountPriority = openaiState.source.entries.sub2api.sub2apiAccountPriority;
|
next.sub2apiGroupName = openaiState.integrationTargets.sub2api.sub2apiGroupName;
|
||||||
next.sub2apiDefaultProxyName = openaiState.source.entries.sub2api.sub2apiDefaultProxyName;
|
next.sub2apiGroupNames = cloneValue(openaiState.integrationTargets.sub2api.sub2apiGroupNames);
|
||||||
next.codex2apiUrl = openaiState.source.entries.codex2api.codex2apiUrl;
|
next.sub2apiAccountPriority = openaiState.integrationTargets.sub2api.sub2apiAccountPriority;
|
||||||
next.codex2apiAdminKey = openaiState.source.entries.codex2api.codex2apiAdminKey;
|
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.customPassword = normalizedState.services.account.customPassword;
|
||||||
next.signupMethod = openaiState.signup.signupMethod;
|
next.signupMethod = openaiState.signup.signupMethod;
|
||||||
next.phoneVerificationEnabled = openaiState.signup.phoneVerificationEnabled;
|
next.phoneVerificationEnabled = openaiState.signup.phoneVerificationEnabled;
|
||||||
@@ -385,13 +440,8 @@
|
|||||||
next.ipProxyEnabled = normalizedState.services.proxy.enabled;
|
next.ipProxyEnabled = normalizedState.services.proxy.enabled;
|
||||||
next.ipProxyService = normalizedState.services.proxy.provider;
|
next.ipProxyService = normalizedState.services.proxy.provider;
|
||||||
next.ipProxyMode = normalizedState.services.proxy.mode;
|
next.ipProxyMode = normalizedState.services.proxy.mode;
|
||||||
next.kiroRsUrl = kiroState.source.entries['kiro-rs'].kiroRsUrl;
|
next.kiroRsUrl = kiroState.integrationTargets['kiro-rs'].baseUrl;
|
||||||
next.kiroRsKey = kiroState.source.entries['kiro-rs'].kiroRsKey;
|
next.kiroRsKey = kiroState.integrationTargets['kiro-rs'].apiKey;
|
||||||
next.kiroRsPriority = kiroState.options.kiroRsPriority;
|
|
||||||
next.kiroRsEndpoint = kiroState.options.kiroRsEndpoint;
|
|
||||||
next.kiroRsAuthRegion = kiroState.options.kiroRsAuthRegion;
|
|
||||||
next.kiroRsApiRegion = kiroState.options.kiroRsApiRegion;
|
|
||||||
delete next.kiroRegion;
|
|
||||||
next.stepExecutionRangeByFlow = buildStepExecutionRangeByFlow(normalizedState);
|
next.stepExecutionRangeByFlow = buildStepExecutionRangeByFlow(normalizedState);
|
||||||
next.settingsSchemaVersion = normalizedState.schemaVersion;
|
next.settingsSchemaVersion = normalizedState.schemaVersion;
|
||||||
next.settingsState = cloneValue(normalizedState);
|
next.settingsState = cloneValue(normalizedState);
|
||||||
@@ -401,34 +451,29 @@
|
|||||||
function getFlowInputState(settingsState = {}, flowId) {
|
function getFlowInputState(settingsState = {}, flowId) {
|
||||||
const normalizedState = normalizeSettingsState(settingsState);
|
const normalizedState = normalizeSettingsState(settingsState);
|
||||||
const normalizedFlowId = normalizeFlowId(flowId, normalizedState.activeFlowId);
|
const normalizedFlowId = normalizeFlowId(flowId, normalizedState.activeFlowId);
|
||||||
|
const integrationTargetId = getSelectedIntegrationTargetId(normalizedState, normalizedFlowId);
|
||||||
if (normalizedFlowId === 'kiro') {
|
if (normalizedFlowId === 'kiro') {
|
||||||
return {
|
return {
|
||||||
activeFlowId: normalizedFlowId,
|
activeFlowId: normalizedFlowId,
|
||||||
sourceId: getSelectedSourceId(normalizedState, 'kiro'),
|
integrationTargetId,
|
||||||
kiroRsUrl: normalizedState.flows.kiro.source.entries['kiro-rs'].kiroRsUrl,
|
kiroRsUrl: normalizedState.flows.kiro.integrationTargets['kiro-rs'].baseUrl,
|
||||||
kiroRsKey: normalizedState.flows.kiro.source.entries['kiro-rs'].kiroRsKey,
|
kiroRsKey: normalizedState.flows.kiro.integrationTargets['kiro-rs'].apiKey,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
activeFlowId: normalizedFlowId,
|
activeFlowId: normalizedFlowId,
|
||||||
sourceId: getSelectedSourceId(normalizedState, 'openai'),
|
integrationTargetId,
|
||||||
panelMode: mapSourceIdToPanelMode('openai', normalizedState.flows.openai.source.selected, defaultOpenAiSourceId),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function normalizeFlatInput(input = {}) {
|
|
||||||
const state = normalizeSettingsState(input);
|
|
||||||
return buildLegacySettingsPayload(state, input);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
buildDefaultSettingsState,
|
buildDefaultSettingsState,
|
||||||
buildLegacySettingsPayload,
|
buildSettingsView,
|
||||||
buildStepExecutionRangeByFlow,
|
buildStepExecutionRangeByFlow,
|
||||||
getFlowInputState,
|
getFlowInputState,
|
||||||
getFlowSettings,
|
getFlowSettings,
|
||||||
getSelectedSourceId,
|
getSelectedIntegrationTargetId,
|
||||||
normalizeFlatInput,
|
mergeSettingsState,
|
||||||
normalizeSettingsState,
|
normalizeSettingsState,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -683,10 +683,10 @@
|
|||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="setting-group setting-group-secondary step-range-controls">
|
<div class="setting-group setting-group-secondary step-range-controls">
|
||||||
<span class="setting-caption">步骤</span>
|
<span class="setting-caption">节点</span>
|
||||||
<input type="number" id="input-step-execution-range-from" class="data-input auto-delay-input step-range-input" value="1" min="1" step="1" title="允许执行的起始步骤" />
|
<select id="input-step-execution-range-from" class="data-select step-range-input" title="允许执行的起始节点"></select>
|
||||||
<span class="data-unit">到</span>
|
<span class="data-unit">到</span>
|
||||||
<input type="number" id="input-step-execution-range-to" class="data-input auto-delay-input step-range-input" value="1" min="1" step="1" title="允许执行的结束步骤" />
|
<select id="input-step-execution-range-to" class="data-select step-range-input" title="允许执行的结束节点"></select>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+81
-31
@@ -884,18 +884,17 @@ function getWorkflowNodesForMode(plusModeEnabled = false, options = {}) {
|
|||||||
});
|
});
|
||||||
if (Array.isArray(nodes) && nodes.length) {
|
if (Array.isArray(nodes) && nodes.length) {
|
||||||
return nodes.slice().sort((left, right) => {
|
return nodes.slice().sort((left, right) => {
|
||||||
const leftOrder = Number.isFinite(Number(left.displayOrder)) ? Number(left.displayOrder) : Number(left.legacyStepId);
|
const leftOrder = Number.isFinite(Number(left.displayOrder)) ? Number(left.displayOrder) : 0;
|
||||||
const rightOrder = Number.isFinite(Number(right.displayOrder)) ? Number(right.displayOrder) : Number(right.legacyStepId);
|
const rightOrder = Number.isFinite(Number(right.displayOrder)) ? Number(right.displayOrder) : 0;
|
||||||
if (leftOrder !== rightOrder) return leftOrder - rightOrder;
|
if (leftOrder !== rightOrder) return leftOrder - rightOrder;
|
||||||
return String(left.nodeId || '').localeCompare(String(right.nodeId || ''));
|
return String(left.nodeId || '').localeCompare(String(right.nodeId || ''));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return getStepDefinitionsForMode(plusModeEnabled, options).map((step) => ({
|
return getStepDefinitionsForMode(plusModeEnabled, options).map((step) => ({
|
||||||
legacyStepId: Number(step.id),
|
|
||||||
nodeId: String(step.key || '').trim(),
|
nodeId: String(step.key || '').trim(),
|
||||||
title: step.title,
|
title: 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),
|
||||||
executeKey: String(step.key || '').trim(),
|
executeKey: String(step.key || '').trim(),
|
||||||
})).filter((node) => node.nodeId);
|
})).filter((node) => node.nodeId);
|
||||||
}
|
}
|
||||||
@@ -911,7 +910,7 @@ function getStepIdByKeyForCurrentMode(stepKey = '') {
|
|||||||
|
|
||||||
function getNodeIdByStepForCurrentMode(step) {
|
function getNodeIdByStepForCurrentMode(step) {
|
||||||
const numericStep = Number(step);
|
const numericStep = Number(step);
|
||||||
const node = (workflowNodes || []).find((candidate) => Number(candidate?.legacyStepId) === numericStep);
|
const node = (workflowNodes || []).find((candidate) => Number(candidate?.displayOrder) === numericStep);
|
||||||
if (node?.nodeId) {
|
if (node?.nodeId) {
|
||||||
return String(node.nodeId).trim();
|
return String(node.nodeId).trim();
|
||||||
}
|
}
|
||||||
@@ -925,9 +924,9 @@ function getStepIdByNodeIdForCurrentMode(nodeId = '') {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
const node = (workflowNodes || []).find((candidate) => String(candidate?.nodeId || '').trim() === normalizedNodeId);
|
const node = (workflowNodes || []).find((candidate) => String(candidate?.nodeId || '').trim() === normalizedNodeId);
|
||||||
const legacyStepId = Number(node?.legacyStepId);
|
const displayOrder = Number(node?.displayOrder);
|
||||||
if (Number.isInteger(legacyStepId) && legacyStepId > 0) {
|
if (Number.isInteger(displayOrder) && displayOrder > 0) {
|
||||||
return legacyStepId;
|
return displayOrder;
|
||||||
}
|
}
|
||||||
return getStepIdByKeyForCurrentMode(normalizedNodeId);
|
return getStepIdByKeyForCurrentMode(normalizedNodeId);
|
||||||
}
|
}
|
||||||
@@ -970,10 +969,9 @@ function rebuildStepDefinitionState(plusModeEnabled = false, options = {}) {
|
|||||||
phoneSignupReloginAfterBindEmailEnabled: currentPhoneSignupReloginAfterBindEmailEnabled,
|
phoneSignupReloginAfterBindEmailEnabled: currentPhoneSignupReloginAfterBindEmailEnabled,
|
||||||
})
|
})
|
||||||
: stepDefinitions.map((step) => ({
|
: stepDefinitions.map((step) => ({
|
||||||
legacyStepId: Number(step.id),
|
|
||||||
nodeId: String(step.key || step.id || '').trim(),
|
nodeId: String(step.key || step.id || '').trim(),
|
||||||
title: step.title,
|
title: 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),
|
||||||
}));
|
}));
|
||||||
if (typeof workflowNodes !== 'undefined') {
|
if (typeof workflowNodes !== 'undefined') {
|
||||||
workflowNodes = nextWorkflowNodes;
|
workflowNodes = nextWorkflowNodes;
|
||||||
@@ -2436,24 +2434,76 @@ function getLastCurrentStepId() {
|
|||||||
return STEP_IDS.length ? Math.max(...STEP_IDS) : 1;
|
return STEP_IDS.length ? Math.max(...STEP_IDS) : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getStepExecutionRangeNodes() {
|
||||||
|
return Array.isArray(workflowNodes)
|
||||||
|
? workflowNodes.filter((node) => String(node?.nodeId || '').trim())
|
||||||
|
: [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStepExecutionRangeNodeLabel(node = {}) {
|
||||||
|
const nodeId = String(node?.nodeId || '').trim();
|
||||||
|
const displayOrder = Number(node?.displayOrder);
|
||||||
|
const title = String(node?.title || nodeId).trim();
|
||||||
|
const orderLabel = Number.isInteger(displayOrder) && displayOrder > 0
|
||||||
|
? `步骤 ${displayOrder}`
|
||||||
|
: nodeId;
|
||||||
|
return title ? `${orderLabel} · ${title}` : orderLabel;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStepExecutionRangeBoundaryNodeId(stepNumber, boundary = 'start') {
|
||||||
|
const nodes = getStepExecutionRangeNodes();
|
||||||
|
const fallbackNode = boundary === 'end' ? nodes[nodes.length - 1] : nodes[0];
|
||||||
|
const resolvedNodeId = getNodeIdByStepForCurrentMode(stepNumber);
|
||||||
|
return String(resolvedNodeId || fallbackNode?.nodeId || '').trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
function syncStepExecutionRangeSelectOptions(selectedFromNodeId = '', selectedToNodeId = '') {
|
||||||
|
const nodes = getStepExecutionRangeNodes();
|
||||||
|
const fromSelect = inputStepExecutionRangeFrom;
|
||||||
|
const toSelect = inputStepExecutionRangeTo;
|
||||||
|
if (!fromSelect || !toSelect) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const buildOptions = (selectedValue) => nodes.map((node) => {
|
||||||
|
const nodeId = String(node?.nodeId || '').trim();
|
||||||
|
const label = getStepExecutionRangeNodeLabel(node);
|
||||||
|
return `<option value="${escapeHtml(nodeId)}"${nodeId === selectedValue ? ' selected' : ''}>${escapeHtml(label)}</option>`;
|
||||||
|
}).join('');
|
||||||
|
|
||||||
|
fromSelect.innerHTML = buildOptions(String(selectedFromNodeId || nodes[0]?.nodeId || '').trim());
|
||||||
|
toSelect.innerHTML = buildOptions(String(selectedToNodeId || nodes[nodes.length - 1]?.nodeId || '').trim());
|
||||||
|
}
|
||||||
|
|
||||||
function isStepExecutionRangeUiAvailable(state = latestState) {
|
function isStepExecutionRangeUiAvailable(state = latestState) {
|
||||||
return getCurrentStepExecutionRangeFlowId(state) === DEFAULT_ACTIVE_FLOW_ID;
|
return getCurrentStepExecutionRangeFlowId(state) === DEFAULT_ACTIVE_FLOW_ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
function clampStepExecutionRangeInputs() {
|
function clampStepExecutionRangeInputs() {
|
||||||
const maxStep = getLastCurrentStepId();
|
const nodes = getStepExecutionRangeNodes();
|
||||||
const fromStep = Math.min(maxStep, Math.max(1, normalizePositiveStepNumber(inputStepExecutionRangeFrom?.value, 1)));
|
const firstNodeId = String(nodes[0]?.nodeId || '').trim();
|
||||||
const toStep = Math.min(maxStep, Math.max(1, normalizePositiveStepNumber(inputStepExecutionRangeTo?.value, maxStep)));
|
const lastNodeId = String(nodes[nodes.length - 1]?.nodeId || '').trim();
|
||||||
const normalizedFrom = Math.min(fromStep, toStep);
|
const selectedFromNodeId = String(inputStepExecutionRangeFrom?.value || firstNodeId).trim() || firstNodeId;
|
||||||
const normalizedTo = Math.max(fromStep, toStep);
|
const selectedToNodeId = String(inputStepExecutionRangeTo?.value || lastNodeId).trim() || lastNodeId;
|
||||||
|
const fromStep = Math.max(1, getStepIdByNodeIdForCurrentMode(selectedFromNodeId) || 1);
|
||||||
|
const toStep = Math.max(1, getStepIdByNodeIdForCurrentMode(selectedToNodeId) || fromStep);
|
||||||
|
const normalizedFromStep = Math.min(fromStep, toStep);
|
||||||
|
const normalizedToStep = Math.max(fromStep, toStep);
|
||||||
|
const normalizedFromNodeId = getStepExecutionRangeBoundaryNodeId(normalizedFromStep, 'start');
|
||||||
|
const normalizedToNodeId = getStepExecutionRangeBoundaryNodeId(normalizedToStep, 'end');
|
||||||
|
syncStepExecutionRangeSelectOptions(normalizedFromNodeId, normalizedToNodeId);
|
||||||
if (inputStepExecutionRangeFrom) {
|
if (inputStepExecutionRangeFrom) {
|
||||||
inputStepExecutionRangeFrom.max = String(maxStep);
|
inputStepExecutionRangeFrom.value = normalizedFromNodeId;
|
||||||
inputStepExecutionRangeFrom.value = String(normalizedFrom);
|
|
||||||
}
|
}
|
||||||
if (inputStepExecutionRangeTo) {
|
if (inputStepExecutionRangeTo) {
|
||||||
inputStepExecutionRangeTo.max = String(maxStep);
|
inputStepExecutionRangeTo.value = normalizedToNodeId;
|
||||||
inputStepExecutionRangeTo.value = String(normalizedTo);
|
|
||||||
}
|
}
|
||||||
|
return {
|
||||||
|
fromNodeId: normalizedFromNodeId,
|
||||||
|
toNodeId: normalizedToNodeId,
|
||||||
|
fromStep: normalizedFromStep,
|
||||||
|
toStep: normalizedToStep,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildStepExecutionRangeByFlowPayload(existingConfig = latestState?.stepExecutionRangeByFlow || {}) {
|
function buildStepExecutionRangeByFlowPayload(existingConfig = latestState?.stepExecutionRangeByFlow || {}) {
|
||||||
@@ -2461,12 +2511,12 @@ function buildStepExecutionRangeByFlowPayload(existingConfig = latestState?.step
|
|||||||
if (!isStepExecutionRangeUiAvailable(latestState)) {
|
if (!isStepExecutionRangeUiAvailable(latestState)) {
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
clampStepExecutionRangeInputs();
|
const normalizedRange = clampStepExecutionRangeInputs();
|
||||||
const flowId = getCurrentStepExecutionRangeFlowId(latestState);
|
const flowId = getCurrentStepExecutionRangeFlowId(latestState);
|
||||||
config[flowId] = normalizeStepExecutionRangeEntry({
|
config[flowId] = normalizeStepExecutionRangeEntry({
|
||||||
enabled: Boolean(inputStepExecutionRangeEnabled?.checked),
|
enabled: Boolean(inputStepExecutionRangeEnabled?.checked),
|
||||||
fromStep: inputStepExecutionRangeFrom?.value,
|
fromStep: normalizedRange?.fromStep,
|
||||||
toStep: inputStepExecutionRangeTo?.value,
|
toStep: normalizedRange?.toStep,
|
||||||
});
|
});
|
||||||
return config;
|
return config;
|
||||||
}
|
}
|
||||||
@@ -2493,17 +2543,15 @@ function applyStepExecutionRangeState(state = latestState) {
|
|||||||
}
|
}
|
||||||
const available = isStepExecutionRangeUiAvailable(state);
|
const available = isStepExecutionRangeUiAvailable(state);
|
||||||
rowStepExecutionRange.style.display = available ? '' : 'none';
|
rowStepExecutionRange.style.display = available ? '' : 'none';
|
||||||
const maxStep = getLastCurrentStepId();
|
|
||||||
const range = getStepExecutionRangeForCurrentFlow(state);
|
const range = getStepExecutionRangeForCurrentFlow(state);
|
||||||
|
const fromNodeId = getStepExecutionRangeBoundaryNodeId(range.fromStep, 'start');
|
||||||
|
const toNodeId = getStepExecutionRangeBoundaryNodeId(range.toStep, 'end');
|
||||||
|
syncStepExecutionRangeSelectOptions(fromNodeId, toNodeId);
|
||||||
if (inputStepExecutionRangeFrom) {
|
if (inputStepExecutionRangeFrom) {
|
||||||
inputStepExecutionRangeFrom.min = '1';
|
inputStepExecutionRangeFrom.value = fromNodeId;
|
||||||
inputStepExecutionRangeFrom.max = String(maxStep);
|
|
||||||
inputStepExecutionRangeFrom.value = String(Math.min(maxStep, Math.max(1, normalizePositiveStepNumber(range.fromStep, 1))));
|
|
||||||
}
|
}
|
||||||
if (inputStepExecutionRangeTo) {
|
if (inputStepExecutionRangeTo) {
|
||||||
inputStepExecutionRangeTo.min = '1';
|
inputStepExecutionRangeTo.value = toNodeId;
|
||||||
inputStepExecutionRangeTo.max = String(maxStep);
|
|
||||||
inputStepExecutionRangeTo.value = String(Math.min(maxStep, Math.max(1, normalizePositiveStepNumber(range.toStep, maxStep))));
|
|
||||||
}
|
}
|
||||||
if (inputStepExecutionRangeEnabled) {
|
if (inputStepExecutionRangeEnabled) {
|
||||||
inputStepExecutionRangeEnabled.checked = Boolean(range.enabled);
|
inputStepExecutionRangeEnabled.checked = Boolean(range.enabled);
|
||||||
@@ -14932,14 +14980,16 @@ selectFlow?.addEventListener('change', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
[inputStepExecutionRangeFrom, inputStepExecutionRangeTo].forEach((input) => {
|
[inputStepExecutionRangeFrom, inputStepExecutionRangeTo].forEach((input) => {
|
||||||
input?.addEventListener('input', () => {
|
const handleRangeChange = () => {
|
||||||
const stepExecutionRangeByFlow = buildStepExecutionRangeByFlowPayload(latestState?.stepExecutionRangeByFlow);
|
const stepExecutionRangeByFlow = buildStepExecutionRangeByFlowPayload(latestState?.stepExecutionRangeByFlow);
|
||||||
syncLatestState({ stepExecutionRangeByFlow });
|
syncLatestState({ stepExecutionRangeByFlow });
|
||||||
markSettingsDirty(true);
|
markSettingsDirty(true);
|
||||||
renderStepStatuses(latestState);
|
renderStepStatuses(latestState);
|
||||||
updateButtonStates();
|
updateButtonStates();
|
||||||
scheduleSettingsAutoSave();
|
scheduleSettingsAutoSave();
|
||||||
});
|
};
|
||||||
|
input?.addEventListener('input', handleRangeChange);
|
||||||
|
input?.addEventListener('change', handleRangeChange);
|
||||||
input?.addEventListener('blur', () => {
|
input?.addEventListener('blur', () => {
|
||||||
clampStepExecutionRangeInputs();
|
clampStepExecutionRangeInputs();
|
||||||
saveSettings({ silent: true }).catch(() => { });
|
saveSettings({ silent: true }).catch(() => { });
|
||||||
|
|||||||
@@ -124,8 +124,8 @@ test('runtime-state patch accepts nested flow and node updates without legacy st
|
|||||||
assert.equal(patch.activeFlowId, 'openai');
|
assert.equal(patch.activeFlowId, 'openai');
|
||||||
assert.equal(patch.activeRunId, 'run-001');
|
assert.equal(patch.activeRunId, 'run-001');
|
||||||
assert.equal(patch.currentNodeId, 'oauth-login');
|
assert.equal(patch.currentNodeId, 'oauth-login');
|
||||||
assert.equal(patch.oauthUrl, 'https://new.example.com/start');
|
assert.equal(Object.prototype.hasOwnProperty.call(patch, 'oauthUrl'), false);
|
||||||
assert.equal(patch.plusCheckoutTabId, 99);
|
assert.equal(Object.prototype.hasOwnProperty.call(patch, 'plusCheckoutTabId'), false);
|
||||||
assert.equal(Object.prototype.hasOwnProperty.call(patch, 'currentStep'), false);
|
assert.equal(Object.prototype.hasOwnProperty.call(patch, 'currentStep'), false);
|
||||||
assert.equal(Object.prototype.hasOwnProperty.call(patch, 'stepStatuses'), false);
|
assert.equal(Object.prototype.hasOwnProperty.call(patch, 'stepStatuses'), false);
|
||||||
assert.deepStrictEqual(patch.nodeStatuses, {
|
assert.deepStrictEqual(patch.nodeStatuses, {
|
||||||
@@ -135,6 +135,10 @@ test('runtime-state patch accepts nested flow and node updates without legacy st
|
|||||||
});
|
});
|
||||||
assert.equal(patch.runtimeState.flowState.openai.auth.oauthUrl, 'https://new.example.com/start');
|
assert.equal(patch.runtimeState.flowState.openai.auth.oauthUrl, 'https://new.example.com/start');
|
||||||
assert.equal(patch.runtimeState.flowState.openai.plus.plusCheckoutTabId, 99);
|
assert.equal(patch.runtimeState.flowState.openai.plus.plusCheckoutTabId, 99);
|
||||||
|
|
||||||
|
const view = helpers.buildStateView(patch);
|
||||||
|
assert.equal(view.oauthUrl, 'https://new.example.com/start');
|
||||||
|
assert.equal(view.plusCheckoutTabId, 99);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('runtime-state patch prefers explicit activeFlowId over stale legacy flowId', () => {
|
test('runtime-state patch prefers explicit activeFlowId over stale legacy flowId', () => {
|
||||||
|
|||||||
@@ -57,6 +57,39 @@ ${flowRegistrySource}
|
|||||||
${settingsSchemaSource}
|
${settingsSchemaSource}
|
||||||
const DEFAULT_ACTIVE_FLOW_ID = 'openai';
|
const DEFAULT_ACTIVE_FLOW_ID = 'openai';
|
||||||
const DEFAULT_SUB2API_GROUP_NAMES = ['codex', 'openai-plus'];
|
const DEFAULT_SUB2API_GROUP_NAMES = ['codex', 'openai-plus'];
|
||||||
|
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 PERSISTED_SETTING_DEFAULTS = {
|
const PERSISTED_SETTING_DEFAULTS = {
|
||||||
activeFlowId: DEFAULT_ACTIVE_FLOW_ID,
|
activeFlowId: DEFAULT_ACTIVE_FLOW_ID,
|
||||||
panelMode: 'cpa',
|
panelMode: 'cpa',
|
||||||
@@ -77,6 +110,9 @@ const PERSISTED_SETTING_KEYS = Object.keys(PERSISTED_SETTING_DEFAULTS);
|
|||||||
const PERSISTED_SETTINGS_SCHEMA_KEYS = ['settingsSchemaVersion', 'settingsState'];
|
const PERSISTED_SETTINGS_SCHEMA_KEYS = ['settingsSchemaVersion', 'settingsState'];
|
||||||
const LEGACY_AUTO_STEP_DELAY_KEYS = [];
|
const LEGACY_AUTO_STEP_DELAY_KEYS = [];
|
||||||
const LEGACY_VERIFICATION_RESEND_COUNT_KEYS = [];
|
const LEGACY_VERIFICATION_RESEND_COUNT_KEYS = [];
|
||||||
|
function isPlainObjectValue(value) {
|
||||||
|
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
||||||
|
}
|
||||||
function normalizePanelMode(value = '') {
|
function normalizePanelMode(value = '') {
|
||||||
const normalized = String(value || '').trim().toLowerCase();
|
const normalized = String(value || '').trim().toLowerCase();
|
||||||
return normalized === 'sub2api' || normalized === 'codex2api' ? normalized : 'cpa';
|
return normalized === 'sub2api' || normalized === 'codex2api' ? normalized : 'cpa';
|
||||||
@@ -131,6 +167,8 @@ function resolveSignupMethod(state = {}) {
|
|||||||
function resolveLegacyAutoStepDelaySeconds() { return undefined; }
|
function resolveLegacyAutoStepDelaySeconds() { return undefined; }
|
||||||
${extractFunction('normalizePersistentSettingValue')}
|
${extractFunction('normalizePersistentSettingValue')}
|
||||||
${extractFunction('getSettingsSchemaApi')}
|
${extractFunction('getSettingsSchemaApi')}
|
||||||
|
${extractFunction('projectSettingsSchemaView')}
|
||||||
|
${extractFunction('buildPersistedSettingsStoragePayload')}
|
||||||
${extractFunction('buildPersistentSettingsPayload')}
|
${extractFunction('buildPersistentSettingsPayload')}
|
||||||
${extractFunction('getPersistedSettings')}
|
${extractFunction('getPersistedSettings')}
|
||||||
${extractFunction('setPersistentSettings')}
|
${extractFunction('setPersistentSettings')}
|
||||||
@@ -141,6 +179,7 @@ return {
|
|||||||
setPersistentSettings,
|
setPersistentSettings,
|
||||||
getRequestedKeys: typeof getRequestedKeys === 'function' ? getRequestedKeys : () => [],
|
getRequestedKeys: typeof getRequestedKeys === 'function' ? getRequestedKeys : () => [],
|
||||||
getPersistedWrites: typeof getPersistedWrites === 'function' ? getPersistedWrites : () => [],
|
getPersistedWrites: typeof getPersistedWrites === 'function' ? getPersistedWrites : () => [],
|
||||||
|
getRemovedKeys: typeof getRemovedKeys === 'function' ? getRemovedKeys : () => [],
|
||||||
};
|
};
|
||||||
`)();
|
`)();
|
||||||
}
|
}
|
||||||
@@ -159,26 +198,50 @@ test('buildPersistentSettingsPayload writes canonical settings schema into persi
|
|||||||
assert.equal(payload.kiroRsUrl, 'https://kiro.example.com/admin');
|
assert.equal(payload.kiroRsUrl, 'https://kiro.example.com/admin');
|
||||||
assert.equal(payload.kiroRsKey, 'secret-key');
|
assert.equal(payload.kiroRsKey, 'secret-key');
|
||||||
assert.equal(Object.prototype.hasOwnProperty.call(payload, 'kiroRegion'), false);
|
assert.equal(Object.prototype.hasOwnProperty.call(payload, 'kiroRegion'), false);
|
||||||
assert.equal(payload.settingsSchemaVersion, 3);
|
assert.equal(payload.settingsSchemaVersion, 4);
|
||||||
assert.equal(payload.settingsState.activeFlowId, 'kiro');
|
assert.equal(payload.settingsState.activeFlowId, 'kiro');
|
||||||
assert.equal(payload.settingsState.flows.kiro.source.selected, 'kiro-rs');
|
assert.equal(payload.settingsState.flows.kiro.integrationTargetId, 'kiro-rs');
|
||||||
|
assert.equal(
|
||||||
|
payload.settingsState.flows.kiro.integrationTargets['kiro-rs'].baseUrl,
|
||||||
|
'https://kiro.example.com/admin'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('buildPersistentSettingsPayload accepts schema-only input when requireKnownKeys is enabled', () => {
|
test('buildPersistentSettingsPayload accepts schema-only input when requireKnownKeys is enabled', () => {
|
||||||
const api = buildHarness();
|
const api = buildHarness();
|
||||||
|
|
||||||
const payload = api.buildPersistentSettingsPayload({
|
const payload = api.buildPersistentSettingsPayload({
|
||||||
settingsSchemaVersion: 3,
|
settingsSchemaVersion: 4,
|
||||||
settingsState: {
|
settingsState: {
|
||||||
activeFlowId: 'kiro',
|
activeFlowId: 'kiro',
|
||||||
services: {
|
services: {
|
||||||
|
account: { customPassword: '' },
|
||||||
email: { provider: '163' },
|
email: { provider: '163' },
|
||||||
proxy: { enabled: false, provider: '711proxy', mode: 'account' },
|
proxy: { enabled: false, provider: '711proxy', mode: 'account' },
|
||||||
},
|
},
|
||||||
flows: {
|
flows: {
|
||||||
openai: {
|
openai: {
|
||||||
source: { selected: 'cpa', entries: {} },
|
integrationTargetId: 'cpa',
|
||||||
account: { customPassword: '' },
|
integrationTargets: {
|
||||||
|
cpa: {
|
||||||
|
vpsUrl: '',
|
||||||
|
vpsPassword: '',
|
||||||
|
localCpaStep9Mode: 'submit',
|
||||||
|
},
|
||||||
|
sub2api: {
|
||||||
|
sub2apiUrl: '',
|
||||||
|
sub2apiEmail: '',
|
||||||
|
sub2apiPassword: '',
|
||||||
|
sub2apiGroupName: 'codex',
|
||||||
|
sub2apiGroupNames: ['codex', 'openai-plus'],
|
||||||
|
sub2apiAccountPriority: 1,
|
||||||
|
sub2apiDefaultProxyName: '',
|
||||||
|
},
|
||||||
|
codex2api: {
|
||||||
|
codex2apiUrl: '',
|
||||||
|
codex2apiAdminKey: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
signup: {
|
signup: {
|
||||||
signupMethod: 'email',
|
signupMethod: 'email',
|
||||||
phoneVerificationEnabled: false,
|
phoneVerificationEnabled: false,
|
||||||
@@ -193,17 +256,13 @@ test('buildPersistentSettingsPayload accepts schema-only input when requireKnown
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
kiro: {
|
kiro: {
|
||||||
source: {
|
integrationTargetId: 'kiro-rs',
|
||||||
selected: 'kiro-rs',
|
integrationTargets: {
|
||||||
entries: {
|
'kiro-rs': {
|
||||||
'kiro-rs': {
|
baseUrl: 'https://kiro.example.com/admin',
|
||||||
kiroRsUrl: 'https://kiro.example.com/admin',
|
apiKey: 'schema-only-key',
|
||||||
kiroRsKey: 'schema-only-key',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
options: {
|
|
||||||
},
|
|
||||||
autoRun: {
|
autoRun: {
|
||||||
stepExecutionRange: { enabled: true, fromStep: 1, toStep: 7 },
|
stepExecutionRange: { enabled: true, fromStep: 1, toStep: 7 },
|
||||||
},
|
},
|
||||||
@@ -217,7 +276,7 @@ test('buildPersistentSettingsPayload accepts schema-only input when requireKnown
|
|||||||
assert.equal(payload.kiroRsUrl, 'https://kiro.example.com/admin');
|
assert.equal(payload.kiroRsUrl, 'https://kiro.example.com/admin');
|
||||||
assert.equal(payload.kiroRsKey, 'schema-only-key');
|
assert.equal(payload.kiroRsKey, 'schema-only-key');
|
||||||
assert.equal(Object.prototype.hasOwnProperty.call(payload, 'kiroRegion'), false);
|
assert.equal(Object.prototype.hasOwnProperty.call(payload, 'kiroRegion'), false);
|
||||||
assert.equal(payload.settingsSchemaVersion, 3);
|
assert.equal(payload.settingsSchemaVersion, 4);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('getPersistedSettings reads schema keys alongside legacy flat settings keys', async () => {
|
test('getPersistedSettings reads schema keys alongside legacy flat settings keys', async () => {
|
||||||
@@ -242,7 +301,7 @@ function getRequestedKeys() {
|
|||||||
|
|
||||||
assert.ok(api.getRequestedKeys().includes('settingsSchemaVersion'));
|
assert.ok(api.getRequestedKeys().includes('settingsSchemaVersion'));
|
||||||
assert.ok(api.getRequestedKeys().includes('settingsState'));
|
assert.ok(api.getRequestedKeys().includes('settingsState'));
|
||||||
assert.equal(state.settingsSchemaVersion, 3);
|
assert.equal(state.settingsSchemaVersion, 4);
|
||||||
assert.equal(state.settingsState.activeFlowId, 'openai');
|
assert.equal(state.settingsState.activeFlowId, 'openai');
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -253,20 +312,37 @@ const chrome = {
|
|||||||
local: {
|
local: {
|
||||||
async get() {
|
async get() {
|
||||||
return {
|
return {
|
||||||
settingsSchemaVersion: 3,
|
settingsSchemaVersion: 4,
|
||||||
settingsState: {
|
settingsState: {
|
||||||
activeFlowId: 'kiro',
|
activeFlowId: 'kiro',
|
||||||
services: {
|
services: {
|
||||||
|
account: { customPassword: '' },
|
||||||
email: { provider: 'hotmail' },
|
email: { provider: 'hotmail' },
|
||||||
proxy: { enabled: true, provider: '711proxy', mode: 'account' },
|
proxy: { enabled: true, provider: '711proxy', mode: 'account' },
|
||||||
},
|
},
|
||||||
flows: {
|
flows: {
|
||||||
openai: {
|
openai: {
|
||||||
source: {
|
integrationTargetId: 'sub2api',
|
||||||
selected: 'sub2api',
|
integrationTargets: {
|
||||||
entries: {},
|
cpa: {
|
||||||
|
vpsUrl: '',
|
||||||
|
vpsPassword: '',
|
||||||
|
localCpaStep9Mode: 'submit',
|
||||||
|
},
|
||||||
|
sub2api: {
|
||||||
|
sub2apiUrl: '',
|
||||||
|
sub2apiEmail: '',
|
||||||
|
sub2apiPassword: '',
|
||||||
|
sub2apiGroupName: 'codex',
|
||||||
|
sub2apiGroupNames: ['codex', 'openai-plus'],
|
||||||
|
sub2apiAccountPriority: 1,
|
||||||
|
sub2apiDefaultProxyName: '',
|
||||||
|
},
|
||||||
|
codex2api: {
|
||||||
|
codex2apiUrl: '',
|
||||||
|
codex2apiAdminKey: '',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
account: { customPassword: '' },
|
|
||||||
signup: {
|
signup: {
|
||||||
signupMethod: 'email',
|
signupMethod: 'email',
|
||||||
phoneVerificationEnabled: false,
|
phoneVerificationEnabled: false,
|
||||||
@@ -281,17 +357,13 @@ const chrome = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
kiro: {
|
kiro: {
|
||||||
source: {
|
integrationTargetId: 'kiro-rs',
|
||||||
selected: 'kiro-rs',
|
integrationTargets: {
|
||||||
entries: {
|
'kiro-rs': {
|
||||||
'kiro-rs': {
|
baseUrl: 'https://kiro.example.com/admin',
|
||||||
kiroRsUrl: 'https://kiro.example.com/admin',
|
apiKey: 'stored-key',
|
||||||
kiroRsKey: 'stored-key',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
options: {
|
|
||||||
},
|
|
||||||
autoRun: {
|
autoRun: {
|
||||||
stepExecutionRange: { enabled: true, fromStep: 1, toStep: 7 },
|
stepExecutionRange: { enabled: true, fromStep: 1, toStep: 7 },
|
||||||
},
|
},
|
||||||
@@ -324,12 +396,16 @@ const chrome = {
|
|||||||
test('setPersistentSettings materializes canonical schema keys for schema-only updates', async () => {
|
test('setPersistentSettings materializes canonical schema keys for schema-only updates', async () => {
|
||||||
const api = buildHarness(`
|
const api = buildHarness(`
|
||||||
const persistedWrites = [];
|
const persistedWrites = [];
|
||||||
|
const removedKeys = [];
|
||||||
const chrome = {
|
const chrome = {
|
||||||
storage: {
|
storage: {
|
||||||
local: {
|
local: {
|
||||||
async get() {
|
async get() {
|
||||||
return {};
|
return {};
|
||||||
},
|
},
|
||||||
|
async remove(keys) {
|
||||||
|
removedKeys.push(...(Array.isArray(keys) ? keys : [keys]));
|
||||||
|
},
|
||||||
async set(payload) {
|
async set(payload) {
|
||||||
persistedWrites.push(JSON.parse(JSON.stringify(payload)));
|
persistedWrites.push(JSON.parse(JSON.stringify(payload)));
|
||||||
},
|
},
|
||||||
@@ -339,20 +415,43 @@ const chrome = {
|
|||||||
function getPersistedWrites() {
|
function getPersistedWrites() {
|
||||||
return persistedWrites;
|
return persistedWrites;
|
||||||
}
|
}
|
||||||
|
function getRemovedKeys() {
|
||||||
|
return removedKeys;
|
||||||
|
}
|
||||||
`);
|
`);
|
||||||
|
|
||||||
const persisted = await api.setPersistentSettings({
|
const persisted = await api.setPersistentSettings({
|
||||||
settingsSchemaVersion: 3,
|
settingsSchemaVersion: 4,
|
||||||
settingsState: {
|
settingsState: {
|
||||||
activeFlowId: 'kiro',
|
activeFlowId: 'kiro',
|
||||||
services: {
|
services: {
|
||||||
|
account: { customPassword: '' },
|
||||||
email: { provider: '163' },
|
email: { provider: '163' },
|
||||||
proxy: { enabled: false, provider: '711proxy', mode: 'account' },
|
proxy: { enabled: false, provider: '711proxy', mode: 'account' },
|
||||||
},
|
},
|
||||||
flows: {
|
flows: {
|
||||||
openai: {
|
openai: {
|
||||||
source: { selected: 'cpa', entries: {} },
|
integrationTargetId: 'cpa',
|
||||||
account: { customPassword: '' },
|
integrationTargets: {
|
||||||
|
cpa: {
|
||||||
|
vpsUrl: '',
|
||||||
|
vpsPassword: '',
|
||||||
|
localCpaStep9Mode: 'submit',
|
||||||
|
},
|
||||||
|
sub2api: {
|
||||||
|
sub2apiUrl: '',
|
||||||
|
sub2apiEmail: '',
|
||||||
|
sub2apiPassword: '',
|
||||||
|
sub2apiGroupName: 'codex',
|
||||||
|
sub2apiGroupNames: ['codex', 'openai-plus'],
|
||||||
|
sub2apiAccountPriority: 1,
|
||||||
|
sub2apiDefaultProxyName: '',
|
||||||
|
},
|
||||||
|
codex2api: {
|
||||||
|
codex2apiUrl: '',
|
||||||
|
codex2apiAdminKey: '',
|
||||||
|
},
|
||||||
|
},
|
||||||
signup: {
|
signup: {
|
||||||
signupMethod: 'email',
|
signupMethod: 'email',
|
||||||
phoneVerificationEnabled: false,
|
phoneVerificationEnabled: false,
|
||||||
@@ -367,17 +466,13 @@ function getPersistedWrites() {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
kiro: {
|
kiro: {
|
||||||
source: {
|
integrationTargetId: 'kiro-rs',
|
||||||
selected: 'kiro-rs',
|
integrationTargets: {
|
||||||
entries: {
|
'kiro-rs': {
|
||||||
'kiro-rs': {
|
baseUrl: 'https://kiro.example.com/admin',
|
||||||
kiroRsUrl: 'https://kiro.example.com/admin',
|
apiKey: 'nested-only-key',
|
||||||
kiroRsKey: 'nested-only-key',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
options: {
|
|
||||||
},
|
|
||||||
autoRun: {
|
autoRun: {
|
||||||
stepExecutionRange: { enabled: true, fromStep: 1, toStep: 7 },
|
stepExecutionRange: { enabled: true, fromStep: 1, toStep: 7 },
|
||||||
},
|
},
|
||||||
@@ -392,11 +487,14 @@ function getPersistedWrites() {
|
|||||||
assert.equal(persisted.kiroRsUrl, 'https://kiro.example.com/admin');
|
assert.equal(persisted.kiroRsUrl, 'https://kiro.example.com/admin');
|
||||||
assert.equal(persisted.kiroRsKey, 'nested-only-key');
|
assert.equal(persisted.kiroRsKey, 'nested-only-key');
|
||||||
assert.equal(Object.prototype.hasOwnProperty.call(persisted, 'kiroRegion'), false);
|
assert.equal(Object.prototype.hasOwnProperty.call(persisted, 'kiroRegion'), false);
|
||||||
assert.equal(persisted.settingsSchemaVersion, 3);
|
assert.equal(persisted.settingsSchemaVersion, 4);
|
||||||
assert.equal(write.activeFlowId, 'kiro');
|
assert.equal(Object.prototype.hasOwnProperty.call(write, 'activeFlowId'), false);
|
||||||
assert.equal(write.kiroRsUrl, 'https://kiro.example.com/admin');
|
assert.equal(Object.prototype.hasOwnProperty.call(write, 'kiroRsUrl'), false);
|
||||||
assert.equal(write.kiroRsKey, 'nested-only-key');
|
assert.equal(Object.prototype.hasOwnProperty.call(write, 'kiroRsKey'), false);
|
||||||
assert.equal(Object.prototype.hasOwnProperty.call(write, 'kiroRegion'), false);
|
assert.equal(Object.prototype.hasOwnProperty.call(write, 'kiroRegion'), false);
|
||||||
assert.equal(write.settingsSchemaVersion, 3);
|
assert.equal(write.settingsSchemaVersion, 4);
|
||||||
assert.equal(write.settingsState.activeFlowId, 'kiro');
|
assert.equal(write.settingsState.activeFlowId, 'kiro');
|
||||||
|
assert.equal(write.settingsState.flows.kiro.integrationTargetId, 'kiro-rs');
|
||||||
|
assert.ok(api.getRemovedKeys().includes('panelMode'));
|
||||||
|
assert.ok(api.getRemovedKeys().includes('kiroRsUrl'));
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -8,9 +8,8 @@ test('background node registry preserves node metadata even before an executor i
|
|||||||
const registry = api.createNodeRegistry([
|
const registry = api.createNodeRegistry([
|
||||||
{
|
{
|
||||||
flowId: 'kiro',
|
flowId: 'kiro',
|
||||||
legacyStepId: 1,
|
|
||||||
nodeId: 'kiro-start-device-login',
|
nodeId: 'kiro-start-device-login',
|
||||||
displayOrder: 10,
|
displayOrder: 1,
|
||||||
executeKey: 'kiro-start-device-login',
|
executeKey: 'kiro-start-device-login',
|
||||||
title: 'Start device login',
|
title: 'Start device login',
|
||||||
},
|
},
|
||||||
@@ -19,7 +18,7 @@ test('background node registry preserves node metadata even before an executor i
|
|||||||
const node = registry.getNodeDefinition('kiro-start-device-login');
|
const node = registry.getNodeDefinition('kiro-start-device-login');
|
||||||
|
|
||||||
assert.equal(node.flowId, 'kiro');
|
assert.equal(node.flowId, 'kiro');
|
||||||
assert.equal(node.legacyStepId, 1);
|
assert.equal(node.displayOrder, 1);
|
||||||
assert.equal(node.title, 'Start device login');
|
assert.equal(node.title, 'Start device login');
|
||||||
assert.throws(
|
assert.throws(
|
||||||
() => registry.executeNode('kiro-start-device-login', {}),
|
() => registry.executeNode('kiro-start-device-login', {}),
|
||||||
@@ -34,9 +33,8 @@ test('background node registry executes registered nodes in display order', asyn
|
|||||||
const registry = api.createNodeRegistry([
|
const registry = api.createNodeRegistry([
|
||||||
{
|
{
|
||||||
flowId: 'openai',
|
flowId: 'openai',
|
||||||
legacyStepId: 2,
|
displayOrder: 2,
|
||||||
nodeId: 'submit-signup-email',
|
nodeId: 'submit-signup-email',
|
||||||
displayOrder: 20,
|
|
||||||
executeKey: 'submit-signup-email',
|
executeKey: 'submit-signup-email',
|
||||||
title: 'Submit signup email',
|
title: 'Submit signup email',
|
||||||
execute: async (state) => {
|
execute: async (state) => {
|
||||||
@@ -45,9 +43,8 @@ test('background node registry executes registered nodes in display order', asyn
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
flowId: 'openai',
|
flowId: 'openai',
|
||||||
legacyStepId: 1,
|
displayOrder: 1,
|
||||||
nodeId: 'open-chatgpt',
|
nodeId: 'open-chatgpt',
|
||||||
displayOrder: 10,
|
|
||||||
executeKey: 'open-chatgpt',
|
executeKey: 'open-chatgpt',
|
||||||
title: 'Open ChatGPT',
|
title: 'Open ChatGPT',
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ test('flow capability registry keeps OpenAI phone signup available only when run
|
|||||||
const enabledState = registry.resolveSidepanelCapabilities({
|
const enabledState = registry.resolveSidepanelCapabilities({
|
||||||
state: {
|
state: {
|
||||||
activeFlowId: 'openai',
|
activeFlowId: 'openai',
|
||||||
panelMode: 'cpa',
|
openaiIntegrationTargetId: 'cpa',
|
||||||
phoneVerificationEnabled: true,
|
phoneVerificationEnabled: true,
|
||||||
plusModeEnabled: false,
|
plusModeEnabled: false,
|
||||||
contributionMode: false,
|
contributionMode: false,
|
||||||
@@ -37,7 +37,7 @@ test('flow capability registry keeps OpenAI phone signup available only when run
|
|||||||
const plusLockedState = registry.resolveSidepanelCapabilities({
|
const plusLockedState = registry.resolveSidepanelCapabilities({
|
||||||
state: {
|
state: {
|
||||||
activeFlowId: 'openai',
|
activeFlowId: 'openai',
|
||||||
panelMode: 'sub2api',
|
openaiIntegrationTargetId: 'sub2api',
|
||||||
phoneVerificationEnabled: true,
|
phoneVerificationEnabled: true,
|
||||||
plusModeEnabled: true,
|
plusModeEnabled: true,
|
||||||
contributionMode: false,
|
contributionMode: false,
|
||||||
@@ -58,7 +58,7 @@ test('flow capability registry defaults unknown flows to minimal non-phone capab
|
|||||||
const capabilityState = registry.resolveSidepanelCapabilities({
|
const capabilityState = registry.resolveSidepanelCapabilities({
|
||||||
state: {
|
state: {
|
||||||
activeFlowId: 'site-a',
|
activeFlowId: 'site-a',
|
||||||
panelMode: 'codex2api',
|
openaiIntegrationTargetId: 'codex2api',
|
||||||
phoneVerificationEnabled: true,
|
phoneVerificationEnabled: true,
|
||||||
plusModeEnabled: true,
|
plusModeEnabled: true,
|
||||||
contributionMode: true,
|
contributionMode: true,
|
||||||
@@ -73,7 +73,7 @@ test('flow capability registry defaults unknown flows to minimal non-phone capab
|
|||||||
assert.equal(capabilityState.canUsePhoneSignup, false);
|
assert.equal(capabilityState.canUsePhoneSignup, false);
|
||||||
assert.equal(capabilityState.effectiveSignupMethod, 'email');
|
assert.equal(capabilityState.effectiveSignupMethod, 'email');
|
||||||
assert.equal(capabilityState.panelMode, 'codex2api');
|
assert.equal(capabilityState.panelMode, 'codex2api');
|
||||||
assert.deepEqual(capabilityState.supportedPanelModes, []);
|
assert.deepEqual(capabilityState.supportedIntegrationTargets, []);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('flow capability registry exposes Kiro as an independent flow with its own visible groups', () => {
|
test('flow capability registry exposes Kiro as an independent flow with its own visible groups', () => {
|
||||||
@@ -83,8 +83,8 @@ test('flow capability registry exposes Kiro as an independent flow with its own
|
|||||||
const capabilityState = registry.resolveSidepanelCapabilities({
|
const capabilityState = registry.resolveSidepanelCapabilities({
|
||||||
state: {
|
state: {
|
||||||
activeFlowId: 'kiro',
|
activeFlowId: 'kiro',
|
||||||
kiroSourceId: 'kiro-rs',
|
kiroIntegrationTargetId: 'kiro-rs',
|
||||||
panelMode: 'sub2api',
|
openaiIntegrationTargetId: 'sub2api',
|
||||||
signupMethod: 'phone',
|
signupMethod: 'phone',
|
||||||
plusModeEnabled: true,
|
plusModeEnabled: true,
|
||||||
phoneVerificationEnabled: true,
|
phoneVerificationEnabled: true,
|
||||||
@@ -95,21 +95,21 @@ test('flow capability registry exposes Kiro as an independent flow with its own
|
|||||||
assert.equal(capabilityState.canShowPhoneSettings, false);
|
assert.equal(capabilityState.canShowPhoneSettings, false);
|
||||||
assert.equal(capabilityState.canShowPlusSettings, false);
|
assert.equal(capabilityState.canShowPlusSettings, false);
|
||||||
assert.equal(capabilityState.effectiveSignupMethod, 'email');
|
assert.equal(capabilityState.effectiveSignupMethod, 'email');
|
||||||
assert.equal(capabilityState.effectiveSourceId, 'kiro-rs');
|
assert.equal(capabilityState.effectiveIntegrationTargetId, 'kiro-rs');
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
capabilityState.visibleGroupIds,
|
capabilityState.visibleGroupIds,
|
||||||
['kiro-runtime-status', 'kiro-source-kiro-rs', 'service-account', 'service-email', 'service-proxy']
|
['kiro-runtime-status', 'kiro-target-kiro-rs', 'service-account', 'service-email', 'service-proxy']
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('flow capability registry exposes shared auto-run validation for phone locks and panel support', () => {
|
test('flow capability registry exposes shared auto-run validation for phone locks and target support', () => {
|
||||||
const api = loadApi();
|
const api = loadApi();
|
||||||
const registry = api.createFlowCapabilityRegistry({
|
const registry = api.createFlowCapabilityRegistry({
|
||||||
flowCapabilities: {
|
flowCapabilities: {
|
||||||
openai: api.FLOW_CAPABILITIES.openai,
|
openai: api.FLOW_CAPABILITIES.openai,
|
||||||
'site-a': {
|
'site-a': {
|
||||||
...api.DEFAULT_FLOW_CAPABILITIES,
|
...api.DEFAULT_FLOW_CAPABILITIES,
|
||||||
supportsPlatformBinding: ['cpa'],
|
supportedIntegrationTargets: ['cpa'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -117,7 +117,7 @@ test('flow capability registry exposes shared auto-run validation for phone lock
|
|||||||
const plusLockedResult = registry.validateAutoRunStart({
|
const plusLockedResult = registry.validateAutoRunStart({
|
||||||
state: {
|
state: {
|
||||||
activeFlowId: 'openai',
|
activeFlowId: 'openai',
|
||||||
panelMode: 'cpa',
|
openaiIntegrationTargetId: 'cpa',
|
||||||
signupMethod: 'phone',
|
signupMethod: 'phone',
|
||||||
phoneVerificationEnabled: true,
|
phoneVerificationEnabled: true,
|
||||||
plusModeEnabled: true,
|
plusModeEnabled: true,
|
||||||
@@ -131,7 +131,7 @@ test('flow capability registry exposes shared auto-run validation for phone lock
|
|||||||
const unsupportedPanelResult = registry.validateAutoRunStart({
|
const unsupportedPanelResult = registry.validateAutoRunStart({
|
||||||
state: {
|
state: {
|
||||||
activeFlowId: 'site-a',
|
activeFlowId: 'site-a',
|
||||||
panelMode: 'sub2api',
|
openaiIntegrationTargetId: 'sub2api',
|
||||||
signupMethod: 'email',
|
signupMethod: 'email',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -147,7 +147,7 @@ test('flow capability registry normalizes unsupported mode switches back to the
|
|||||||
openai: api.FLOW_CAPABILITIES.openai,
|
openai: api.FLOW_CAPABILITIES.openai,
|
||||||
'site-a': {
|
'site-a': {
|
||||||
...api.DEFAULT_FLOW_CAPABILITIES,
|
...api.DEFAULT_FLOW_CAPABILITIES,
|
||||||
supportsPlatformBinding: ['cpa'],
|
supportedIntegrationTargets: ['cpa'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -155,14 +155,14 @@ test('flow capability registry normalizes unsupported mode switches back to the
|
|||||||
const validation = registry.validateModeSwitch({
|
const validation = registry.validateModeSwitch({
|
||||||
state: {
|
state: {
|
||||||
activeFlowId: 'site-a',
|
activeFlowId: 'site-a',
|
||||||
panelMode: 'sub2api',
|
openaiIntegrationTargetId: 'sub2api',
|
||||||
signupMethod: 'phone',
|
signupMethod: 'phone',
|
||||||
phoneVerificationEnabled: true,
|
phoneVerificationEnabled: true,
|
||||||
plusModeEnabled: true,
|
plusModeEnabled: true,
|
||||||
contributionMode: true,
|
contributionMode: true,
|
||||||
},
|
},
|
||||||
changedKeys: [
|
changedKeys: [
|
||||||
'panelMode',
|
'openaiIntegrationTargetId',
|
||||||
'signupMethod',
|
'signupMethod',
|
||||||
'phoneVerificationEnabled',
|
'phoneVerificationEnabled',
|
||||||
'plusModeEnabled',
|
'plusModeEnabled',
|
||||||
@@ -173,6 +173,9 @@ test('flow capability registry normalizes unsupported mode switches back to the
|
|||||||
assert.equal(validation.ok, false);
|
assert.equal(validation.ok, false);
|
||||||
assert.deepEqual(validation.normalizedUpdates, {
|
assert.deepEqual(validation.normalizedUpdates, {
|
||||||
panelMode: 'cpa',
|
panelMode: 'cpa',
|
||||||
|
openaiIntegrationTargetId: 'cpa',
|
||||||
|
kiroIntegrationTargetId: 'cpa',
|
||||||
|
kiroSourceId: 'cpa',
|
||||||
signupMethod: 'email',
|
signupMethod: 'email',
|
||||||
phoneVerificationEnabled: false,
|
phoneVerificationEnabled: false,
|
||||||
plusModeEnabled: false,
|
plusModeEnabled: false,
|
||||||
|
|||||||
@@ -13,37 +13,31 @@ function loadApis() {
|
|||||||
};`)(scope);
|
};`)(scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
test('flow registry exposes openai and kiro with canonical source metadata', () => {
|
test('flow registry exposes canonical flow and integration target metadata', () => {
|
||||||
const { flowRegistry } = loadApis();
|
const { flowRegistry } = loadApis();
|
||||||
|
|
||||||
assert.deepEqual(flowRegistry.getRegisteredFlowIds(), ['openai', 'kiro']);
|
assert.deepEqual(flowRegistry.getRegisteredFlowIds(), ['openai', 'kiro']);
|
||||||
assert.equal(flowRegistry.getFlowLabel('codex'), 'Codex / OpenAI');
|
assert.equal(flowRegistry.normalizeFlowId('kiro'), 'kiro');
|
||||||
assert.equal(flowRegistry.normalizeFlowId('codex'), 'openai');
|
assert.equal(flowRegistry.normalizeFlowId('unknown'), 'openai');
|
||||||
assert.equal(flowRegistry.normalizeSourceId('openai', 'sub2api'), 'sub2api');
|
assert.equal(flowRegistry.getFlowLabel('openai'), 'Codex / OpenAI');
|
||||||
assert.equal(flowRegistry.normalizeSourceId('kiro', 'anything-else'), 'kiro-rs');
|
assert.equal(flowRegistry.normalizeIntegrationTargetId('openai', 'sub2api'), 'sub2api');
|
||||||
|
assert.equal(flowRegistry.normalizeIntegrationTargetId('kiro', 'anything-else'), 'kiro-rs');
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
flowRegistry.getVisibleGroupIds('openai', 'cpa'),
|
flowRegistry.getVisibleGroupIds('openai', 'cpa'),
|
||||||
['openai-plus', 'openai-phone', 'openai-oauth', 'openai-step6', 'openai-source-cpa', 'service-account', 'service-email', 'service-proxy']
|
['openai-plus', 'openai-phone', 'openai-oauth', 'openai-step6', 'openai-target-cpa', 'service-account', 'service-email', 'service-proxy']
|
||||||
);
|
);
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
flowRegistry.getVisibleGroupIds('kiro', 'kiro-rs'),
|
flowRegistry.getVisibleGroupIds('kiro', 'kiro-rs'),
|
||||||
['kiro-runtime-status', 'kiro-source-kiro-rs', 'service-account', 'service-email', 'service-proxy']
|
['kiro-runtime-status', 'kiro-target-kiro-rs', 'service-account', 'service-email', 'service-proxy']
|
||||||
);
|
);
|
||||||
assert.deepEqual(
|
assert.deepEqual(
|
||||||
flowRegistry.getSettingsGroupDefinition('openai-plus')?.rowIds || [],
|
flowRegistry.getIntegrationTargetOptions('openai').map((entry) => entry.id),
|
||||||
['row-plus-mode']
|
['cpa', 'sub2api', 'codex2api']
|
||||||
);
|
|
||||||
assert.deepEqual(
|
|
||||||
flowRegistry.getSettingsGroupDefinition('openai-phone')?.rowIds || [],
|
|
||||||
[]
|
|
||||||
);
|
|
||||||
assert.deepEqual(
|
|
||||||
flowRegistry.getSettingsGroupDefinition('openai-step6')?.rowIds || [],
|
|
||||||
['row-step6-cookie-settings']
|
|
||||||
);
|
);
|
||||||
|
assert.equal(flowRegistry.getPublicationTargetDefinition('kiro', 'kiro-rs')?.label, 'kiro.rs');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('settings schema normalizes flat input into canonical flow and service namespaces', () => {
|
test('settings schema normalizes view input into canonical nested namespaces', () => {
|
||||||
const { settingsSchema } = loadApis();
|
const { settingsSchema } = loadApis();
|
||||||
const schema = settingsSchema.createSettingsSchema();
|
const schema = settingsSchema.createSettingsSchema();
|
||||||
|
|
||||||
@@ -66,10 +60,10 @@ test('settings schema normalizes flat input into canonical flow and service name
|
|||||||
assert.equal(normalized.services.email.provider, 'hotmail');
|
assert.equal(normalized.services.email.provider, 'hotmail');
|
||||||
assert.equal(normalized.services.proxy.enabled, true);
|
assert.equal(normalized.services.proxy.enabled, true);
|
||||||
assert.equal(normalized.services.account.customPassword, 'SharedSecret123!');
|
assert.equal(normalized.services.account.customPassword, 'SharedSecret123!');
|
||||||
assert.equal(normalized.flows.openai.source.selected, 'sub2api');
|
assert.equal(normalized.flows.openai.integrationTargetId, 'sub2api');
|
||||||
assert.equal(normalized.flows.kiro.source.selected, 'kiro-rs');
|
assert.equal(normalized.flows.kiro.integrationTargetId, 'kiro-rs');
|
||||||
assert.equal(normalized.flows.kiro.source.entries['kiro-rs'].kiroRsUrl, 'https://kiro.example.com/admin');
|
assert.equal(normalized.flows.kiro.integrationTargets['kiro-rs'].baseUrl, 'https://kiro.example.com/admin');
|
||||||
assert.equal(normalized.flows.kiro.source.entries['kiro-rs'].kiroRsKey, 'secret-key');
|
assert.equal(normalized.flows.kiro.integrationTargets['kiro-rs'].apiKey, 'secret-key');
|
||||||
assert.deepEqual(normalized.flows.kiro.autoRun.stepExecutionRange, {
|
assert.deepEqual(normalized.flows.kiro.autoRun.stepExecutionRange, {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
fromStep: 1,
|
fromStep: 1,
|
||||||
@@ -77,22 +71,24 @@ test('settings schema normalizes flat input into canonical flow and service name
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test('settings schema can project canonical state back to legacy payload without losing flow selection', () => {
|
test('settings schema can project canonical state into a read view without legacy rebuild helpers', () => {
|
||||||
const { settingsSchema } = loadApis();
|
const { settingsSchema } = loadApis();
|
||||||
const schema = settingsSchema.createSettingsSchema();
|
const schema = settingsSchema.createSettingsSchema();
|
||||||
const payload = schema.buildLegacySettingsPayload(schema.normalizeSettingsState({
|
const normalized = schema.normalizeSettingsState({
|
||||||
activeFlowId: 'kiro',
|
activeFlowId: 'kiro',
|
||||||
kiroSourceId: 'kiro-rs',
|
kiroIntegrationTargetId: 'kiro-rs',
|
||||||
kiroRsUrl: 'https://kiro.example.com/admin',
|
kiroRsUrl: 'https://kiro.example.com/admin',
|
||||||
kiroRsKey: 'key-123',
|
kiroRsKey: 'key-123',
|
||||||
}));
|
});
|
||||||
|
const view = schema.buildSettingsView(normalized);
|
||||||
|
|
||||||
assert.equal(payload.activeFlowId, 'kiro');
|
assert.equal(view.activeFlowId, 'kiro');
|
||||||
assert.equal(payload.panelMode, 'cpa');
|
assert.equal(view.openaiIntegrationTargetId, 'cpa');
|
||||||
assert.equal(payload.kiroSourceId, 'kiro-rs');
|
assert.equal(view.kiroIntegrationTargetId, 'kiro-rs');
|
||||||
assert.equal(payload.kiroRsUrl, 'https://kiro.example.com/admin');
|
assert.equal(view.panelMode, 'cpa');
|
||||||
assert.equal(payload.kiroRsKey, 'key-123');
|
assert.equal(view.kiroSourceId, 'kiro-rs');
|
||||||
assert.equal(Object.prototype.hasOwnProperty.call(payload, 'kiroRegion'), false);
|
assert.equal(view.kiroRsUrl, 'https://kiro.example.com/admin');
|
||||||
assert.equal(payload.settingsSchemaVersion, 3);
|
assert.equal(view.kiroRsKey, 'key-123');
|
||||||
assert.equal(payload.settingsState.activeFlowId, 'kiro');
|
assert.equal(view.settingsSchemaVersion, 4);
|
||||||
|
assert.equal(view.settingsState.activeFlowId, 'kiro');
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user